4.5 KiB
title, description
| title | description |
|---|---|
| Object Types | 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
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
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
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
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
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
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 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 |