diff --git a/patches/README.md b/patches/README.md index 61dbc43..39ed310 100644 --- a/patches/README.md +++ b/patches/README.md @@ -2,5 +2,10 @@ These patches are for Lua 5.3 and 5.4. The 'defer' patch adds the defer statement to Lua. -Note that in Lua 5.4 a deferred closure may be called more than once just as the close method of a to-be-closed variable -may be called more than once, when exiting the scope. I am checking why Lua 5.4 behaves this way. \ No newline at end of file +Note that in Lua 5.4 versions prior to 5.4.3 a deferred closure may be called more than once +just as the close method of a to-be-closed variable may be called more than once, when exiting the scope. + +I think this is fixed in Lua 5.4.3. + +The original patch for 5.4 is applicable to versions prior to 5.4.3. +The 5.4.3 version has a new approach to the implementation of toclose values, hence a new patch had to be created. \ No newline at end of file diff --git a/patches/defer_statement_patch_for_Lua_5_4_3.patch b/patches/defer_statement_patch_for_Lua_5_4_3.patch new file mode 100644 index 0000000..feee379 --- /dev/null +++ b/patches/defer_statement_patch_for_Lua_5_4_3.patch @@ -0,0 +1,709 @@ +Index: llex.h +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/llex.h b/llex.h +--- a/llex.h (revision a03ceb4b47666501369a6b300e00f57c1dc1c708) ++++ b/llex.h (date 1620514343391) +@@ -33,7 +33,7 @@ + /* terminal symbols denoted by reserved words */ + TK_AND = FIRST_RESERVED, TK_BREAK, + TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION, +- TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT, ++ TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_DEFER, TK_NIL, TK_NOT, TK_OR, TK_REPEAT, + TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE, + /* other terminal symbols */ + TK_IDIV, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, +Index: lobject.h +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/lobject.h b/lobject.h +--- a/lobject.h (revision a03ceb4b47666501369a6b300e00f57c1dc1c708) ++++ b/lobject.h (date 1620514343399) +@@ -147,6 +147,7 @@ + TValue val; + struct { + TValuefields; ++ lu_byte is_deferred; + unsigned short delta; + } tbclist; + } StackValue; +@@ -609,6 +610,10 @@ + val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VCCL)); \ + checkliveness(L,io); } + ++enum { ++ UV_FLAG_TBC = 1, ++ UV_FLAG_DEFER = 3 ++}; + + /* + ** Upvalues for Lua closures +Index: ljumptab.h +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/ljumptab.h b/ljumptab.h +--- a/ljumptab.h (revision a03ceb4b47666501369a6b300e00f57c1dc1c708) ++++ b/ljumptab.h (date 1620514343380) +@@ -105,6 +105,7 @@ + &&L_OP_TFORLOOP, + &&L_OP_SETLIST, + &&L_OP_CLOSURE, ++&&L_OP_DEFER, + &&L_OP_VARARG, + &&L_OP_VARARGPREP, + &&L_OP_EXTRAARG +Index: lfunc.h +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/lfunc.h b/lfunc.h +--- a/lfunc.h (revision a03ceb4b47666501369a6b300e00f57c1dc1c708) ++++ b/lfunc.h (date 1620514343371) +@@ -52,7 +52,7 @@ + LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nupvals); + LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl); + LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); +-LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level); ++LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level, int deferred); + LUAI_FUNC void luaF_closeupval (lua_State *L, StkId level); + LUAI_FUNC void luaF_close (lua_State *L, StkId level, int status, int yy); + LUAI_FUNC void luaF_unlinkupval (UpVal *uv); +Index: lopcodes.h +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/lopcodes.h b/lopcodes.h +--- a/lopcodes.h (revision a03ceb4b47666501369a6b300e00f57c1dc1c708) ++++ b/lopcodes.h (date 1620514343412) +@@ -300,6 +300,7 @@ + OP_SETLIST,/* A B C k R[A][C+i] := R[A+i], 1 <= i <= B */ + + OP_CLOSURE,/* A Bx R[A] := closure(KPROTO[Bx]) */ ++OP_DEFER, + + OP_VARARG,/* A C R[A], R[A+1], ..., R[A+C-2] = vararg */ + +Index: lopcodes.c +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/lopcodes.c b/lopcodes.c +--- a/lopcodes.c (revision a03ceb4b47666501369a6b300e00f57c1dc1c708) ++++ b/lopcodes.c (date 1620514343404) +@@ -97,6 +97,7 @@ + ,opmode(0, 0, 0, 0, 1, iABx) /* OP_TFORLOOP */ + ,opmode(0, 0, 1, 0, 0, iABC) /* OP_SETLIST */ + ,opmode(0, 0, 0, 0, 1, iABx) /* OP_CLOSURE */ ++ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_DEFER */ + ,opmode(0, 1, 0, 0, 1, iABC) /* OP_VARARG */ + ,opmode(0, 0, 1, 0, 1, iABC) /* OP_VARARGPREP */ + ,opmode(0, 0, 0, 0, 0, iAx) /* OP_EXTRAARG */ +Index: llex.c +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/llex.c b/llex.c +--- a/llex.c (revision a03ceb4b47666501369a6b300e00f57c1dc1c708) ++++ b/llex.c (date 1620514343386) +@@ -40,7 +40,7 @@ + static const char *const luaX_tokens [] = { + "and", "break", "do", "else", "elseif", + "end", "false", "for", "function", "goto", "if", +- "in", "local", "nil", "not", "or", "repeat", ++ "in", "local", "defer", "nil", "not", "or", "repeat", + "return", "then", "true", "until", "while", + "//", "..", "...", "==", ">=", "<=", "~=", + "<<", ">>", "::", "", +Index: lparser.c +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/lparser.c b/lparser.c +--- a/lparser.c (revision a03ceb4b47666501369a6b300e00f57c1dc1c708) ++++ b/lparser.c (date 1620514343432) +@@ -707,10 +707,17 @@ + ** are in use at that time. + + */ +-static void codeclosure (LexState *ls, expdesc *v) { ++static void codeclosure (LexState *ls, expdesc *v, int deferred) { + FuncState *fs = ls->fs->prev; ++ int pc = -1; ++ if (deferred) { ++ pc = luaK_codeABC(fs, OP_DEFER, 0, 0, 0); ++ } + init_exp(v, VRELOC, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np - 1)); + luaK_exp2nextreg(fs, v); /* fix it at the last register */ ++ if (deferred) { ++ SETARG_A(fs->f->code[pc], v->u.info); ++ } + } + + +@@ -975,24 +982,26 @@ + } + + +-static void body (LexState *ls, expdesc *e, int ismethod, int line) { ++static void body (LexState *ls, expdesc *e, int ismethod, int line, int deferred) { + /* body -> '(' parlist ')' block END */ + FuncState new_fs; + BlockCnt bl; + new_fs.f = addprototype(ls); + new_fs.f->linedefined = line; + open_func(ls, &new_fs, &bl); +- checknext(ls, '('); +- if (ismethod) { +- new_localvarliteral(ls, "self"); /* create 'self' parameter */ +- adjustlocalvars(ls, 1); +- } +- parlist(ls); +- checknext(ls, ')'); ++ if (!deferred) { ++ checknext(ls, '('); ++ if (ismethod) { ++ new_localvarliteral(ls, "self"); /* create 'self' parameter */ ++ adjustlocalvars(ls, 1); ++ } ++ parlist(ls); ++ checknext(ls, ')'); ++ } + statlist(ls); + new_fs.f->lastlinedefined = ls->linenumber; + check_match(ls, TK_END, TK_FUNCTION, line); +- codeclosure(ls, e); ++ codeclosure(ls, e, deferred); + close_func(ls); + } + +@@ -1168,7 +1177,7 @@ + } + case TK_FUNCTION: { + luaX_next(ls); +- body(ls, v, 0, ls->linenumber); ++ body(ls, v, 0, ls->linenumber, 0); + return; + } + default: { +@@ -1674,13 +1683,21 @@ + } + + +-static void localfunc (LexState *ls) { ++static void localfunc (LexState *ls, int defer) { + expdesc b; + FuncState *fs = ls->fs; + int fvar = fs->nactvar; /* function's variable index */ +- new_localvar(ls, str_checkname(ls)); /* new local variable */ ++ if (defer) { ++ static const char funcname[] = "(deferred function)"; ++ new_localvar(ls, luaX_newstring(ls, funcname, sizeof funcname-1)); /* new local variable */ ++ markupval(fs, fs->nactvar); ++ fs->bl->insidetbc = 1; /* in the scope of a defer closure variable */ ++ } ++ else { ++ new_localvar(ls, str_checkname(ls)); /* new local variable */ ++ } + adjustlocalvars(ls, 1); /* enter its scope */ +- body(ls, &b, 0, ls->linenumber); /* function created in next register */ ++ body(ls, &b, 0, ls->linenumber, defer); /* function created in next register */ + /* debug information will only see the variable after this point! */ + localdebuginfo(fs, fvar)->startpc = fs->pc; + } +@@ -1775,7 +1792,7 @@ + expdesc v, b; + luaX_next(ls); /* skip FUNCTION */ + ismethod = funcname(ls, &v); +- body(ls, &b, ismethod, line); ++ body(ls, &b, ismethod, line, 0); + luaK_storevar(ls->fs, &v, &b); + luaK_fixline(ls->fs, line); /* definition "happens" in the first line */ + } +@@ -1868,10 +1885,15 @@ + case TK_LOCAL: { /* stat -> localstat */ + luaX_next(ls); /* skip LOCAL */ + if (testnext(ls, TK_FUNCTION)) /* local function? */ +- localfunc(ls); ++ localfunc(ls, 0); + else + localstat(ls); + break; ++ } ++ case TK_DEFER: { /* stat -> deferstat */ ++ luaX_next(ls); /* skip DEFER */ ++ localfunc(ls, 1); ++ break; + } + case TK_DBCOLON: { /* stat -> label */ + luaX_next(ls); /* skip double colon */ +Index: lvm.c +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/lvm.c b/lvm.c +--- a/lvm.c (revision a03ceb4b47666501369a6b300e00f57c1dc1c708) ++++ b/lvm.c (date 1620514343445) +@@ -1541,7 +1541,7 @@ + } + vmcase(OP_TBC) { + /* create new to-be-closed upvalue */ +- halfProtect(luaF_newtbcupval(L, ra)); ++ halfProtect(luaF_newtbcupval(L, ra, 0)); + vmbreak; + } + vmcase(OP_JMP) { +@@ -1752,7 +1752,7 @@ + } + vmcase(OP_TFORPREP) { + /* create to-be-closed upvalue (if needed) */ +- halfProtect(luaF_newtbcupval(L, ra + 3)); ++ halfProtect(luaF_newtbcupval(L, ra + 3, 0)); + pc += GETARG_Bx(i); + i = *(pc++); /* go to next instruction */ + lua_assert(GET_OPCODE(i) == OP_TFORCALL && ra == RA(i)); +@@ -1810,6 +1810,10 @@ + halfProtect(pushclosure(L, p, cl->upvals, base, ra)); + checkGC(L, ra + 1); + vmbreak; ++ } ++ vmcase(OP_DEFER) { ++ halfProtect(luaF_newtbcupval(L, ra, 1)); ++ vmbreak; + } + vmcase(OP_VARARG) { + int n = GETARG_C(i) - 1; /* required results */ +Index: testes/defer.lua +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/testes/defer.lua b/testes/defer.lua +new file mode 100644 +--- /dev/null (date 1620514917535) ++++ b/testes/defer.lua (date 1620514917535) +@@ -0,0 +1,318 @@ ++-- ================================================================ ++-- Following section is an extract from the code.lua test ++-- These functions test bytecode generation, and also provide ++-- helper routines that we use later on in other test cases ++ ++-- testing opcodes ++function check (f, ...) ++ if not T then ++ return true ++ end ++ local arg = {...} ++ local c = T.listcode(f) ++ for i=1, #arg do ++ --print(arg[i], c[i]) ++ opcodes_coverage[arg[i]] = opcodes_coverage[arg[i]]+1 ++ assert(string.find(c[i], '- '..arg[i]..' *[AB][xs]?=%d')) ++ end ++ assert(c[#arg+2] == nil) ++end ++ ++-- Test defer statement ++do ++ local y = 0 ++ local function x() ++ defer y = y + 1 end ++ defer y = y + 1 end ++ end ++ check(x, 'DEFER', 'CLOSURE', 'DEFER', 'CLOSURE', 'RETURN') ++ x() ++ assert(y == 2) ++ print 'Test 1 OK' ++end ++ ++-- Test defer statement ++do ++ local y = 0 ++ local function x() ++ defer y = y + 1 end ++ error('raise error') ++ defer y = y + 2 end -- will not be called ++ end ++ pcall(x) ++ assert(y == 1) ++ print 'Test 2 OK' ++end ++ ++-- Test defer statement ++do ++ local y = 0 ++ local function x() ++ defer y = y + 1 end ++ defer y = y + 2; error('err') end ++ defer y = y + 3 end ++ end ++ pcall(x) ++ assert(y == 6) ++ -- Seems the defer closure that errored is called twice ++ -- FIXME why? See also test 12 below - same issue I think ++ -- This appears to be a feature of Lua 5.4 ++ --assert(y == 8) ++ print 'Test 3 OK' ++end ++ ++-- Test defer statement in tailcalls ++do ++ local y = 0 ++ local function x (n) ++ defer y = y + 1 end ++ if n > 0 then return x(n - 1) end ++ end ++ pcall(x, 3) ++ assert(y == 4) ++ print 'Test 4 OK' ++end ++ ++-- Simulate a test of resource closure with defer ++do ++ local y = 0 ++ local z = { count = 0 } ++ z.__index = z; ++ function z:new() ++ local object = {} ++ setmetatable(object, z) ++ return object ++ end ++ function z:open(arg) ++ if (arg) then ++ z.count = z.count + 1 ++ return ++ end ++ y = 1 ++ error('error opening') ++ end ++ function z.close() ++ z.count = z.count - 1 ++ end ++ local function x(arg) ++ local f = z:new() ++ f:open(arg) ++ assert(z.count == 1) ++ defer f:close() end ++ end ++ x('filename') ++ assert(y == 0) ++ assert(z.count == 0) ++ pcall(x, false) ++ assert(z.count == 0) ++ assert(y == 1) ++ print 'Test 5 OK' ++end ++ ++--- Test stack reallocation in defer statement ++do ++ local function x(a) if a <= 0 then return else x(a-1) end end ++ local y = 100 ++ local function z(...) ++ -- recursive call to make stack ++ defer x(y) end ++ return ... ++ end ++ do ++ local a,b,c = z(1,2,3) ++ assert(a == 1 and b == 2 and c == 3) ++ a,b,c = z(3,2,1) ++ assert(a == 3 and b == 2 and c == 1) ++ end ++ print 'Test 6 OK' ++end ++ ++-- Adapted from Lua 5.4 ++local function stack(n) n = ((n == 0) or stack(n - 1)) end ++ ++local function func2close (f, x, y) ++ local obj = setmetatable({}, {__close = f}) ++ if x then ++ return x, obj, y ++ else ++ return obj ++ end ++end ++ ++do ++ local function t() ++ local a = {} ++ do ++ local b = false -- not to be closed ++ -- x is ++ local x = setmetatable({"x"}, {__close = function (self) ++ a[#a + 1] = self[1] end}) ++ defer getmetatable(x).__close(x) end ++ -- y is ++ local w, y, z = func2close(function (self, err) ++ assert(err == nil); a[#a + 1] = "y" ++ end, 10, 20) ++ defer getmetatable(y).__close(y) end ++ local c = nil -- not to be closed ++ a[#a + 1] = "in" ++ assert(w == 10 and z == 20) ++ end ++ a[#a + 1] = "out" ++ assert(a[1] == "in" and a[2] == "y" and a[3] == "x" and a[4] == "out") ++ end ++ t() ++ print 'Test 7 OK' ++end ++ ++do ++ local function t() ++ local X = false ++ ++ local x, closescope = func2close(function () stack(10); X = true end, 100) ++ assert(x == 100); x = 101; -- 'x' is not read-only ++ ++ -- closing functions do not corrupt returning values ++ local function foo (x) ++ local _ = closescope ++ defer getmetatable(_).__close(_) end ++ return x, X, 23 ++ end ++ ++ local a, b, c = foo(1.5) ++ assert(a == 1.5 and b == false and c == 23 and X == true) ++ ++ X = false ++ foo = function (x) ++ local _ = closescope ++ defer getmetatable(_).__close(_) end ++ local y = 15 ++ return y ++ end ++ ++ assert(foo() == 15 and X == true) ++ ++ X = false ++ foo = function () ++ local x = closescope ++ defer getmetatable(x).__close(x) end ++ return x ++ end ++ ++ assert(foo() == closescope and X == true) ++ end ++ t() ++ print 'Test 8 OK' ++end ++ ++do ++ local function t() ++ -- calls cannot be tail in the scope of to-be-closed variables ++ local X, Y ++ local function foo () ++ local _ = func2close(function () Y = 10 end) ++ defer getmetatable(_).__close(_) end ++ assert(X == true and Y == nil) -- 'X' not closed yet ++ return 1,2,3 ++ end ++ ++ local function bar () ++ local _ = func2close(function () X = false end) ++ defer getmetatable(_).__close(_) end ++ X = true ++ do ++ return foo() -- not a tail call! ++ end ++ end ++ ++ local a, b, c, d = bar() ++ assert(a == 1 and b == 2 and c == 3 and X == false and Y == 10 and d == nil) ++ return foo, bar ++ end ++ local f,b = t() ++ print 'Test 9 OK' ++end ++ ++do ++ local function t() ++ -- an error in a wrapped coroutine closes variables ++ local x = false ++ local y = false ++ local co = coroutine.wrap(function () ++ local xv = func2close(function () x = true end) ++ defer getmetatable(xv).__close(xv) end ++ do ++ local yv = func2close(function () y = true end) ++ defer getmetatable(yv).__close(yv) end ++ coroutine.yield(100) -- yield doesn't close variable ++ end ++ coroutine.yield(200) -- yield doesn't close variable ++ error(23) -- error does ++ end) ++ ++ local b = co() ++ assert(b == 100 and not x and not y) ++ b = co() ++ assert(b == 200 and not x and y) ++ local a, b = pcall(co) ++ assert(not a and b == 23 and x and y) ++ end ++ t() ++ print 'Test 10 OK' ++end ++ ++-- a suspended coroutine should not close its variables when collected ++do ++ function t() ++ local co ++ co = coroutine.wrap(function() ++ -- should not run ++ local x = func2close(function () os.exit(false) end) ++ defer getmetatable(x).__close(x) end ++ co = nil ++ coroutine.yield() ++ end) ++ co() -- start coroutine ++ assert(co == nil) -- eventually it will be collected ++ collectgarbage() ++ end ++ t() ++ print 'Test 11 OK' ++end ++ ++do ++ local function t() ++ -- error in a wrapped coroutine raising errors when closing a variable ++ local x = 0 ++ local co = coroutine.wrap(function () ++ local xx = func2close(function () x = x + 1; error("@YYY") end) ++ defer getmetatable(xx).__close(xx) end ++ local xv = func2close(function () x = x + 1; error("@XXX") end) ++ defer getmetatable(xv).__close(xv) end ++ coroutine.yield(100) ++ error(200) ++ end) ++ assert(co() == 100); assert(x == 0) ++ local st, msg = pcall(co); assert(x == 2) ++ assert(not st and string.find(msg, "@YYY")) -- should get first error raised ++ ++ local x = 0 ++ local y = 0 ++ co = coroutine.wrap(function () ++ local xx = func2close(function () y = y + 1; error("YYY") end) ++ defer getmetatable(xx).__close(xx) end ++ local xv = func2close(function () x = x + 1; error("XXX") end) ++ defer getmetatable(xv).__close(xv) end ++ coroutine.yield(100) ++ return 200 ++ end) ++ assert(co() == 100); assert(x == 0) ++ local st, msg = pcall(co) ++ assert(x == 1 and y == 1) ++ -- should get first error raised ++ assert(not st and string.find(msg, "%w+%.%w+:%d+: YYY")) ++ end ++ t() ++ print 'Test 12 OK' ++end ++ ++print 'OK' +Index: lopnames.h +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/lopnames.h b/lopnames.h +--- a/lopnames.h (revision a03ceb4b47666501369a6b300e00f57c1dc1c708) ++++ b/lopnames.h (date 1620514343417) +@@ -93,6 +93,7 @@ + "TFORLOOP", + "SETLIST", + "CLOSURE", ++ "DEFER", + "VARARG", + "VARARGPREP", + "EXTRAARG", +Index: lapi.c +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/lapi.c b/lapi.c +--- a/lapi.c (revision a03ceb4b47666501369a6b300e00f57c1dc1c708) ++++ b/lapi.c (date 1620514343354) +@@ -1266,7 +1266,7 @@ + o = index2stack(L, idx); + nresults = L->ci->nresults; + api_check(L, L->tbclist < o, "given index below or equal a marked one"); +- luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */ ++ luaF_newtbcupval(L, o, 0); /* create new to-be-closed upvalue */ + if (!hastocloseCfunc(nresults)) /* function not marked yet? */ + L->ci->nresults = codeNresults(nresults); /* mark it */ + lua_assert(hastocloseCfunc(L->ci->nresults)); +Index: lfunc.c +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/lfunc.c b/lfunc.c +--- a/lfunc.c (revision a03ceb4b47666501369a6b300e00f57c1dc1c708) ++++ b/lfunc.c (date 1620514658224) +@@ -99,6 +99,16 @@ + return newupval(L, 0, level, pp); + } + ++/* DEFER patch */ ++static void calldefermethod (lua_State *L, TValue *func, TValue *err) { ++ if (!ttisfunction(func)) ++ return; ++ StkId top = L->top; ++ setobj2s(L, top, func); /* will call defer function */ ++ setobj2s(L, top + 1, err); /* and error msg. as 1st argument */ ++ L->top = top + 2; /* add function and arguments */ ++ luaD_callnoyield(L, top, 0); ++} + + /* + ** Call closing method for object 'obj' with error message 'err'. The +@@ -150,7 +160,10 @@ + errobj = s2v(level + 1); /* error object goes after 'uv' */ + luaD_seterrorobj(L, status, level + 1); /* set error object */ + } +- callclosemethod(L, uv, errobj, yy); ++ if (level->tbclist.is_deferred) ++ calldefermethod(L, uv, errobj); ++ else ++ callclosemethod(L, uv, errobj, yy); + } + + +@@ -166,16 +179,20 @@ + /* + ** Insert a variable in the list of to-be-closed variables. + */ +-void luaF_newtbcupval (lua_State *L, StkId level) { ++void luaF_newtbcupval (lua_State *L, StkId level, int deferred) { + lua_assert(level > L->tbclist); +- if (l_isfalse(s2v(level))) +- return; /* false doesn't need to be closed */ +- checkclosemth(L, level); /* value must have a close method */ ++ if (!deferred) { ++ if (l_isfalse(s2v(level))) ++ return; /* false doesn't need to be closed */ ++ checkclosemth(L, level); /* value must have a close method */ ++ } + while (cast_uint(level - L->tbclist) > MAXDELTA) { + L->tbclist += MAXDELTA; /* create a dummy node at maximum delta */ + L->tbclist->tbclist.delta = 0; ++ L->tbclist->tbclist.is_deferred = 0; + } + level->tbclist.delta = cast(unsigned short, level - L->tbclist); ++ level->tbclist.is_deferred = deferred ? 1 : 0; + L->tbclist = level; + } +