143 lines
4.5 KiB
Markdown
143 lines
4.5 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, 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
|
|
|
|
```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, 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
|
|
|
|
```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` |
|