diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2021-03-09 11:42:45 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2021-03-09 11:42:45 -0300 |
commit | 511d53a826760dd11cd82947184583e2d094e2d2 (patch) | |
tree | 60dc1a2da1198fc7c9e058b794b03c757e0c48f7 | |
parent | f5df7f91f70234850484d26caf24e71e001e5304 (diff) | |
download | lua-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.c | 15 | ||||
-rw-r--r-- | manual/manual.of | 23 | ||||
-rw-r--r-- | testes/api.lua | 26 |
3 files changed, 44 insertions, 20 deletions
@@ -173,7 +173,7 @@ LUA_API int lua_gettop (lua_State *L) { | |||
173 | 173 | ||
174 | LUA_API void lua_settop (lua_State *L, int idx) { | 174 | LUA_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, | |||
4253 | then the new elements are filled with @nil. | 4253 | then the new elements are filled with @nil. |
4254 | If @id{index} @N{is 0}, then all stack elements are removed. | 4254 | If @id{index} @N{is 0}, then all stack elements are removed. |
4255 | 4255 | ||
4256 | For compatibility reasons, | 4256 | This function can run arbitrary code when removing an index |
4257 | this function may close slots marked as to-be-closed @see{lua_toclose}, | 4257 | marked as to-be-closed from the stack. |
4258 | and therefore it can run arbitrary code. | ||
4259 | You should not rely on this behavior: | ||
4260 | Instead, always close to-be-closed slots explicitly, | ||
4261 | with @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 | ||
4349 | Marks the given index in the stack as a | 4345 | Marks the given index in the stack as a |
4350 | to-be-closed @Q{variable} @see{to-be-closed}. | 4346 | to-be-closed slot @see{to-be-closed}. |
4351 | Like a to-be-closed variable in Lua, | 4347 | Like a to-be-closed variable in Lua, |
4352 | the value at that index in the stack will be closed | 4348 | the value at that slot in the stack will be closed |
4353 | when it goes out of scope. | 4349 | when it goes out of scope. |
4354 | Here, in the context of a C function, | 4350 | Here, in the context of a C function, |
4355 | to go out of scope means that the running function returns to Lua, | 4351 | to go out of scope means that the running function returns to Lua, |
4356 | there is an error, | 4352 | or there is an error, |
4353 | or the slot is removed from the stack through | ||
4354 | @Lid{lua_settop} or @Lid{lua_pop}, | ||
4357 | or there is a call to @Lid{lua_closeslot}. | 4355 | or there is a call to @Lid{lua_closeslot}. |
4358 | An index marked as to-be-closed should neither be removed from the stack | 4356 | A slot marked as to-be-closed should not be removed from the stack |
4359 | nor modified before a corresponding call to @Lid{lua_closeslot}. | 4357 | by any other function in the API except @Lid{lua_settop} or @Lid{lua_pop}, |
4358 | unless previously deactivated by @Lid{lua_closeslot}. | ||
4360 | 4359 | ||
4361 | This function should not be called for an index | 4360 | This function should not be called for an index |
4362 | that is equal to or below an active to-be-closed index. | 4361 | that is equal to or below an active to-be-closed slot. |
4363 | 4362 | ||
4364 | Note that, both in case of errors and of a regular return, | 4363 | Note that, both in case of errors and of a regular return, |
4365 | by the time the @idx{__close} metamethod runs, | 4364 | by 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 |