mirror of
https://github.com/lua/lua
synced 2024-11-22 21:01:26 +03:00
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.
This commit is contained in:
parent
ce101dcaf7
commit
cc1692515e
19
lapi.c
19
lapi.c
@ -187,9 +187,26 @@ LUA_API void lua_settop (lua_State *L, int idx) {
|
|||||||
api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top");
|
api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top");
|
||||||
diff = idx + 1; /* will "subtract" index (as it is negative) */
|
diff = idx + 1; /* will "subtract" index (as it is negative) */
|
||||||
}
|
}
|
||||||
|
#if defined(LUA_COMPAT_5_4_0)
|
||||||
if (diff < 0 && hastocloseCfunc(ci->nresults))
|
if (diff < 0 && hastocloseCfunc(ci->nresults))
|
||||||
luaF_close(L, L->top + diff, CLOSEKTOP);
|
luaF_close(L, L->top + diff, CLOSEKTOP);
|
||||||
L->top += diff; /* correct top only after closing any upvalue */
|
#endif
|
||||||
|
L->top += diff;
|
||||||
|
api_check(L, L->openupval == NULL || uplevel(L->openupval) < L->top,
|
||||||
|
"cannot pop an unclosed slot");
|
||||||
|
lua_unlock(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LUA_API void lua_closeslot (lua_State *L, int idx) {
|
||||||
|
StkId level;
|
||||||
|
lua_lock(L);
|
||||||
|
level = index2stack(L, idx);
|
||||||
|
api_check(L, hastocloseCfunc(L->ci->nresults) && L->openupval != NULL &&
|
||||||
|
uplevel(L->openupval) == level,
|
||||||
|
"no variable to close at given level");
|
||||||
|
luaF_close(L, level, CLOSEKTOP);
|
||||||
|
setnilvalue(s2v(level));
|
||||||
lua_unlock(L);
|
lua_unlock(L);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -545,10 +545,8 @@ static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) {
|
|||||||
if (buffonstack(B)) /* buffer already has a box? */
|
if (buffonstack(B)) /* buffer already has a box? */
|
||||||
newbuff = (char *)resizebox(L, boxidx, newsize); /* resize it */
|
newbuff = (char *)resizebox(L, boxidx, newsize); /* resize it */
|
||||||
else { /* no box yet */
|
else { /* no box yet */
|
||||||
lua_pushnil(L); /* reserve slot for final result */
|
|
||||||
newbox(L); /* create a new box */
|
newbox(L); /* create a new box */
|
||||||
/* move box (and slot) to its intended position */
|
lua_insert(L, boxidx); /* move box to its intended position */
|
||||||
lua_rotate(L, boxidx - 1, 2);
|
|
||||||
lua_toclose(L, boxidx);
|
lua_toclose(L, boxidx);
|
||||||
newbuff = (char *)resizebox(L, boxidx, newsize);
|
newbuff = (char *)resizebox(L, boxidx, newsize);
|
||||||
memcpy(newbuff, B->b, B->n * sizeof(char)); /* copy original content */
|
memcpy(newbuff, B->b, B->n * sizeof(char)); /* copy original content */
|
||||||
@ -585,8 +583,8 @@ LUALIB_API void luaL_pushresult (luaL_Buffer *B) {
|
|||||||
lua_State *L = B->L;
|
lua_State *L = B->L;
|
||||||
lua_pushlstring(L, B->b, B->n);
|
lua_pushlstring(L, B->b, B->n);
|
||||||
if (buffonstack(B)) {
|
if (buffonstack(B)) {
|
||||||
lua_copy(L, -1, -3); /* move string to reserved slot */
|
lua_closeslot(L, -2); /* close the box */
|
||||||
lua_pop(L, 2); /* pop string and box (closing the box) */
|
lua_remove(L, -2); /* remove box from the stack */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
3
ltests.c
3
ltests.c
@ -1766,6 +1766,9 @@ static struct X { int x; } x;
|
|||||||
else if EQ("toclose") {
|
else if EQ("toclose") {
|
||||||
lua_toclose(L1, getnum);
|
lua_toclose(L1, getnum);
|
||||||
}
|
}
|
||||||
|
else if EQ("closeslot") {
|
||||||
|
lua_closeslot(L1, getnum);
|
||||||
|
}
|
||||||
else luaL_error(L, "unknown instruction %s", buff);
|
else luaL_error(L, "unknown instruction %s", buff);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
1
lua.h
1
lua.h
@ -348,6 +348,7 @@ LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
|
|||||||
LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud);
|
LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud);
|
||||||
|
|
||||||
LUA_API void (lua_toclose) (lua_State *L, int idx);
|
LUA_API void (lua_toclose) (lua_State *L, int idx);
|
||||||
|
LUA_API void (lua_closeslot) (lua_State *L, int idx);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -3095,6 +3095,18 @@ will probably need to close states as soon as they are not needed.
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@APIEntry{void lua_closeslot (lua_State *L, int index);|
|
||||||
|
@apii{0,0,e}
|
||||||
|
|
||||||
|
Close the to-be-closed slot at the given index and set its value to @nil.
|
||||||
|
The index must be the last index previously marked to be closed
|
||||||
|
@see{lua_toclose} that is still active (that is, not closed yet).
|
||||||
|
|
||||||
|
(Exceptionally, this function was introduced in release 5.4.3.
|
||||||
|
It is not present in previous 5.4 releases.)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@APIEntry{int lua_compare (lua_State *L, int index1, int index2, int op);|
|
@APIEntry{int lua_compare (lua_State *L, int index1, int index2, int op);|
|
||||||
@apii{0,0,e}
|
@apii{0,0,e}
|
||||||
|
|
||||||
@ -3747,9 +3759,7 @@ except that it allows the called function to yield @see{continuations}.
|
|||||||
@apii{n,0,e}
|
@apii{n,0,e}
|
||||||
|
|
||||||
Pops @id{n} elements from the stack.
|
Pops @id{n} elements from the stack.
|
||||||
|
It is implemented as a macro over @Lid{lua_settop}.
|
||||||
This function can run arbitrary code when removing an index
|
|
||||||
marked as to-be-closed from the stack.
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4240,8 +4250,12 @@ If the new top is greater than the old one,
|
|||||||
then the new elements are filled with @nil.
|
then the new elements are filled with @nil.
|
||||||
If @id{index} @N{is 0}, then all stack elements are removed.
|
If @id{index} @N{is 0}, then all stack elements are removed.
|
||||||
|
|
||||||
This function can run arbitrary code when removing an index
|
For compatibility reasons,
|
||||||
marked as to-be-closed from the stack.
|
this function may close slots marked as to-be-closed @see{lua_toclose},
|
||||||
|
and therefore it can run arbitrary code.
|
||||||
|
You should not rely on this behavior:
|
||||||
|
Instead, always close to-be-closed slots explicitly,
|
||||||
|
with @Lid{lua_closeslot}, before removing them from the stack.
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4337,10 +4351,9 @@ when it goes out of scope.
|
|||||||
Here, in the context of a C function,
|
Here, in the context of a C function,
|
||||||
to go out of scope means that the running function returns to Lua,
|
to go out of scope means that the running function returns to Lua,
|
||||||
there is an error,
|
there is an error,
|
||||||
or the index is removed from the stack through
|
or there is a call to @Lid{lua_closeslot}.
|
||||||
@Lid{lua_settop} or @Lid{lua_pop}.
|
An index marked as to-be-closed should neither be removed from the stack
|
||||||
An index marked as to-be-closed should not be removed from the stack
|
nor modified before a corresponding call to @Lid{lua_closeslot}.
|
||||||
by any other function in the API except @Lid{lua_settop} or @Lid{lua_pop}.
|
|
||||||
|
|
||||||
This function should not be called for an index
|
This function should not be called for an index
|
||||||
that is equal to or below an active to-be-closed index.
|
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,
|
|||||||
by the time the @idx{__close} metamethod runs,
|
by the time the @idx{__close} metamethod runs,
|
||||||
the @N{C stack} was already unwound,
|
the @N{C stack} was already unwound,
|
||||||
so that any automatic @N{C variable} declared in the calling function
|
so that any automatic @N{C variable} declared in the calling function
|
||||||
will be out of scope.
|
(e.g., a buffer) will be out of scope.
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -507,10 +507,12 @@ function checkerrnopro (code, msg)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if not _soft then
|
if not _soft then
|
||||||
|
collectgarbage("stop") -- avoid __gc with full stack
|
||||||
checkerrnopro("pushnum 3; call 0 0", "attempt to call")
|
checkerrnopro("pushnum 3; call 0 0", "attempt to call")
|
||||||
print"testing stack overflow in unprotected thread"
|
print"testing stack overflow in unprotected thread"
|
||||||
function f () f() end
|
function f () f() end
|
||||||
checkerrnopro("getglobal 'f'; call 0 0;", "stack overflow")
|
checkerrnopro("getglobal 'f'; call 0 0;", "stack overflow")
|
||||||
|
collectgarbage("restart")
|
||||||
end
|
end
|
||||||
print"+"
|
print"+"
|
||||||
|
|
||||||
@ -1125,26 +1127,29 @@ do
|
|||||||
assert(#openresource == n)
|
assert(#openresource == n)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- closing resources with 'settop'
|
-- closing resources with 'closeslot'
|
||||||
|
_ENV.xxx = true
|
||||||
local a = T.testC([[
|
local a = T.testC([[
|
||||||
pushvalue 2
|
pushvalue 2 # stack: S, NR, CH
|
||||||
call 0 1 # create resource
|
call 0 1 # create resource; stack: S, NR, CH, R
|
||||||
toclose -1 # mark it to be closed
|
toclose -1 # mark it to be closed
|
||||||
pushvalue 2
|
pushvalue 2 # stack: S, NR, CH, R, NR
|
||||||
call 0 1 # create another resource
|
call 0 1 # create another resource; stack: S, NR, CH, R, R
|
||||||
toclose -1 # mark it to be closed
|
toclose -1 # mark it to be closed
|
||||||
pushvalue 3
|
pushvalue 3 # stack: S, NR, CH, R, R, CH
|
||||||
pushint 2 # there should be two open resources
|
pushint 2 # there should be two open resources
|
||||||
call 1 0
|
call 1 0 # stack: S, NR, CH, R, R
|
||||||
pop 1 # pop second resource from the stack
|
closeslot -1 # close second resource
|
||||||
pushvalue 3
|
pushvalue 3 # stack: S, NR, CH, R, R, CH
|
||||||
pushint 1 # there should be one open resource
|
pushint 1 # there should be one open resource
|
||||||
call 1 0
|
call 1 0 # stack: S, NR, CH, R, R
|
||||||
pop 1 # pop second resource from the stack
|
closeslot 4
|
||||||
|
setglobal "xxx" # previous op. erased the slot
|
||||||
|
pop 1 # pop other resource from the stack
|
||||||
pushint *
|
pushint *
|
||||||
return 1 # return stack size
|
return 1 # return stack size
|
||||||
]], newresource, check)
|
]], newresource, check)
|
||||||
assert(a == 3) -- no extra items left in the stack
|
assert(a == 3 and _ENV.xxx == nil) -- no extra items left in the stack
|
||||||
|
|
||||||
-- non-closable value
|
-- non-closable value
|
||||||
local a, b = pcall(T.makeCfunc[[
|
local a, b = pcall(T.makeCfunc[[
|
||||||
|
Loading…
Reference in New Issue
Block a user