Files
cell/docs/spec/objects.md

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