aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-10-17 10:44:42 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-10-17 10:44:42 -0300
commitbd96330d037660d9a1769c6c0d989f017e5f0278 (patch)
treec3f4580c628a71f512b057b7f52987153cb88d74
parent4cd1f4aac01184765818e0cebf02da454ccf6590 (diff)
downloadlua-bd96330d037660d9a1769c6c0d989f017e5f0278.tar.gz
lua-bd96330d037660d9a1769c6c0d989f017e5f0278.tar.bz2
lua-bd96330d037660d9a1769c6c0d989f017e5f0278.zip
First "complete" implementation of to-be-closed variables
Still missing: - handling of memory errors when creating upvalue (must run closing method all the same) - interaction with coroutines
-rw-r--r--ldo.c20
-rw-r--r--ldo.h1
-rw-r--r--lfunc.c47
-rw-r--r--lfunc.h2
-rw-r--r--lgc.c5
-rw-r--r--lparser.c2
-rw-r--r--lstate.c4
-rw-r--r--ltests.c2
-rw-r--r--ltm.c2
-rw-r--r--ltm.h1
-rw-r--r--lvm.c7
-rw-r--r--testes/api.lua14
-rw-r--r--testes/locals.lua64
13 files changed, 145 insertions, 26 deletions
diff --git a/ldo.c b/ldo.c
index 2349aaed..f2d12f04 100644
--- a/ldo.c
+++ b/ldo.c
@@ -88,7 +88,7 @@ struct lua_longjmp {
88}; 88};
89 89
90 90
91static void seterrorobj (lua_State *L, int errcode, StkId oldtop) { 91void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {
92 switch (errcode) { 92 switch (errcode) {
93 case LUA_ERRMEM: { /* memory error? */ 93 case LUA_ERRMEM: { /* memory error? */
94 setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */ 94 setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */
@@ -121,7 +121,7 @@ l_noret luaD_throw (lua_State *L, int errcode) {
121 } 121 }
122 else { /* no handler at all; abort */ 122 else { /* no handler at all; abort */
123 if (g->panic) { /* panic function? */ 123 if (g->panic) { /* panic function? */
124 seterrorobj(L, errcode, L->top); /* assume EXTRA_STACK */ 124 luaD_seterrorobj(L, errcode, L->top); /* assume EXTRA_STACK */
125 if (L->ci->top < L->top) 125 if (L->ci->top < L->top)
126 L->ci->top = L->top; /* pushing msg. can break this invariant */ 126 L->ci->top = L->top; /* pushing msg. can break this invariant */
127 lua_unlock(L); 127 lua_unlock(L);
@@ -584,8 +584,8 @@ static int recover (lua_State *L, int status) {
584 if (ci == NULL) return 0; /* no recovery point */ 584 if (ci == NULL) return 0; /* no recovery point */
585 /* "finish" luaD_pcall */ 585 /* "finish" luaD_pcall */
586 oldtop = restorestack(L, ci->u2.funcidx); 586 oldtop = restorestack(L, ci->u2.funcidx);
587 luaF_close(L, oldtop); 587 luaF_close(L, oldtop, status);
588 seterrorobj(L, status, oldtop); 588 luaD_seterrorobj(L, status, oldtop);
589 L->ci = ci; 589 L->ci = ci;
590 L->allowhook = getoah(ci->callstatus); /* restore original 'allowhook' */ 590 L->allowhook = getoah(ci->callstatus); /* restore original 'allowhook' */
591 L->nny = 0; /* should be zero to be yieldable */ 591 L->nny = 0; /* should be zero to be yieldable */
@@ -678,7 +678,7 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
678 } 678 }
679 if (unlikely(errorstatus(status))) { /* unrecoverable error? */ 679 if (unlikely(errorstatus(status))) { /* unrecoverable error? */
680 L->status = cast_byte(status); /* mark thread as 'dead' */ 680 L->status = cast_byte(status); /* mark thread as 'dead' */
681 seterrorobj(L, status, L->top); /* push error message */ 681 luaD_seterrorobj(L, status, L->top); /* push error message */
682 L->ci->top = L->top; 682 L->ci->top = L->top;
683 } 683 }
684 else lua_assert(status == L->status); /* normal end or yield */ 684 else lua_assert(status == L->status); /* normal end or yield */
@@ -726,6 +726,11 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx,
726} 726}
727 727
728 728
729/*
730** Call the C function 'func' in protected mode, restoring basic
731** thread information ('allowhook', 'nny', etc.) and in particular
732** its stack level in case of errors.
733*/
729int luaD_pcall (lua_State *L, Pfunc func, void *u, 734int luaD_pcall (lua_State *L, Pfunc func, void *u,
730 ptrdiff_t old_top, ptrdiff_t ef) { 735 ptrdiff_t old_top, ptrdiff_t ef) {
731 int status; 736 int status;
@@ -737,11 +742,12 @@ int luaD_pcall (lua_State *L, Pfunc func, void *u,
737 status = luaD_rawrunprotected(L, func, u); 742 status = luaD_rawrunprotected(L, func, u);
738 if (unlikely(status != LUA_OK)) { /* an error occurred? */ 743 if (unlikely(status != LUA_OK)) { /* an error occurred? */
739 StkId oldtop = restorestack(L, old_top); 744 StkId oldtop = restorestack(L, old_top);
740 luaF_close(L, oldtop); /* close possible pending closures */
741 seterrorobj(L, status, oldtop);
742 L->ci = old_ci; 745 L->ci = old_ci;
743 L->allowhook = old_allowhooks; 746 L->allowhook = old_allowhooks;
744 L->nny = old_nny; 747 L->nny = old_nny;
748 status = luaF_close(L, oldtop, status);
749 oldtop = restorestack(L, old_top); /* previous call may change stack */
750 luaD_seterrorobj(L, status, oldtop);
745 luaD_shrinkstack(L); 751 luaD_shrinkstack(L);
746 } 752 }
747 L->errfunc = old_errfunc; 753 L->errfunc = old_errfunc;
diff --git a/ldo.h b/ldo.h
index c836a2a1..7760f853 100644
--- a/ldo.h
+++ b/ldo.h
@@ -50,6 +50,7 @@
50/* type of protected functions, to be ran by 'runprotected' */ 50/* type of protected functions, to be ran by 'runprotected' */
51typedef void (*Pfunc) (lua_State *L, void *ud); 51typedef void (*Pfunc) (lua_State *L, void *ud);
52 52
53LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop);
53LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, 54LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
54 const char *mode); 55 const char *mode);
55LUAI_FUNC void luaD_hook (lua_State *L, int event, int line, 56LUAI_FUNC void luaD_hook (lua_State *L, int event, int line,
diff --git a/lfunc.c b/lfunc.c
index 16e00731..fde72b8c 100644
--- a/lfunc.c
+++ b/lfunc.c
@@ -14,6 +14,7 @@
14 14
15#include "lua.h" 15#include "lua.h"
16 16
17#include "ldo.h"
17#include "lfunc.h" 18#include "lfunc.h"
18#include "lgc.h" 19#include "lgc.h"
19#include "lmem.h" 20#include "lmem.h"
@@ -83,6 +84,40 @@ UpVal *luaF_findupval (lua_State *L, StkId level) {
83} 84}
84 85
85 86
87static void callclose (lua_State *L, void *ud) {
88 luaD_callnoyield(L, cast(StkId, ud), 0);
89}
90
91
92static int closeupval (lua_State *L, UpVal *uv, StkId level, int status) {
93 StkId func = level + 1; /* save slot for old error message */
94 if (status != LUA_OK) /* was there an error? */
95 luaD_seterrorobj(L, status, level); /* save error message */
96 else
97 setnilvalue(s2v(level));
98 if (ttisfunction(uv->v)) { /* object to-be-closed is a function? */
99 setobj2s(L, func, uv->v); /* will call it */
100 setobjs2s(L, func + 1, level); /* error msg. as argument */
101 }
102 else { /* try '__close' metamethod */
103 const TValue *tm = luaT_gettmbyobj(L, uv->v, TM_CLOSE);
104 if (ttisnil(tm))
105 return status; /* no metamethod */
106 setobj2s(L, func, tm); /* will call metamethod */
107 setobj2s(L, func + 1, uv->v); /* with 'self' as argument */
108 }
109 L->top = func + 2; /* add function and argument */
110 if (status == LUA_OK) /* not in "error mode"? */
111 callclose(L, func); /* call closing method */
112 else { /* already inside error handler; cannot raise another error */
113 int newstatus = luaD_pcall(L, callclose, func, savestack(L, level), 0);
114 if (newstatus != LUA_OK) /* error when closing? */
115 status = newstatus; /* this will be the new error */
116 }
117 return status;
118}
119
120
86void luaF_unlinkupval (UpVal *uv) { 121void luaF_unlinkupval (UpVal *uv) {
87 lua_assert(upisopen(uv)); 122 lua_assert(upisopen(uv));
88 *uv->u.open.previous = uv->u.open.next; 123 *uv->u.open.previous = uv->u.open.next;
@@ -91,10 +126,10 @@ void luaF_unlinkupval (UpVal *uv) {
91} 126}
92 127
93 128
94void luaF_close (lua_State *L, StkId level) { 129int luaF_close (lua_State *L, StkId level, int status) {
95 UpVal *uv; 130 UpVal *uv;
96 while (L->openupval != NULL && 131 while ((uv = L->openupval) != NULL && uplevel(uv) >= level) {
97 (uv = L->openupval, uplevel(uv) >= level)) { 132 StkId upl = uplevel(uv);
98 TValue *slot = &uv->u.value; /* new position for value */ 133 TValue *slot = &uv->u.value; /* new position for value */
99 luaF_unlinkupval(uv); 134 luaF_unlinkupval(uv);
100 setobj(L, slot, uv->v); /* move value to upvalue slot */ 135 setobj(L, slot, uv->v); /* move value to upvalue slot */
@@ -102,7 +137,13 @@ void luaF_close (lua_State *L, StkId level) {
102 if (!iswhite(uv)) 137 if (!iswhite(uv))
103 gray2black(uv); /* closed upvalues cannot be gray */ 138 gray2black(uv); /* closed upvalues cannot be gray */
104 luaC_barrier(L, uv, slot); 139 luaC_barrier(L, uv, slot);
140 if (status >= 0 && uv->tt == LUA_TUPVALTBC) { /* must be closed? */
141 ptrdiff_t levelrel = savestack(L, level);
142 status = closeupval(L, uv, upl, status); /* may reallocate the stack */
143 level = restorestack(L, levelrel);
144 }
105 } 145 }
146 return status;
106} 147}
107 148
108 149
diff --git a/lfunc.h b/lfunc.h
index 859ccc12..4c788005 100644
--- a/lfunc.h
+++ b/lfunc.h
@@ -47,7 +47,7 @@ LUAI_FUNC CClosure *luaF_newCclosure (lua_State *L, int nelems);
47LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nelems); 47LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nelems);
48LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl); 48LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl);
49LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); 49LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);
50LUAI_FUNC void luaF_close (lua_State *L, StkId level); 50LUAI_FUNC int luaF_close (lua_State *L, StkId level, int status);
51LUAI_FUNC void luaF_unlinkupval (UpVal *uv); 51LUAI_FUNC void luaF_unlinkupval (UpVal *uv);
52LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); 52LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);
53LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, 53LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number,
diff --git a/lgc.c b/lgc.c
index 39b3ab73..9d196a18 100644
--- a/lgc.c
+++ b/lgc.c
@@ -609,6 +609,7 @@ static int traverseLclosure (global_State *g, LClosure *cl) {
609** That ensures that the entire stack have valid (non-dead) objects. 609** That ensures that the entire stack have valid (non-dead) objects.
610*/ 610*/
611static int traversethread (global_State *g, lua_State *th) { 611static int traversethread (global_State *g, lua_State *th) {
612 UpVal *uv;
612 StkId o = th->stack; 613 StkId o = th->stack;
613 if (o == NULL) 614 if (o == NULL)
614 return 1; /* stack not completely built yet */ 615 return 1; /* stack not completely built yet */
@@ -616,6 +617,10 @@ static int traversethread (global_State *g, lua_State *th) {
616 th->openupval == NULL || isintwups(th)); 617 th->openupval == NULL || isintwups(th));
617 for (; o < th->top; o++) /* mark live elements in the stack */ 618 for (; o < th->top; o++) /* mark live elements in the stack */
618 markvalue(g, s2v(o)); 619 markvalue(g, s2v(o));
620 for (uv = th->openupval; uv != NULL; uv = uv->u.open.next) {
621 if (uv->tt == LUA_TUPVALTBC) /* to be closed? */
622 markobject(g, uv); /* cannot be collected */
623 }
619 if (g->gcstate == GCSatomic) { /* final traversal? */ 624 if (g->gcstate == GCSatomic) { /* final traversal? */
620 StkId lim = th->stack + th->stacksize; /* real end of stack */ 625 StkId lim = th->stack + th->stacksize; /* real end of stack */
621 for (; o < lim; o++) /* clear not-marked stack slice */ 626 for (; o < lim; o++) /* clear not-marked stack slice */
diff --git a/lparser.c b/lparser.c
index 84abeb90..6b14b800 100644
--- a/lparser.c
+++ b/lparser.c
@@ -1536,9 +1536,9 @@ static void scopedlocalstat (LexState *ls) {
1536 FuncState *fs = ls->fs; 1536 FuncState *fs = ls->fs;
1537 new_localvar(ls, str_checkname(ls)); 1537 new_localvar(ls, str_checkname(ls));
1538 checknext(ls, '='); 1538 checknext(ls, '=');
1539 exp1(ls, 0);
1539 luaK_codeABC(fs, OP_TBC, fs->nactvar, 0, 0); 1540 luaK_codeABC(fs, OP_TBC, fs->nactvar, 0, 0);
1540 markupval(fs, fs->nactvar); 1541 markupval(fs, fs->nactvar);
1541 exp1(ls, 0);
1542 adjustlocalvars(ls, 1); 1542 adjustlocalvars(ls, 1);
1543} 1543}
1544 1544
diff --git a/lstate.c b/lstate.c
index 8b0219bc..4a2453d1 100644
--- a/lstate.c
+++ b/lstate.c
@@ -258,7 +258,7 @@ static void preinit_thread (lua_State *L, global_State *g) {
258 258
259static void close_state (lua_State *L) { 259static void close_state (lua_State *L) {
260 global_State *g = G(L); 260 global_State *g = G(L);
261 luaF_close(L, L->stack); /* close all upvalues for this thread */ 261 luaF_close(L, L->stack, -1); /* close all upvalues for this thread */
262 luaC_freeallobjects(L); /* collect all objects */ 262 luaC_freeallobjects(L); /* collect all objects */
263 if (ttisnil(&g->nilvalue)) /* closing a fully built state? */ 263 if (ttisnil(&g->nilvalue)) /* closing a fully built state? */
264 luai_userstateclose(L); 264 luai_userstateclose(L);
@@ -301,7 +301,7 @@ LUA_API lua_State *lua_newthread (lua_State *L) {
301 301
302void luaE_freethread (lua_State *L, lua_State *L1) { 302void luaE_freethread (lua_State *L, lua_State *L1) {
303 LX *l = fromstate(L1); 303 LX *l = fromstate(L1);
304 luaF_close(L1, L1->stack); /* close all upvalues for this thread */ 304 luaF_close(L1, L1->stack, -1); /* close all upvalues for this thread */
305 lua_assert(L1->openupval == NULL); 305 lua_assert(L1->openupval == NULL);
306 luai_userstatefree(L, L1); 306 luai_userstatefree(L, L1);
307 freestack(L1); 307 freestack(L1);
diff --git a/ltests.c b/ltests.c
index ff962543..a6968653 100644
--- a/ltests.c
+++ b/ltests.c
@@ -1208,7 +1208,7 @@ static int getindex_aux (lua_State *L, lua_State *L1, const char **pc) {
1208 1208
1209static void pushcode (lua_State *L, int code) { 1209static void pushcode (lua_State *L, int code) {
1210 static const char *const codes[] = {"OK", "YIELD", "ERRRUN", 1210 static const char *const codes[] = {"OK", "YIELD", "ERRRUN",
1211 "ERRSYNTAX", "ERRMEM", "ERRGCMM", "ERRERR"}; 1211 "ERRSYNTAX", MEMERRMSG, "ERRGCMM", "ERRERR"};
1212 lua_pushstring(L, codes[code]); 1212 lua_pushstring(L, codes[code]);
1213} 1213}
1214 1214
diff --git a/ltm.c b/ltm.c
index 5c148180..53e15c7f 100644
--- a/ltm.c
+++ b/ltm.c
@@ -43,7 +43,7 @@ void luaT_init (lua_State *L) {
43 "__div", "__idiv", 43 "__div", "__idiv",
44 "__band", "__bor", "__bxor", "__shl", "__shr", 44 "__band", "__bor", "__bxor", "__shl", "__shr",
45 "__unm", "__bnot", "__lt", "__le", 45 "__unm", "__bnot", "__lt", "__le",
46 "__concat", "__call" 46 "__concat", "__call", "__close"
47 }; 47 };
48 int i; 48 int i;
49 for (i=0; i<TM_N; i++) { 49 for (i=0; i<TM_N; i++) {
diff --git a/ltm.h b/ltm.h
index 89aee78a..e8560f82 100644
--- a/ltm.h
+++ b/ltm.h
@@ -40,6 +40,7 @@ typedef enum {
40 TM_LE, 40 TM_LE,
41 TM_CONCAT, 41 TM_CONCAT,
42 TM_CALL, 42 TM_CALL,
43 TM_CLOSE,
43 TM_N /* number of elements in the enum */ 44 TM_N /* number of elements in the enum */
44} TMS; 45} TMS;
45 46
diff --git a/lvm.c b/lvm.c
index fdd99a42..e2994aa9 100644
--- a/lvm.c
+++ b/lvm.c
@@ -1452,13 +1452,12 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
1452 vmbreak; 1452 vmbreak;
1453 } 1453 }
1454 vmcase(OP_CLOSE) { 1454 vmcase(OP_CLOSE) {
1455 luaF_close(L, ra); 1455 luaF_close(L, ra, LUA_OK);
1456 vmbreak; 1456 vmbreak;
1457 } 1457 }
1458 vmcase(OP_TBC) { 1458 vmcase(OP_TBC) {
1459 UpVal *up = luaF_findupval(L, ra); /* create new upvalue */ 1459 UpVal *up = luaF_findupval(L, ra); /* create new upvalue */
1460 up->tt = LUA_TUPVALTBC; /* mark it to be closed */ 1460 up->tt = LUA_TUPVALTBC; /* mark it to be closed */
1461 setnilvalue(s2v(ra)); /* intialize it with nil */
1462 vmbreak; 1461 vmbreak;
1463 } 1462 }
1464 vmcase(OP_JMP) { 1463 vmcase(OP_JMP) {
@@ -1591,7 +1590,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
1591 int nparams1 = GETARG_C(i); 1590 int nparams1 = GETARG_C(i);
1592 if (nparams1) /* vararg function? */ 1591 if (nparams1) /* vararg function? */
1593 delta = ci->u.l.nextraargs + nparams1; 1592 delta = ci->u.l.nextraargs + nparams1;
1594 luaF_close(L, base); /* close upvalues from current call */ 1593 luaF_close(L, base, LUA_OK); /* close upvalues from current call */
1595 } 1594 }
1596 if (!ttisfunction(s2v(ra))) { /* not a function? */ 1595 if (!ttisfunction(s2v(ra))) { /* not a function? */
1597 luaD_tryfuncTM(L, ra); /* try '__call' metamethod */ 1596 luaD_tryfuncTM(L, ra); /* try '__call' metamethod */
@@ -1625,7 +1624,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
1625 int nparams1 = GETARG_C(i); 1624 int nparams1 = GETARG_C(i);
1626 if (nparams1) /* vararg function? */ 1625 if (nparams1) /* vararg function? */
1627 ci->func -= ci->u.l.nextraargs + nparams1; 1626 ci->func -= ci->u.l.nextraargs + nparams1;
1628 luaF_close(L, base); /* there may be open upvalues */ 1627 luaF_close(L, base, LUA_OK); /* there may be open upvalues */
1629 } 1628 }
1630 halfProtect(luaD_poscall(L, ci, n)); 1629 halfProtect(luaD_poscall(L, ci, n));
1631 return; 1630 return;
diff --git a/testes/api.lua b/testes/api.lua
index bebb6d2d..925a80c1 100644
--- a/testes/api.lua
+++ b/testes/api.lua
@@ -1,4 +1,4 @@
1-- $Id: testes/api.lua $ 1-- $Id: testes/api.lua 2018-07-25 15:31:04 -0300 $
2-- See Copyright Notice in file all.lua 2-- See Copyright Notice in file all.lua
3 3
4if T==nil then 4if T==nil then
@@ -1027,6 +1027,18 @@ testamem("coroutine creation", function()
1027end) 1027end)
1028 1028
1029 1029
1030-- testing to-be-closed variables
1031testamem("to-be-closed variables", function()
1032 local flag
1033 do
1034 local scoped x = function () flag = true end
1035 flag = false
1036 local x = {}
1037 end
1038 return flag
1039end)
1040
1041
1030-- testing threads 1042-- testing threads
1031 1043
1032-- get main thread from registry (at index LUA_RIDX_MAINTHREAD == 1) 1044-- get main thread from registry (at index LUA_RIDX_MAINTHREAD == 1)
diff --git a/testes/locals.lua b/testes/locals.lua
index 20ecae4b..8d55e9f5 100644
--- a/testes/locals.lua
+++ b/testes/locals.lua
@@ -173,15 +173,69 @@ end
173assert(x==20) 173assert(x==20)
174 174
175 175
176-- tests for to-be-closed variables 176print"testing to-be-closed variables"
177
178do
179 local a = {}
180 do
181 local scoped x = setmetatable({"x"}, {__close = function (self)
182 a[#a + 1] = self[1] end})
183 local scoped y = function () a[#a + 1] = "y" end
184 a[#a + 1] = "in"
185 end
186 a[#a + 1] = "out"
187 assert(a[1] == "in" and a[2] == "y" and a[3] == "x" and a[4] == "out")
188end
189
190
191do -- errors in __close
192 local log = {}
193 local function foo (err)
194 local scoped x = function (msg) log[#log + 1] = msg; error(1) end
195 local scoped x1 = function (msg) log[#log + 1] = msg; end
196 local scoped gc = function () collectgarbage() end
197 local scoped y = function (msg) log[#log + 1] = msg; error(2) end
198 local scoped z = function (msg) log[#log + 1] = msg or 10; error(3) end
199 if err then error(4) end
200 end
201 local stat, msg = pcall(foo, false)
202 assert(msg == 1)
203 assert(log[1] == 10 and log[2] == 3 and log[3] == 2 and log[4] == 2
204 and #log == 4)
205
206 log = {}
207 local stat, msg = pcall(foo, true)
208 assert(msg == 1)
209 assert(log[1] == 4 and log[2] == 3 and log[3] == 2 and log[4] == 2
210 and #log == 4)
211end
212
177do 213do
178 local scoped x = 3 214 -- memory error inside closing function
179 local a 215 local function foo ()
180 local scoped y = 5 216 local scoped y = function () io.write(2); T.alloccount() end
181 assert(x == 3 and y == 5) 217 local scoped x = setmetatable({}, {__close = function ()
218 T.alloccount(0); local x = {} -- force a memory error
219 end})
220 io.write("1\n")
221 error("a") -- common error inside the function's body
222 end
223
224 local _, msg = pcall(foo)
225T.alloccount()
226 assert(msg == "not enough memory")
227
182end 228end
183 229
184 230
231-- a suspended coroutine should not close its variables when collected
232local co = coroutine.wrap(function()
233 local scoped x = function () os.exit(1) end -- should not run
234 coroutine.yield()
235end)
236co()
237co = nil
238
185print('OK') 239print('OK')
186 240
187return 5,f 241return 5,f