aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2021-01-11 15:03:01 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2021-01-11 15:03:01 -0300
commitcc1692515e2a6aabc6d07159e7926656e38eda53 (patch)
treebc89a17cdc55a52897cc0d5a862cbc34314ada93
parentce101dcaf73ff6d610593230d41b63c163a91519 (diff)
downloadlua-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.c19
-rw-r--r--lauxlib.c8
-rw-r--r--ltests.c3
-rw-r--r--lua.h3
-rw-r--r--manual/manual.of33
-rw-r--r--testes/api.lua29
6 files changed, 66 insertions, 29 deletions
diff --git a/lapi.c b/lapi.c
index 00e95a11..0f0e31af 100644
--- a/lapi.c
+++ b/lapi.c
@@ -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
201LUA_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
diff --git a/lauxlib.c b/lauxlib.c
index 074ff08c..e8fc486e 100644
--- a/lauxlib.c
+++ b/lauxlib.c
@@ -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
diff --git a/ltests.c b/ltests.c
index 6920dd69..9c13338a 100644
--- a/ltests.c
+++ b/ltests.c
@@ -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;
diff --git a/lua.h b/lua.h
index c9d64d7f..aec70dac 100644
--- a/lua.h
+++ b/lua.h
@@ -347,7 +347,8 @@ LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s);
347LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); 347LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
348LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud); 348LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud);
349 349
350LUA_API void (lua_toclose) (lua_State *L, int idx); 350LUA_API void (lua_toclose) (lua_State *L, int idx);
351LUA_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
3101Close the to-be-closed slot at the given index and set its value to @nil.
3102The 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.
3106It 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
3749Pops @id{n} elements from the stack. 3761Pops @id{n} elements from the stack.
3750 3762It is implemented as a macro over @Lid{lua_settop}.
3751This function can run arbitrary code when removing an index
3752marked 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,
4240then the new elements are filled with @nil. 4250then the new elements are filled with @nil.
4241If @id{index} @N{is 0}, then all stack elements are removed. 4251If @id{index} @N{is 0}, then all stack elements are removed.
4242 4252
4243This function can run arbitrary code when removing an index 4253For compatibility reasons,
4244marked as to-be-closed from the stack. 4254this function may close slots marked as to-be-closed @see{lua_toclose},
4255and therefore it can run arbitrary code.
4256You should not rely on this behavior:
4257Instead, always close to-be-closed slots explicitly,
4258with @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.
4337Here, in the context of a C function, 4351Here, in the context of a C function,
4338to go out of scope means that the running function returns to Lua, 4352to go out of scope means that the running function returns to Lua,
4339there is an error, 4353there is an error,
4340or the index is removed from the stack through 4354or there is a call to @Lid{lua_closeslot}.
4341@Lid{lua_settop} or @Lid{lua_pop}. 4355An index marked as to-be-closed should neither be removed from the stack
4342An index marked as to-be-closed should not be removed from the stack 4356nor modified before a corresponding call to @Lid{lua_closeslot}.
4343by any other function in the API except @Lid{lua_settop} or @Lid{lua_pop}.
4344 4357
4345This function should not be called for an index 4358This function should not be called for an index
4346that is equal to or below an active to-be-closed index. 4359that 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,
4353by the time the @idx{__close} metamethod runs, 4366by the time the @idx{__close} metamethod runs,
4354the @N{C stack} was already unwound, 4367the @N{C stack} was already unwound,
4355so that any automatic @N{C variable} declared in the calling function 4368so that any automatic @N{C variable} declared in the calling function
4356will 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)
507end 507end
508 508
509if not _soft then 509if 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")
514end 516end
515print"+" 517print"+"
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[[