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 |
