Portable program model specification
Since: 0.1.0
This document defines the language-neutral ownership and data contract of
GameEventScriptProgram. Binary format defines the normative byte encoding;
this document defines which information may exist in the parsed in-memory model.
Role
A program is an immutable parsed representation of one .gesb file. It contains
transportable compiler output, not an executable host binding. The same program
may be retained, read concurrently, and loaded into multiple independent hosts.
Every program value must have a lossless language-neutral representation in
.gesb. A port may choose different classes, structs, arrays, slices, or other
storage strategies, but these implementation choices must not add observable
program state.
Permitted data
The complete program graph consists only of:
- the
.gesbformat version and program metadata; - fixed-width integer fields and explicitly numbered enum-like values;
- UTF-8 string-pool bytes and immutable slice metadata;
- immutable
u16index lists; - immutable binding records;
- immutable instruction words with the logical fields defined by Binary format;
- optional immutable debug symbols, source-map records, source archive bytes, and build metadata;
- optional opaque-section metadata plus its retained raw bytes.
Strings in the C# model are immutable decoded Unicode values. Source archive and opaque payloads remain immutable bytes. Source-map hashes are the specified SHA-256 bytes of logical UTF-8 source content; runtime or platform hash codes are never program data.
Forbidden data
A program must never contain or retain:
- host, context, instance, subscription, queue, observer, or publish-sink state;
- VM state, active execution frames, registers, random state, budgets, or runtime caches;
- extension registries, external-type runtime registries, executable callbacks, delegates, function pointers, reflection objects, or arbitrary platform objects;
- AST nodes, parser tokens, compiler passes, mutable builders, or other compiler working state;
- platform-dependent object identities, addresses, native handles, default hash codes, timestamps, hostnames, usernames, or absolute build paths.
Declarative extension and external-type references are allowed only through the
portable names, signatures, IDs, and table entries encoded in .gesb. Their
executable implementations belong to the host-specific linked program created by
Host.Load.
Ownership and immutability
Program construction takes a snapshot of all supplied sequences. The program and every nested segment or entry own immutable copies of their table and payload data. No mutable array or collection owned by a program may be exposed to a caller or another Core component.
Entries may share storage that is already immutable. Repeated references to one encoded index list must not require a separate copy of its elements for every binding. This sharing does not expose mutable aliases or change program data.
The C# implementation enforces this through GameEventScriptReadOnlyArray<T>:
- construction copies the supplied sequence, or reuses the immutable storage of
another
GameEventScriptReadOnlyArray<T>; - public access is read-only and indexed;
- internal bulk reads use
ReadOnlySpan<T>and never expose the backing array; - nested reference entries are themselves immutable and defensively copy their byte and index payloads;
GameEventScriptBytecodeInstructionis returned by value, so its mutable C# struct representation cannot mutate the stored instruction table.
After a program has been returned by the compiler or reader, no operation may change its observable data. Decoded strings, resolved imports, ID-indexed tables, scalar-count tables, and other accelerators are linked-program data and must not be cached back into the program.
Concurrent read access to a program is permitted. This does not make a host or a VM thread-safe; their synchronization contracts are separate.
Construction and validation boundaries
The portable public API has exactly two program-producing operations:
- the compiler materializes a program from validated source and runs the shared complete program validator before returning it;
- the reader performs bounded structural decoding and runs the same complete program validator before returning the decoded program.
There is no public free-form GameEventScriptProgram constructor or public
factory that combines arbitrary segments. Public segment constructors are data
helpers only and cannot create a program. Internal compiler builders and
test-only friend access are implementation details and do not extend the
portable construction contract.
Validation is deliberately repeated at output and execution trust boundaries:
- the writer validates before calculating or writing the canonical encoding;
Host.Loadvalidates before checking host resource limits, resolving any executable binding, allocating VM capacity, registering handlers, or queuing initialization.
This repetition is intentional. A writer and a host must not rely on the claimed origin of a program, on a prior validation performed by another component, or on language-specific visibility rules. Validation failure leaves the destination buffer and host state unchanged.
Representation independence
The .gesb payload fields define the portable meaning. C# struct layout, CPU
endianness, padding, object layout, and overlapping field aliases are
implementation details only. Other language ports need not reproduce them in
memory.
In particular, the C# instruction stores three numeric u16 words and one
numeric u64 payload. Semantic aliases use casts, shifts, masks, and exact
Binary64 bit conversion rather than overlapping CLR fields. Its sequential
16-byte size is a compact runtime optimization only. Serialization and
deserialization read and write the individual V1 fields in canonical
little-endian order. Raw CLR struct bytes are never a valid shortcut for .gesb
encoding.
All identifiers carried by program bytes have fixed numeric values: section types and flags, binding kinds, debug symbol kinds, opcodes, instruction units and flags, bytecode type kinds, pattern kinds, and series kinds. A source-language enum declaration is only a convenient implementation view of those IDs; ordinal position or declaration order is never an encoding rule.
Preservation and roundtrip meaning
All known retained segments are parsed into the immutable model and are encoded
again from that model. Unknown optional sections retained by PreserveAll keep
their payload bytes and section metadata. PreserveKnown and RuntimeOnly
intentionally discard sections according to their documented retention policy;
discarded data is not part of the resulting program.
For every resulting program, the writer must be able to encode all retained program data. Reading those canonical bytes must reconstruct the same portable meaning. Byte identity is required only where Binary format or a canonical fixture explicitly requires it.
Regression gates
Every implementation must cover the semantic equivalents of these gates:
- mutating any input sequence after segment or program construction cannot alter retained program data;
- the public API cannot freely construct an arbitrary program graph;
compile -> write -> read -> canonical rewrite -> load -> executepreserves both encoded meaning and runtime behavior;- primary word aliases, signed views, four payload words, signed
i64, and exact Binary64 bits have the numeric relationships specified by.gesbregardless of native memory byte order; - canonical fixtures protect encoded bytes and the shared numeric definition protects every serialized enum-like ID.
The C# port additionally snapshots its public API and verifies its compact instruction size. Those two checks are implementation-specific and are not requirements for the in-memory representation used by another language.