github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/notes/to-be-closed.md (about) 1 # Implementation notes for to-be-closed variables 2 3 This is a feature introduced in Lua 5.4. See 4 https://www.lua.org/manual/5.4/manual.html#3.3.8 5 6 ## Approach 7 8 I try to give a succinct explanation of the implementation approach. 9 10 ### Height of a lexical scope 11 12 During AST -> IR compilation, a **height** is assigned to each lexical scope. 13 If L2 is a lexical scope with parent L1, then 14 15 height(L2) = height(L1) + n, where n is the number of to-be-closed variables that L2 defines 16 17 If L is a root lexical scope (i.e. it is the outermost block in a function body) 18 then 19 20 height(L) = 0 21 22 ### Code generation 23 24 There are 2 new opcodes: 25 - `clpush <reg>`: push the contents of `<reg>` onto the close stack 26 - `cltrunc h`: truncate the close stack to height `h` (`h` is encoded in the 27 opcode and must be known at compile time) 28 29 This is how the opcodes are inserted 30 - Whenever a to-be-closed varable is defined (`local x <close> = val`), a 31 `clpush r1` instruction is emitted, where `r1` is the register containing 32 `val`. 33 - Whenever a lexical scope L is exited, a `cltrunc h` instruction is emitted 34 where `h` is the height of the parent scope of `L` 35 - Whenever a Jump is emitted, just before the jump is emitted a `cltrunc h` 36 instruction is emitted where `h` is the height of the lexical scope of the 37 jump destination. 38 39 ### Runtime 40 41 There are two aspects to the runtime machinery - normal execution of Lua 42 continutaions and error handling 43 44 #### Normal execution of Lua continuations 45 46 Each Lua thread maintains a **close stack**, which is a stack of Lua 47 values. It starts empty and is modified as follows 48 - when a Lua continuation is created it records the current height of the close 49 stack as an offset, called `base`. 50 - `clpush <reg>` pushes the value of `<reg>` on top of the close stack. 51 - `cltrunc h` pops values from the top of the close stack and executes their 52 `__close` metamethod until the height of the close stack is at most `h + base`. 53 - When returning from a Lua function, a `cltrunc 0` instruction is executed. 54 - tail-calls are forbidden if there are to-be-closed values pending at the point 55 of the tail-call, because they need to be run after returning from the call. 56 57 #### Error handling 58 59 If there is an error, then all close stacks in the "call stack" should be called 60 until the error is caught. The `Thread.CallContext` function makes sure the 61 pending to-be-closed values are called after execution has finished, whether 62 there was an error or not. 63 64 #### Coroutines 65 66 Lua 5.4 adds a `coroutine.close()` function that allows stopping a suspended 67 coroutine but executing all pending to-be-closed variables. Given the approach 68 above this is simply a matter of executing the `cleanupCloseStack()` method on 69 the suspended thread. 70