Bytecode & VM
Runtime as a contract
- VM behavior must be deterministic and fully specified
- execution =
decode -> dispatch -> mutate machine state - one mutable value store (operand stack) with explicit invariants
State model
- Global state: stack, frame stack, function set
- Frame state:
ip, active instruction list, current scope chain ipstarts at0, then increments before each dispatch
Critical VM invariants
ipis always in frame bounds- stack never underflows
CALLandJUMPnever leave machine in inconsistent state- exactly one value is returned per function frame
Scope behavior
DEFINE_VAR: mutable current scope bindingDEFINE_CONST: immutable current scope bindingLOAD: search outward through scope chainSTORE: mutate nearest mutable binding onlyENTER_SCOPE/EXIT_SCOPE: make lifetime explicit
Stack + op order
- binary ops pop
rhsfirst, thenlhs lhs op rhspushed back- wrong pop order causes silent semantic bugs
Function call protocol
CALL npops args right-to-left- open callee frame and bind params
- explicit
RETURNsends one value - missing return = implicit
nil
Branching semantics
JUMPis absoluteJUMP_IF_FALSEpops one value; jumps if value isfalseornil- everything else is truthy
Error classes to teach clearly
- undefined name
- const reassignment
- bad arity / bad callee
- arithmetic/type mismatch
- division by zero
- invalid jump target
- stack underflow
Debug friendliness
- optional trace mode:
ip + opcode + stack depth + scope depth - trace should not mutate execution behavior
Why this matters for compilers
- VM is the last executor: bugs here invalidate all earlier stages
- fixed runtime contract makes later optimizations and serialization safe
Extension path
- bytecode persistence format
- compiler validation/lint mode
- register-based execution backend
- deterministic VM golden tests
1 / 1