diff options
| -rw-r--r-- | lfunc.c | 64 | ||||
| -rw-r--r-- | lparser.c | 15 | ||||
| -rw-r--r-- | lvm.c | 6 | ||||
| -rw-r--r-- | testes/locals.lua | 55 |
4 files changed, 98 insertions, 42 deletions
| @@ -98,27 +98,29 @@ UpVal *luaF_findupval (lua_State *L, StkId level) { | |||
| 98 | 98 | ||
| 99 | 99 | ||
| 100 | static void callclose (lua_State *L, void *ud) { | 100 | static 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 | */ |
| 110 | static int prepclosingmethod (lua_State *L, StkId func) { | 110 | static 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 | */ |
| 133 | static int closeupval (lua_State *L, TValue *uv, StkId level, int status) { | 139 | static 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) { | |||
| 169 | void luaF_newtbcupval (lua_State *L, StkId level) { | 173 | void 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 | } |
| @@ -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 | } |
| @@ -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 | ||
| 176 | print"testing to-be-closed variables" | 176 | print"testing to-be-closed variables" |
| 177 | 177 | ||
| 178 | local function stack(n) n = ((n == 0) or stack(n - 1)) end | ||
| 179 | |||
| 180 | |||
| 178 | do | 181 | do |
| 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") |
| 188 | end | 191 | end |
| 189 | 192 | ||
| 193 | do | ||
| 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 | |||
| 224 | end | ||
| 225 | |||
| 226 | |||
| 227 | do | ||
| 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) | ||
| 243 | end | ||
| 190 | 244 | ||
| 191 | do -- errors in __close | 245 | do -- errors in __close |
| 192 | local log = {} | 246 | local log = {} |
| @@ -211,7 +265,6 @@ do -- errors in __close | |||
| 211 | end | 265 | end |
| 212 | 266 | ||
| 213 | if rawget(_G, "T") then | 267 | if 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 |
