pub struct VirtualMachine {
pub resource_usage: Option<ResourceUsage>,
pub metrics: Option<VmMetrics>,
/* private fields */
}Expand description
The Shape virtual machine
Fields§
§resource_usage: Option<ResourceUsage>Optional resource usage tracker for sandboxed execution.
When set, the dispatch loop calls tick_instruction() each cycle.
metrics: Option<VmMetrics>Optional VM metrics collector. None when VMConfig.metrics_enabled
is false (the default), giving zero per-instruction overhead.
Implementations§
Source§impl VirtualMachine
impl VirtualMachine
Sourcepub fn execute_function_by_name(
&mut self,
name: &str,
args: Vec<KindedSlot>,
ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError>
pub fn execute_function_by_name( &mut self, name: &str, args: Vec<KindedSlot>, ctx: Option<&mut ExecutionContext>, ) -> Result<KindedSlot, VMError>
Execute a named function with arguments, returning its result.
W7-cv-method (Round 3 close). Resolves name to func_id via
the program function table and routes to
[execute_function_by_id] per W7 playbook §4.
Sourcepub fn execute_function_by_id(
&mut self,
func_id: u16,
args: Vec<KindedSlot>,
ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError>
pub fn execute_function_by_id( &mut self, func_id: u16, args: Vec<KindedSlot>, ctx: Option<&mut ExecutionContext>, ) -> Result<KindedSlot, VMError>
Execute a function by its ID with positional arguments.
W7-cv-method (Round 3 close). Captures saved_depth before
frame setup, routes through [call_function_with_nb_args], drives
the callee to completion via
execute_until_call_depth, and
pops the result via the kinded API (W7 playbook §4 + §2.7.10 / Q11
dispatch shape).
Ownership. Each KindedSlot in args holds a strong-count
share. cluster-1.5 v2-raw-empirical-isolation-and-fix
(2026-05-17): post-fix call_function_with_nb_args is share-
neutral (clones each arg before frame-write so the frame’s
teardown truncate_stack retire balances the in-helper clone;
caller’s carrier shares are preserved by the borrow-only
&[KindedSlot] signature). We let args drop normally at
scope exit — the per-slot KindedSlot::Drop retires each
caller-owned share. The legacy mem::forget pre-fix was a
load-bearing leak that compensated for the missing clone in
the helper; with the helper now share-neutral, the forget would
LEAK one share per heap-bearing arg.
Sourcepub fn execute_closure(
&mut self,
closure_block: &OwnedClosureBlock,
args: Vec<KindedSlot>,
ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError>
pub fn execute_closure( &mut self, closure_block: &OwnedClosureBlock, args: Vec<KindedSlot>, ctx: Option<&mut ExecutionContext>, ) -> Result<KindedSlot, VMError>
Execute a closure with its captured upvalues and arguments.
W7-cv-method (Round 3 close). The pre-§2.7.8 _upvalue_bits: Vec<u64> parameter — the deleted-ABI raw-bits shape — is replaced
by closure_block: &OwnedClosureBlock. Captures flow from the
block’s parallel-kind track via read_capture_kinded inside
[call_closure_with_nb_args_keepalive], not from a side-channel
payload (W7 playbook §4 + ADR-006 §2.7.8 / Q10).
The keep-alive companion fields carry the closure-self share so
op_return / op_return_value release it via drop_with_kind
on frame teardown — same B9 lockstep pattern as
call_value_immediate_nb’s closure arm.
Sourcepub fn execute_function_fast(
&mut self,
func_id: u16,
ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError>
pub fn execute_function_fast( &mut self, func_id: u16, ctx: Option<&mut ExecutionContext>, ) -> Result<KindedSlot, VMError>
Fast function execution for hot loops (backtesting).
W7-cv-method (Round 3 close). Pre-computed func_id, no name
lookup, no args (callers that need args route through
execute_function_by_id). Same saved_depth pattern as the
other public entry-points.
Sourcepub fn execute_function_with_named_args(
&mut self,
func_id: u16,
named_args: &[(String, KindedSlot)],
ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError>
pub fn execute_function_with_named_args( &mut self, func_id: u16, named_args: &[(String, KindedSlot)], ctx: Option<&mut ExecutionContext>, ) -> Result<KindedSlot, VMError>
Execute a function with named arguments.
W7-cv-method (Round 3 close). Maps &[(String, KindedSlot)]
to a positional Vec<KindedSlot> via descriptor.param_names
lookup, then routes through [execute_function_by_id] per W7
playbook §4. Missing positional slots are sentinel-filled with
(NONE_BITS, NativeKind::Bool) per W6.5 §2 Null/Unit row —
Drop/Clone-no-op so the pre-population is leak-free.
Ownership. The caller’s named-args carry one share per slot;
clone_with_kind is NOT used (we re-home the slot’s bits by
reading the slot directly). After mapping, the positional vec
owns the same shares; they transfer into the new frame via the
execute_function_by_id mem::forget discipline.
Sourcepub fn resume(
&mut self,
_value: KindedSlot,
_ctx: Option<&mut ExecutionContext>,
) -> Result<ExecutionResult, VMError>
pub fn resume( &mut self, _value: KindedSlot, _ctx: Option<&mut ExecutionContext>, ) -> Result<ExecutionResult, VMError>
Resume execution after a suspension.
§2.7.4 Phase-2c — stays todo!(). The suspension shape
requires snapshot-tier work: the resume body’s pre-§2.7.7 form
pushed value onto the stack and re-entered the suspendable
dispatch loop, but the snapshot/restore family
(apply_pending_resume / apply_pending_frame_resume in
executor/resume.rs) is itself §2.7.4 deferred — its bodies
return VMError::NotImplemented(PHASE_2C_SNAPSHOT_SURFACE). Until
the snapshot rebuild lands a kind-threaded
slot_to_serializable / serializable_to_slot pair plus the
§2.7.8 cell-storage parallel-kind tracks for module_bindings
and frame-resume payloads, this entry-point cannot be wired —
surface-and-stop trigger per W7 playbook §8 (snapshot-tier
resume).
Sourcepub fn execute_with_async(
&mut self,
ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError>
pub fn execute_with_async( &mut self, ctx: Option<&mut ExecutionContext>, ) -> Result<KindedSlot, VMError>
Execute with automatic async task resolution.
Filled by W7-cv-async (Round 3 close). Per W7 playbook §4
W7-cv-async row, sync-resolution only — suspension state crossing
a call_value_immediate_* boundary is OUT OF SCOPE per ADR-006
§2.7.11 out-of-scope clause (Phase-2c snapshot tier; same
out-of-scope clause as §2.7.10).
Drives the program forward via execute_fast(ctx) (the standard
run-to-halt loop that pops the top-of-stack result on completion).
Inline task resolution at op_await / op_join_await sites in
executor/async_ops/mod.rs is the integration point with
[resolve_spawned_task] (below) — once the §2.7.4 task-scheduler
kinded-ABI re-light closes those todo!() arms, the await-site
handler invokes resolve_spawned_task(task_id) directly inside
the dispatch loop, and this driver re-enters execute_fast to
continue the program after the suspended op_await opcode
returns.
The pre-bulldozer execute_with_async shape — drive a loop
over task_scheduler.iter_pending() calling resolve_spawned_task
per ready task — depends on a public iterator over
TaskScheduler.callables that does not exist in the current
scheduler API surface (W7-cv-async owns only call_convention.rs
per W7 playbook §10 forbidden zones — task_scheduler.rs is
out-of-territory). When a future cluster lands the iteration
API, the loop body in this function is the natural extension
point: while there is a Pending-with-callable task, call
resolve_spawned_task(id) and discard the per-task result; the
program-level result still comes from execute_fast at the end.
Sourcepub fn call_value_immediate_nb(
&mut self,
callee: &KindedSlot,
args: &[KindedSlot],
ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError>
pub fn call_value_immediate_nb( &mut self, callee: &KindedSlot, args: &[KindedSlot], ctx: Option<&mut ExecutionContext>, ) -> Result<KindedSlot, VMError>
call_value_immediate (kinded carrier form): dispatches on the
callee’s KindedSlot.kind. ADR-006 §2.7.11 / Q12 caller-side
shape — both callee and args travel as KindedSlot.
Filled by W7-cv-static (Round 2 close). Per W7 playbook §4:
matches on callee.kind and routes — Ptr(HeapKind::Closure)
recovers the OwnedClosureBlock via slot.as_heap_value() +
HeapValue::ClosureRaw (single discriminator per ADR-005 §1)
and routes to call_closure_with_nb_args_keepalive; UInt64
callee bits are the function-id and route to
call_function_with_nb_args. Both arms drive the callee to
completion via execute_until_call_depth(saved_depth, ctx)
(the call-stack-bounded run loop in dispatch.rs) and pop the
result from the value stack via pop_kinded. Other kinds fall
through to a RuntimeError (VMError::TypeError is
&'static str-bound and incompatible with the format!-style
dynamic-kind error message; the convention used by the existing
op_call_value surfaces is RuntimeError(format!(...))). The
HeapValue::HostClosure variant referenced in pre-Wave-7 docs
has been deleted; only ClosureRaw survives in the
closure-dispatch path.
Sourcepub fn jit_trampoline_call_closure(
&mut self,
func_id: u16,
upvalue_bits: &[(u64, NativeKind)],
args: &[(u64, NativeKind)],
ctx: Option<&mut ExecutionContext>,
) -> Result<u64, VMError>
pub fn jit_trampoline_call_closure( &mut self, func_id: u16, upvalue_bits: &[(u64, NativeKind)], args: &[(u64, NativeKind)], ctx: Option<&mut ExecutionContext>, ) -> Result<u64, VMError>
Trampoline entry: call a closure by func_id with pre-extracted
raw upvalue bits and raw args, returning the result as raw u64
bits.
W7-cv-method (Round 3 close). This is the only _raw
survivor in call_convention.rs per ADR-006 §2.7.11
migration-scope refinement: it is the §2.7.5 cross-crate
stable-FFI consumer where the parallel-pair shape (raw u64
data + NativeKind) is canonical. Consumers translate
&[KindedSlot] → raw u64 at the FFI boundary; this function
is the inverse hop on the runtime side.
Body wraps args as a transient &[KindedSlot] slice (no Arc
bump — the JIT pre-incremented each share before crossing the
boundary), constructs a fresh OwnedClosureBlock from
upvalue_bits per the existing closure-construction convention
(allocate → write each capture’s bits at its layout offset →
OwnedClosureBlock::from_raw), routes through
[call_closure_with_nb_args_keepalive], drives the callee, pops
the result via pop_kinded, and returns the bits as raw u64
(the kind is discarded — the JIT caller knows the static return
kind from the callee signature).
Ownership. Each (bits, kind) in upvalue_bits carries a
pre-incremented share. We transfer those shares into the new
closure block via write_capture_typed (which stores the bit
pattern without bumping the refcount). The OwnedClosureBlock
then owns the captures’ shares — its Drop walks the layout’s
capture masks and releases them. Same for args: the JIT
pre-incremented; we hand each transient KindedSlot over by
move (no clone), and call_closure_with_nb_args_keepalive
transfers the shares into the new frame via stack_write_kinded.
We mem::forget the transient args vec so its Drop does not
double-free.
Sourcepub fn jit_trampoline_call_method(
&mut self,
method_name: &str,
receiver: (u64, NativeKind),
args: &[(u64, NativeKind)],
ctx: Option<&mut ExecutionContext>,
) -> Result<u64, VMError>
pub fn jit_trampoline_call_method( &mut self, method_name: &str, receiver: (u64, NativeKind), args: &[(u64, NativeKind)], ctx: Option<&mut ExecutionContext>, ) -> Result<u64, VMError>
Trampoline entry: dispatch a method call on a kinded receiver +
kinded args, returning the result as raw u64 bits.
W12-jit-call-method-shell-rebuild (Phase 3 cluster-0 Round 10 /
8B.2 close). Sibling to [jit_trampoline_call_closure]; same
§2.7.5 cross-crate stable-FFI consumer shape. The JIT-side
jit_call_method shell pops (bits, kind) pairs from the JIT’s
ctx.stack + ctx.stack_kinds parallel-kind track per §2.7.7 / Q9,
passes them across the FFI boundary as &[(u64, NativeKind)]
pair-slices, and this function converts them to the kinded
carrier form before delegating to
dispatch_method_kinded — the
§2.7.10 / Q11 kinded method-dispatch entry shared with
op_call_method.
Pair-slice → KindedSlot conversion is single-direction per
the module-level docstring’s “sole _raw survivor” rule. The
pair-slice is the canonical §2.7.5 boundary shape; internally
only &[KindedSlot] flows. Forbidden alternatives (per ADR-006
§2.7.6 / Q8 + §2.7.10 / Q11):
- parallel
&[NativeKind]second-slice parameter (carrier-API- bound rejection — kind goes on the carrier struct, not a side-channel); - decoding receiver kind from
receiver.0raw bits via tag-bit probe (the deleted §2.7.7 #4 / #7 dispatch); - Bool-default kinded carrier for unknown receiver kind (§2.7.7 #9 — the surface-and-stop discipline forbids this).
Ownership. Each (bits, kind) pair carries a pre-incremented
share installed by the JIT producer (per §2.7.7 retain-on-read
semantics on the JIT-side stack). The transient KindedSlot
carriers adopt those shares for the call duration. PHF handlers
borrow-only (&[KindedSlot] per §2.7.10 / Q11), so the carriers
retain ownership of the JIT-pre-incremented shares throughout
dispatch. When the carriers Drop at end of scope, each kind’s
drop_with_kind releases its share — balancing the JIT-side
retain-before-crossing pattern. The returned KindedSlot’s share
is transferred back to the JIT caller as raw u64 bits via
mem::forget; the kind is discarded — the JIT caller knows the
static return kind from the callee method signature at the
§2.7.5 stamp-at-compile-time producing site.
Lifetime accounting contrast vs. jit_trampoline_call_closure.
The closure trampoline’s mem::forget(kinded_args) (line 1035)
is because the args were transferred into the callee’s frame
locals via stack_write_kinded — the shares moved into the
frame, so the transient carriers must NOT release them. Method
dispatch’s PHF handlers do not transfer the shares anywhere —
they only borrow — so the transient carriers DO release at end
of scope. Both patterns preserve §2.7.7 retain-on-read +
drop-on-write discipline; the difference is which slot owns the
share at the call’s exit boundary.
Source§impl VirtualMachine
impl VirtualMachine
Sourcepub fn execute(
&mut self,
ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError>
pub fn execute( &mut self, ctx: Option<&mut ExecutionContext>, ) -> Result<KindedSlot, VMError>
Execute the loaded program.
§Arguments
ctx- Optional ExecutionContext for trading operations (rows, indicators, etc.)
Returns a KindedSlot — the canonical post-ValueWord
runtime-value carrier (ADR-006 §2.7 / Q7). The slot’s
NativeKind is sourced from BytecodeProgram::top_level_frame. return_kind when present (the compiler-proven kind), with the
raw u64 bits taken from the top of the value stack. Hosts
dispatch on result.kind() and use the per-variant
KindedSlot::as_* accessors per §2.7.6.
Sourcepub fn execute_raw(
&mut self,
ctx: Option<&mut ExecutionContext>,
) -> Result<u64, VMError>
pub fn execute_raw( &mut self, ctx: Option<&mut ExecutionContext>, ) -> Result<u64, VMError>
Execute the loaded program and return the raw u64 bits at the top
of stack. Top-level emission pushes raw native values
(e.g. i64, f64::to_bits(), 0u64/1u64 for bool, raw heap
pointer for ptr); the kind is recovered from
program_top_level_return_kind(). Use this when the host wants
the raw bits without a KindedSlot wrapper.
Sourcepub fn execute_with_suspend(
&mut self,
ctx: Option<&mut ExecutionContext>,
) -> Result<ExecutionResult, VMError>
pub fn execute_with_suspend( &mut self, ctx: Option<&mut ExecutionContext>, ) -> Result<ExecutionResult, VMError>
Execute the loaded program, returning either a completed value or suspension info.
Unlike execute(), this method distinguishes between completion and suspension,
allowing the host to resume execution after resolving a future.
Source§impl VirtualMachine
impl VirtualMachine
Sourcepub fn op_call_method(
&mut self,
instruction: &Instruction,
ctx: Option<&mut ExecutionContext>,
) -> Result<(), VMError>
pub fn op_call_method( &mut self, instruction: &Instruction, ctx: Option<&mut ExecutionContext>, ) -> Result<(), VMError>
CallMethod dispatch shell (W16-op-call-method close).
ADR-006 §2.7.10 / Q11 dispatch shell — pops the receiver +
arg-count call args from the §2.7.7 kinded stack, classifies
the receiver kind to pick the matching PHF method registry,
dispatches through MethodFnV2, and pushes the kinded result.
Body shape per the W7-op-call-value precedent (close commit
27812cf, executor/control_flow/mod.rs:dispatch_call_value_immediate):
- Pop
arg_count + 1slots viapop_kinded()(receiver included). Each pop transfers one share (heap-bearing kinds) into the returned(bits, kind)pair (WB2.4 retain-on-read, §2.7.7); theKindedSlot::newcarrier takes ownership of that share. Pop order is reverse of push order, so reverse the vec back to position-aligned order withargs[0]= receiver. - Decode
arg_count+ method name fromOperand::TypedMethodCall { arg_count, string_id, .. }(bytecode/opcode_defs.rs:2023). The method name string is indexed viastring_idintoself.program.strings. - Classify
args[0].kindto pick a PHF registry per the §2.7.6 / Q8 heterogeneous-kind body pattern. Numeric / Bool / String scalars route to the matching scalar registry;Ptr(HeapKind::*)heap kinds route to the per-heap-kind registry, withHeapKind::TypedArraysub-classified on the innerTypedArrayData::{I64, F64, Bool, ...}variant viaslot.as_heap_value()andHeapKind::Temporalsub-classified on the innerTemporalData::{DateTime, TimeSpan, ...}variant. The v2 typed-array fast path (UInt64-tagged raw*mut TypedArray<T>pointer) routes throughas_v2_typed_array; post-W16.2-J.1 every numeric element kind falls through to the kind-genericARRAY_METHODSPHF (per thetyped_array_method_registryhelper, which returnsNoneforV2ElemType::{I*, U*, F*}).V2ElemType::Boolcontinues to route viaBOOL_ARRAY_METHODS. - PHF lookup keyed on
&strmethod name returns theMethodFnV2handler. A miss surfaces aRuntimeErrorciting the receiver kind + method name; user-defined methods onHeapValue::TypedObjectfall through to a UFCS function-name lookup (function_name_index) before the finalUnknown methoderror. Closure / Future / Reference / SharedCell / FilterExpr receivers reject — they are not method-call targets. - Dispatch:
handler(self, &args, ctx)returnsResult<KindedSlot, VMError>. The&[KindedSlot]borrow leaves the shares with the carriers in this stack frame — handlers borrow each entry per §2.7.10 / Q11 borrow-only ABI. - Push the result via
push_kinded(result.raw(), result.kind())andstd::mem::forget(result)so the result share transfers cleanly to the stack (no double-drop). Theargscarriers drop at end of scope;KindedSlot::Dropdispatches on kind and releases each share viadrop_with_kind(no barevw_drop, no Bool-default fallback).
Forbidden surfaces (per CLAUDE.md “Renames to refuse on sight”
- ADR-006 §2.7.10 / Q11):
Vec<KindedSlot>by-move into a dispatch helper;args: &mut [KindedSlot]; tag-bits decode on receiver bits;is_heap()probe on raw bits; Bool-default fallback for unknown kind; defection-attractor framing on the method-dispatch ABI (MethodFn/MethodFnLegacy/dispatch_method_handler_raw/call_handler_with_u64_slice).
Surfaces remaining (out of W16 territory):
- IC fast-path recording / hit:
method_ic_check/method_ic_recordalready accept the kindedMethodFnV2transmute (ic_fast_paths.rs:42-44) — wiring the IC recording at the dispatch shell is a downstream JIT-IC follow-up, not a correctness gate. The dispatch shell stays correct without IC; the IC adds speed only. HeapKind::Closurereceivers (e.g. closure-as-trait- object dispatch). Trait-object dispatch goes throughop_dyn_method_call, notop_call_method; the closure arm here rejects with a clear error.
Source§impl VirtualMachine
impl VirtualMachine
Sourcepub fn snapshot(&self, store: &SnapshotStore) -> Result<VmSnapshot, VMError>
pub fn snapshot(&self, store: &SnapshotStore) -> Result<VmSnapshot, VMError>
Create a serializable snapshot of VM state.
W17-snapshot-roundtrip (Phase 2d Wave 2.6, 2026-05-11). Uses
the kind-threaded slot_to_serializable(bits, kind, store) API
landed alongside the §2.7.5.1 wire-format extension. Round-trips
the stack, module bindings, IP, and exception-handler stack at
landing. Call-stack frames, loop contexts, locals living in
register windows on the stack (versus their inlined-into-stack
projection), and timeframe state are landed via the existing
VmSnapshot carrier with the per-slot kind threaded through
slot_to_serializable per slot. Deep heap kinds that don’t yet
have a wire-format arm surface via the per-slot error path —
callers observe a structured VMError::NotImplemented, not
silent state loss.
Sourcepub fn from_snapshot(
program: BytecodeProgram,
snapshot: &VmSnapshot,
store: &SnapshotStore,
) -> Result<Self, VMError>
pub fn from_snapshot( program: BytecodeProgram, snapshot: &VmSnapshot, store: &SnapshotStore, ) -> Result<Self, VMError>
Restore a VM from a snapshot and bytecode program.
W17-snapshot-roundtrip (Phase 2d Wave 2.6, 2026-05-11).
Symmetric inverse of snapshot(): rebuilds the VM from the
kind-threaded serializable_to_slot API. Stack, module
bindings, IP, exception handlers, and structural loop/
timeframe state restore deterministically when the snapshot’s
per-slot kinds align with the program’s FrameDescriptor.slots
at the resume IP. Discriminator-vs-kind mismatches surface as
structured errors per §2.7.5.1.
Per-slot kind reconstruction: the snapshot’s
SerializableVMValue discriminator (its variant tag) is the
authoritative carrier of the slot’s kind. serializable_to_slot
takes an expected_kind hint per Q9 §2.7.7 (the post-proof
stack-kind invariant) but the discriminator wins on actual
projection. Restore picks the kind from the discriminator and
hands (bits, kind) back to the parallel-kind tracks.
Source§impl VirtualMachine
impl VirtualMachine
pub fn op_builtin_call( &mut self, instruction: &Instruction, ctx: Option<&mut ExecutionContext>, ) -> Result<(), VMError>
Source§impl VirtualMachine
impl VirtualMachine
pub fn new(config: VMConfig) -> Self
Sourcepub fn with_resource_limits(self, limits: ResourceLimits) -> Self
pub fn with_resource_limits(self, limits: ResourceLimits) -> Self
Attach resource limits to this VM. The dispatch loop will enforce them.
Sourcepub fn set_interrupt(&mut self, flag: Arc<AtomicU8>)
pub fn set_interrupt(&mut self, flag: Arc<AtomicU8>)
Set the interrupt flag (shared with Ctrl+C handler).
Sourcepub fn enable_time_travel(&mut self, mode: CaptureMode, max_entries: usize)
pub fn enable_time_travel(&mut self, mode: CaptureMode, max_entries: usize)
Enable time-travel debugging with the given capture mode and history limit.
Sourcepub fn disable_time_travel(&mut self)
pub fn disable_time_travel(&mut self)
Disable time-travel debugging and discard history.
Sourcepub fn set_jit_compiled(&mut self)
pub fn set_jit_compiled(&mut self)
Mark this VM as having been JIT-compiled selectively.
Call this after using shape_jit::JITCompiler::compile_program_selective
externally to JIT-compile functions that benefit from native execution.
The caller is responsible for performing the compilation via shape-jit
(which depends on shape-vm, so the dependency flows one way).
§Example (in a crate that depends on both shape-vm and shape-jit):
let mut compiler = shape_jit::JITCompiler::new()?;
let (_jitted_fn, _table) = compiler.compile_program_selective("main", vm.program())?;
vm.set_jit_compiled();Sourcepub fn is_jit_compiled(&self) -> bool
pub fn is_jit_compiled(&self) -> bool
Returns whether selective JIT compilation has been applied to this VM.
Sourcepub fn register_jit_function(&mut self, function_id: u16, ptr: JitFnPtr)
pub fn register_jit_function(&mut self, function_id: u16, ptr: JitFnPtr)
Register a JIT-compiled function in the dispatch table.
After registration, calls to this function_id will attempt JIT dispatch before falling back to bytecode interpretation.
Sourcepub fn jit_dispatch_table(&self) -> &HashMap<u16, JitFnPtr>
pub fn jit_dispatch_table(&self) -> &HashMap<u16, JitFnPtr>
Get the JIT dispatch table for inspection or external use.
Sourcepub fn enable_tiered_compilation(
&mut self,
) -> (Receiver<CompilationRequest>, Sender<CompilationResult>)
pub fn enable_tiered_compilation( &mut self, ) -> (Receiver<CompilationRequest>, Sender<CompilationResult>)
Enable tiered compilation for this VM.
Must be called after load_program() so the function count is known.
The caller is responsible for spawning a background compilation thread
that reads from the request channel and sends results back.
Returns (request_rx, result_tx) that the background thread should use.
Sourcepub fn tier_manager(&self) -> Option<&TierManager>
pub fn tier_manager(&self) -> Option<&TierManager>
Get a reference to the tier manager, if tiered compilation is enabled.
Sourcepub fn feedback_vectors(&self) -> &[Option<FeedbackVector>]
pub fn feedback_vectors(&self) -> &[Option<FeedbackVector>]
Access the feedback vectors (for JIT compilation).
Sourcepub fn program(&self) -> &BytecodeProgram
pub fn program(&self) -> &BytecodeProgram
Get a reference to the loaded program (for external JIT compilation).
Sourcepub fn time_travel(&self) -> Option<&TimeTravel>
pub fn time_travel(&self) -> Option<&TimeTravel>
Get a reference to the time-travel debugger, if enabled.
Sourcepub fn time_travel_mut(&mut self) -> Option<&mut TimeTravel>
pub fn time_travel_mut(&mut self) -> Option<&mut TimeTravel>
Get a mutable reference to the time-travel debugger, if enabled.
Sourcepub fn module_registry(&self) -> &ModuleExportRegistry
pub fn module_registry(&self) -> &ModuleExportRegistry
Get a reference to the extension module registry.
Sourcepub fn get_function_id(&self, name: &str) -> Option<u16>
pub fn get_function_id(&self, name: &str) -> Option<u16>
Get function ID for fast repeated calls (avoids name lookup in hot loops)
Source§impl VirtualMachine
impl VirtualMachine
Sourcepub fn register_stdlib_module(&mut self, module: ModuleExports)
pub fn register_stdlib_module(&mut self, module: ModuleExports)
Register a built-in stdlib module into the VM’s module registry.
Delegates to register_extension — this is a semantic alias to
distinguish VM-native stdlib modules from user-installed extension plugins.
Sourcepub fn register_extension(&mut self, module: ModuleExports)
pub fn register_extension(&mut self, module: ModuleExports)
Register an external/user extension module (e.g. loaded from a .so plugin) into the VM’s module registry. Also merges any method intrinsics for fast Object dispatch.
Phase-2c surface (ADR-006 §2.7.4 / §2.7.5): the body wraps each
TypedModuleFunction into a ModuleFn whose signature is
Fn(&[ValueWord], &ModuleContext) -> Result<ValueWord, String>.
ValueWord was deleted by the strict-typing bulldozer (no type to
import); the kinded rebuild per §2.7.5 makes ModuleFn’s argument
slice &[KindedSlot] and its return Result<KindedSlot, String>.
Extensions stay on the stable raw-bits ABI and convert at the
RawCallableInvoker boundary inside shape-runtime.
The cross-crate ModuleFn signature change is shape-runtime
territory (R-shape-runtime sub-cluster) and the corresponding
TypedReturn::into_value_word() helper is also deleted; this
caller hand-off lands in the Phase-2c rebuild session.
Sourcepub fn register_module_fn_entry(&mut self, entry: ModuleFnEntry) -> usize
pub fn register_module_fn_entry(&mut self, entry: ModuleFnEntry) -> usize
Register a module-function entry in the table and return its ID.
Phase-2c surface (ADR-006 §2.7.4 / §2.7.5): the
ValueWord::ModuleFunction carrier shape this function feeds
depends on the deleted ValueWord runtime representation.
Replaced with a kinded NativeKind::ModuleFunction-style ID
carrier in the Phase-2c rebuild.
Sourcepub fn populate_module_objects(&mut self)
pub fn populate_module_objects(&mut self)
Populate extension module objects as module_bindings — W17-comptime-vm-dispatch rebuild.
W17-comptime-vm-dispatch (Phase 2d Wave 3, 2026-05-12).
Per ADR-006 §2.7.26 amendment. Builds a kinded TypedObject
per registered extension module, with field slots that store
module-function-id field references as Ptr(HeapKind::ModuleFn)
inline-scalar payloads. The dispatch chain
LoadModuleBinding(idx) + GetFieldTyped(...) + CallValue routes
through:
LoadModuleBinding(idx)reads the kinded module-binding slot (TypedObject +Ptr(HeapKind::TypedObject)kind) and pushes it viaclone_with_kindretain-on-read (§2.7.7).GetFieldTyped { type_id, field_idx, field_type_tag }pops the receiver, recovers theArc<TypedObjectStorage>per ADR-005 §1, and reads the field. The compiler emitsfield_type_tag = FIELD_TAG_ANYfor schema fields ofFieldType::Any(the comptime predeclared schema shape). Theop_get_field_typedbody falls through topush_field_value_with_kind, which sources the kind fromstorage.field_kinds[field_idx](the §2.7.7 parallel-kind track) — resolving hardening item (f) — and pushes themodule_fn_id as u64bits with kindPtr(HeapKind::ModuleFn).CallValuepops args + callee, dispatches viacall_value_immediate_nbwhosePtr(HeapKind::ModuleFn)arm routes toinvoke_module_fn_id_stub(bits as usize, args)— the same path used byW17-snapshot-roundtripfor direct module-fn invocation.
Per-module construction:
- Look up the predeclared
__mod_<name>schema (registered bycompiler/comptime.rs::ensure_module_object_schemabefore bytecode compilation). The schema field names define the storage’s field order; missing schemas are skipped (the module’s exports remain unreachable through this binding). - For each typed export (sync and async), register a
ModuleFnEntry::Typed/TypedAsyncintomodule_fn_tableto obtain amodule_fn_id. - For each schema field, look up the matching
module_fn_id, write aValueSlot::from_raw(module_fn_id as u64)withfield_kinds[i] = Ptr(HeapKind::ModuleFn)andheap_maskbit set. Unmatched fields (a schema field with no corresponding export) get the(0u64, NativeKind::Bool)sentinel pair — same shape as themodule_binding_pad_to_kindeduninitialised-slot convention. - Construct
Arc<TypedObjectStorage>via the typed constructor per ADR-006 §2.4 and write thePtr(HeapKind::TypedObject)slot to the module-binding viamodule_binding_write_kinded(§2.7.8 / Q10 lockstep).
Resolves the upstream populate_module_objects no-op blocker
flagged by W17-snapshot-roundtrip (commit fbfbfb6). The
4 comptime introspection forms wired by C2-comptime-rebuild
(a5df165) — build_config / implements / warning /
error — now dispatch end-to-end via VM mode.
Source§impl VirtualMachine
impl VirtualMachine
Sourcepub fn enable_output_capture(&mut self)
pub fn enable_output_capture(&mut self)
Enable output capture for testing When enabled, print output goes to an internal buffer instead of stdout
Sourcepub fn disable_output_capture(&mut self)
pub fn disable_output_capture(&mut self)
Disable output capture — print goes to stdout again.
Sourcepub fn get_captured_output(&self) -> Vec<String>
pub fn get_captured_output(&self) -> Vec<String>
Get captured output (returns empty vec if capture not enabled)
Sourcepub fn clear_captured_output(&mut self)
pub fn clear_captured_output(&mut self)
Clear captured output
Sourcepub fn last_error_line(&self) -> Option<u32>
pub fn last_error_line(&self) -> Option<u32>
Get the line number of the last error (for LSP integration)
Sourcepub fn last_error_file(&self) -> Option<&str>
pub fn last_error_file(&self) -> Option<&str>
Get the file path of the last error (for LSP integration)
Sourcepub fn take_last_uncaught_exception(&mut self) -> Option<KindedSlot>
pub fn take_last_uncaught_exception(&mut self) -> Option<KindedSlot>
Take the last uncaught exception payload if present.
Source§impl VirtualMachine
impl VirtualMachine
Sourcepub fn load_program(&mut self, program: BytecodeProgram)
pub fn load_program(&mut self, program: BytecodeProgram)
Load a program into the VM
Sourcepub fn load_linked_program(&mut self, linked: LinkedProgram)
pub fn load_linked_program(&mut self, linked: LinkedProgram)
Load a LinkedProgram into the VM, extracting content-addressed metadata
directly from the linked function table.
This converts the LinkedProgram into the flat BytecodeProgram layout that
the executor expects, then populates function_hashes and function_entry_points
from the linked function metadata.
Sourcepub fn patch_function(
&mut self,
fn_id: u16,
new_blob: FunctionBlob,
) -> Result<Option<FunctionHash>, String>
pub fn patch_function( &mut self, fn_id: u16, new_blob: FunctionBlob, ) -> Result<Option<FunctionHash>, String>
Hot-patch a single function in the loaded program with a new blob.
The new blob’s instructions, constants, and strings replace the existing function’s bytecode in-place. The function’s metadata (arity, param names, locals count, etc.) is also updated. The content hash is recorded so that in-flight frames referencing the old hash remain valid (they execute from their saved IP which is now stale, but callers that resolve by function ID will pick up the new code on the next call).
Returns Ok(old_hash) on success (the previous content hash, if any),
or Err(msg) if the function ID is out of range.
Sourcepub fn load_program_with_permissions(
&mut self,
program: Program,
granted: &PermissionSet,
) -> Result<(), PermissionError>
pub fn load_program_with_permissions( &mut self, program: Program, granted: &PermissionSet, ) -> Result<(), PermissionError>
Load a content-addressed Program with permission checking.
Links the program, checks that total_required_permissions is a subset of
granted, and loads normally if the check passes. Returns an error listing
the missing permissions if the check fails.
Sourcepub fn load_linked_program_with_permissions(
&mut self,
linked: LinkedProgram,
granted: &PermissionSet,
) -> Result<(), PermissionError>
pub fn load_linked_program_with_permissions( &mut self, linked: LinkedProgram, granted: &PermissionSet, ) -> Result<(), PermissionError>
Load a LinkedProgram with permission checking.
Checks that total_required_permissions is a subset of granted, then
loads normally. Returns an error listing the missing permissions if the
check fails.
Sourcepub fn module_bindings_snapshot(&self) -> Vec<KindedSlot>
pub fn module_bindings_snapshot(&self) -> Vec<KindedSlot>
Reset VM state Get a snapshot of all module binding values.
Phase-1b-vm Wave-ε E-vm-impl-tail: the legacy Vec<ValueWord>
return type referenced the deleted runtime carrier (CLAUDE.md
“Forbidden Patterns”). The signature is flipped to the kinded
carrier Vec<shape_value::KindedSlot> per ADR-006 §2.7 / Q7.
The §2.7.8 / Q10 parallel-kind track is now live on
module_binding_kinds, and module_binding_read_owned_kinded
returns KindedSlot shares per binding — the kinded read
backbone is in place. The body remains a todo!() until the
Phase-2c snapshot revival lands the host-API surface (§2.7.4) —
at that point the body is one map+collect over
(0..self.module_bindings_len()).map(|i| self.module_binding_read_owned_kinded(i)).collect().
Sourcepub fn reset_for_trampoline(&mut self)
pub fn reset_for_trampoline(&mut self)
Reset VM execution state for trampoline use. Clears stack, call frames, error state, and exception handlers but preserves the loaded program, module bindings, module_fn_table, and registered extensions.
pub fn reset(&mut self)
Sourcepub fn reset_stack(&mut self)
pub fn reset_stack(&mut self)
Reset stack only (for reusing compiled program across iterations) Keeps program, module_bindings, and GC state intact - only clears execution state
Sourcepub fn reset_minimal(&mut self)
pub fn reset_minimal(&mut self)
Minimal reset for hot loops - only clears essential state Use this when you know the function doesn’t create GC objects or use exceptions
Sourcepub fn push_value(&mut self, _value: KindedSlot)
pub fn push_value(&mut self, _value: KindedSlot)
Push a value onto the stack (public, for testing and host integration).
Phase-1b-vm Wave-ε E-vm-impl-tail: the legacy ValueWord
parameter type referenced the deleted runtime carrier (CLAUDE.md
“Forbidden Patterns”). The signature is flipped to the kinded
carrier shape_value::KindedSlot per ADR-006 §2.7 / Q7. The
kinded direct-write path on the VM is push_kinded(bits, kind);
host-integration / test helpers that still build values via the
legacy carrier need a Phase-2c host-API rebuild (see ADR-006
§2.7.4 — output-adapter cluster). The body is preserved as a
todo!() so callers fail loudly rather than silently dropping
the value.
Trait Implementations§
Source§impl DebuggerIntegration for VirtualMachine
impl DebuggerIntegration for VirtualMachine
Source§fn trace_state(&self)
fn trace_state(&self)
Source§fn debug_break(&self)
fn debug_break(&self)
Source§fn instruction_pointer(&self) -> usize
fn instruction_pointer(&self) -> usize
Source§fn stack_size(&self) -> usize
fn stack_size(&self) -> usize
Source§fn stack_top(&self) -> Option<String>
fn stack_top(&self) -> Option<String>
Source§fn stack_values_vec(&self) -> Vec<String>
fn stack_values_vec(&self) -> Vec<String>
Source§fn call_stack_depth(&self) -> usize
fn call_stack_depth(&self) -> usize
Source§fn call_frames(&self) -> &[CallFrame]
fn call_frames(&self) -> &[CallFrame]
Source§fn local_values_vec(&self) -> Vec<String>
fn local_values_vec(&self) -> Vec<String>
Source§fn module_binding_values(&self) -> Vec<(u64, NativeKind)>
fn module_binding_values(&self) -> Vec<(u64, NativeKind)>
Source§fn set_module_binding(&mut self, index: usize, bits: u64, kind: NativeKind)
fn set_module_binding(&mut self, index: usize, bits: u64, kind: NativeKind)
Source§fn set_trace_mode(&mut self, enabled: bool)
fn set_trace_mode(&mut self, enabled: bool)
Source§fn debugger_mut(&mut self) -> Option<&mut VMDebugger>
fn debugger_mut(&mut self) -> Option<&mut VMDebugger>
Source§fn has_debugger(&self) -> bool
fn has_debugger(&self) -> bool
Source§impl Drop for VirtualMachine
Drop implementation for VirtualMachine.
impl Drop for VirtualMachine
Drop implementation for VirtualMachine.
Releases the strong-count share that each live stack slot and each
module-binding slot owns over its heap-tagged payload. Per ADR-006
§2.7.7 / §2.7.8, the stack and the module-binding store each carry a
parallel Vec<NativeKind> track; teardown dispatches
drop_with_kind(bits, kind) per slot — the kind-aware counterpart of
the deleted vw_drop_slice call (forbidden #8 per §2.7.7).
The shared_module_bindings Arc reclamation still runs first because
those slots hold raw Arc::into_raw pointer bits (not heap-tagged
values) and the producer is the unique strong owner — the kind-aware
drop loop over module_bindings skips them because the slot bits
have been zeroed and drop_with_kind is a no-op on the zero bit
pattern.
Source§impl GCIntegration for VirtualMachine
Available on non-crate feature gc only.
impl GCIntegration for VirtualMachine
gc only.Source§fn maybe_collect_garbage(&mut self)
fn maybe_collect_garbage(&mut self)
Source§fn gc_heap_size(&self) -> usize
fn gc_heap_size(&self) -> usize
Source§fn gc_object_count(&self) -> usize
fn gc_object_count(&self) -> usize
Source§fn gc(&self) -> &GarbageCollector
fn gc(&self) -> &GarbageCollector
Source§fn gc_mut(&mut self) -> &mut GarbageCollector
fn gc_mut(&mut self) -> &mut GarbageCollector
Source§impl TypedObjectOps for VirtualMachine
impl TypedObjectOps for VirtualMachine
Source§fn op_get_field_typed(
&mut self,
instruction: &Instruction,
) -> Result<(), VMError>
fn op_get_field_typed( &mut self, instruction: &Instruction, ) -> Result<(), VMError>
Get field from typed object using precomputed field type tag.
ADR-006 §2.7.7 / Wave 6.5 cluster D-typed-obj-ops — receiver pop
uses the kinded API; heap dispatch is slot.as_heap_value() +
HeapValue::TypedObject(arc) match (Q8 single-discriminator).
The receiver share is dropped via drop_with_kind after the field
load completes (the loaded value owns its own retained share via
clone_with_kind inside push_field_value).
Source§fn op_set_field_typed(
&mut self,
instruction: &Instruction,
) -> Result<(), VMError>
fn op_set_field_typed( &mut self, instruction: &Instruction, ) -> Result<(), VMError>
Set field on typed object using precomputed field type tag.
W17-typed-object-mutation (2026-05-11) — write-path rebuild on
top of TypedObjectStorage::write_slot_in_place (the kinded
in-place projection writer added by W17-references-mutation close
30b9ebf, ADR-006 §2.7.13 / Q14). Mirror of the
RefTarget::TypedField arm in write_ref_target
(variables/mod.rs:3100) — same single-threaded VM contract,
same kind-invariance debug_assert, same heap_mask-driven
drop_with_kind on the prior occupant.
Stack contract (per assignment.rs:611-625 emit pattern):
pop value, pop receiver; mutate the receiver’s slot in place;
push the (now-mutated) receiver back so emit_nested_store_back
can either store it back to a local/binding identifier or Pop
it for non-identifier roots. Schema-mismatch falls back through
the same name-based + IC lookup chain as op_get_field_typed.
Auto Trait Implementations§
impl !Freeze for VirtualMachine
impl !RefUnwindSafe for VirtualMachine
impl Send for VirtualMachine
impl !Sync for VirtualMachine
impl Unpin for VirtualMachine
impl UnsafeUnpin for VirtualMachine
impl !UnwindSafe for VirtualMachine
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more