diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2021-01-11 15:03:01 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2021-01-11 15:03:01 -0300 |
commit | cc1692515e2a6aabc6d07159e7926656e38eda53 (patch) | |
tree | bc89a17cdc55a52897cc0d5a862cbc34314ada93 | |
parent | ce101dcaf73ff6d610593230d41b63c163a91519 (diff) | |
download | lua-cc1692515e2a6aabc6d07159e7926656e38eda53.tar.gz lua-cc1692515e2a6aabc6d07159e7926656e38eda53.tar.bz2 lua-cc1692515e2a6aabc6d07159e7926656e38eda53.zip |
New API function 'lua_closeslot'
Closing a to-be-closed variable with 'lua_settop' is too restrictive,
as it erases all slots above the variable. Moreover, it adds side
effects to 'lua_settop', which should be a fairly basic function.
-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[[ |