Skip to main content

Chunk

Struct Chunk 

Source
pub struct Chunk {
    pub code: Vec<u8>,
    pub constants: Vec<ConstValue>,
    pub source_lines: Vec<u32>,
    pub local_names: Vec<String>,
}
Expand description

a compiled function (or top-level chunk): instruction bytes + a parallel per-byte source-line map + a constant pool indexed by u16 from the bytes.

the three vectors stay in lockstep; the Chunk::write_op / Chunk::write_u16 / Chunk::write_i16 helpers are the only way to append to code, so the invariant is structurally enforced.

Fields§

§code: Vec<u8>

the instruction byte stream; one byte per opcode, plus per-operand bytes per Opcode::operand_bytes.

§constants: Vec<ConstValue>

the per-function constant pool, indexed by u16 from CONST / FIELD / MAKE_ENUM_VARIANT operands. append-only – a rewound CONST leaves a dangling entry, harmless because disassemble walks instructions, not pool entries. holds ConstValue entries; the VM converts each to a runtime crate::value::Value when it executes the CONST.

§source_lines: Vec<u32>

the 1-based source line of the byte at the same index in code. multi-byte instructions duplicate the line across operand bytes so source_lines[i] is valid for any i in 0..code.len().

§local_names: Vec<String>

the source name of each local slot, indexed by slot number (the GET_LOCAL / SET_LOCAL operand). local_names[i] is the name the programmer wrote for slot i"x", "sum", a for loop variable, a match payload binding. a compiler-synthesized temporary (a hidden for-loop array/index slot) has an empty string; the VM falls back to slot{i} for those. codegen fills this when it finalizes a function’s chunk; the VM’s get_state reads it to surface the in-scope variables with their real names rather than slot indices. a chunk built by hand (a test, a comptime body) leaves it empty – Default gives an empty vec, so older callers are unaffected.

Implementations§

Source§

impl Chunk

Source

pub fn new() -> Self

construct an empty chunk. all three vectors start empty; the lockstep invariant code.len() == source_lines.len() holds trivially.

Source

pub fn write_op(&mut self, op: Opcode, line: u32)

emit a zero-operand opcode. line is the 1-based source line of the originating expression (looked up via LineIndex::location at the call site). pushes one byte to code and one entry to source_lines so the invariant code.len() == source_lines.len() is preserved.

Source

pub fn write_u16(&mut self, v: u16, line: u32)

emit a u16 operand, little-endian. line is the same line as the preceding opcode – the map duplicates the line per byte so source_lines[i] is valid for any i in 0..code.len().

Source

pub fn write_i16(&mut self, v: i16, line: u32)

emit a signed i16 operand for relative jump offsets. uses two’s-complement little-endian, the same byte sequence on every platform Rust supports. delegates to Chunk::write_u16 via a v as u16 cast (the bit pattern is identical).

Source

pub fn read_u16(&self, off: usize) -> u16

read a u16 operand at byte offset off. the inverse of Chunk::write_u16. callers must ensure off + 2 <= code.len().

Source

pub fn read_i16(&self, off: usize) -> i16

read an i16 operand at byte offset off. the inverse of Chunk::write_i16. callers must ensure off + 2 <= code.len().

Source

pub fn add_constant(&mut self, v: ConstValue) -> u16

append a value to the constant pool and return its u16 index. the pool is append-only (no dedupe in v1 per Phase 4 research A6); determinism follows from this. the assumption is that 65536 constants per chunk is more than realistic; debug builds assert the cap, production builds keep the cast for v1. widening to u32 is a v2 concern.

Source

pub fn last_const(&self) -> Option<(usize, u16)>

peek the most recent CONST emission. returns (byte_offset, pool_idx) when the last full instruction in code is a CONST + u16 operand; None otherwise.

the constant folder in codegen uses this before emitting a binary opcode: when both operands are CONSTs the folder can compute the result at compile time.

Source

pub fn second_to_last_const(&self) -> Option<(usize, u16)>

peek the CONST emission BEFORE the most recent CONST. returns (byte_offset, pool_idx) when the chunk ends in two consecutive CONST u16 instructions; None otherwise. used by the binary- expression constant folder to peek both operands before deciding to fold.

Source

pub fn rewind_last_const(&mut self) -> Option<u16>

rewind the most recent CONST: drop its 3 bytes from code and 3 entries from source_lines, returning the pool index. the pool itself is NOT shrunk – the rewound entry remains in Chunk::constants as a harmless orphan. the determinism contract (append-only pool) depends on never mutating prior pool entries.

returns None when the chunk does not end with a CONST instruction (i.e. Chunk::last_const returns None). callers that have already peeked via last_const / second_to_last_const will always receive Some; the None path exists so the WASM build cannot panic on misuse.

Source

pub fn emit_jump(&mut self, op: Opcode, line: u32) -> usize

emit a forward jump with a placeholder i16 operand. returns the byte position of the FIRST operand byte so the caller can later Chunk::patch_jump it to the real target.

uses i16::MAX as the placeholder so an unpatched offset is visually obvious in a disassembly. callers MUST patch the jump before completing the chunk; an unpatched jump is a codegen bug.

Source

pub fn patch_jump(&mut self, pos: usize) -> Result<(), QalaError>

patch the i16 operand bytes at pos to reach the current end of code. the offset is computed relative to the byte AFTER the operand (i.e. code.len() - pos - 2), matching the VM’s branch arithmetic per Crafting Interpreters chapter 23.

returns Err(QalaError::Parse { ..., "branch too far" }) when the computed offset would not fit in i16. this is a v1 limitation (Phase 4 research A1); widening to i32 is a v2 concern.

Source

pub fn emit_loop( &mut self, loop_start: usize, line: u32, ) -> Result<(), QalaError>

emit a backward jump whose target is loop_start. unlike Chunk::emit_jump, the offset is known up-front so no patching is needed. the offset is computed from the byte AFTER the 2-byte operand back to loop_start (i.e. loop_start - pos - 3 where pos is the byte position of the JUMP opcode itself).

returns the same Err(QalaError::Parse { ..., "branch too far" }) when the offset exceeds i16 range.

Source

pub fn disassemble(&self, program: &Program) -> String

produce the human-readable listing for this chunk. one line per instruction: byte offset, opcode name (uppercase), operand interpretation, source line. the format is the documented contract for Phase 6’s WASM bridge and Phase 8’s playground bytecode panel.

byte-deterministic: same input bytes produce byte-identical output on every run. no HashMap iteration, no debug formatters; constants and program-side name tables are accessed by direct index.

the program argument supplies the function-name and enum-variant- name lookup tables; the chunk’s own constant pool supplies CONST operand renderings.

Trait Implementations§

Source§

impl Clone for Chunk

Source§

fn clone(&self) -> Chunk

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Chunk

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Chunk

Source§

fn default() -> Chunk

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl Freeze for Chunk

§

impl RefUnwindSafe for Chunk

§

impl Send for Chunk

§

impl Sync for Chunk

§

impl Unpin for Chunk

§

impl UnsafeUnpin for Chunk

§

impl UnwindSafe for Chunk

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<S, T> Upcast<T> for S
where T: UpcastFrom<S> + ?Sized, S: ?Sized,

Source§

fn upcast(&self) -> &T
where Self: ErasableGeneric, T: ErasableGeneric<Repr = Self::Repr>,

Perform a zero-cost type-safe upcast to a wider ref type within the Wasm bindgen generics type system. Read more
Source§

fn upcast_into(self) -> T
where Self: Sized + ErasableGeneric, T: ErasableGeneric<Repr = Self::Repr>,

Perform a zero-cost type-safe upcast to a wider type within the Wasm bindgen generics type system. Read more