You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
670 lines
20 KiB
670 lines
20 KiB
Index: lopnames.h
|
|
IDEA additional info:
|
|
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
<+>UTF-8
|
|
===================================================================
|
|
--- lopnames.h (revision 170752d62751963767b93263989bac427d7c785f)
|
|
+++ lopnames.h (date 1594983665385)
|
|
@@ -93,6 +93,7 @@
|
|
"TFORLOOP",
|
|
"SETLIST",
|
|
"CLOSURE",
|
|
+ "DEFER",
|
|
"VARARG",
|
|
"VARARGPREP",
|
|
"EXTRAARG",
|
|
Index: lfunc.c
|
|
IDEA additional info:
|
|
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
<+>UTF-8
|
|
===================================================================
|
|
--- lfunc.c (revision 170752d62751963767b93263989bac427d7c785f)
|
|
+++ lfunc.c (date 1594987185491)
|
|
@@ -91,7 +91,7 @@
|
|
lua_assert(isintwups(L) || L->openupval == NULL);
|
|
while ((p = *pp) != NULL && uplevel(p) >= level) { /* search for it */
|
|
lua_assert(!isdead(G(L), p));
|
|
- if (uplevel(p) == level) /* corresponding upvalue? */
|
|
+ if (uplevel(p) == level && p->tbc != UV_FLAG_DEFER) /* corresponding upvalue? - not deferred */
|
|
return p; /* return it */
|
|
pp = &p->u.open.next;
|
|
}
|
|
@@ -99,6 +99,27 @@
|
|
return newupval(L, 0, level, pp);
|
|
}
|
|
|
|
+static void calldeferred(lua_State *L, void *ud) {
|
|
+ UNUSED(ud);
|
|
+ luaD_callnoyield(L, L->top - 2, 0);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Prepare deferred function plus its arguments for object 'obj' with
|
|
+** error message 'err'. (This function assumes EXTRA_STACK.)
|
|
+*/
|
|
+static int preparetocall(lua_State *L, TValue *func, TValue *err) {
|
|
+ StkId top = L->top;
|
|
+ setobj2s(L, top, func); /* will call deferred function */
|
|
+ if (err) {
|
|
+ setobj2s(L, top + 1, err); /* and error msg. as 1st argument */
|
|
+ }
|
|
+ else {
|
|
+ setnilvalue(s2v(top + 1));
|
|
+ }
|
|
+ L->top = top + 2; /* add function and arguments */
|
|
+ return 1;
|
|
+}
|
|
|
|
static void callclose (lua_State *L, void *ud) {
|
|
UNUSED(ud);
|
|
@@ -147,11 +168,15 @@
|
|
** the 'level' of the upvalue being closed, as everything after
|
|
** that won't be used again.
|
|
*/
|
|
-static int callclosemth (lua_State *L, StkId level, int status) {
|
|
+static int callclosemth (lua_State *L, StkId level, int status, int tbc) {
|
|
TValue *uv = s2v(level); /* value being closed */
|
|
if (likely(status == LUA_OK)) {
|
|
- if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */
|
|
+ if (tbc == UV_FLAG_TBC && prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */
|
|
callclose(L, NULL); /* call closing method */
|
|
+ else if (tbc == UV_FLAG_DEFER && ttisfunction(uv)) {
|
|
+ preparetocall(L, uv, &G(L)->nilvalue);
|
|
+ calldeferred(L, NULL);
|
|
+ }
|
|
else if (!l_isfalse(uv)) /* non-closable non-false value? */
|
|
varerror(L, level, "attempt to close non-closable variable '%s'");
|
|
}
|
|
@@ -160,8 +185,16 @@
|
|
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);
|
|
+ int docall = 1;
|
|
+ if (tbc == UV_FLAG_TBC) {
|
|
+ docall = prepclosingmethod(L, uv, s2v(level));
|
|
+ }
|
|
+ else {
|
|
+ lua_assert(tbc == UV_FLAG_DEFER);
|
|
+ preparetocall(L, uv, s2v(level));
|
|
+ }
|
|
+ if (docall) { /* something to call? */
|
|
+ int newstatus = luaD_pcall(L, tbc == UV_FLAG_TBC ? callclose: calldeferred, NULL, oldtop, 0);
|
|
if (newstatus != LUA_OK && status == CLOSEPROTECT) /* first error? */
|
|
status = newstatus; /* this will be the new error */
|
|
else {
|
|
@@ -228,7 +261,7 @@
|
|
if (uv->tbc && status != NOCLOSINGMETH) {
|
|
/* must run closing method, which may change the stack */
|
|
ptrdiff_t levelrel = savestack(L, level);
|
|
- status = callclosemth(L, uplevel(uv), status);
|
|
+ status = callclosemth(L, uplevel(uv), status, uv->tbc);
|
|
level = restorestack(L, levelrel);
|
|
}
|
|
luaF_unlinkupval(uv);
|
|
Index: ljumptab.h
|
|
IDEA additional info:
|
|
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
<+>UTF-8
|
|
===================================================================
|
|
--- ljumptab.h (revision 170752d62751963767b93263989bac427d7c785f)
|
|
+++ ljumptab.h (date 1594983953252)
|
|
@@ -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: llex.c
|
|
IDEA additional info:
|
|
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
<+>UTF-8
|
|
===================================================================
|
|
--- llex.c (revision 170752d62751963767b93263989bac427d7c785f)
|
|
+++ llex.c (date 1594983621522)
|
|
@@ -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",
|
|
"//", "..", "...", "==", ">=", "<=", "~=",
|
|
"<<", ">>", "::", "<eof>",
|
|
Index: lopcodes.c
|
|
IDEA additional info:
|
|
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
<+>UTF-8
|
|
===================================================================
|
|
--- lopcodes.c (revision 170752d62751963767b93263989bac427d7c785f)
|
|
+++ lopcodes.c (date 1594984035037)
|
|
@@ -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: lparser.c
|
|
IDEA additional info:
|
|
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
<+>UTF-8
|
|
===================================================================
|
|
--- lparser.c (revision 170752d62751963767b93263989bac427d7c785f)
|
|
+++ lparser.c (date 1594983592571)
|
|
@@ -709,10 +709,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);
|
|
+ }
|
|
}
|
|
|
|
|
|
@@ -977,24 +984,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);
|
|
}
|
|
|
|
@@ -1170,7 +1179,7 @@
|
|
}
|
|
case TK_FUNCTION: {
|
|
luaX_next(ls);
|
|
- body(ls, v, 0, ls->linenumber);
|
|
+ body(ls, v, 0, ls->linenumber, 0);
|
|
return;
|
|
}
|
|
default: {
|
|
@@ -1714,13 +1723,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;
|
|
}
|
|
@@ -1815,7 +1832,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 */
|
|
}
|
|
@@ -1908,10 +1925,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: llex.h
|
|
IDEA additional info:
|
|
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
<+>UTF-8
|
|
===================================================================
|
|
--- llex.h (revision 170752d62751963767b93263989bac427d7c785f)
|
|
+++ llex.h (date 1594983196960)
|
|
@@ -27,7 +27,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: lvm.c
|
|
IDEA additional info:
|
|
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
<+>UTF-8
|
|
===================================================================
|
|
--- lvm.c (revision 170752d62751963767b93263989bac427d7c785f)
|
|
+++ lvm.c (date 1594983746500)
|
|
@@ -1785,6 +1785,12 @@
|
|
checkGC(L, ra + 1);
|
|
vmbreak;
|
|
}
|
|
+ vmcase(OP_DEFER) {
|
|
+ UpVal *up = luaF_findupval(L, ra); /* create new upvalue */
|
|
+ up->tbc = UV_FLAG_DEFER; /* mark it as deferred */
|
|
+ setnilvalue(s2v(ra)); /* initialize it with nil */
|
|
+ vmbreak;
|
|
+ }
|
|
vmcase(OP_VARARG) {
|
|
int n = GETARG_C(i) - 1; /* required results */
|
|
Protect(luaT_getvarargs(L, ci, ra, n));
|
|
Index: testes/defer.lua
|
|
IDEA additional info:
|
|
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
<+>UTF-8
|
|
===================================================================
|
|
--- testes/defer.lua (date 1594989486660)
|
|
+++ testes/defer.lua (date 1594989486660)
|
|
@@ -0,0 +1,319 @@
|
|
+-- ================================================================
|
|
+-- 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 <close>
|
|
+ local x = setmetatable({"x"}, {__close = function (self)
|
|
+ a[#a + 1] = self[1] end})
|
|
+ defer getmetatable(x).__close(x) end
|
|
+ -- y is <close>
|
|
+ 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 msg == 200) -- 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(not st and string.find(msg, "%w+%.%w+:%d+: XXX"))
|
|
+ -- Seems the close is called twice here
|
|
+ -- FIXME why ?
|
|
+ assert(x == 2 and y == 1)
|
|
+ end
|
|
+ t()
|
|
+ print 'Test 12 OK'
|
|
+end
|
|
+
|
|
+print 'OK'
|
|
Index: lobject.h
|
|
IDEA additional info:
|
|
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
<+>UTF-8
|
|
===================================================================
|
|
--- lobject.h (revision 170752d62751963767b93263989bac427d7c785f)
|
|
+++ lobject.h (date 1594981708043)
|
|
@@ -596,6 +596,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: lopcodes.h
|
|
IDEA additional info:
|
|
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
<+>UTF-8
|
|
===================================================================
|
|
--- lopcodes.h (revision 170752d62751963767b93263989bac427d7c785f)
|
|
+++ lopcodes.h (date 1594983320288)
|
|
@@ -300,6 +300,7 @@
|
|
OP_SETLIST,/* A B C k R[A][(C-1)*FPF+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 */
|
|
|