Files
cell/docs/spec/objects.md
2026-02-08 08:25:48 -06:00

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