Exercise 2: Confirming Two Closures Share One Upvalue Object — Possible Solution ==================================================================== THE CHECK ------------------------------ # inside makePair's own body, setter and getter are both compiled # BEFORE makePair itself is called -- so this check has to happen # by instrumenting the VM or capturing the closures as they're # created. Simplest: expose them as globals for inspection instead # of returning just one value. fun makePair() { var shared = 0; fun setter(v) { shared = v; } fun getter() { return shared; } gSetter = setter; # stash both in globals for inspection gGetter = getter; setter(42); return getter(); } var gSetter = nil; var gGetter = nil; print makePair(); # after running: print(vm.globals['gSetter'].upvalues[0] is vm.globals['gGetter'].upvalues[0]) RESULT ------------------------------ True -- setter's upvalues[0] and getter's upvalues[0] are the identical Python object, not merely two objects with equal values. WHICH LINE IN capture_upvalue IS RESPONSIBLE ------------------------------ def capture_upvalue(self, abs_slot): if abs_slot in self.open_upvalues: return self.open_upvalues[abs_slot] # <-- this line upv = Upvalue(self.stack, abs_slot) self.open_upvalues[abs_slot] = upv return upv When OP_CLOSURE runs for `setter`, it calls capture_upvalue(slot_of_shared), which finds no existing entry for that slot, creates a NEW Upvalue, and records it in self.open_upvalues keyed by that slot. When OP_CLOSURE runs moments later for `getter`, referencing the SAME `shared` variable, it calls capture_upvalue with the SAME abs_slot -- and this time the dictionary lookup succeeds, returning the exact same Upvalue object that was just created for setter, instead of building a second one. WHY THIS WORKS AS AN ANSWER ------------------------------ Without this dedup check, setter and getter would each get their OWN Upvalue instance, both initially pointing at the same live stack slot -- and reads/writes would still happen to agree WHILE the slot is still open, purely because they're both reading the same underlying stack position. The real difference would only show up once makePair returns and BOTH upvalues close independently: each would copy out its own snapshot of the value at that moment, and from then on setter's own copy and getter's own copy would be two separate floats that could never be kept in sync again by any future write through either closure. The dedup check is what guarantees setter and getter are actually looking at ONE variable for the rest of the program's life, not two variables that happen to start out equal.