aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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