Skip to content

Runtime representation

WebAssembly provides numbers, functions, tables, and linear memory. Gleam values such as strings, bit arrays, lists, tuples, records, closures, and custom types use explicit runtime representations in linear memory.

Scalar values

Scalars stay unboxed inside WebAssembly functions and locals:

Gleam valueWASM value
Inti64
Floatf64
Booli32, where 0 is false and 1 is true
Nilno result value; stored fields use 0
Orderi32, where -1, 0, and 1 mean LT, EQ, and GT

Managed values cross internal and host ABI boundaries as i32 pointers into linear memory. The null pointer is reserved for the empty list.

Object model

Heap objects begin with an 8-byte header:

text
0..4  tag:  i32
4..8  size: i32, length, arity, or field count depending on object kind
8..   payload

Objects are aligned to 8 bytes. Payload fields that can contain arbitrary Gleam values use 8-byte slots. Scalar Int and Float values occupy the whole slot. Pointer, bool, nil, and ordering values use the low 32 bits and leave padding zeroed.

The current runtime uses a bump allocator with a mutable heap pointer and no freeing. Allocation checks the aligned end pointer and grows linear memory when needed. Objects are non-moving: existing managed pointers remain stable across memory.grow and live until the instance is reset. Static objects are emitted as data segments before the dynamic heap.

Tags

TagObject kindHeader word 1Payload
1Stringbyte lengthUTF-8 bytes, padded
2List consarity 2head slot, tail pointer
3Tuplearityfield slots
4Recordfield countfield slots in declaration order
5Custom valuefield countconstructor tag, field slots
6Closurecapture countfunction id, capture pointers
7Bit arraybit lengthpacked bits, most-significant bit first
8Opaque0dependency type tag, host/runtime payload pointer
9Runtime errorfield countreason tag, field slots
10Panic valuefield countreason tag, field slots

Managed value rules

Strings store UTF-8 bytes exactly as written after unescaping. String equality and ordering compare UTF-8 bytes after length checks.

Bit arrays store a bit length and packed payload bytes. Segment operations must preserve bit offsets and reject invalid sizes before memory access.

Lists use cons cells. The empty list is 0; non-empty lists are tag-2 objects. The tail field is either another cons pointer or 0.

Tuples and records use the same field-slot layout. Records keep fields in the constructor declaration order so field access can use a fixed offset.

Custom values represent user-defined variants, plus dependency-defined Result and Option values. Ok, Error, Some, and None are constructor tags in normal tag-5 custom objects. Runtime error objects use tag 9 and are separate from the Result.Error constructor.

Closures store a stable function id followed by captured values in 8-byte slots. Managed captures use pointers and scalar captures use their raw slot representation.

Opaque values are owned by the dependency or host interface that created them. Tag-8 opaque objects use a zero size word, followed by a host-owned type tag and a host/runtime payload pointer or handle id. The backend may pass opaque pointers around and compare identity, but may not inspect payload contents without a dependency-specific helper.

Panic and runtime error objects are materialized when a helper needs to report or render a failure. Direct panic paths may trap without allocating one. Allocation failure writes a reserved tag-10 panic object before trapping. Its reason tag is 1, payload slot 0 is the requested allocation size, and payload slot 1 is the heap pointer before allocation.

ABI and ownership

Internal calls pass scalars as raw WASM values and managed values as pointers. Host ABI support is target-specific. For Wasmtime, managed pointers are borrowed by the host; ownership stays with the guest runtime unless an adapter documents transfer. A borrowed pointer may be read after the exporting call returns and remains stable until the Wasm instance is reset or a future explicit arena reset runs. Hosts must not retain pointers across those reset boundaries.

For JavaScript hosts, opaque object payload word 1 is an adapter-table handle id. The JS adapter owns the referenced JavaScript object and keeps it alive until the adapter releases that id or initializes a new Wasm instance.

Releasing the handle does not free the tag-8 guest object; it only invalidates future JS table lookups for that handle id.

Tests

Runtime tests inspect encoded objects directly.

Wasmtime tests inspect static and dynamic memory for tags, lengths, fields, payload bytes, and alignment.