From 41c800b352149e037bdebd5f20d2f25ed2a0e2a5 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Thu, 25 Oct 2018 12:50:20 -0300 Subject: Closing methods should not interfere with returning values A closing method cannot be called in its own stack slot, as there may be returning values in the stack after that slot, and the call would corrupt those values. Instead, the closing method must be copied to the top of the stack to be called. Moreover, even when a function returns no value, its return istruction still has to have its position (which will set the stack top) after the local variables, otherwise a closing method might corrupt another not-yet-called closing method. --- testes/locals.lua | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) (limited to 'testes') diff --git a/testes/locals.lua b/testes/locals.lua index f21fa2ec..65b145db 100644 --- a/testes/locals.lua +++ b/testes/locals.lua @@ -175,6 +175,9 @@ assert(x==20) print"testing to-be-closed variables" +local function stack(n) n = ((n == 0) or stack(n - 1)) end + + do local a = {} do @@ -187,6 +190,57 @@ do assert(a[1] == "in" and a[2] == "y" and a[3] == "x" and a[4] == "out") end +do + local X = false + + local function closescope () stack(10); X = true end + + -- closing functions do not corrupt returning values + local function foo (x) + local scoped _ = closescope + return x, X, 23 + end + + local a, b, c = foo(1.5) + assert(a == 1.5 and b == false and c == 23 and X == true) + + X = false + foo = function (x) + local scoped _ = closescope + local y = 15 + return y + end + + assert(foo() == 15 and X == true) + + X = false + foo = function () + local scoped x = closescope + return x + end + + assert(foo() == closescope and X == true) + +end + + +do + -- to-be-closed variables must be closed in tail calls + local X, Y + local function foo () + local scoped _ = function () Y = 10 end + assert(X == 20 and Y == nil) + return 1,2,3 + end + + local function bar () + local scoped _ = function () X = 20 end + return foo() + end + + local a, b, c, d = bar() + assert(a == 1 and b == 2 and c == 3 and X == 20 and Y == 10 and d == nil) +end do -- errors in __close local log = {} @@ -211,7 +265,6 @@ do -- errors in __close end if rawget(_G, "T") then - local function stack(n) n = (n == 0) or stack(n - 1); end; -- memory error inside closing function local function foo () local scoped y = function () T.alloccount() end -- cgit v1.2.3-55-g6feb