aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2021-03-09 11:42:45 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2021-03-09 11:42:45 -0300
commit511d53a826760dd11cd82947184583e2d094e2d2 (patch)
tree60dc1a2da1198fc7c9e058b794b03c757e0c48f7
parentf5df7f91f70234850484d26caf24e71e001e5304 (diff)
downloadlua-511d53a826760dd11cd82947184583e2d094e2d2.tar.gz
lua-511d53a826760dd11cd82947184583e2d094e2d2.tar.bz2
lua-511d53a826760dd11cd82947184583e2d094e2d2.zip
lua_settop/lua_pop closes to-be-closed variables
The existence of 'lua_closeslot' is no reason for lua_pop not to close to-be-closed variables too. It is too error-prone for lua_pop not to close tbc variables being popped from the stack.
-rw-r--r--lapi.c15
-rw-r--r--manual/manual.of23
-rw-r--r--testes/api.lua26
3 files changed, 44 insertions, 20 deletions
diff --git a/lapi.c b/lapi.c
index a9cf2fdb..f8f70cd0 100644
--- a/lapi.c
+++ b/lapi.c
@@ -173,7 +173,7 @@ LUA_API int lua_gettop (lua_State *L) {
173 173
174LUA_API void lua_settop (lua_State *L, int idx) { 174LUA_API void lua_settop (lua_State *L, int idx) {
175 CallInfo *ci; 175 CallInfo *ci;
176 StkId func; 176 StkId func, newtop;
177 ptrdiff_t diff; /* difference for new top */ 177 ptrdiff_t diff; /* difference for new top */
178 lua_lock(L); 178 lua_lock(L);
179 ci = L->ci; 179 ci = L->ci;
@@ -188,12 +188,13 @@ LUA_API void lua_settop (lua_State *L, int idx) {
188 api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top"); 188 api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top");
189 diff = idx + 1; /* will "subtract" index (as it is negative) */ 189 diff = idx + 1; /* will "subtract" index (as it is negative) */
190 } 190 }
191#if defined(LUA_COMPAT_5_4_0) 191 api_check(L, L->tbclist < L->top, "previous pop of an unclosed slot");
192 if (diff < 0 && hastocloseCfunc(ci->nresults)) 192 newtop = L->top + diff;
193 luaF_close(L, L->top + diff, CLOSEKTOP, 0); 193 if (diff < 0 && L->tbclist >= newtop) {
194#endif 194 lua_assert(hastocloseCfunc(ci->nresults));
195 api_check(L, L->tbclist < L->top + diff, "cannot pop an unclosed slot"); 195 luaF_close(L, newtop, CLOSEKTOP, 0);
196 L->top += diff; 196 }
197 L->top = newtop; /* correct top only after closing any upvalue */
197 lua_unlock(L); 198 lua_unlock(L);
198} 199}
199 200
diff --git a/manual/manual.of b/manual/manual.of
index 2e15839a..c69970d2 100644
--- a/manual/manual.of
+++ b/manual/manual.of
@@ -4253,12 +4253,8 @@ If the new top is greater than the old one,
4253then the new elements are filled with @nil. 4253then the new elements are filled with @nil.
4254If @id{index} @N{is 0}, then all stack elements are removed. 4254If @id{index} @N{is 0}, then all stack elements are removed.
4255 4255
4256For compatibility reasons, 4256This function can run arbitrary code when removing an index
4257this function may close slots marked as to-be-closed @see{lua_toclose}, 4257marked as to-be-closed from the stack.
4258and therefore it can run arbitrary code.
4259You should not rely on this behavior:
4260Instead, always close to-be-closed slots explicitly,
4261with @Lid{lua_closeslot}, before removing them from the stack.
4262 4258
4263} 4259}
4264 4260
@@ -4347,19 +4343,22 @@ otherwise, returns @id{NULL}.
4347@apii{0,0,m} 4343@apii{0,0,m}
4348 4344
4349Marks the given index in the stack as a 4345Marks the given index in the stack as a
4350to-be-closed @Q{variable} @see{to-be-closed}. 4346to-be-closed slot @see{to-be-closed}.
4351Like a to-be-closed variable in Lua, 4347Like a to-be-closed variable in Lua,
4352the value at that index in the stack will be closed 4348the value at that slot in the stack will be closed
4353when it goes out of scope. 4349when it goes out of scope.
4354Here, in the context of a C function, 4350Here, in the context of a C function,
4355to go out of scope means that the running function returns to Lua, 4351to go out of scope means that the running function returns to Lua,
4356there is an error, 4352or there is an error,
4353or the slot is removed from the stack through
4354@Lid{lua_settop} or @Lid{lua_pop},
4357or there is a call to @Lid{lua_closeslot}. 4355or there is a call to @Lid{lua_closeslot}.
4358An index marked as to-be-closed should neither be removed from the stack 4356A slot marked as to-be-closed should not be removed from the stack
4359nor modified before a corresponding call to @Lid{lua_closeslot}. 4357by any other function in the API except @Lid{lua_settop} or @Lid{lua_pop},
4358unless previously deactivated by @Lid{lua_closeslot}.
4360 4359
4361This function should not be called for an index 4360This function should not be called for an index
4362that is equal to or below an active to-be-closed index. 4361that is equal to or below an active to-be-closed slot.
4363 4362
4364Note that, both in case of errors and of a regular return, 4363Note that, both in case of errors and of a regular return,
4365by the time the @idx{__close} metamethod runs, 4364by the time the @idx{__close} metamethod runs,
diff --git a/testes/api.lua b/testes/api.lua
index fb7e7085..c1bcb4b7 100644
--- a/testes/api.lua
+++ b/testes/api.lua
@@ -1130,7 +1130,7 @@ do
1130 -- closing resources with 'closeslot' 1130 -- closing resources with 'closeslot'
1131 _ENV.xxx = true 1131 _ENV.xxx = true
1132 local a = T.testC([[ 1132 local a = T.testC([[
1133 pushvalue 2 # stack: S, NR, CH 1133 pushvalue 2 # stack: S, NR, CH, NR
1134 call 0 1 # create resource; stack: S, NR, CH, R 1134 call 0 1 # create resource; stack: S, NR, CH, R
1135 toclose -1 # mark it to be closed 1135 toclose -1 # mark it to be closed
1136 pushvalue 2 # stack: S, NR, CH, R, NR 1136 pushvalue 2 # stack: S, NR, CH, R, NR
@@ -1151,6 +1151,30 @@ do
1151 ]], newresource, check) 1151 ]], newresource, check)
1152 assert(a == 3 and _ENV.xxx == nil) -- no extra items left in the stack 1152 assert(a == 3 and _ENV.xxx == nil) -- no extra items left in the stack
1153 1153
1154 -- closing resources with 'pop'
1155 local a = T.testC([[
1156 pushvalue 2 # stack: S, NR, CH, NR
1157 call 0 1 # create resource; stack: S, NR, CH, R
1158 toclose -1 # mark it to be closed
1159 pushvalue 2 # stack: S, NR, CH, R, NR
1160 call 0 1 # create another resource; stack: S, NR, CH, R, R
1161 toclose -1 # mark it to be closed
1162 pushvalue 3 # stack: S, NR, CH, R, R, CH
1163 pushint 2 # there should be two open resources
1164 call 1 0 # stack: S, NR, CH, R, R
1165 pop 1 # pop second resource
1166 pushvalue 3 # stack: S, NR, CH, R, CH
1167 pushint 1 # there should be one open resource
1168 call 1 0 # stack: S, NR, CH, R
1169 pop 1 # pop other resource from the stack
1170 pushvalue 3 # stack: S, NR, CH, CH
1171 pushint 0 # there should be no open resources
1172 call 1 0 # stack: S, NR, CH
1173 pushint *
1174 return 1 # return stack size
1175 ]], newresource, check)
1176 assert(a == 3) -- no extra items left in the stack
1177
1154 -- non-closable value 1178 -- non-closable value
1155 local a, b = pcall(T.makeCfunc[[ 1179 local a, b = pcall(T.makeCfunc[[
1156 pushint 32 1180 pushint 32