v3 Bytecode: Closures and Indexing
Lesson, slides, and applied problem sets.
View SlidesLesson
v3 Bytecode: Closures + Collections
Why this module exists
v3 compiler output is now richer than expression math. It must model:
- lexical closures
- composite heap values
- index operations on mutable containers
This means bytecode is not just arithmetic anymore; it is a compact contract between compiler and runtime.
1) Closure-oriented bytecode model
v3 introduces explicit closure creation and call flow:
- function declarations compile into function objects in the code section
MAKE_CLOSURE <id>creates a callable value in the operand stackCALL <arity>executes whatever callable is on the stack
If there is no closure object at call time, VM should fail with a runtime error.
Design implication:
- compile-time does not execute closure contents
- runtime owns lifetime of captured values and callable identity
2) Function object identity and environment binding
Even before GC is introduced, treat function values as identity-bearing values.
At compile time, each function declaration should:
- have stable entry index/id
- compile body as separate function bytecode unit
- be referenced through id in closure/call instructions
MAKE_CLOSURE can be viewed as:
- lookup function metadata by
<id> - capture current lexical references (in later stages, this becomes closure env)
- push a callable object to stack
This keeps callsites uniform regardless of whether callee is named or passed around.
3) Collections as first-class bytecoded operations
MAKE_ARRAY <n>
- pops
nvalues in reverse push order - returns one array value on stack
MAKE_MAP <n>
- pops
2noperands:k1 v1 k2 v2 ... kn vn - stores by pair order
GET_INDEX
- pops container + index
- reads by key/index semantics configured by VM
- returns looked-up value or nil/error per semantics
SET_INDEX
- pops container + index + value (or value + index + container depending on VM ordering)
- mutates container in place
- pushes updated container or nothing depending on design choice
For this course, explicit ordering should be deterministic and taught once, not rediscovered by students in implementation.
4) Bytecode stack discipline for container ops
Index and collection instructions are easy to get subtly wrong. Recommended mental model:
- arithmetic and call ops are stack-consuming in a fixed arity
- container ops should mirror this predictability:
- create ops consume source values and return 1 container result
- access ops consume exactly the operands needed and push result/error signal
Avoid implicit popping patterns that depend on implementation internals; make operand order visible in the spec.
5) Control flow and scope op ordering (v3 continuation)
JUMP and JUMP_IF_FALSE remain the same branching core from earlier:
- absolute target addresses
JUMP_IF_FALSEpops one condition value first- explicit
ENTER_SCOPE/EXIT_SCOPE
What changes with closures/collections is that values on these instructions can now be containers or callable values; operand stack traces become more important.
6) Error behavior as compiler/VM contract
v3 bytecode execution should make failure explicit:
- calling a non-closure value
- indexing non-indexable type
- map key arity mismatch in map literal creation
- bad
CALLarity when target is known function
A robust instruction contract is more valuable than clever op additions.
7) Practice checkpoints (almost solved)
1) Array construction ordering + +Input bytecode: PUSH 1, PUSH 2, PUSH 3, MAKE_ARRAY 3 + +Expected stack: one array value containing [1,2,3] in that order. + +2) Map construction ordering + +Input: PUSH "a", PUSH 1, PUSH "b", PUSH 2, MAKE_MAP 2 + +Expected: map with keys "a"->1, "b"->2. + +3) Closure call flow + +Compile-time expectation: MAKE_CLOSURE places callable; CALL n consumes it and n args and pushes return value. + +4) Non-callable dispatch + +Stack contains 1 then CALL 0 executes + +Expected: runtime error "call target is not callable" (or equivalent). + +5) Indexing contract + +Given map m, op sequence should consistently consume operands and return either value or error by defined policy. + +This module should give students a clean contract for rich bytecode before moving into runtime GC behavior.
Module Items
Compile to Bytecode v3