pub struct Bytecode {
pub instructions: Instructions,
pub constants: Vec<Value>,
pub source_map: SourceMap,
pub type_registry: Vec<TypeDef>,
}Expand description
Compiled bytecode output containing instructions and constants.
This represents the complete compiled program ready for execution by the
virtual machine. The Bytecode struct is the interface between the compiler
(which produces it) and the VM (which executes it).
§Architecture
The bytecode uses a two-part structure:
-
Instruction Stream (
instructions): A linear sequence of bytecode instructions that the VM executes. Each instruction consists of an opcode byte optionally followed by operand bytes. -
Constant Pool (
constants): An array of runtime values referenced byOpConstantinstructions. The constant pool allows large or frequently-used values to be stored once and referenced by a compact 2-byte index.
§Constant Pool Design
Constants are stored separately from instructions for several reasons:
-
Space efficiency: A large integer like
999_999_999_999is stored once in the constant pool, not embedded in every instruction that uses it. -
Fixed instruction size:
OpConstantinstructions are always 3 bytes (1 opcode + 2 index bytes), regardless of the size of the referenced value. -
Type flexibility: The constant pool can hold any
Valuetype (integers, booleans, strings, arrays, functions) while the bytecode remains uniform.
§Example
For the expression 1 + 2:
// Constant pool stores the actual values:
let constants = vec![
Value::Integer(Integer::I64(1)), // constants[0]
Value::Integer(Integer::I64(2)), // constants[1]
];
// Instructions reference constants by index:
let mut instructions = Instructions::new();
instructions.extend(&Instructions::from(encode(Opcode::Constant, &[0]))); // Push constants[0]
instructions.extend(&Instructions::from(encode(Opcode::Constant, &[1]))); // Push constants[1]
instructions.extend(&Instructions::from(encode(Opcode::Add, &[]))); // Add them
instructions.extend(&Instructions::from(encode(Opcode::Pop, &[]))); // Pop result
let bytecode = Bytecode { instructions, constants, source_map: Default::default(), type_registry: vec![] };When executed by the VM:
OpConstant 0pushesValue::I64(1)onto the stackOpConstant 1pushesValue::I64(2)onto the stackOpAddpops both values, adds them, and pushesValue::I64(3)OpPopremoves the result from the stack
Fields§
§instructions: InstructionsThe sequence of bytecode instructions to execute.
Each instruction consists of an opcode byte optionally followed by operand bytes. Instructions are executed sequentially by the VM.
constants: Vec<Value>The constant pool containing runtime values.
This array holds all constant values referenced by OpConstant instructions.
The index into this array is encoded as a 2-byte operand (allowing up to
65,535 distinct constants).
source_map: SourceMapMaps instruction byte offsets to source spans for error reporting.
type_registry: Vec<TypeDef>Type definitions for user-defined structs and enums.
Registered at compile time and read by the VM for field access, struct construction, and pattern matching operations.
Implementations§
Source§impl Bytecode
impl Bytecode
Sourcepub fn serialize(&self) -> Result<Vec<u8>, SerializationError>
pub fn serialize(&self) -> Result<Vec<u8>, SerializationError>
Serializes this bytecode to a binary representation.
The output can be written to a .mtc file and later restored with
deserialize. The format consists of an 8-byte
header (magic + version) followed by a postcard-encoded payload.
§Errors
Returns an error if the constant pool contains value types that cannot be represented in
the binary format (e.g., Builtin function pointers or tree-walking Function objects).