aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-10-25 12:50:20 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-10-25 12:50:20 -0300
commit41c800b352149e037bdebd5f20d2f25ed2a0e2a5 (patch)
tree740a459fd69d687dfe200fc91762208079e0c25b
parent0a9aca56caa925c42aaa683b43560357ab736ea4 (diff)
downloadlua-41c800b352149e037bdebd5f20d2f25ed2a0e2a5.tar.gz
lua-41c800b352149e037bdebd5f20d2f25ed2a0e2a5.tar.bz2
lua-41c800b352149e037bdebd5f20d2f25ed2a0e2a5.zip
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.
-rw-r--r--lfunc.c64
-rw-r--r--lparser.c15
-rw-r--r--lvm.c6
-rw-r--r--testes/locals.lua55
4 files changed, 98 insertions, 42 deletions
diff --git a/lfunc.c b/lfunc.c
index 4f9362f3..15874f88 100644
--- a/lfunc.c
+++ b/lfunc.c
@@ -98,27 +98,29 @@ UpVal *luaF_findupval (lua_State *L, StkId level) {
98 98
99 99
100static void callclose (lua_State *L, void *ud) { 100static void callclose (lua_State *L, void *ud) {
101 luaD_callnoyield(L, cast(StkId, ud), 0); 101 UNUSED(ud);
102 luaD_callnoyield(L, L->top - 2, 0);
102} 103}
103 104
104 105
105/* 106/*
106** Prepare closing method with its argument for object at 107** Prepare closing method plus its argument for object 'obj' with
107** index 'func' in the stack. Assume there is an error message 108** error message 'err'. (This function assumes EXTRA_STACK.)
108** (or nil) just below the object.
109*/ 109*/
110static int prepclosingmethod (lua_State *L, StkId func) { 110static int prepclosingmethod (lua_State *L, TValue *obj, TValue *err) {
111 if (ttisfunction(s2v(func))) { /* object to-be-closed is a function? */ 111 StkId top = L->top;
112 setobjs2s(L, func + 1, func - 1); /* push error msg. as argument */ 112 if (ttisfunction(obj)) { /* object to-be-closed is a function? */
113 setobj2s(L, top, obj); /* push function */
114 setobj2s(L, top + 1, err); /* push error msg. as argument */
113 } 115 }
114 else { /* try '__close' metamethod */ 116 else { /* try '__close' metamethod */
115 const TValue *tm = luaT_gettmbyobj(L, s2v(func), TM_CLOSE); 117 const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE);
116 if (ttisnil(tm)) /* no metamethod? */ 118 if (ttisnil(tm)) /* no metamethod? */
117 return 0; /* nothing to call */ 119 return 0; /* nothing to call */
118 setobjs2s(L, func + 1, func); /* 'self' is the argument */ 120 setobj2s(L, top, tm); /* will call metamethod... */
119 setobj2s(L, func, tm); /* will call metamethod */ 121 setobj2s(L, top + 1, obj); /* with 'self' as the argument */
120 } 122 }
121 L->top = func + 2; /* add function and argument */ 123 L->top = top + 2; /* add function and argument */
122 return 1; 124 return 1;
123} 125}
124 126
@@ -129,22 +131,24 @@ static int prepclosingmethod (lua_State *L, StkId func) {
129** will be handled there. Otherwise, a previous error already 131** will be handled there. Otherwise, a previous error already
130** activated original protected call, and so the call to the 132** activated original protected call, and so the call to the
131** closing method must be protected here. 133** closing method must be protected here.
134** If status is OK, the call to the closing method will be pushed
135** at the top of the stack. Otherwise, values are pushed after
136** the 'level' of the upvalue being closed, as everything after
137** that won't be used again.
132*/ 138*/
133static int closeupval (lua_State *L, TValue *uv, StkId level, int status) { 139static int closeupval (lua_State *L, TValue *uv, StkId level, int status) {
134 StkId func = level + 1; /* save slot for old error message */ 140 if (likely(status == LUA_OK)) {
135 if (unlikely(status != LUA_OK)) /* was there an error? */ 141 if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */
136 luaD_seterrorobj(L, status, level); /* save error message */ 142 callclose(L, NULL); /* call closing method */
137 else 143 }
138 setnilvalue(s2v(level)); /* no error message */ 144 else { /* there was an error */
139 setobj2s(L, func, uv); /* put object on top of error message */ 145 /* save error message and set stack top to 'level + 1' */
140 if (!prepclosingmethod(L, func)) 146 luaD_seterrorobj(L, status, level);
141 return status; /* nothing to call */ 147 if (prepclosingmethod(L, uv, s2v(level))) { /* something to call? */
142 if (likely(status == LUA_OK)) /* not in "error mode"? */ 148 int newstatus = luaD_pcall(L, callclose, NULL, savestack(L, level), 0);
143 callclose(L, func); /* call closing method */ 149 if (newstatus != LUA_OK) /* another error when closing? */
144 else { /* already inside error handler; cannot raise another error */ 150 status = newstatus; /* this will be the new error */
145 int newstatus = luaD_pcall(L, callclose, func, savestack(L, level), 0); 151 }
146 if (newstatus != LUA_OK) /* error when closing? */
147 status = newstatus; /* this will be the new error */
148 } 152 }
149 return status; 153 return status;
150} 154}
@@ -169,12 +173,10 @@ static void trynewtbcupval (lua_State *L, void *ud) {
169void luaF_newtbcupval (lua_State *L, StkId level) { 173void luaF_newtbcupval (lua_State *L, StkId level) {
170 int status = luaD_rawrunprotected(L, trynewtbcupval, level); 174 int status = luaD_rawrunprotected(L, trynewtbcupval, level);
171 if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */ 175 if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */
172 StkId func = level + 1;
173 lua_assert(status == LUA_ERRMEM); 176 lua_assert(status == LUA_ERRMEM);
174 setobjs2s(L, func, level); /* open space for error message */ 177 luaD_seterrorobj(L, LUA_ERRMEM, level + 1); /* save error message */
175 luaD_seterrorobj(L, status, level); /* save error message */ 178 if (prepclosingmethod(L, s2v(level), s2v(level + 1)))
176 if (prepclosingmethod(L, func)) 179 callclose(L, NULL); /* call closing method */
177 callclose(L, func); /* call closing method */
178 luaD_throw(L, LUA_ERRMEM); /* throw memory error */ 180 luaD_throw(L, LUA_ERRMEM); /* throw memory error */
179 } 181 }
180} 182}
@@ -201,7 +203,7 @@ int luaF_close (lua_State *L, StkId level, int status) {
201 luaC_barrier(L, uv, slot); 203 luaC_barrier(L, uv, slot);
202 if (status >= 0 && uv->tt == LUA_TUPVALTBC) { /* must be closed? */ 204 if (status >= 0 && uv->tt == LUA_TUPVALTBC) { /* must be closed? */
203 ptrdiff_t levelrel = savestack(L, level); 205 ptrdiff_t levelrel = savestack(L, level);
204 status = closeupval(L, uv->v, upl, status); /* may reallocate the stack */ 206 status = closeupval(L, uv->v, upl, status); /* may realloc. the stack */
205 level = restorestack(L, levelrel); 207 level = restorestack(L, levelrel);
206 } 208 }
207 } 209 }
diff --git a/lparser.c b/lparser.c
index 6b14b800..c0c40eae 100644
--- a/lparser.c
+++ b/lparser.c
@@ -561,7 +561,7 @@ static void close_func (LexState *ls) {
561 lua_State *L = ls->L; 561 lua_State *L = ls->L;
562 FuncState *fs = ls->fs; 562 FuncState *fs = ls->fs;
563 Proto *f = fs->f; 563 Proto *f = fs->f;
564 luaK_ret(fs, 0, 0); /* final return */ 564 luaK_ret(fs, fs->nactvar, 0); /* final return */
565 leaveblock(fs); 565 leaveblock(fs);
566 lua_assert(fs->bl == NULL); 566 lua_assert(fs->bl == NULL);
567 luaK_finish(fs); 567 luaK_finish(fs);
@@ -1602,9 +1602,10 @@ static void retstat (LexState *ls) {
1602 /* stat -> RETURN [explist] [';'] */ 1602 /* stat -> RETURN [explist] [';'] */
1603 FuncState *fs = ls->fs; 1603 FuncState *fs = ls->fs;
1604 expdesc e; 1604 expdesc e;
1605 int first, nret; /* registers with returned values */ 1605 int nret; /* number of values being returned */
1606 int first = fs->nactvar; /* first slot to be returned */
1606 if (block_follow(ls, 1) || ls->t.token == ';') 1607 if (block_follow(ls, 1) || ls->t.token == ';')
1607 first = nret = 0; /* return no values */ 1608 nret = 0; /* return no values */
1608 else { 1609 else {
1609 nret = explist(ls, &e); /* optional return values */ 1610 nret = explist(ls, &e); /* optional return values */
1610 if (hasmultret(e.k)) { 1611 if (hasmultret(e.k)) {
@@ -1613,15 +1614,13 @@ static void retstat (LexState *ls) {
1613 SET_OPCODE(getinstruction(fs,&e), OP_TAILCALL); 1614 SET_OPCODE(getinstruction(fs,&e), OP_TAILCALL);
1614 lua_assert(GETARG_A(getinstruction(fs,&e)) == fs->nactvar); 1615 lua_assert(GETARG_A(getinstruction(fs,&e)) == fs->nactvar);
1615 } 1616 }
1616 first = fs->nactvar;
1617 nret = LUA_MULTRET; /* return all values */ 1617 nret = LUA_MULTRET; /* return all values */
1618 } 1618 }
1619 else { 1619 else {
1620 if (nret == 1) /* only one single value? */ 1620 if (nret == 1) /* only one single value? */
1621 first = luaK_exp2anyreg(fs, &e); 1621 first = luaK_exp2anyreg(fs, &e); /* can use original slot */
1622 else { 1622 else { /* values must go to the top of the stack */
1623 luaK_exp2nextreg(fs, &e); /* values must go to the stack */ 1623 luaK_exp2nextreg(fs, &e);
1624 first = fs->nactvar; /* return all active values */
1625 lua_assert(nret == fs->freereg - first); 1624 lua_assert(nret == fs->freereg - first);
1626 } 1625 }
1627 } 1626 }
diff --git a/lvm.c b/lvm.c
index 0d82756b..2a1ee175 100644
--- a/lvm.c
+++ b/lvm.c
@@ -1452,7 +1452,8 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
1452 vmbreak; 1452 vmbreak;
1453 } 1453 }
1454 vmcase(OP_CLOSE) { 1454 vmcase(OP_CLOSE) {
1455 luaF_close(L, ra, LUA_OK); 1455 L->top = ra + 1; /* everything is free after this slot */
1456 ProtectNT(luaF_close(L, ra, LUA_OK));
1456 vmbreak; 1457 vmbreak;
1457 } 1458 }
1458 vmcase(OP_TBC) { 1459 vmcase(OP_TBC) {
@@ -1619,13 +1620,14 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
1619 n = cast_int(L->top - ra); /* get what is available */ 1620 n = cast_int(L->top - ra); /* get what is available */
1620 else 1621 else
1621 L->top = ra + n; /* set call for 'luaD_poscall' */ 1622 L->top = ra + n; /* set call for 'luaD_poscall' */
1623 savepc(ci);
1622 if (TESTARG_k(i)) { 1624 if (TESTARG_k(i)) {
1623 int nparams1 = GETARG_C(i); 1625 int nparams1 = GETARG_C(i);
1624 if (nparams1) /* vararg function? */ 1626 if (nparams1) /* vararg function? */
1625 ci->func -= ci->u.l.nextraargs + nparams1; 1627 ci->func -= ci->u.l.nextraargs + nparams1;
1626 luaF_close(L, base, LUA_OK); /* there may be open upvalues */ 1628 luaF_close(L, base, LUA_OK); /* there may be open upvalues */
1627 } 1629 }
1628 halfProtect(luaD_poscall(L, ci, n)); 1630 luaD_poscall(L, ci, n);
1629 return; 1631 return;
1630 } 1632 }
1631 vmcase(OP_RETURN0) { 1633 vmcase(OP_RETURN0) {
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)
175 175
176print"testing to-be-closed variables" 176print"testing to-be-closed variables"
177 177
178local function stack(n) n = ((n == 0) or stack(n - 1)) end
179
180
178do 181do
179 local a = {} 182 local a = {}
180 do 183 do
@@ -187,6 +190,57 @@ do
187 assert(a[1] == "in" and a[2] == "y" and a[3] == "x" and a[4] == "out") 190 assert(a[1] == "in" and a[2] == "y" and a[3] == "x" and a[4] == "out")
188end 191end
189 192
193do
194 local X = false
195
196 local function closescope () stack(10); X = true end
197
198 -- closing functions do not corrupt returning values
199 local function foo (x)
200 local scoped _ = closescope
201 return x, X, 23
202 end
203
204 local a, b, c = foo(1.5)
205 assert(a == 1.5 and b == false and c == 23 and X == true)
206
207 X = false
208 foo = function (x)
209 local scoped _ = closescope
210 local y = 15
211 return y
212 end
213
214 assert(foo() == 15 and X == true)
215
216 X = false
217 foo = function ()
218 local scoped x = closescope
219 return x
220 end
221
222 assert(foo() == closescope and X == true)
223
224end
225
226
227do
228 -- to-be-closed variables must be closed in tail calls
229 local X, Y
230 local function foo ()
231 local scoped _ = function () Y = 10 end
232 assert(X == 20 and Y == nil)
233 return 1,2,3
234 end
235
236 local function bar ()
237 local scoped _ = function () X = 20 end
238 return foo()
239 end
240
241 local a, b, c, d = bar()
242 assert(a == 1 and b == 2 and c == 3 and X == 20 and Y == 10 and d == nil)
243end
190 244
191do -- errors in __close 245do -- errors in __close
192 local log = {} 246 local log = {}
@@ -211,7 +265,6 @@ do -- errors in __close
211end 265end
212 266
213if rawget(_G, "T") then 267if rawget(_G, "T") then
214 local function stack(n) n = (n == 0) or stack(n - 1); end;
215 -- memory error inside closing function 268 -- memory error inside closing function
216 local function foo () 269 local function foo ()
217 local scoped y = function () T.alloccount() end 270 local scoped y = function () T.alloccount() end