diff --git a/lfunc.c b/lfunc.c index 68d0632a..cd85cc1f 100644 --- a/lfunc.c +++ b/lfunc.c @@ -133,7 +133,8 @@ static int prepclosingmethod (lua_State *L, TValue *obj, TValue *err) { ** the 'level' of the upvalue being closed, as everything after ** that won't be used again. */ -static int callclosemth (lua_State *L, TValue *uv, StkId level, int status) { +static int callclosemth (lua_State *L, StkId level, int status) { + TValue *uv = s2v(level); /* value being closed */ if (likely(status == LUA_OK)) { if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */ callclose(L, NULL); /* call closing method */ @@ -145,9 +146,10 @@ static int callclosemth (lua_State *L, TValue *uv, StkId level, int status) { } } else { /* must close the object in protected mode */ - ptrdiff_t oldtop = savestack(L, level + 1); - /* save error message and set stack top to 'level + 1' */ - luaD_seterrorobj(L, status, level); + ptrdiff_t oldtop; + level++; /* space for error message */ + oldtop = savestack(L, level + 1); /* top will be after that */ + luaD_seterrorobj(L, status, level); /* set error message */ if (prepclosingmethod(L, uv, s2v(level))) { /* something to call? */ int newstatus = luaD_pcall(L, callclose, NULL, oldtop, 0); if (newstatus != LUA_OK && status == CLOSEPROTECT) /* first error? */ @@ -203,18 +205,18 @@ int luaF_close (lua_State *L, StkId level, int status) { StkId upl = uplevel(uv); TValue *slot = &uv->u.value; /* new position for value */ lua_assert(upl < L->top); + if (uv->tt == LUA_TUPVALTBC && status != NOCLOSINGMETH) { + /* must run closing method */ + ptrdiff_t levelrel = savestack(L, level); + status = callclosemth(L, upl, status); /* may change the stack */ + level = restorestack(L, levelrel); + } luaF_unlinkupval(uv); setobj(L, slot, uv->v); /* move value to upvalue slot */ uv->v = slot; /* now current value lives here */ if (!iswhite(uv)) gray2black(uv); /* closed upvalues cannot be gray */ luaC_barrier(L, uv, slot); - if (uv->tt == LUA_TUPVALTBC && status != NOCLOSINGMETH) { - /* must run closing method */ - ptrdiff_t levelrel = savestack(L, level); - status = callclosemth(L, uv->v, upl, status); /* may change the stack */ - level = restorestack(L, levelrel); - } } return status; } 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. If there is any error while running a closing method, that error is handled like an error in the regular code -where the variable was defined; -in particular, -the other pending closing methods will still be called. +where the variable was defined. +However, Lua may call the method one more time. + After an error, -other errors in closing methods +the other pending closing methods will still be called. +Errors in these methods interrupt the respective method, but are otherwise ignored; -the error reported is the original one. +the error reported is only the original one. If a coroutine yields and is never resumed again, 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 local y = func2close(function (self, msg) log[#log + 1] = msg; error(2) end) local z = - func2close(function (self, msg) log[#log + 1] = msg or 10; error(3) end) + func2close(function (self, msg) + log[#log + 1] = (msg or 10) + 1; + error(3) + end) if err then error(4) end end local stat, msg = pcall(foo, false) assert(msg == 3) - assert(log[1] == 10 and log[2] == 3 and log[3] == 3 and log[4] == 3 - and #log == 4) + -- 'z' close is called twice + assert(log[1] == 11 and log[2] == 4 and log[3] == 3 and log[4] == 3 + and log[5] == 3 and #log == 5) log = {} local stat, msg = pcall(foo, true) assert(msg == 4) - assert(log[1] == 4 and log[2] == 4 and log[3] == 4 and log[4] == 4 + -- 'z' close is called once + assert(log[1] == 5 and log[2] == 4 and log[3] == 4 and log[4] == 4 and #log == 4) -- error in toclose in vararg function @@ -495,15 +500,17 @@ do local st, msg = pcall(co); assert(x == 2) assert(not st and msg == 200) -- should get first error raised - x = 0 + local x = 0 + local y = 0 co = coroutine.wrap(function () - local xx = func2close(function () x = x + 1; error("YYY") end) + local xx = func2close(function () y = y + 1; error("YYY") end) local xv = func2close(function () x = x + 1; error("XXX") end) coroutine.yield(100) return 200 end) assert(co() == 100); assert(x == 0) - local st, msg = pcall(co); assert(x == 2) + local st, msg = pcall(co) + assert(x == 2 and y == 1) -- first close is called twice -- should get first error raised assert(not st and string.find(msg, "%w+%.%w+:%d+: XXX")) end