heyq02/moonjs/src/value does not have a README file

    Function

    pub struct Function {
    chunk_id : Int
    upvalues : Array[Upvalue]
    name : String
    is_constructor : Bool
    prototype : JSValue
    } derive(
    Debug
    )

    A runtime JS function. Carries four pieces of information:

    • chunk_id: opaque handle into the current Engine's chunk registry. The Engine is responsible for handing out and resolving these ids. Value-package code never dereferences the id itself.
    • upvalues: shared Upvalue cells captured from the enclosing scope, one per UpvalueSlotDecl in the underlying chunk. OP_GET_UPVALUE / OP_SET_UPVALUE read/write through these cells.
    • name: display name used for Function.name, stack traces, and the disassembler. "<anonymous>" for unnamed function expressions.
    • is_constructor: false for arrow functions once M2 lands; always true in M1 (only ordinary functions exist).
    • prototype: the .prototype object exposed via f.prototype and used as the [[Prototype]] for new f(...)'s freshly-allocated receiver. The VM populates this when OP_NEW_CLOSURE runs: a fresh empty Object whose own [[Prototype]] links to Object.prototype. Consulted by OP_INSTANCEOF to walk the receiver's proto chain looking for identity match, and by OP_CONSTRUCT to set the new receiver's proto. (Introduced in M1 Step 8b2.)

    Function deliberately does NOT include a home_object / bound_this slot in M1. Method dispatch relies on the caller supplying this via OP_CALL_METHOD; there is no bound-function form yet.

    Function::chunk_id

    fn Function::chunk_id(self : Function) -> Int

    Accessor for the chunk id. Exposed so vm-package code can look up the backing Chunk in the Engine's registry.

    Function::is_constructor

    fn Function::is_constructor(self : Function) -> Bool

    Whether new f(...) is permitted. Always true in M1 (arrow functions arrive in M2 and are the first non-constructor callable).

    Function::name

    fn Function::name(self : Function) -> String

    The function's display name.

    Function::new

    fn Function::new(chunk_id : Int, upvalues : Array[Upvalue], name : String, is_constructor : Bool) -> Function

    Constructor. All fields except prototype are set at creation; the prototype defaults to Undefined and is populated by the VM when the Function value is produced via OP_NEW_CLOSURE. (Kept mutable so the VM can wire the prototype after the value is constructed, matching the two-step dance used by NativeFunction::set_prototype.)

    Function::prototype

    fn Function::prototype(self : Function) -> JSValue

    The .prototype object attached to this function value. Undefined until the VM populates it via set_prototype (which happens inside OP_NEW_CLOSURE for every user function, so JS-visible reads of f.prototype always land on a real Object). Consulted by OP_INSTANCEOF and OP_CONSTRUCT.

    Function::set_prototype

    fn Function::set_prototype(self : Function, proto : JSValue) -> Unit

    Rewire the .prototype object. Called by OP_NEW_CLOSURE in the VM immediately after the Function value is allocated (the VM knows the engine's Object.prototype and thus can wire the prototype's own [[Prototype]] correctly — the value package does not).

    Function::upvalues

    fn Function::upvalues(self : Function) -> Array[Upvalue]

    Accessor for the captured upvalues array. Callers get the same array reference the Function holds; reads via Upvalue::get observe the current value in the shared cell.

    JSException

    pub struct JSException {
    value : JSValue
    stack : Array[StackFrameInfo]
    } derive(
    Debug
    )

    A JS exception in flight, with the value being thrown plus a captured stack trace ready to be formatted into Error.prototype.stack.

    value is the actual JS value the program throwed — usually an Object(_) holding a properly-constructed Error (with .name, .message, .stack), but JS permits throwing any value so this stays a plain JSValue.

    stack is captured eagerly at throw time. M1 attaches every frame in the caller chain; M6 may optionally lazy-format the string form for cheaper try/catch that never reads .stack.

    JSException::new

    fn JSException::new(value : JSValue, stack : Array[StackFrameInfo]) -> JSException

    Constructor for JSException. Explicit over field-literal syntax so callers (VM throw handling, host layer) do not need to know or lock in the field order — later milestones may add e.g. an is_uncatchable marker.

    JSValue

    pub(all) enum JSValue {
    Undefined
    Null
    Bool(Bool)
    Int32(Int)
    Number(Double)
    Str(String)
    Object(Object)
    Function(Function)
    NativeFn(NativeFunction)
    } derive(
    Debug
    )

    The tagged runtime representation of every JS value produced by the engine.

    Int32 is the "fast path" for small integers that the JS spec still models as Number(Double); the VM promotes to Number on overflow or non-integer results. Bit operations (|, &, <<, >>>, ...) always land back in Int32 per ES ToInt32 / ToUint32. This step (M1 Step 2) only defines the representation — the arithmetic promotion rules live in the VM (Step 8) and are intentionally not implemented here.

    Str uses MoonBit String, which is already UTF-16 code units and matches JS string semantics directly (s[i] gives a UInt16 charcode).

    Object holds a heap-shared ObjectRef. ObjectRef and ShapeRef alias their underlying structs rather than wrapping them in @ref.Ref: MoonBit structs with mutable fields already have reference semantics (mutation through any alias is visible through every alias), so the extra Ref indirection design.md sketched would be pure overhead. This deviation is noted in the design deviations section of the check report.

    Equality is implemented manually rather than derived: primitives use structural equality but Object requires identity (physical_equal) to match JS === semantics for objects. Int32 and Number are treated as distinct variants (never equal to each other) at this layer — the VM will add ES Abstract Equality and Strict Equality conversions in Step 8.

    The Function(Function) variant (added in M1 Step 8b1) is treated like Object for equality: two distinct Function values with identical chunk / upvalues are NOT equal — only same-reference is equal — matching JS === on function values.

    NativeFn(NativeFunction) (added in M1 Step 9) carries a MoonBit-defined callable — Object / Error / TypeError / … constructors and any other builtin function fall into this variant. Same physical-equality rule as Function.
    impl Eq for JSValue

    NativeError

    pub struct NativeError {
    name : String
    message : String
    } derive(
    Debug
    )

    Error payload for a native function's failure path. Native functions do not directly construct JSException values — they return Result[JSValue, NativeError], and the VM converts the error to a proper exception with a real Error object and captured stack trace.

    name is one of "Error" / "TypeError" / "RangeError" / "SyntaxError" / "ReferenceError". The VM uses this to pick the corresponding Error prototype when it wraps the message into a JS Error object.

    NativeError::message

    fn NativeError::message(self : NativeError) -> String

    Accessor for the error message.

    NativeError::name

    fn NativeError::name(self : NativeError) -> String

    Accessor for the error class name.

    NativeError::new

    fn NativeError::new(name : String, message : String) -> NativeError

    Constructor for a native error. Callers pass the error class name and the user-visible message.

    NativeFunction

    pub struct NativeFunction {
    name : String
    is_constructor : Bool
    impl_ : (JSValue, Array[JSValue]) -> Result[JSValue, NativeError]
    prototype : JSValue
    } derive(
    Debug
    )

    A native (MoonBit-defined) JS-callable function. Its runtime shape:

    • name: display name — surfaces via Function.name, typeof, and in error messages ("X is not a function"). "" for anonymous.
    • is_constructor: whether new X(...) is permitted. true for Object / Error / etc. Native functions that must not be constructed (e.g. parseInt in later milestones) set this to false.
    • impl: the MoonBit body. Signature (this_val, args) -> Result[return,error]. this_val is the receiver:
      • For a plain call X(a, b), this_val is Undefined.
      • For a method call obj.X(a, b), this_val is Object(obj).
      • For new X(a, b), this_val is a freshly-created object whose [[Prototype]] is X.prototype (see OP_CONSTRUCT in vm.mbt).
    • prototype: the .prototype object attached to this function value. Written by builtin initialisation via set_prototype, then consulted by OP_CONSTRUCT when instantiating a new receiver. Undefined if never assigned.

    NativeFunction::call

    fn NativeFunction::call(self : NativeFunction, this_val : JSValue, args : Array[JSValue]) -> Result[JSValue, NativeError]

    Invoke the native function's body. The VM does not call this directly; it goes through the higher-level call helper that also handles the Result[JSValue, NativeError] → JSException conversion. This accessor exists so callers that already have both this and args in hand can invoke without duplicating the boilerplate.

    NativeFunction::is_constructor

    fn NativeFunction::is_constructor(self : NativeFunction) -> Bool

    Whether new X(...) is permitted.

    NativeFunction::name

    fn NativeFunction::name(self : NativeFunction) -> String

    Function name. Used for Function.name and stack traces.

    NativeFunction::new

    fn NativeFunction::new(name : String, is_constructor : Bool, impl_ : (JSValue, Array[JSValue]) -> Result[JSValue, NativeError]) -> NativeFunction

    Constructor for a native function. Sets prototype to Undefined; builtins install a real prototype object via set_prototype after both the function value and its prototype have been allocated (this two-step dance is unavoidable because a constructor's prototype often refers back to the constructor via .constructor, so neither can be fully wired until both exist).

    NativeFunction::prototype

    fn NativeFunction::prototype(self : NativeFunction) -> JSValue

    The .prototype object. Undefined until set_prototype runs.

    NativeFunction::set_prototype

    fn NativeFunction::set_prototype(self : NativeFunction, proto : JSValue) -> Unit

    Rewire the .prototype object. Called by builtin initialisation once the constructor and its prototype have both been allocated.

    Object

    #alias(ObjectRef)
    pub struct Object {
    shape : Shape
    slots : Array[JSValue]
    proto : JSValue
    extensible : Bool
    } derive(
    Debug
    )

    A JS object as seen by the runtime.

    • shape: the Shape describing which properties live at which slot. mut because a future M6 optimisation may re-point the object at a shared shape after a transition.
    • slots: values indexed by PropMeta.slot_idx. Kept the same length as shape.keys_ordered by every mutating API.
    • proto: prototype chain link. Null for Object.prototype itself and Object(_) for every other object.
    • extensible: JS [[Extensible]] internal slot. false after Object.preventExtensions. In M1 nothing calls that, so it stays true, but VM opcode set_own already respects it.

    Object::add_property

    fn Object::add_property(obj : Object, key : String, value : JSValue, attrs : Byte) -> Unit

    Append a new data property. Preconditions:

    • key must NOT already be present in obj.shape. Callers verify this with has_own first; violating the precondition aborts because a duplicate insertion would silently corrupt keys_ordered / slots alignment. [[DefineOwnProperty]]'s "update existing" branch lives in set_own.

    M1 caller list: the compiler emitting define_prop, the object literal evaluator in the VM, and internal builtin initialisation.

    Object::get_own

    fn Object::get_own(obj : Object, key : String) -> JSValue?

    Fetch an own property value without walking the prototype chain. Returns None if obj does not own key. Analogous to Object.getOwnPropertyDescriptor(obj, key)?.value for data properties.

    Object::get_property

    fn Object::get_property(obj : Object, key : String) -> JSValue

    JS [[Get]] for data properties: walk the prototype chain and return the first hit, or Undefined if the chain ends without finding key. Accessors (getter/setter) are ignored — they will be handled in M3 when PropMeta.attrs bit 3 becomes meaningful.

    The chain terminates when proto is anything other than Object(_) (typically Null, but any non-object proto is treated as end-of-chain). There is no explicit cycle guard: JS prohibits proto chains that produce cycles, and constructing one requires Object.setPrototypeOf which is not implemented in M1.

    Object::has_own

    fn Object::has_own(obj : Object, key : String) -> Bool

    True iff obj owns key. No prototype walk. Corresponds to the Object.hasOwn(obj, key) builtin (added in ES2022) and to Object.prototype.hasOwnProperty.call(obj, key).

    Object::has_property

    fn Object::has_property(obj : Object, key : String) -> Bool

    JS in operator (key in obj): does obj or any prototype own key?

    Object::new

    fn Object::new(shape : Shape, proto : JSValue) -> Object

    Create a new object with the given shape and prototype. The slots array starts filled with Undefined — one entry per key already present in the shape. In M1 all callers pass a fresh Shape::new(), so this path normally allocates an empty slots and lets add_property grow both arrays in lockstep. Once shape sharing arrives, this constructor will need to seed slots with Undefined values for the pre-existing keys — hence the loop below.

    Object::prevent_extensions

    fn Object::prevent_extensions(obj : Object) -> Unit

    Flip the object to non-extensible (Object.preventExtensions). No new properties can then be added; existing writes still respect their per-property writable bit.

    Object::set_own

    fn Object::set_own(obj : Object, key : String, value : JSValue) -> Bool

    Set an own property. Returns whether the write "succeeded" in the sense that the VM should NOT throw a TypeError:

    • If key is an existing own property:
      • Writable → update the slot; return true.
      • Non-writable → return false (VM throws TypeError in strict mode; silently ignores in sloppy mode — M1 defers that decision to the VM).
    • If key is not an own property:
      • Extensible → append a fresh data property with default attrs; return true.
      • Non-extensible → return false.

    This function does NOT walk the prototype chain; JS [[Set]] with proto walk is more subtle (a non-writable data property on the proto stops the set) and lives in the VM.

    Object::set_proto

    fn Object::set_proto(obj : Object, proto : JSValue) -> Unit

    Replace the object's prototype link. JS Object.setPrototypeOf. Not used by the M1 opcode set (the compiler emits closures whose prototype is resolved by builtin init), but VM builtins in Step 9 need it to wire Function.prototype etc. Also keeps the mut proto field's mutability honest for the type checker.

    Object::set_shape

    fn Object::set_shape(obj : Object, shape : Shape) -> Unit

    Replace the object's shape reference. Used by shape-transition code (M6); in M1 no caller reaches for this, but declaring the setter keeps mutshape in Object's field list without a dead-code warning.

    PropMeta

    pub struct PropMeta {
    slot_idx : Int
    attrs : Byte
    } derive(Eq,
    Debug
    )

    Property attributes packed into a byte. Bit layout:

    bit 3 | bit 2 | bit 1 | bit 0 accessor| configurable| enumerable | writable

    M1 never sets bit 3 — accessor properties (getter/setter) arrive in M3. Reserving the bit now keeps the byte layout stable across milestones so serialised bytecode / snapshot tests written against M1 stay valid.

    PropMeta::data

    fn PropMeta::data(slot_idx : Int) -> PropMeta

    Convenience constructor for a data property with default attrs.

    PropMeta::is_accessor

    fn PropMeta::is_accessor(self : PropMeta) -> Bool

    PropMeta::is_configurable

    fn PropMeta::is_configurable(self : PropMeta) -> Bool

    PropMeta::is_enumerable

    fn PropMeta::is_enumerable(self : PropMeta) -> Bool

    PropMeta::is_writable

    fn PropMeta::is_writable(self : PropMeta) -> Bool

    PropMeta::new

    fn PropMeta::new(slot_idx : Int, attrs : Byte) -> PropMeta

    General-purpose constructor. Callers pass an explicit attrs byte — typically composed by lor-ing the ATTR_* constants — so unit tests and M3's Object.defineProperty implementation can express non-default combinations (e.g. writable-only, or accessor). PropMeta::data is the preferred shortcut for the very common all-true data case.

    Shape

    #alias(ShapeRef)
    pub struct Shape {
    props :
    HashMap
    [String, PropMeta]
    keys_ordered : Array[String]
    } derive(
    Debug
    )

    Structural descriptor of an object's properties, separate from the values themselves. In M1 every object has its own Shape — sharing is deferred to M6 (see file-level notes).

    keys_ordered records insertion order because JS for-in, Object.keys, and JSON.stringify all iterate in insertion order for string keys. M1 has no consumer of this array yet, but recording the order now costs almost nothing and locks in the invariant so later milestones can rely on it without a migration.

    Shape::new

    fn Shape::new() -> Shape

    Create an empty shape. HashMap([]) starts at the stdlib default capacity, which is fine for M1 workloads.

    StackFrameInfo

    pub struct StackFrameInfo {
    chunk_name : String
    filename : String
    loc :
    SourceLoc

    } derive(Eq,
    Debug
    )

    One frame of a captured stack trace. chunk_name is the function name for user functions, "<anonymous>" for anonymous function expressions, "<top>" for the top-level script frame. filename matches whatever the engine received via Engine::eval_script. loc is the source location of the currently-executing instruction (or the call site, depending on which frame in the chain we are looking at) — see design.md §8.1 for the exact convention the VM uses when composing this array.

    StackFrameInfo::new

    fn StackFrameInfo::new(chunk_name : String, filename : String, loc :
    SourceLoc
    ) -> StackFrameInfo

    Constructor for StackFrameInfo. Same field-order-stability rationale as JSException::new.

    Upvalue

    pub struct Upvalue {
    value : JSValue
    } derive(
    Debug
    )

    A shared heap cell used by JS closure semantics. Upvalue is the storage for any variable that might be closed over: the enclosing function's local slot holds an Upvalue, and every closure that captures the variable holds the SAME Upvalue. Reads and writes go through .get() / .set(), giving the JS-visible "modifying x in the closure updates x in the outer function" behaviour.

    M1 simplification: every local slot in a Frame is an Upvalue cell (uniform), regardless of whether the compiler determined the local is actually captured. This costs one small heap alloc per local, but avoids the state machine of "open vs closed" upvalues from Lua-style implementations. M6 can revisit if profiling shows it matters.

    Upvalue::get

    fn Upvalue::get(self : Upvalue) -> JSValue

    Read the current value stored in the cell.

    Upvalue::new

    fn Upvalue::new(v : JSValue) -> Upvalue

    Allocate a fresh upvalue cell initialised to v.

    Upvalue::set

    fn Upvalue::set(self : Upvalue, v : JSValue) -> Unit

    Write a new value into the cell. All aliases of the cell observe the update — this is the whole point of the type.

    ATTR_ACCESSOR

    let ATTR_ACCESSOR : Byte

    ATTR_CONFIGURABLE

    let ATTR_CONFIGURABLE : Byte

    ATTR_DEFAULT_DATA

    let ATTR_DEFAULT_DATA : Byte

    Default "data property" attrs: writable, enumerable, configurable, and NOT an accessor. This matches the defaults used by JS object literal syntax ({a: 1}) and by [[DefineOwnProperty]] when the descriptor omits a field. M1's compiler emits these for every property; M3 gains the ability to pick different attrs at emit time.

    ATTR_ENUMERABLE

    let ATTR_ENUMERABLE : Byte

    ATTR_WRITABLE

    let ATTR_WRITABLE : Byte

    Bit masks for PropMeta.attrs. Public because the compiler and VM both need to construct property meta with specific attribute combinations (e.g. Object.defineProperty in M3 with {writable: false}).