> It sounds like the stack is perhaps a Stack<Frame pointer¹>,
No, the stack isn't a list of frames where each frame is a separate heap allocated list of slots for values. You're right that that's a very common (but not super fast) implementation technique.
The Lua stack is a single contiguous array of value slots that is used by all call frames. In fact, call frames use overlapping stack windows so that function call arguments don't need to be moved during a call.
That makes implementing closures particularly tricky since when a function returns, its region of the stack is immediately reused by later code.
Upvalues address that by moving just the local variables that are closed over off that contiguous stack array and into the heap. And they do that only when the function where the variable is declared returns so that during that functionn's lifetime, it can be accessed directly from the stack. Also, the compiler is able to manage all this just with a single pass compilation.
No, the stack isn't a list of frames where each frame is a separate heap allocated list of slots for values. You're right that that's a very common (but not super fast) implementation technique.
The Lua stack is a single contiguous array of value slots that is used by all call frames. In fact, call frames use overlapping stack windows so that function call arguments don't need to be moved during a call.
That makes implementing closures particularly tricky since when a function returns, its region of the stack is immediately reused by later code.
Upvalues address that by moving just the local variables that are closed over off that contiguous stack array and into the heap. And they do that only when the function where the variable is declared returns so that during that functionn's lifetime, it can be accessed directly from the stack. Also, the compiler is able to manage all this just with a single pass compilation.
It's really clever.