diff options
-rw-r--r-- | lapi.c | 9 | ||||
-rw-r--r-- | ldo.c | 2 | ||||
-rw-r--r-- | lfunc.c | 66 | ||||
-rw-r--r-- | lfunc.h | 9 | ||||
-rw-r--r-- | lobject.h | 10 | ||||
-rw-r--r-- | lstate.c | 15 | ||||
-rw-r--r-- | lstate.h | 2 | ||||
-rw-r--r-- | ltests.c | 1 | ||||
-rw-r--r-- | lvm.c | 6 | ||||
-rw-r--r-- | testes/locals.lua | 49 |
10 files changed, 103 insertions, 66 deletions
@@ -192,9 +192,8 @@ LUA_API void lua_settop (lua_State *L, int idx) { | |||
192 | if (diff < 0 && hastocloseCfunc(ci->nresults)) | 192 | if (diff < 0 && hastocloseCfunc(ci->nresults)) |
193 | luaF_close(L, L->top + diff, CLOSEKTOP, 0); | 193 | luaF_close(L, L->top + diff, CLOSEKTOP, 0); |
194 | #endif | 194 | #endif |
195 | api_check(L, L->tbclist < L->top + diff, "cannot pop an unclosed slot"); | ||
195 | L->top += diff; | 196 | L->top += diff; |
196 | api_check(L, L->openupval == NULL || uplevel(L->openupval) < L->top, | ||
197 | "cannot pop an unclosed slot"); | ||
198 | lua_unlock(L); | 197 | lua_unlock(L); |
199 | } | 198 | } |
200 | 199 | ||
@@ -203,8 +202,7 @@ LUA_API void lua_closeslot (lua_State *L, int idx) { | |||
203 | StkId level; | 202 | StkId level; |
204 | lua_lock(L); | 203 | lua_lock(L); |
205 | level = index2stack(L, idx); | 204 | level = index2stack(L, idx); |
206 | api_check(L, hastocloseCfunc(L->ci->nresults) && L->openupval != NULL && | 205 | api_check(L, hastocloseCfunc(L->ci->nresults) && L->tbclist == level, |
207 | uplevel(L->openupval) == level, | ||
208 | "no variable to close at given level"); | 206 | "no variable to close at given level"); |
209 | luaF_close(L, level, CLOSEKTOP, 0); | 207 | luaF_close(L, level, CLOSEKTOP, 0); |
210 | level = index2stack(L, idx); /* stack may be moved */ | 208 | level = index2stack(L, idx); /* stack may be moved */ |
@@ -1266,8 +1264,7 @@ LUA_API void lua_toclose (lua_State *L, int idx) { | |||
1266 | lua_lock(L); | 1264 | lua_lock(L); |
1267 | o = index2stack(L, idx); | 1265 | o = index2stack(L, idx); |
1268 | nresults = L->ci->nresults; | 1266 | nresults = L->ci->nresults; |
1269 | api_check(L, L->openupval == NULL || uplevel(L->openupval) <= o, | 1267 | api_check(L, L->tbclist < o, "given index below or equal a marked one"); |
1270 | "marked index below or equal new one"); | ||
1271 | luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */ | 1268 | luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */ |
1272 | if (!hastocloseCfunc(nresults)) /* function not marked yet? */ | 1269 | if (!hastocloseCfunc(nresults)) /* function not marked yet? */ |
1273 | L->ci->nresults = codeNresults(nresults); /* mark it */ | 1270 | L->ci->nresults = codeNresults(nresults); /* mark it */ |
@@ -163,7 +163,7 @@ static void correctstack (lua_State *L, StkId oldstack, StkId newstack) { | |||
163 | if (oldstack == newstack) | 163 | if (oldstack == newstack) |
164 | return; /* stack address did not change */ | 164 | return; /* stack address did not change */ |
165 | L->top = (L->top - oldstack) + newstack; | 165 | L->top = (L->top - oldstack) + newstack; |
166 | lua_assert(L->ptbc == NULL); | 166 | L->tbclist = (L->tbclist - oldstack) + newstack; |
167 | for (up = L->openupval; up != NULL; up = up->u.open.next) | 167 | for (up = L->openupval; up != NULL; up = up->u.open.next) |
168 | up->v = s2v((uplevel(up) - oldstack) + newstack); | 168 | up->v = s2v((uplevel(up) - oldstack) + newstack); |
169 | for (ci = L->ci; ci != NULL; ci = ci->previous) { | 169 | for (ci = L->ci; ci != NULL; ci = ci->previous) { |
@@ -120,11 +120,11 @@ static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) { | |||
120 | 120 | ||
121 | 121 | ||
122 | /* | 122 | /* |
123 | ** Check whether 'obj' has a close metamethod and raise an error | 123 | ** Check whether object at given level has a close metamethod and raise |
124 | ** if not. | 124 | ** an error if not. |
125 | */ | 125 | */ |
126 | static void checkclosemth (lua_State *L, StkId level, const TValue *obj) { | 126 | static void checkclosemth (lua_State *L, StkId level) { |
127 | const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE); | 127 | const TValue *tm = luaT_gettmbyobj(L, s2v(level), TM_CLOSE); |
128 | if (ttisnil(tm)) { /* no metamethod? */ | 128 | if (ttisnil(tm)) { /* no metamethod? */ |
129 | int idx = cast_int(level - L->ci->func); /* variable index */ | 129 | int idx = cast_int(level - L->ci->func); /* variable index */ |
130 | const char *vname = luaG_findlocal(L, L->ci, idx, NULL); | 130 | const char *vname = luaG_findlocal(L, L->ci, idx, NULL); |
@@ -155,20 +155,21 @@ static void prepcallclosemth (lua_State *L, StkId level, int status, int yy) { | |||
155 | 155 | ||
156 | 156 | ||
157 | /* | 157 | /* |
158 | ** Create a to-be-closed upvalue. If there is a memory allocation error, | 158 | ** Insert a variable in the list of to-be-closed variables. |
159 | ** 'ptbc' keeps the object so it can be closed as soon as possible. | ||
160 | ** (Since memory errors have no handler, that will happen before any | ||
161 | ** stack reallocation.) | ||
162 | */ | 159 | */ |
163 | void luaF_newtbcupval (lua_State *L, StkId level) { | 160 | void luaF_newtbcupval (lua_State *L, StkId level) { |
164 | TValue *obj = s2v(level); | 161 | lua_assert(level > L->tbclist); |
165 | lua_assert(L->openupval == NULL || uplevel(L->openupval) < level); | 162 | if (l_isfalse(s2v(level))) |
166 | if (!l_isfalse(obj)) { /* false doesn't need to be closed */ | 163 | return; /* false doesn't need to be closed */ |
167 | checkclosemth(L, level, obj); | 164 | checkclosemth(L, level); /* value must have a close method */ |
168 | L->ptbc = level; /* in case of allocation error */ | 165 | while (level - L->tbclist > USHRT_MAX) { /* is delta too large? */ |
169 | newupval(L, 1, level, &L->openupval); | 166 | L->tbclist += USHRT_MAX; /* create a dummy node at maximum delta */ |
170 | L->ptbc = NULL; /* no errors */ | 167 | L->tbclist->tbclist.delta = USHRT_MAX; |
168 | L->tbclist->tbclist.isdummy = 1; | ||
171 | } | 169 | } |
170 | level->tbclist.delta = level - L->tbclist; | ||
171 | level->tbclist.isdummy = 0; | ||
172 | L->tbclist = level; | ||
172 | } | 173 | } |
173 | 174 | ||
174 | 175 | ||
@@ -181,23 +182,11 @@ void luaF_unlinkupval (UpVal *uv) { | |||
181 | 182 | ||
182 | 183 | ||
183 | /* | 184 | /* |
184 | ** Close all upvalues up to the given stack level. A 'status' equal | 185 | ** Close all upvalues up to the given stack level. |
185 | ** to NOCLOSINGMETH closes upvalues without running any __close | ||
186 | ** metamethods. If there is a pending to-be-closed value, close | ||
187 | ** it before anything else. | ||
188 | */ | 186 | */ |
189 | void luaF_close (lua_State *L, StkId level, int status, int yy) { | 187 | void luaF_closeupval (lua_State *L, StkId level) { |
190 | UpVal *uv; | 188 | UpVal *uv; |
191 | StkId upl; /* stack index pointed by 'uv' */ | 189 | StkId upl; /* stack index pointed by 'uv' */ |
192 | if (unlikely(status == LUA_ERRMEM && L->ptbc != NULL)) { | ||
193 | ptrdiff_t levelrel = savestack(L, level); | ||
194 | upl = L->ptbc; | ||
195 | L->ptbc = NULL; /* remove from "list" before closing */ | ||
196 | prepcallclosemth(L, upl, status, yy); | ||
197 | level = restorestack(L, levelrel); | ||
198 | } | ||
199 | else | ||
200 | lua_assert(L->ptbc == NULL); /* must be empty for other status */ | ||
201 | while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) { | 190 | while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) { |
202 | TValue *slot = &uv->u.value; /* new position for value */ | 191 | TValue *slot = &uv->u.value; /* new position for value */ |
203 | lua_assert(uplevel(uv) < L->top); | 192 | lua_assert(uplevel(uv) < L->top); |
@@ -208,9 +197,22 @@ void luaF_close (lua_State *L, StkId level, int status, int yy) { | |||
208 | nw2black(uv); /* closed upvalues cannot be gray */ | 197 | nw2black(uv); /* closed upvalues cannot be gray */ |
209 | luaC_barrier(L, uv, slot); | 198 | luaC_barrier(L, uv, slot); |
210 | } | 199 | } |
211 | if (uv->tbc && status != NOCLOSINGMETH) { | 200 | } |
212 | ptrdiff_t levelrel = savestack(L, level); | 201 | } |
213 | prepcallclosemth(L, upl, status, yy); /* may change the stack */ | 202 | |
203 | |||
204 | /* | ||
205 | ** Close all upvalues and to-be-closed variables up to the given stack | ||
206 | ** level. | ||
207 | */ | ||
208 | void luaF_close (lua_State *L, StkId level, int status, int yy) { | ||
209 | ptrdiff_t levelrel = savestack(L, level); | ||
210 | luaF_closeupval(L, level); /* first, close the upvalues */ | ||
211 | while (L->tbclist >= level) { /* traverse tbc's down to that level */ | ||
212 | StkId tbc = L->tbclist; /* get variable index */ | ||
213 | L->tbclist -= tbc->tbclist.delta; /* remove it from list */ | ||
214 | if (!tbc->tbclist.isdummy) { /* not a dummy entry? */ | ||
215 | prepcallclosemth(L, tbc, status, yy); /* close variable */ | ||
214 | level = restorestack(L, levelrel); | 216 | level = restorestack(L, levelrel); |
215 | } | 217 | } |
216 | } | 218 | } |
@@ -42,15 +42,9 @@ | |||
42 | #define MAXMISS 10 | 42 | #define MAXMISS 10 |
43 | 43 | ||
44 | 44 | ||
45 | /* | ||
46 | ** Special "status" for 'luaF_close' | ||
47 | */ | ||
48 | |||
49 | /* close upvalues without running their closing methods */ | ||
50 | #define NOCLOSINGMETH (-1) | ||
51 | 45 | ||
52 | /* special status to close upvalues preserving the top of the stack */ | 46 | /* special status to close upvalues preserving the top of the stack */ |
53 | #define CLOSEKTOP (-2) | 47 | #define CLOSEKTOP (-1) |
54 | 48 | ||
55 | 49 | ||
56 | LUAI_FUNC Proto *luaF_newproto (lua_State *L); | 50 | LUAI_FUNC Proto *luaF_newproto (lua_State *L); |
@@ -59,6 +53,7 @@ LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nupvals); | |||
59 | LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl); | 53 | LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl); |
60 | LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); | 54 | LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); |
61 | LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level); | 55 | LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level); |
56 | LUAI_FUNC void luaF_closeupval (lua_State *L, StkId level); | ||
62 | LUAI_FUNC void luaF_close (lua_State *L, StkId level, int status, int yy); | 57 | LUAI_FUNC void luaF_close (lua_State *L, StkId level, int status, int yy); |
63 | LUAI_FUNC void luaF_unlinkupval (UpVal *uv); | 58 | LUAI_FUNC void luaF_unlinkupval (UpVal *uv); |
64 | LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); | 59 | LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); |
@@ -136,10 +136,18 @@ typedef struct TValue { | |||
136 | 136 | ||
137 | 137 | ||
138 | /* | 138 | /* |
139 | ** Entries in the Lua stack | 139 | ** Entries in a Lua stack. Field 'tbclist' forms a list of all |
140 | ** to-be-closed variables active in this stack. Dummy entries are | ||
141 | ** used when the distance between two tbc variables does not fit | ||
142 | ** in an unsigned short. | ||
140 | */ | 143 | */ |
141 | typedef union StackValue { | 144 | typedef union StackValue { |
142 | TValue val; | 145 | TValue val; |
146 | struct { | ||
147 | TValuefields; | ||
148 | lu_byte isdummy; | ||
149 | unsigned short delta; | ||
150 | } tbclist; | ||
143 | } StackValue; | 151 | } StackValue; |
144 | 152 | ||
145 | 153 | ||
@@ -181,6 +181,7 @@ static void stack_init (lua_State *L1, lua_State *L) { | |||
181 | int i; CallInfo *ci; | 181 | int i; CallInfo *ci; |
182 | /* initialize stack array */ | 182 | /* initialize stack array */ |
183 | L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue); | 183 | L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue); |
184 | L1->tbclist = L1->stack; | ||
184 | for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++) | 185 | for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++) |
185 | setnilvalue(s2v(L1->stack + i)); /* erase new stack */ | 186 | setnilvalue(s2v(L1->stack + i)); /* erase new stack */ |
186 | L1->top = L1->stack; | 187 | L1->top = L1->stack; |
@@ -262,16 +263,18 @@ static void preinit_thread (lua_State *L, global_State *g) { | |||
262 | L->status = LUA_OK; | 263 | L->status = LUA_OK; |
263 | L->errfunc = 0; | 264 | L->errfunc = 0; |
264 | L->oldpc = 0; | 265 | L->oldpc = 0; |
265 | L->ptbc = NULL; | ||
266 | } | 266 | } |
267 | 267 | ||
268 | 268 | ||
269 | static void close_state (lua_State *L) { | 269 | static void close_state (lua_State *L) { |
270 | global_State *g = G(L); | 270 | global_State *g = G(L); |
271 | luaD_closeprotected(L, 0, LUA_OK); /* close all upvalues */ | 271 | if (!completestate(g)) /* closing a partially built state? */ |
272 | luaC_freeallobjects(L); /* collect all objects */ | 272 | luaC_freeallobjects(L); /* jucst collect its objects */ |
273 | if (completestate(g)) /* closing a fully built state? */ | 273 | else { /* closing a fully built state */ |
274 | luaD_closeprotected(L, 1, LUA_OK); /* close all upvalues */ | ||
275 | luaC_freeallobjects(L); /* collect all objects */ | ||
274 | luai_userstateclose(L); | 276 | luai_userstateclose(L); |
277 | } | ||
275 | luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size); | 278 | luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size); |
276 | freestack(L); | 279 | freestack(L); |
277 | lua_assert(gettotalbytes(g) == sizeof(LG)); | 280 | lua_assert(gettotalbytes(g) == sizeof(LG)); |
@@ -312,7 +315,7 @@ LUA_API lua_State *lua_newthread (lua_State *L) { | |||
312 | 315 | ||
313 | void luaE_freethread (lua_State *L, lua_State *L1) { | 316 | void luaE_freethread (lua_State *L, lua_State *L1) { |
314 | LX *l = fromstate(L1); | 317 | LX *l = fromstate(L1); |
315 | luaF_close(L1, L1->stack, NOCLOSINGMETH, 0); /* close all upvalues */ | 318 | luaF_closeupval(L1, L1->stack); /* close all upvalues */ |
316 | lua_assert(L1->openupval == NULL); | 319 | lua_assert(L1->openupval == NULL); |
317 | luai_userstatefree(L, L1); | 320 | luai_userstatefree(L, L1); |
318 | freestack(L1); | 321 | freestack(L1); |
@@ -327,7 +330,7 @@ int luaE_resetthread (lua_State *L, int status) { | |||
327 | ci->callstatus = CIST_C; | 330 | ci->callstatus = CIST_C; |
328 | if (status == LUA_YIELD) | 331 | if (status == LUA_YIELD) |
329 | status = LUA_OK; | 332 | status = LUA_OK; |
330 | status = luaD_closeprotected(L, 0, status); | 333 | status = luaD_closeprotected(L, 1, status); |
331 | if (status != LUA_OK) /* errors? */ | 334 | if (status != LUA_OK) /* errors? */ |
332 | luaD_seterrorobj(L, status, L->stack + 1); | 335 | luaD_seterrorobj(L, status, L->stack + 1); |
333 | else | 336 | else |
@@ -307,6 +307,7 @@ struct lua_State { | |||
307 | StkId stack_last; /* end of stack (last element + 1) */ | 307 | StkId stack_last; /* end of stack (last element + 1) */ |
308 | StkId stack; /* stack base */ | 308 | StkId stack; /* stack base */ |
309 | UpVal *openupval; /* list of open upvalues in this stack */ | 309 | UpVal *openupval; /* list of open upvalues in this stack */ |
310 | StkId tbclist; /* list of to-be-closed variables */ | ||
310 | GCObject *gclist; | 311 | GCObject *gclist; |
311 | struct lua_State *twups; /* list of threads with open upvalues */ | 312 | struct lua_State *twups; /* list of threads with open upvalues */ |
312 | struct lua_longjmp *errorJmp; /* current error recover point */ | 313 | struct lua_longjmp *errorJmp; /* current error recover point */ |
@@ -318,7 +319,6 @@ struct lua_State { | |||
318 | int basehookcount; | 319 | int basehookcount; |
319 | int hookcount; | 320 | int hookcount; |
320 | volatile l_signalT hookmask; | 321 | volatile l_signalT hookmask; |
321 | StkId ptbc; /* pending to-be-closed variable */ | ||
322 | }; | 322 | }; |
323 | 323 | ||
324 | 324 | ||
@@ -446,6 +446,7 @@ static void checkstack (global_State *g, lua_State *L1) { | |||
446 | for (uv = L1->openupval; uv != NULL; uv = uv->u.open.next) | 446 | for (uv = L1->openupval; uv != NULL; uv = uv->u.open.next) |
447 | assert(upisopen(uv)); /* must be open */ | 447 | assert(upisopen(uv)); /* must be open */ |
448 | assert(L1->top <= L1->stack_last); | 448 | assert(L1->top <= L1->stack_last); |
449 | assert(L1->tbclist <= L1->top); | ||
449 | for (ci = L1->ci; ci != NULL; ci = ci->previous) { | 450 | for (ci = L1->ci; ci != NULL; ci = ci->previous) { |
450 | assert(ci->top <= L1->stack_last); | 451 | assert(ci->top <= L1->stack_last); |
451 | assert(lua_checkpc(ci)); | 452 | assert(lua_checkpc(ci)); |
@@ -1635,10 +1635,8 @@ void luaV_execute (lua_State *L, CallInfo *ci) { | |||
1635 | b = cast_int(L->top - ra); | 1635 | b = cast_int(L->top - ra); |
1636 | savepc(ci); /* several calls here can raise errors */ | 1636 | savepc(ci); /* several calls here can raise errors */ |
1637 | if (TESTARG_k(i)) { | 1637 | if (TESTARG_k(i)) { |
1638 | /* close upvalues from current call; the compiler ensures | 1638 | luaF_closeupval(L, base); /* close upvalues from current call */ |
1639 | that there are no to-be-closed variables here, so this | 1639 | lua_assert(L->tbclist < base); /* no pending tbc variables */ |
1640 | call cannot change the stack */ | ||
1641 | luaF_close(L, base, NOCLOSINGMETH, 0); | ||
1642 | lua_assert(base == ci->func + 1); | 1640 | lua_assert(base == ci->func + 1); |
1643 | } | 1641 | } |
1644 | while (!ttisfunction(s2v(ra))) { /* not a function? */ | 1642 | while (!ttisfunction(s2v(ra))) { /* not a function? */ |
diff --git a/testes/locals.lua b/testes/locals.lua index a25b2b9f..446ec13a 100644 --- a/testes/locals.lua +++ b/testes/locals.lua | |||
@@ -529,6 +529,40 @@ local function checktable (t1, t2) | |||
529 | end | 529 | end |
530 | 530 | ||
531 | 531 | ||
532 | do -- test for tbc variable high in the stack | ||
533 | |||
534 | -- function to force a stack overflow | ||
535 | local function overflow (n) | ||
536 | overflow(n + 1) | ||
537 | end | ||
538 | |||
539 | -- error handler will create tbc variable handling a stack overflow, | ||
540 | -- high in the stack | ||
541 | local function errorh (m) | ||
542 | assert(string.find(m, "stack overflow")) | ||
543 | local x <close> = func2close(function (o) o[1] = 10 end) | ||
544 | return x | ||
545 | end | ||
546 | |||
547 | local flag | ||
548 | local st, obj | ||
549 | -- run test in a coroutine so as not to swell the main stack | ||
550 | local co = coroutine.wrap(function () | ||
551 | -- tbc variable down the stack | ||
552 | local y <close> = func2close(function (obj, msg) | ||
553 | assert(msg == nil) | ||
554 | obj[1] = 100 | ||
555 | flag = obj | ||
556 | end) | ||
557 | collectgarbage("stop") | ||
558 | st, obj = xpcall(overflow, errorh, 0) | ||
559 | collectgarbage("restart") | ||
560 | end) | ||
561 | co() | ||
562 | assert(not st and obj[1] == 10 and flag[1] == 100) | ||
563 | end | ||
564 | |||
565 | |||
532 | if rawget(_G, "T") then | 566 | if rawget(_G, "T") then |
533 | 567 | ||
534 | -- memory error inside closing function | 568 | -- memory error inside closing function |
@@ -563,13 +597,13 @@ if rawget(_G, "T") then | |||
563 | 597 | ||
564 | local function test () | 598 | local function test () |
565 | local x <close> = enter(0) -- set a memory limit | 599 | local x <close> = enter(0) -- set a memory limit |
566 | -- creation of previous upvalue will raise a memory error | 600 | local y = {} -- raise a memory error |
567 | assert(false) -- should not run | ||
568 | end | 601 | end |
569 | 602 | ||
570 | local _, msg = pcall(test) | 603 | local _, msg = pcall(test) |
571 | assert(msg == "not enough memory" and closemsg == "not enough memory") | 604 | assert(msg == "not enough memory" and closemsg == "not enough memory") |
572 | 605 | ||
606 | |||
573 | -- repeat test with extra closing upvalues | 607 | -- repeat test with extra closing upvalues |
574 | local function test () | 608 | local function test () |
575 | local xxx <close> = func2close(function (self, msg) | 609 | local xxx <close> = func2close(function (self, msg) |
@@ -580,8 +614,7 @@ if rawget(_G, "T") then | |||
580 | assert(msg == "not enough memory"); | 614 | assert(msg == "not enough memory"); |
581 | end) | 615 | end) |
582 | local x <close> = enter(0) -- set a memory limit | 616 | local x <close> = enter(0) -- set a memory limit |
583 | -- creation of previous upvalue will raise a memory error | 617 | local y = {} -- raise a memory error |
584 | os.exit(false) -- should not run | ||
585 | end | 618 | end |
586 | 619 | ||
587 | local _, msg = pcall(test) | 620 | local _, msg = pcall(test) |
@@ -607,7 +640,7 @@ if rawget(_G, "T") then | |||
607 | -- concat this table needs two buffer resizes (one for each 's') | 640 | -- concat this table needs two buffer resizes (one for each 's') |
608 | local a = {s, s} | 641 | local a = {s, s} |
609 | 642 | ||
610 | collectgarbage() | 643 | collectgarbage(); collectgarbage() |
611 | 644 | ||
612 | m = T.totalmem() | 645 | m = T.totalmem() |
613 | collectgarbage("stop") | 646 | collectgarbage("stop") |
@@ -630,7 +663,7 @@ if rawget(_G, "T") then | |||
630 | -- second buffer was released by 'toclose' | 663 | -- second buffer was released by 'toclose' |
631 | assert(T.totalmem() - m <= extra) | 664 | assert(T.totalmem() - m <= extra) |
632 | 665 | ||
633 | -- userdata, upvalue, buffer, buffer, final string | 666 | -- userdata, buffer, buffer, final string |
634 | T.totalmem(m + 4*lim + extra) | 667 | T.totalmem(m + 4*lim + extra) |
635 | assert(#table.concat(a) == 2*lim) | 668 | assert(#table.concat(a) == 2*lim) |
636 | 669 | ||
@@ -753,8 +786,8 @@ do | |||
753 | checktable({co()}, {true, 10, 20, 30}) | 786 | checktable({co()}, {true, 10, 20, 30}) |
754 | checktable(trace, {"nowX", "z1", "z2", "nowY", "y1", "y2", "x1", "x2"}) | 787 | checktable(trace, {"nowX", "z1", "z2", "nowY", "y1", "y2", "x1", "x2"}) |
755 | 788 | ||
756 | end | 789 | end |
757 | 790 | ||
758 | 791 | ||
759 | do | 792 | do |
760 | -- yielding inside closing metamethods after an error | 793 | -- yielding inside closing metamethods after an error |