---
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. 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

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

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

```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
  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](#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 + 8 + capacity * 8` |
