Files
cell/docs/spec/objects.md

142 lines
4.4 KiB
Markdown

---
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, 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 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, 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
```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` |