diff options
| -rw-r--r-- | lfunc.c | 44 | ||||
| -rw-r--r-- | testes/locals.lua | 46 |
2 files changed, 63 insertions, 27 deletions
| @@ -101,31 +101,32 @@ UpVal *luaF_findupval (lua_State *L, StkId level) { | |||
| 101 | 101 | ||
| 102 | 102 | ||
| 103 | /* | 103 | /* |
| 104 | ** Prepare closing method plus its arguments for object 'obj' with | 104 | ** Call closing method for object 'obj' with error message 'err'. |
| 105 | ** error message 'err'. (This function assumes EXTRA_STACK.) | 105 | ** (This function assumes EXTRA_STACK.) |
| 106 | */ | 106 | */ |
| 107 | static int prepclosingmethod (lua_State *L, TValue *obj, TValue *err) { | 107 | static void callclosemethod (lua_State *L, TValue *obj, TValue *err) { |
| 108 | StkId top = L->top; | 108 | StkId top = L->top; |
| 109 | const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE); | 109 | const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE); |
| 110 | if (ttisnil(tm)) /* no metamethod? */ | ||
| 111 | return 0; /* nothing to call */ | ||
| 112 | setobj2s(L, top, tm); /* will call metamethod... */ | 110 | setobj2s(L, top, tm); /* will call metamethod... */ |
| 113 | setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */ | 111 | setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */ |
| 114 | setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */ | 112 | setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */ |
| 115 | L->top = top + 3; /* add function and arguments */ | 113 | L->top = top + 3; /* add function and arguments */ |
| 116 | return 1; | 114 | luaD_callnoyield(L, top, 0); /* call method */ |
| 117 | } | 115 | } |
| 118 | 116 | ||
| 119 | 117 | ||
| 120 | /* | 118 | /* |
| 121 | ** Raise an error with message 'msg', inserting the name of the | 119 | ** Check whether 'obj' has a close metamethod and raise an error |
| 122 | ** local variable at position 'level' in the stack. | 120 | ** if not. |
| 123 | */ | 121 | */ |
| 124 | static void varerror (lua_State *L, StkId level, const char *msg) { | 122 | static void checkclosemth (lua_State *L, StkId level, const TValue *obj) { |
| 125 | int idx = cast_int(level - L->ci->func); | 123 | const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE); |
| 126 | const char *vname = luaG_findlocal(L, L->ci, idx, NULL); | 124 | if (ttisnil(tm)) { /* no metamethod? */ |
| 127 | if (vname == NULL) vname = "?"; | 125 | int idx = cast_int(level - L->ci->func); /* variable index */ |
| 128 | luaG_runerror(L, msg, vname); | 126 | const char *vname = luaG_findlocal(L, L->ci, idx, NULL); |
| 127 | if (vname == NULL) vname = "?"; | ||
| 128 | luaG_runerror(L, "variable '%s' got a non-closable value", vname); | ||
| 129 | } | ||
| 129 | } | 130 | } |
| 130 | 131 | ||
| 131 | 132 | ||
| @@ -136,7 +137,7 @@ static void varerror (lua_State *L, StkId level, const char *msg) { | |||
| 136 | ** the 'level' of the upvalue being closed, as everything after that | 137 | ** the 'level' of the upvalue being closed, as everything after that |
| 137 | ** won't be used again. | 138 | ** won't be used again. |
| 138 | */ | 139 | */ |
| 139 | static void callclosemth (lua_State *L, StkId level, int status) { | 140 | static void prepcallclosemth (lua_State *L, StkId level, int status) { |
| 140 | TValue *uv = s2v(level); /* value being closed */ | 141 | TValue *uv = s2v(level); /* value being closed */ |
| 141 | TValue *errobj; | 142 | TValue *errobj; |
| 142 | if (status == CLOSEKTOP) | 143 | if (status == CLOSEKTOP) |
| @@ -145,10 +146,7 @@ static void callclosemth (lua_State *L, StkId level, int status) { | |||
| 145 | errobj = s2v(level + 1); /* error object goes after 'uv' */ | 146 | errobj = s2v(level + 1); /* error object goes after 'uv' */ |
| 146 | luaD_seterrorobj(L, status, level + 1); /* set error object */ | 147 | luaD_seterrorobj(L, status, level + 1); /* set error object */ |
| 147 | } | 148 | } |
| 148 | if (prepclosingmethod(L, uv, errobj)) /* something to call? */ | 149 | callclosemethod(L, uv, errobj); |
| 149 | luaD_callnoyield(L, L->top - 3, 0); /* call method */ | ||
| 150 | else if (!l_isfalse(uv)) /* non-closable non-false value? */ | ||
| 151 | varerror(L, level, "attempt to close non-closable variable '%s'"); | ||
| 152 | } | 150 | } |
| 153 | 151 | ||
| 154 | 152 | ||
| @@ -171,16 +169,12 @@ void luaF_newtbcupval (lua_State *L, StkId level) { | |||
| 171 | lua_assert(L->openupval == NULL || uplevel(L->openupval) < level); | 169 | lua_assert(L->openupval == NULL || uplevel(L->openupval) < level); |
| 172 | if (!l_isfalse(obj)) { /* false doesn't need to be closed */ | 170 | if (!l_isfalse(obj)) { /* false doesn't need to be closed */ |
| 173 | int status; | 171 | int status; |
| 174 | const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE); | 172 | checkclosemth(L, level, obj); |
| 175 | if (ttisnil(tm)) /* no metamethod? */ | ||
| 176 | varerror(L, level, "variable '%s' got a non-closable value"); | ||
| 177 | status = luaD_rawrunprotected(L, trynewtbcupval, level); | 173 | status = luaD_rawrunprotected(L, trynewtbcupval, level); |
| 178 | if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */ | 174 | if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */ |
| 179 | lua_assert(status == LUA_ERRMEM); | 175 | lua_assert(status == LUA_ERRMEM); |
| 180 | luaD_seterrorobj(L, LUA_ERRMEM, level + 1); /* save error message */ | 176 | luaD_seterrorobj(L, LUA_ERRMEM, level + 1); /* save error message */ |
| 181 | /* next call must succeed, as object is closable */ | 177 | callclosemethod(L, s2v(level), s2v(level + 1)); |
| 182 | prepclosingmethod(L, s2v(level), s2v(level + 1)); | ||
| 183 | luaD_callnoyield(L, L->top - 3, 0); /* call method */ | ||
| 184 | luaD_throw(L, LUA_ERRMEM); /* throw memory error */ | 178 | luaD_throw(L, LUA_ERRMEM); /* throw memory error */ |
| 185 | } | 179 | } |
| 186 | } | 180 | } |
| @@ -215,7 +209,7 @@ void luaF_close (lua_State *L, StkId level, int status) { | |||
| 215 | } | 209 | } |
| 216 | if (uv->tbc && status != NOCLOSINGMETH) { | 210 | if (uv->tbc && status != NOCLOSINGMETH) { |
| 217 | ptrdiff_t levelrel = savestack(L, level); | 211 | ptrdiff_t levelrel = savestack(L, level); |
| 218 | callclosemth(L, upl, status); /* may change the stack */ | 212 | prepcallclosemth(L, upl, status); /* may change the stack */ |
| 219 | level = restorestack(L, levelrel); | 213 | level = restorestack(L, levelrel); |
| 220 | } | 214 | } |
| 221 | } | 215 | } |
diff --git a/testes/locals.lua b/testes/locals.lua index 84e0b03a..1b43609b 100644 --- a/testes/locals.lua +++ b/testes/locals.lua | |||
| @@ -459,8 +459,50 @@ do -- errors due to non-closable values | |||
| 459 | getmetatable(xyz).__close = nil -- remove metamethod | 459 | getmetatable(xyz).__close = nil -- remove metamethod |
| 460 | end | 460 | end |
| 461 | local stat, msg = pcall(foo) | 461 | local stat, msg = pcall(foo) |
| 462 | assert(not stat and | 462 | assert(not stat and string.find(msg, "attempt to call a nil value")) |
| 463 | string.find(msg, "attempt to close non%-closable variable 'xyz'")) | 463 | end |
| 464 | |||
| 465 | |||
| 466 | do -- tbc inside close methods | ||
| 467 | local track = {} | ||
| 468 | local function foo () | ||
| 469 | local x <close> = func2close(function () | ||
| 470 | local xx <close> = func2close(function (_, msg) | ||
| 471 | assert(msg == nil) | ||
| 472 | track[#track + 1] = "xx" | ||
| 473 | end) | ||
| 474 | track[#track + 1] = "x" | ||
| 475 | end) | ||
| 476 | track[#track + 1] = "foo" | ||
| 477 | return 20, 30, 40 | ||
| 478 | end | ||
| 479 | local a, b, c, d = foo() | ||
| 480 | assert(a == 20 and b == 30 and c == 40 and d == nil) | ||
| 481 | assert(track[1] == "foo" and track[2] == "x" and track[3] == "xx") | ||
| 482 | |||
| 483 | -- again, with errors | ||
| 484 | local track = {} | ||
| 485 | local function foo () | ||
| 486 | local x0 <close> = func2close(function (_, msg) | ||
| 487 | assert(msg == 202) | ||
| 488 | track[#track + 1] = "x0" | ||
| 489 | end) | ||
| 490 | local x <close> = func2close(function () | ||
| 491 | local xx <close> = func2close(function (_, msg) | ||
| 492 | assert(msg == 101) | ||
| 493 | track[#track + 1] = "xx" | ||
| 494 | error(202) | ||
| 495 | end) | ||
| 496 | track[#track + 1] = "x" | ||
| 497 | error(101) | ||
| 498 | end) | ||
| 499 | track[#track + 1] = "foo" | ||
| 500 | return 20, 30, 40 | ||
| 501 | end | ||
| 502 | local st, msg = pcall(foo) | ||
| 503 | assert(not st and msg == 202) | ||
| 504 | assert(track[1] == "foo" and track[2] == "x" and track[3] == "xx" and | ||
| 505 | track[4] == "x0") | ||
| 464 | end | 506 | end |
| 465 | 507 | ||
| 466 | 508 | ||
