aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2020-12-21 15:21:45 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2020-12-21 15:21:45 -0300
commitf9d29b0c442447ebe429bcaad1e2b4bf13c5dc93 (patch)
tree05864d5bb96e6efe207e7d7eeff0fd251494562b
parent409256b7849ec5ab3296cb0ab9eba3d65955d5ea (diff)
downloadlua-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.c30
-rw-r--r--manual/manual.of1
-rw-r--r--testes/locals.lua26
3 files changed, 31 insertions, 26 deletions
diff --git a/lfunc.c b/lfunc.c
index c4360f09..6608592b 100644
--- a/lfunc.c
+++ b/lfunc.c
@@ -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*/
223int luaF_close (lua_State *L, StkId level, int status) { 236int 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.
1630If there is any error while running a closing method, 1630If there is any error while running a closing method,
1631that error is handled like an error in the regular code 1631that error is handled like an error in the regular code
1632where the variable was defined. 1632where the variable was defined.
1633However, Lua may call the method one more time.
1634 1633
1635After an error, 1634After an error,
1636the other pending closing methods will still be called. 1635the 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()
500end 490end
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")