fix: close only existing upvalues for locals in scope during stack unwinding

The previous implementation unconditionally closed the first open upvalue when
executing CLOSE_UPVALUE, which could crash when a local variable was never
actually captured by a closure. Changed closeUpvalue to closeUpvalues with a
stack threshold parameter, so only upvalues whose stack slot is at or above the
given pointer are closed. This prevents closing nonexistent upvalues for locals
that were never closed over, fixing segfaults in unused closure scenarios.

Added regression tests for unused closures and for nested scopes where only
some locals are captured.
This commit is contained in:
Bob Nystrom
2015-10-08 15:06:29 +00:00
parent ddc3ec855e
commit 21c5611bf0
3 changed files with 49 additions and 17 deletions
+11
View File
@@ -0,0 +1,11 @@
// This is a regression test. There was a bug where the VM would try to close
// an upvalue even if the upvalue was never created because the codepath for
// the closure was not executed.
{
var a = "a"
if (false) Fn.new { a }
}
// If we get here, we didn't segfault when a went out of scope.
System.print("ok") // expect: ok
@@ -0,0 +1,19 @@
// This is a regression test. When closing upvalues for discarded locals, it
// wouldn't make sure it discarded the upvalue for the correct stack slot.
//
// Here we create two locals that can be closed over, but only the first one
// actually is. When "b" goes out of scope, we need to make sure we don't
// prematurely close "a".
var closure
{
var a = "a"
{
var b = "b"
closure = Fn.new { a }
if (false) Fn.new { b }
}
System.print(closure.call()) // expect: a
}