diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-10-17 10:44:42 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-10-17 10:44:42 -0300 |
commit | bd96330d037660d9a1769c6c0d989f017e5f0278 (patch) | |
tree | c3f4580c628a71f512b057b7f52987153cb88d74 | |
parent | 4cd1f4aac01184765818e0cebf02da454ccf6590 (diff) | |
download | lua-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.c | 20 | ||||
-rw-r--r-- | ldo.h | 1 | ||||
-rw-r--r-- | lfunc.c | 47 | ||||
-rw-r--r-- | lfunc.h | 2 | ||||
-rw-r--r-- | lgc.c | 5 | ||||
-rw-r--r-- | lparser.c | 2 | ||||
-rw-r--r-- | lstate.c | 4 | ||||
-rw-r--r-- | ltests.c | 2 | ||||
-rw-r--r-- | ltm.c | 2 | ||||
-rw-r--r-- | ltm.h | 1 | ||||
-rw-r--r-- | lvm.c | 7 | ||||
-rw-r--r-- | testes/api.lua | 14 | ||||
-rw-r--r-- | testes/locals.lua | 64 |
13 files changed, 145 insertions, 26 deletions
@@ -88,7 +88,7 @@ struct lua_longjmp { | |||
88 | }; | 88 | }; |
89 | 89 | ||
90 | 90 | ||
91 | static void seterrorobj (lua_State *L, int errcode, StkId oldtop) { | 91 | void 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 | */ | ||
729 | int luaD_pcall (lua_State *L, Pfunc func, void *u, | 734 | int 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; |
@@ -50,6 +50,7 @@ | |||
50 | /* type of protected functions, to be ran by 'runprotected' */ | 50 | /* type of protected functions, to be ran by 'runprotected' */ |
51 | typedef void (*Pfunc) (lua_State *L, void *ud); | 51 | typedef void (*Pfunc) (lua_State *L, void *ud); |
52 | 52 | ||
53 | LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop); | ||
53 | LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, | 54 | LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, |
54 | const char *mode); | 55 | const char *mode); |
55 | LUAI_FUNC void luaD_hook (lua_State *L, int event, int line, | 56 | LUAI_FUNC void luaD_hook (lua_State *L, int event, int line, |
@@ -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 | ||
87 | static void callclose (lua_State *L, void *ud) { | ||
88 | luaD_callnoyield(L, cast(StkId, ud), 0); | ||
89 | } | ||
90 | |||
91 | |||
92 | static 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 | |||
86 | void luaF_unlinkupval (UpVal *uv) { | 121 | void 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 | ||
94 | void luaF_close (lua_State *L, StkId level) { | 129 | int 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 | ||
@@ -47,7 +47,7 @@ LUAI_FUNC CClosure *luaF_newCclosure (lua_State *L, int nelems); | |||
47 | LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nelems); | 47 | LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nelems); |
48 | LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl); | 48 | LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl); |
49 | LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); | 49 | LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); |
50 | LUAI_FUNC void luaF_close (lua_State *L, StkId level); | 50 | LUAI_FUNC int luaF_close (lua_State *L, StkId level, int status); |
51 | LUAI_FUNC void luaF_unlinkupval (UpVal *uv); | 51 | LUAI_FUNC void luaF_unlinkupval (UpVal *uv); |
52 | LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); | 52 | LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); |
53 | LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, | 53 | LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, |
@@ -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 | */ |
611 | static int traversethread (global_State *g, lua_State *th) { | 611 | static 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 */ |
@@ -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 | ||
@@ -258,7 +258,7 @@ static void preinit_thread (lua_State *L, global_State *g) { | |||
258 | 258 | ||
259 | static void close_state (lua_State *L) { | 259 | static 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 | ||
302 | void luaE_freethread (lua_State *L, lua_State *L1) { | 302 | void 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); |
@@ -1208,7 +1208,7 @@ static int getindex_aux (lua_State *L, lua_State *L1, const char **pc) { | |||
1208 | 1208 | ||
1209 | static void pushcode (lua_State *L, int code) { | 1209 | static 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 | ||
@@ -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++) { |
@@ -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 | ||
@@ -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 | ||
4 | if T==nil then | 4 | if T==nil then |
@@ -1027,6 +1027,18 @@ testamem("coroutine creation", function() | |||
1027 | end) | 1027 | end) |
1028 | 1028 | ||
1029 | 1029 | ||
1030 | -- testing to-be-closed variables | ||
1031 | testamem("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 | ||
1039 | end) | ||
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 | |||
173 | assert(x==20) | 173 | assert(x==20) |
174 | 174 | ||
175 | 175 | ||
176 | -- tests for to-be-closed variables | 176 | print"testing to-be-closed variables" |
177 | |||
178 | do | ||
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") | ||
188 | end | ||
189 | |||
190 | |||
191 | do -- 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) | ||
211 | end | ||
212 | |||
177 | do | 213 | do |
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) | ||
225 | T.alloccount() | ||
226 | assert(msg == "not enough memory") | ||
227 | |||
182 | end | 228 | end |
183 | 229 | ||
184 | 230 | ||
231 | -- a suspended coroutine should not close its variables when collected | ||
232 | local co = coroutine.wrap(function() | ||
233 | local scoped x = function () os.exit(1) end -- should not run | ||
234 | coroutine.yield() | ||
235 | end) | ||
236 | co() | ||
237 | co = nil | ||
238 | |||
185 | print('OK') | 239 | print('OK') |
186 | 240 | ||
187 | return 5,f | 241 | return 5,f |