4.8 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, register, or mcode) |
| 5 | OBJ_CODE |
Compiled code |
| 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. Stone text in the constant table (ct) is not copied by GC since it lives outside the heap; stone objects on the GC heap are copied normally.
- 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.
A mutable text (pretext) uses capacity for the allocated slot count and length for the current codepoint count. When a pretext is stoned, the capacity field is set to the actual length (codepoint count), and the length field is zeroed for use as a lazy hash cache (computed via fash64 on first use as a key). Since stoned text is immutable, the hash never changes. Stoning is done in-place — no new allocation is needed.
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, register, or mcode
union {
struct { ... } cfunc; // C function pointer
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), 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
JSValue address; // 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 + 8 + capacity * 8 |