Reading — step 1 of 3
Functions and Closures in Bytecode
Compiling Functions & Closures
Functions are the most complex feature to compile. Each function gets its own Chunk of bytecode, and closures require a mechanism to capture variables from enclosing scopes.
Separate Chunks
Each function is compiled into its own Chunk:
fun add(a, b) { return a + b; }
The compiler creates a new Chunk for add, compiles its body into it, then wraps it in a function object stored in the enclosing chunk's constant pool.
Call Frames
The VM needs a call stack to track nested function calls:
CallFrame {
function: Function // the function being called
ip: int // instruction pointer within this function's chunk
slots: int // base index in the value stack for this frame
}
OP_CALL pushes a new CallFrame. OP_RETURN pops it and restores the caller's frame.
Compiling Closures and Upvalues
When a function references a variable from an enclosing scope, the compiler emits OP_CLOSURE instead of OP_CONSTANT:
OP_CLOSURE [function_index] [upvalue_count] [upvalue_descriptors...]
Each upvalue descriptor specifies:
- isLocal: is the captured variable a local in the immediately enclosing function?
- index: the stack slot (if local) or the upvalue index (if already captured)
Upvalue Resolution
fun outer() {
var x = 10;
fun middle() {
fun inner() {
print x; // x is two scopes out
}
inner();
}
middle();
}
inner captures x from outer. But middle is in between. The compiler makes middle capture x as an upvalue too (even though it does not use it directly), so that inner can capture it from middle.
Closing Upvalues
When a local variable goes out of scope, any closure that captured it still needs it. We close the upvalue by moving the value from the stack to the heap:
OP_CLOSE_UPVALUE // move stack local to heap-allocated upvalue
This is Lua's elegant solution, adopted directly in Crafting Interpreters. Before closing, the upvalue points to the stack slot. After closing, it points to its own heap copy.
Your Task
Compile function declarations, implement call frames, and handle closures with upvalue capture and closing.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…