diff options
| -rw-r--r-- | lapi.c | 19 | ||||
| -rw-r--r-- | lauxlib.c | 8 | ||||
| -rw-r--r-- | ltests.c | 3 | ||||
| -rw-r--r-- | lua.h | 3 | ||||
| -rw-r--r-- | manual/manual.of | 33 | ||||
| -rw-r--r-- | testes/api.lua | 29 |
6 files changed, 66 insertions, 29 deletions
| @@ -187,9 +187,26 @@ LUA_API void lua_settop (lua_State *L, int idx) { | |||
| 187 | api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top"); | 187 | api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top"); |
| 188 | diff = idx + 1; /* will "subtract" index (as it is negative) */ | 188 | diff = idx + 1; /* will "subtract" index (as it is negative) */ |
| 189 | } | 189 | } |
| 190 | #if defined(LUA_COMPAT_5_4_0) | ||
| 190 | if (diff < 0 && hastocloseCfunc(ci->nresults)) | 191 | if (diff < 0 && hastocloseCfunc(ci->nresults)) |
| 191 | luaF_close(L, L->top + diff, CLOSEKTOP); | 192 | luaF_close(L, L->top + diff, CLOSEKTOP); |
| 192 | L->top += diff; /* correct top only after closing any upvalue */ | 193 | #endif |
| 194 | L->top += diff; | ||
| 195 | api_check(L, L->openupval == NULL || uplevel(L->openupval) < L->top, | ||
| 196 | "cannot pop an unclosed slot"); | ||
| 197 | lua_unlock(L); | ||
| 198 | } | ||
| 199 | |||
| 200 | |||
| 201 | LUA_API void lua_closeslot (lua_State *L, int idx) { | ||
| 202 | StkId level; | ||
| 203 | lua_lock(L); | ||
| 204 | level = index2stack(L, idx); | ||
| 205 | api_check(L, hastocloseCfunc(L->ci->nresults) && L->openupval != NULL && | ||
| 206 | uplevel(L->openupval) == level, | ||
| 207 | "no variable to close at given level"); | ||
| 208 | luaF_close(L, level, CLOSEKTOP); | ||
| 209 | setnilvalue(s2v(level)); | ||
| 193 | lua_unlock(L); | 210 | lua_unlock(L); |
| 194 | } | 211 | } |
| 195 | 212 | ||
| @@ -545,10 +545,8 @@ static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) { | |||
| 545 | if (buffonstack(B)) /* buffer already has a box? */ | 545 | if (buffonstack(B)) /* buffer already has a box? */ |
| 546 | newbuff = (char *)resizebox(L, boxidx, newsize); /* resize it */ | 546 | newbuff = (char *)resizebox(L, boxidx, newsize); /* resize it */ |
| 547 | else { /* no box yet */ | 547 | else { /* no box yet */ |
| 548 | lua_pushnil(L); /* reserve slot for final result */ | ||
| 549 | newbox(L); /* create a new box */ | 548 | newbox(L); /* create a new box */ |
| 550 | /* move box (and slot) to its intended position */ | 549 | lua_insert(L, boxidx); /* move box to its intended position */ |
| 551 | lua_rotate(L, boxidx - 1, 2); | ||
| 552 | lua_toclose(L, boxidx); | 550 | lua_toclose(L, boxidx); |
| 553 | newbuff = (char *)resizebox(L, boxidx, newsize); | 551 | newbuff = (char *)resizebox(L, boxidx, newsize); |
| 554 | memcpy(newbuff, B->b, B->n * sizeof(char)); /* copy original content */ | 552 | memcpy(newbuff, B->b, B->n * sizeof(char)); /* copy original content */ |
| @@ -585,8 +583,8 @@ LUALIB_API void luaL_pushresult (luaL_Buffer *B) { | |||
| 585 | lua_State *L = B->L; | 583 | lua_State *L = B->L; |
| 586 | lua_pushlstring(L, B->b, B->n); | 584 | lua_pushlstring(L, B->b, B->n); |
| 587 | if (buffonstack(B)) { | 585 | if (buffonstack(B)) { |
| 588 | lua_copy(L, -1, -3); /* move string to reserved slot */ | 586 | lua_closeslot(L, -2); /* close the box */ |
| 589 | lua_pop(L, 2); /* pop string and box (closing the box) */ | 587 | lua_remove(L, -2); /* remove box from the stack */ |
| 590 | } | 588 | } |
| 591 | } | 589 | } |
| 592 | 590 | ||
| @@ -1766,6 +1766,9 @@ static struct X { int x; } x; | |||
| 1766 | else if EQ("toclose") { | 1766 | else if EQ("toclose") { |
| 1767 | lua_toclose(L1, getnum); | 1767 | lua_toclose(L1, getnum); |
| 1768 | } | 1768 | } |
| 1769 | else if EQ("closeslot") { | ||
| 1770 | lua_closeslot(L1, getnum); | ||
| 1771 | } | ||
| 1769 | else luaL_error(L, "unknown instruction %s", buff); | 1772 | else luaL_error(L, "unknown instruction %s", buff); |
| 1770 | } | 1773 | } |
| 1771 | return 0; | 1774 | return 0; |
| @@ -347,7 +347,8 @@ LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s); | |||
| 347 | LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); | 347 | LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); |
| 348 | LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud); | 348 | LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud); |
| 349 | 349 | ||
| 350 | LUA_API void (lua_toclose) (lua_State *L, int idx); | 350 | LUA_API void (lua_toclose) (lua_State *L, int idx); |
| 351 | LUA_API void (lua_closeslot) (lua_State *L, int idx); | ||
| 351 | 352 | ||
| 352 | 353 | ||
| 353 | /* | 354 | /* |
diff --git a/manual/manual.of b/manual/manual.of index c5385258..09297a63 100644 --- a/manual/manual.of +++ b/manual/manual.of | |||
| @@ -3095,6 +3095,18 @@ will probably need to close states as soon as they are not needed. | |||
| 3095 | 3095 | ||
| 3096 | } | 3096 | } |
| 3097 | 3097 | ||
| 3098 | @APIEntry{void lua_closeslot (lua_State *L, int index);| | ||
| 3099 | @apii{0,0,e} | ||
| 3100 | |||
| 3101 | Close the to-be-closed slot at the given index and set its value to @nil. | ||
| 3102 | The index must be the last index previously marked to be closed | ||
| 3103 | @see{lua_toclose} that is still active (that is, not closed yet). | ||
| 3104 | |||
| 3105 | (Exceptionally, this function was introduced in release 5.4.3. | ||
| 3106 | It is not present in previous 5.4 releases.) | ||
| 3107 | |||
| 3108 | } | ||
| 3109 | |||
| 3098 | @APIEntry{int lua_compare (lua_State *L, int index1, int index2, int op);| | 3110 | @APIEntry{int lua_compare (lua_State *L, int index1, int index2, int op);| |
| 3099 | @apii{0,0,e} | 3111 | @apii{0,0,e} |
| 3100 | 3112 | ||
| @@ -3747,9 +3759,7 @@ except that it allows the called function to yield @see{continuations}. | |||
| 3747 | @apii{n,0,e} | 3759 | @apii{n,0,e} |
| 3748 | 3760 | ||
| 3749 | Pops @id{n} elements from the stack. | 3761 | Pops @id{n} elements from the stack. |
| 3750 | 3762 | It is implemented as a macro over @Lid{lua_settop}. | |
| 3751 | This function can run arbitrary code when removing an index | ||
| 3752 | marked as to-be-closed from the stack. | ||
| 3753 | 3763 | ||
| 3754 | } | 3764 | } |
| 3755 | 3765 | ||
| @@ -4240,8 +4250,12 @@ If the new top is greater than the old one, | |||
| 4240 | then the new elements are filled with @nil. | 4250 | then the new elements are filled with @nil. |
| 4241 | If @id{index} @N{is 0}, then all stack elements are removed. | 4251 | If @id{index} @N{is 0}, then all stack elements are removed. |
| 4242 | 4252 | ||
| 4243 | This function can run arbitrary code when removing an index | 4253 | For compatibility reasons, |
| 4244 | marked as to-be-closed from the stack. | 4254 | this function may close slots marked as to-be-closed @see{lua_toclose}, |
| 4255 | and therefore it can run arbitrary code. | ||
| 4256 | You should not rely on this behavior: | ||
| 4257 | Instead, always close to-be-closed slots explicitly, | ||
| 4258 | with @Lid{lua_closeslot}, before removing them from the stack. | ||
| 4245 | 4259 | ||
| 4246 | } | 4260 | } |
| 4247 | 4261 | ||
| @@ -4337,10 +4351,9 @@ when it goes out of scope. | |||
| 4337 | Here, in the context of a C function, | 4351 | Here, in the context of a C function, |
| 4338 | to go out of scope means that the running function returns to Lua, | 4352 | to go out of scope means that the running function returns to Lua, |
| 4339 | there is an error, | 4353 | there is an error, |
| 4340 | or the index is removed from the stack through | 4354 | or there is a call to @Lid{lua_closeslot}. |
| 4341 | @Lid{lua_settop} or @Lid{lua_pop}. | 4355 | An index marked as to-be-closed should neither be removed from the stack |
| 4342 | An index marked as to-be-closed should not be removed from the stack | 4356 | nor modified before a corresponding call to @Lid{lua_closeslot}. |
| 4343 | by any other function in the API except @Lid{lua_settop} or @Lid{lua_pop}. | ||
| 4344 | 4357 | ||
| 4345 | This function should not be called for an index | 4358 | This function should not be called for an index |
| 4346 | that is equal to or below an active to-be-closed index. | 4359 | that is equal to or below an active to-be-closed index. |
| @@ -4353,7 +4366,7 @@ Note that, both in case of errors and of a regular return, | |||
| 4353 | by the time the @idx{__close} metamethod runs, | 4366 | by the time the @idx{__close} metamethod runs, |
| 4354 | the @N{C stack} was already unwound, | 4367 | the @N{C stack} was already unwound, |
| 4355 | so that any automatic @N{C variable} declared in the calling function | 4368 | so that any automatic @N{C variable} declared in the calling function |
| 4356 | will be out of scope. | 4369 | (e.g., a buffer) will be out of scope. |
| 4357 | 4370 | ||
| 4358 | } | 4371 | } |
| 4359 | 4372 | ||
diff --git a/testes/api.lua b/testes/api.lua index 95551481..fb7e7085 100644 --- a/testes/api.lua +++ b/testes/api.lua | |||
| @@ -507,10 +507,12 @@ function checkerrnopro (code, msg) | |||
| 507 | end | 507 | end |
| 508 | 508 | ||
| 509 | if not _soft then | 509 | if not _soft then |
| 510 | collectgarbage("stop") -- avoid __gc with full stack | ||
| 510 | checkerrnopro("pushnum 3; call 0 0", "attempt to call") | 511 | checkerrnopro("pushnum 3; call 0 0", "attempt to call") |
| 511 | print"testing stack overflow in unprotected thread" | 512 | print"testing stack overflow in unprotected thread" |
| 512 | function f () f() end | 513 | function f () f() end |
| 513 | checkerrnopro("getglobal 'f'; call 0 0;", "stack overflow") | 514 | checkerrnopro("getglobal 'f'; call 0 0;", "stack overflow") |
| 515 | collectgarbage("restart") | ||
| 514 | end | 516 | end |
| 515 | print"+" | 517 | print"+" |
| 516 | 518 | ||
| @@ -1125,26 +1127,29 @@ do | |||
| 1125 | assert(#openresource == n) | 1127 | assert(#openresource == n) |
| 1126 | end | 1128 | end |
| 1127 | 1129 | ||
| 1128 | -- closing resources with 'settop' | 1130 | -- closing resources with 'closeslot' |
| 1131 | _ENV.xxx = true | ||
| 1129 | local a = T.testC([[ | 1132 | local a = T.testC([[ |
| 1130 | pushvalue 2 | 1133 | pushvalue 2 # stack: S, NR, CH |
| 1131 | call 0 1 # create resource | 1134 | call 0 1 # create resource; stack: S, NR, CH, R |
| 1132 | toclose -1 # mark it to be closed | 1135 | toclose -1 # mark it to be closed |
| 1133 | pushvalue 2 | 1136 | pushvalue 2 # stack: S, NR, CH, R, NR |
| 1134 | call 0 1 # create another resource | 1137 | call 0 1 # create another resource; stack: S, NR, CH, R, R |
| 1135 | toclose -1 # mark it to be closed | 1138 | toclose -1 # mark it to be closed |
| 1136 | pushvalue 3 | 1139 | pushvalue 3 # stack: S, NR, CH, R, R, CH |
| 1137 | pushint 2 # there should be two open resources | 1140 | pushint 2 # there should be two open resources |
| 1138 | call 1 0 | 1141 | call 1 0 # stack: S, NR, CH, R, R |
| 1139 | pop 1 # pop second resource from the stack | 1142 | closeslot -1 # close second resource |
| 1140 | pushvalue 3 | 1143 | pushvalue 3 # stack: S, NR, CH, R, R, CH |
| 1141 | pushint 1 # there should be one open resource | 1144 | pushint 1 # there should be one open resource |
| 1142 | call 1 0 | 1145 | call 1 0 # stack: S, NR, CH, R, R |
| 1143 | pop 1 # pop second resource from the stack | 1146 | closeslot 4 |
| 1147 | setglobal "xxx" # previous op. erased the slot | ||
| 1148 | pop 1 # pop other resource from the stack | ||
| 1144 | pushint * | 1149 | pushint * |
| 1145 | return 1 # return stack size | 1150 | return 1 # return stack size |
| 1146 | ]], newresource, check) | 1151 | ]], newresource, check) |
| 1147 | assert(a == 3) -- no extra items left in the stack | 1152 | assert(a == 3 and _ENV.xxx == nil) -- no extra items left in the stack |
| 1148 | 1153 | ||
| 1149 | -- non-closable value | 1154 | -- non-closable value |
| 1150 | local a, b = pcall(T.makeCfunc[[ | 1155 | local a, b = pcall(T.makeCfunc[[ |
