--- title: "Object Types" description: "Heap object header format and types" --- ## Object Header Every heap-allocated object begins with a 64-bit header word (`objhdr_t`): ``` [capacity: 56 bits][flags: 5 bits][type: 3 bits] ``` ### Type Field (bits 0-2) | Value | Type | Description | |-------|------|-------------| | 0 | `OBJ_ARRAY` | Dynamic array of JSValues | | 1 | `OBJ_BLOB` | Binary data (bits) | | 2 | `OBJ_TEXT` | Unicode text string | | 3 | `OBJ_RECORD` | Key-value object with prototype chain | | 4 | `OBJ_FUNCTION` | Function (C, bytecode, register, or mcode) | | 5 | `OBJ_CODE` | Compiled bytecode | | 6 | `OBJ_FRAME` | Stack frame for closures | | 7 | `OBJ_FORWARD` | Forwarding pointer (GC) | ### Flags (bits 3-7) - **Bit 3 (S)** — Stone flag. If set, the object is immutable and excluded from GC. - **Bit 4 (P)** — Properties flag. - **Bit 5 (A)** — Array flag. - **Bit 7 (R)** — Reserved. ### Capacity (bits 8-63) The interpretation of the 56-bit capacity field depends on the object type. ## Array ```c struct JSArray { objhdr_t header; // type=0, capacity=element slots word_t len; // current number of elements JSValue values[]; // inline flexible array }; ``` Capacity is the number of JSValue slots allocated. Length is the number currently in use. Arrays grow by reallocating with a larger capacity. ## Blob ```c struct JSBlob { objhdr_t header; // type=1, capacity=allocated bits word_t length; // length in bits uint8_t bits[]; // bit-packed data }; ``` Blobs are bit-addressable. The length field tracks the exact number of bits written. A blob starts as antestone (mutable) for writing, then becomes stone (immutable) for reading. ## Text ```c struct JSText { objhdr_t header; // type=2, capacity=character slots word_t length; // length in codepoints (or hash if stoned) word_t packed[]; // two UTF-32 chars per 64-bit word }; ``` Text is stored as UTF-32, with two 32-bit codepoints packed per 64-bit word. When a text object is stoned, the length field is repurposed to cache the hash value (computed via `fash64`), since stoned text is immutable and the hash never changes. ## Record ```c struct JSRecord { objhdr_t header; // type=3, capacity=hash table slots JSRecord *proto; // prototype chain pointer word_t len; // number of entries slot slots[]; // key-value pairs (hash table) }; ``` Records use a hash table with linear probing. Slot 0 is reserved for internal metadata (class ID and record ID). Empty slots use `JS_NULL` as the key; deleted slots use `JS_EXCEPTION` as a tombstone. The prototype chain is a linked list of JSRecord pointers, traversed during property lookup. ## Function ```c struct JSFunction { objhdr_t header; // type=4 JSValue name; // function name int16_t length; // arity (-1 for variadic) uint8_t kind; // C, bytecode, register, or mcode union { struct { ... } cfunc; // C function pointer struct { ... } bytecode; // bytecode + frame struct { ... } regvm; // register VM code struct { ... } mcode; // mcode IR } u; }; ``` The kind field selects which union variant is active. Functions can be implemented in C (native), bytecode (stack VM), register code (mach VM), or mcode (JSON interpreter). ## Frame ```c struct JSFrame { objhdr_t header; // type=6, capacity=slot count JSValue function; // owning function JSValue caller; // parent frame uint32_t return_pc; // return address JSValue slots[]; // [this][args][captured][locals][temps] }; ``` Frames capture the execution context for closures. The slots array contains the function's `this` binding, arguments, captured upvalues, local variables, and temporaries. Frames are linked via the caller field for upvalue resolution across closure depth. ## Forwarding Pointer ``` [pointer: 61 bits][111] ``` During garbage collection, when an object is copied to the new heap, the old header is replaced with a forwarding pointer to the new location. This is type 7 (`OBJ_FORWARD`) and stores the new address in bits 3-63. See [Garbage Collection](#gc) for details. ## Object Sizing All objects are aligned to 8 bytes. The total size in bytes for each type: | Type | Size | |------|------| | Array | `8 + 8 + capacity * 8` | | Blob | `8 + 8 + ceil(capacity / 8)` | | Text | `8 + 8 + ceil(capacity / 2) * 8` | | Record | `8 + 8 + 8 + (capacity + 1) * 16` | | Function | `sizeof(JSFunction)` (fixed) | | Code | `sizeof(JSFunctionBytecode)` (fixed) | | Frame | `8 + 8 + 8 + 4 + capacity * 8` |