diff options
-rw-r--r-- | lfunc.c | 22 | ||||
-rw-r--r-- | manual/manual.of | 11 | ||||
-rw-r--r-- | testes/locals.lua | 21 |
3 files changed, 32 insertions, 22 deletions
@@ -133,7 +133,8 @@ static int prepclosingmethod (lua_State *L, TValue *obj, TValue *err) { | |||
133 | ** the 'level' of the upvalue being closed, as everything after | 133 | ** the 'level' of the upvalue being closed, as everything after |
134 | ** that won't be used again. | 134 | ** that won't be used again. |
135 | */ | 135 | */ |
136 | static int callclosemth (lua_State *L, TValue *uv, StkId level, int status) { | 136 | static int callclosemth (lua_State *L, StkId level, int status) { |
137 | TValue *uv = s2v(level); /* value being closed */ | ||
137 | if (likely(status == LUA_OK)) { | 138 | if (likely(status == LUA_OK)) { |
138 | if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */ | 139 | if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */ |
139 | callclose(L, NULL); /* call closing method */ | 140 | callclose(L, NULL); /* call closing method */ |
@@ -145,9 +146,10 @@ static int callclosemth (lua_State *L, TValue *uv, StkId level, int status) { | |||
145 | } | 146 | } |
146 | } | 147 | } |
147 | else { /* must close the object in protected mode */ | 148 | else { /* must close the object in protected mode */ |
148 | ptrdiff_t oldtop = savestack(L, level + 1); | 149 | ptrdiff_t oldtop; |
149 | /* save error message and set stack top to 'level + 1' */ | 150 | level++; /* space for error message */ |
150 | luaD_seterrorobj(L, status, level); | 151 | oldtop = savestack(L, level + 1); /* top will be after that */ |
152 | luaD_seterrorobj(L, status, level); /* set error message */ | ||
151 | if (prepclosingmethod(L, uv, s2v(level))) { /* something to call? */ | 153 | if (prepclosingmethod(L, uv, s2v(level))) { /* something to call? */ |
152 | int newstatus = luaD_pcall(L, callclose, NULL, oldtop, 0); | 154 | int newstatus = luaD_pcall(L, callclose, NULL, oldtop, 0); |
153 | if (newstatus != LUA_OK && status == CLOSEPROTECT) /* first error? */ | 155 | if (newstatus != LUA_OK && status == CLOSEPROTECT) /* first error? */ |
@@ -203,18 +205,18 @@ int luaF_close (lua_State *L, StkId level, int status) { | |||
203 | StkId upl = uplevel(uv); | 205 | StkId upl = uplevel(uv); |
204 | TValue *slot = &uv->u.value; /* new position for value */ | 206 | TValue *slot = &uv->u.value; /* new position for value */ |
205 | lua_assert(upl < L->top); | 207 | lua_assert(upl < L->top); |
208 | if (uv->tt == LUA_TUPVALTBC && status != NOCLOSINGMETH) { | ||
209 | /* must run closing method */ | ||
210 | ptrdiff_t levelrel = savestack(L, level); | ||
211 | status = callclosemth(L, upl, status); /* may change the stack */ | ||
212 | level = restorestack(L, levelrel); | ||
213 | } | ||
206 | luaF_unlinkupval(uv); | 214 | luaF_unlinkupval(uv); |
207 | setobj(L, slot, uv->v); /* move value to upvalue slot */ | 215 | setobj(L, slot, uv->v); /* move value to upvalue slot */ |
208 | uv->v = slot; /* now current value lives here */ | 216 | uv->v = slot; /* now current value lives here */ |
209 | if (!iswhite(uv)) | 217 | if (!iswhite(uv)) |
210 | gray2black(uv); /* closed upvalues cannot be gray */ | 218 | gray2black(uv); /* closed upvalues cannot be gray */ |
211 | luaC_barrier(L, uv, slot); | 219 | luaC_barrier(L, uv, slot); |
212 | if (uv->tt == LUA_TUPVALTBC && status != NOCLOSINGMETH) { | ||
213 | /* must run closing method */ | ||
214 | ptrdiff_t levelrel = savestack(L, level); | ||
215 | status = callclosemth(L, uv->v, upl, status); /* may change the stack */ | ||
216 | level = restorestack(L, levelrel); | ||
217 | } | ||
218 | } | 220 | } |
219 | return status; | 221 | return status; |
220 | } | 222 | } |
diff --git a/manual/manual.of b/manual/manual.of index 61fcdaa3..3d2fb4fb 100644 --- a/manual/manual.of +++ b/manual/manual.of | |||
@@ -1548,14 +1548,15 @@ they are closed in the reverse order that they were declared. | |||
1548 | 1548 | ||
1549 | If there is any error while running a closing method, | 1549 | If there is any error while running a closing method, |
1550 | that error is handled like an error in the regular code | 1550 | that error is handled like an error in the regular code |
1551 | where the variable was defined; | 1551 | where the variable was defined. |
1552 | in particular, | 1552 | However, Lua may call the method one more time. |
1553 | the other pending closing methods will still be called. | 1553 | |
1554 | After an error, | 1554 | After an error, |
1555 | other errors in closing methods | 1555 | the other pending closing methods will still be called. |
1556 | Errors in these methods | ||
1556 | interrupt the respective method, | 1557 | interrupt the respective method, |
1557 | but are otherwise ignored; | 1558 | but are otherwise ignored; |
1558 | the error reported is the original one. | 1559 | the error reported is only the original one. |
1559 | 1560 | ||
1560 | If a coroutine yields and is never resumed again, | 1561 | If a coroutine yields and is never resumed again, |
1561 | some variables may never go out of scope, | 1562 | some variables may never go out of scope, |
diff --git a/testes/locals.lua b/testes/locals.lua index 1b82dd7f..73267d02 100644 --- a/testes/locals.lua +++ b/testes/locals.lua | |||
@@ -295,18 +295,23 @@ do -- errors in __close | |||
295 | local <toclose> y = | 295 | local <toclose> y = |
296 | func2close(function (self, msg) log[#log + 1] = msg; error(2) end) | 296 | func2close(function (self, msg) log[#log + 1] = msg; error(2) end) |
297 | local <toclose> z = | 297 | local <toclose> z = |
298 | func2close(function (self, msg) log[#log + 1] = msg or 10; error(3) end) | 298 | func2close(function (self, msg) |
299 | log[#log + 1] = (msg or 10) + 1; | ||
300 | error(3) | ||
301 | end) | ||
299 | if err then error(4) end | 302 | if err then error(4) end |
300 | end | 303 | end |
301 | local stat, msg = pcall(foo, false) | 304 | local stat, msg = pcall(foo, false) |
302 | assert(msg == 3) | 305 | assert(msg == 3) |
303 | assert(log[1] == 10 and log[2] == 3 and log[3] == 3 and log[4] == 3 | 306 | -- 'z' close is called twice |
304 | and #log == 4) | 307 | assert(log[1] == 11 and log[2] == 4 and log[3] == 3 and log[4] == 3 |
308 | and log[5] == 3 and #log == 5) | ||
305 | 309 | ||
306 | log = {} | 310 | log = {} |
307 | local stat, msg = pcall(foo, true) | 311 | local stat, msg = pcall(foo, true) |
308 | assert(msg == 4) | 312 | assert(msg == 4) |
309 | assert(log[1] == 4 and log[2] == 4 and log[3] == 4 and log[4] == 4 | 313 | -- 'z' close is called once |
314 | assert(log[1] == 5 and log[2] == 4 and log[3] == 4 and log[4] == 4 | ||
310 | and #log == 4) | 315 | and #log == 4) |
311 | 316 | ||
312 | -- error in toclose in vararg function | 317 | -- error in toclose in vararg function |
@@ -495,15 +500,17 @@ do | |||
495 | local st, msg = pcall(co); assert(x == 2) | 500 | local st, msg = pcall(co); assert(x == 2) |
496 | assert(not st and msg == 200) -- should get first error raised | 501 | assert(not st and msg == 200) -- should get first error raised |
497 | 502 | ||
498 | x = 0 | 503 | local x = 0 |
504 | local y = 0 | ||
499 | co = coroutine.wrap(function () | 505 | co = coroutine.wrap(function () |
500 | local <toclose> xx = func2close(function () x = x + 1; error("YYY") end) | 506 | local <toclose> xx = func2close(function () y = y + 1; error("YYY") end) |
501 | local <toclose> xv = func2close(function () x = x + 1; error("XXX") end) | 507 | local <toclose> xv = func2close(function () x = x + 1; error("XXX") end) |
502 | coroutine.yield(100) | 508 | coroutine.yield(100) |
503 | return 200 | 509 | return 200 |
504 | end) | 510 | end) |
505 | assert(co() == 100); assert(x == 0) | 511 | assert(co() == 100); assert(x == 0) |
506 | local st, msg = pcall(co); assert(x == 2) | 512 | local st, msg = pcall(co) |
513 | assert(x == 2 and y == 1) -- first close is called twice | ||
507 | -- should get first error raised | 514 | -- should get first error raised |
508 | assert(not st and string.find(msg, "%w+%.%w+:%d+: XXX")) | 515 | assert(not st and string.find(msg, "%w+%.%w+:%d+: XXX")) |
509 | end | 516 | end |