diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2020-12-21 15:21:45 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2020-12-21 15:21:45 -0300 |
commit | f9d29b0c442447ebe429bcaad1e2b4bf13c5dc93 (patch) | |
tree | 05864d5bb96e6efe207e7d7eeff0fd251494562b | |
parent | 409256b7849ec5ab3296cb0ab9eba3d65955d5ea (diff) | |
download | lua-f9d29b0c442447ebe429bcaad1e2b4bf13c5dc93.tar.gz lua-f9d29b0c442447ebe429bcaad1e2b4bf13c5dc93.tar.bz2 lua-f9d29b0c442447ebe429bcaad1e2b4bf13c5dc93.zip |
Upvalues removed from 'openupval' before being closed
Undo commit c220b0a5d0: '__close' is not called again in case of
errors. (Upvalue is removed from the list before the call.) The
common error that justified that change was C stack overflows, which
are much rarer with the stackless implementation.
-rw-r--r-- | lfunc.c | 30 | ||||
-rw-r--r-- | manual/manual.of | 1 | ||||
-rw-r--r-- | testes/locals.lua | 26 |
3 files changed, 31 insertions, 26 deletions
@@ -220,24 +220,38 @@ void luaF_unlinkupval (UpVal *uv) { | |||
220 | } | 220 | } |
221 | 221 | ||
222 | 222 | ||
223 | /* | ||
224 | ** Close all upvalues up to the given stack level. 'status' indicates | ||
225 | ** how/why the function was called: | ||
226 | ** - LUA_OK: regular code exiting the scope of a variable; may raise | ||
227 | ** an error due to errors in __close metamethods; | ||
228 | ** - CLOSEPROTECT: finishing a thread; run all metamethods in protected | ||
229 | ** mode; | ||
230 | ** - NOCLOSINGMETH: close upvalues without running __close metamethods; | ||
231 | ** - other values: error status from previous errors, to be propagated. | ||
232 | ** | ||
233 | ** Returns the resulting status, either the original status or an error | ||
234 | ** in a closing method. | ||
235 | */ | ||
223 | int luaF_close (lua_State *L, StkId level, int status) { | 236 | int luaF_close (lua_State *L, StkId level, int status) { |
224 | UpVal *uv; | 237 | UpVal *uv; |
225 | while ((uv = L->openupval) != NULL && uplevel(uv) >= level) { | 238 | StkId upl; /* stack index pointed by 'uv' */ |
239 | while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) { | ||
226 | TValue *slot = &uv->u.value; /* new position for value */ | 240 | TValue *slot = &uv->u.value; /* new position for value */ |
227 | lua_assert(uplevel(uv) < L->top); | 241 | lua_assert(uplevel(uv) < L->top); |
228 | if (uv->tbc && status != NOCLOSINGMETH) { | 242 | luaF_unlinkupval(uv); /* remove upvalue from 'openupval' list */ |
229 | /* must run closing method, which may change the stack */ | ||
230 | ptrdiff_t levelrel = savestack(L, level); | ||
231 | status = callclosemth(L, uplevel(uv), status); | ||
232 | level = restorestack(L, levelrel); | ||
233 | } | ||
234 | luaF_unlinkupval(uv); | ||
235 | setobj(L, slot, uv->v); /* move value to upvalue slot */ | 243 | setobj(L, slot, uv->v); /* move value to upvalue slot */ |
236 | uv->v = slot; /* now current value lives here */ | 244 | uv->v = slot; /* now current value lives here */ |
237 | if (!iswhite(uv)) { /* neither white nor dead? */ | 245 | if (!iswhite(uv)) { /* neither white nor dead? */ |
238 | nw2black(uv); /* closed upvalues cannot be gray */ | 246 | nw2black(uv); /* closed upvalues cannot be gray */ |
239 | luaC_barrier(L, uv, slot); | 247 | luaC_barrier(L, uv, slot); |
240 | } | 248 | } |
249 | if (uv->tbc && status != NOCLOSINGMETH) { | ||
250 | /* must run closing method, which may change the stack */ | ||
251 | ptrdiff_t levelrel = savestack(L, level); | ||
252 | status = callclosemth(L, upl, status); | ||
253 | level = restorestack(L, levelrel); | ||
254 | } | ||
241 | } | 255 | } |
242 | return status; | 256 | return status; |
243 | } | 257 | } |
diff --git a/manual/manual.of b/manual/manual.of index 164e359a..5d0c35cf 100644 --- a/manual/manual.of +++ b/manual/manual.of | |||
@@ -1630,7 +1630,6 @@ they are closed in the reverse order that they were declared. | |||
1630 | If there is any error while running a closing method, | 1630 | If there is any error while running a closing method, |
1631 | that error is handled like an error in the regular code | 1631 | that error is handled like an error in the regular code |
1632 | where the variable was defined. | 1632 | where the variable was defined. |
1633 | However, Lua may call the method one more time. | ||
1634 | 1633 | ||
1635 | After an error, | 1634 | After an error, |
1636 | the other pending closing methods will still be called. | 1635 | the other pending closing methods will still be called. |
diff --git a/testes/locals.lua b/testes/locals.lua index e2f6f35c..d32a9a3e 100644 --- a/testes/locals.lua +++ b/testes/locals.lua | |||
@@ -392,21 +392,13 @@ do print("testing errors in __close") | |||
392 | 392 | ||
393 | local y <close> = | 393 | local y <close> = |
394 | func2close(function (self, msg) | 394 | func2close(function (self, msg) |
395 | assert(string.find(msg, "@z")) -- first error in 'z' | 395 | assert(string.find(msg, "@z")) -- error in 'z' |
396 | checkwarn("@z") -- second error in 'z' generated a warning | ||
397 | error("@y") | 396 | error("@y") |
398 | end) | 397 | end) |
399 | 398 | ||
400 | local first = true | ||
401 | local z <close> = | 399 | local z <close> = |
402 | -- 'z' close is called twice | ||
403 | func2close(function (self, msg) | 400 | func2close(function (self, msg) |
404 | if first then | 401 | assert(msg == nil) |
405 | assert(msg == nil) | ||
406 | first = false | ||
407 | else | ||
408 | assert(string.find(msg, "@z")) -- own error | ||
409 | end | ||
410 | error("@z") | 402 | error("@z") |
411 | end) | 403 | end) |
412 | 404 | ||
@@ -468,8 +460,8 @@ do print("testing errors in __close") | |||
468 | local function foo (...) | 460 | local function foo (...) |
469 | do | 461 | do |
470 | local x1 <close> = | 462 | local x1 <close> = |
471 | func2close(function () | 463 | func2close(function (self, msg) |
472 | checkwarn("@X") | 464 | assert(string.find(msg, "@X")) |
473 | error("@Y") | 465 | error("@Y") |
474 | end) | 466 | end) |
475 | 467 | ||
@@ -494,8 +486,6 @@ do print("testing errors in __close") | |||
494 | local st, msg = xpcall(foo, debug.traceback) | 486 | local st, msg = xpcall(foo, debug.traceback) |
495 | assert(string.match(msg, "^[^ ]* @x123")) | 487 | assert(string.match(msg, "^[^ ]* @x123")) |
496 | assert(string.find(msg, "in metamethod 'close'")) | 488 | assert(string.find(msg, "in metamethod 'close'")) |
497 | checkwarn("@x123") -- from second call to close 'x123' | ||
498 | |||
499 | endwarn() | 489 | endwarn() |
500 | end | 490 | end |
501 | 491 | ||
@@ -686,8 +676,10 @@ do | |||
686 | local x = 0 | 676 | local x = 0 |
687 | local y = 0 | 677 | local y = 0 |
688 | co = coroutine.wrap(function () | 678 | co = coroutine.wrap(function () |
689 | local xx <close> = func2close(function () | 679 | local xx <close> = func2close(function (_, err) |
690 | y = y + 1; checkwarn("XXX"); error("YYY") | 680 | y = y + 1; |
681 | assert(string.find(err, "XXX")) | ||
682 | error("YYY") | ||
691 | end) | 683 | end) |
692 | local xv <close> = func2close(function () | 684 | local xv <close> = func2close(function () |
693 | x = x + 1; error("XXX") | 685 | x = x + 1; error("XXX") |
@@ -697,7 +689,7 @@ do | |||
697 | end) | 689 | end) |
698 | assert(co() == 100); assert(x == 0) | 690 | assert(co() == 100); assert(x == 0) |
699 | local st, msg = pcall(co) | 691 | local st, msg = pcall(co) |
700 | assert(x == 2 and y == 1) -- first close is called twice | 692 | assert(x == 1 and y == 1) |
701 | -- should get first error raised | 693 | -- should get first error raised |
702 | assert(not st and string.find(msg, "%w+%.%w+:%d+: XXX")) | 694 | assert(not st and string.find(msg, "%w+%.%w+:%d+: XXX")) |
703 | checkwarn("YYY") | 695 | checkwarn("YYY") |