mirror of
https://github.com/lua/lua
synced 2024-11-22 21:01:26 +03:00
To-be-closed variables in the C API
This commit is contained in:
parent
41c800b352
commit
34840301b5
15
lapi.c
15
lapi.c
@ -173,15 +173,17 @@ LUA_API void lua_settop (lua_State *L, int idx) {
|
||||
StkId func = L->ci->func;
|
||||
lua_lock(L);
|
||||
if (idx >= 0) {
|
||||
StkId newtop = (func + 1) + idx;
|
||||
api_check(L, idx <= L->stack_last - (func + 1), "new top too large");
|
||||
while (L->top < (func + 1) + idx)
|
||||
while (L->top < newtop)
|
||||
setnilvalue(s2v(L->top++));
|
||||
L->top = (func + 1) + idx;
|
||||
L->top = newtop;
|
||||
}
|
||||
else {
|
||||
api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top");
|
||||
L->top += idx+1; /* 'subtract' index (index is negative) */
|
||||
}
|
||||
luaF_close(L, L->top, LUA_OK);
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
||||
@ -1205,6 +1207,15 @@ LUA_API int lua_next (lua_State *L, int idx) {
|
||||
}
|
||||
|
||||
|
||||
LUA_API void lua_tobeclosed (lua_State *L) {
|
||||
int nresults = L->ci->nresults;
|
||||
luaF_newtbcupval(L, L->top - 1); /* create new to-be-closed upvalue */
|
||||
if (!hastocloseCfunc(nresults)) /* function not marked yet? */
|
||||
L->ci->nresults = codeNresults(nresults); /* mark it */
|
||||
lua_assert(hastocloseCfunc(L->ci->nresults));
|
||||
}
|
||||
|
||||
|
||||
LUA_API void lua_concat (lua_State *L, int n) {
|
||||
lua_lock(L);
|
||||
api_checknelems(L, n);
|
||||
|
15
lapi.h
15
lapi.h
@ -15,10 +15,23 @@
|
||||
"stack overflow");}
|
||||
|
||||
#define adjustresults(L,nres) \
|
||||
{ if ((nres) == LUA_MULTRET && L->ci->top < L->top) L->ci->top = L->top; }
|
||||
{ if ((nres) <= LUA_MULTRET && L->ci->top < L->top) L->ci->top = L->top; }
|
||||
|
||||
#define api_checknelems(L,n) api_check(L, (n) < (L->top - L->ci->func), \
|
||||
"not enough elements in the stack")
|
||||
|
||||
|
||||
/*
|
||||
** To reduce the overhead of returning from C functions, the presence of
|
||||
** to-be-closed variables in these functions is coded in the CallInfo's
|
||||
** field 'nresults', in a way that functions with no to-be-closed variables
|
||||
** with zero, one, or "all" wanted results have no overhead. Functions
|
||||
** with other number of wanted results, as well as functions with
|
||||
** variables to be closed, have an extra check.
|
||||
*/
|
||||
|
||||
#define hastocloseCfunc(n) ((n) < LUA_MULTRET)
|
||||
|
||||
#define codeNresults(n) (-(n) - 3)
|
||||
|
||||
#endif
|
||||
|
24
ldo.c
24
ldo.c
@ -366,32 +366,38 @@ void luaD_tryfuncTM (lua_State *L, StkId func) {
|
||||
** separated.
|
||||
*/
|
||||
static void moveresults (lua_State *L, StkId res, int nres, int wanted) {
|
||||
StkId firstresult;
|
||||
int i;
|
||||
switch (wanted) { /* handle typical cases separately */
|
||||
case 0: /* no values needed */
|
||||
L->top = res;
|
||||
break;
|
||||
return;
|
||||
case 1: /* one value needed */
|
||||
if (nres == 0) /* no results? */
|
||||
setnilvalue(s2v(res)); /* adjust with nil */
|
||||
else
|
||||
setobjs2s(L, res, L->top - nres); /* move it to proper place */
|
||||
L->top = res + 1;
|
||||
break;
|
||||
return;
|
||||
case LUA_MULTRET:
|
||||
wanted = nres; /* we want all results */
|
||||
/* FALLTHROUGH */
|
||||
default: { /* multiple results */
|
||||
StkId firstresult = L->top - nres; /* index of first result */
|
||||
int i;
|
||||
break;
|
||||
default: /* multiple results (or to-be-closed variables) */
|
||||
if (hastocloseCfunc(wanted)) {
|
||||
luaF_close(L, res, LUA_OK);
|
||||
wanted = codeNresults(wanted); /* correct value */
|
||||
if (wanted == LUA_MULTRET)
|
||||
wanted = nres;
|
||||
}
|
||||
break;
|
||||
}
|
||||
firstresult = L->top - nres; /* index of first result */
|
||||
/* move all results to correct place */
|
||||
for (i = 0; i < nres && i < wanted; i++)
|
||||
setobjs2s(L, res + i, firstresult + i);
|
||||
for (; i < wanted; i++) /* complete wanted number of results */
|
||||
setnilvalue(s2v(res + i));
|
||||
L->top = res + wanted; /* top points after the last result */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
3
ltests.c
3
ltests.c
@ -1554,6 +1554,9 @@ static struct X { int x; } x;
|
||||
int i = getindex;
|
||||
return lua_yieldk(L1, nres, i, Cfunck);
|
||||
}
|
||||
else if EQ("tobeclosed") {
|
||||
lua_tobeclosed(L);
|
||||
}
|
||||
else luaL_error(L, "unknown instruction %s", buff);
|
||||
}
|
||||
return 0;
|
||||
|
2
lua.h
2
lua.h
@ -333,6 +333,8 @@ LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s);
|
||||
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_tobeclosed) (lua_State *L);
|
||||
|
||||
|
||||
/*
|
||||
** {==============================================================
|
||||
|
@ -967,6 +967,77 @@ T.closestate(L1)
|
||||
L1 = nil
|
||||
|
||||
print('+')
|
||||
-------------------------------------------------------------------------
|
||||
-- testing to-be-closed variables
|
||||
-------------------------------------------------------------------------
|
||||
print"testing to-be-closed variables"
|
||||
|
||||
do
|
||||
local openresource = {}
|
||||
|
||||
local function newresource ()
|
||||
local x = setmetatable({10}, {__close = function(y)
|
||||
assert(openresource[#openresource] == y)
|
||||
openresource[#openresource] = nil
|
||||
y[1] = y[1] + 1
|
||||
end})
|
||||
openresource[#openresource + 1] = x
|
||||
return x
|
||||
end
|
||||
|
||||
local a = T.testC([[
|
||||
call 0 1 # create resource
|
||||
tobeclosed # mark it to be closed
|
||||
return 1
|
||||
]], newresource)
|
||||
assert(a[1] == 11)
|
||||
assert(#openresource == 0) -- was closed
|
||||
|
||||
-- repeat the test, but calling function in a 'multret' context
|
||||
local a = {T.testC([[
|
||||
call 0 1 # create resource
|
||||
tobeclosed # mark it to be closed
|
||||
return 2
|
||||
]], newresource)}
|
||||
assert(type(a[1]) == "string" and a[2][1] == 11)
|
||||
assert(#openresource == 0) -- was closed
|
||||
|
||||
-- error
|
||||
local a, b = pcall(T.testC, [[
|
||||
call 0 1 # create resource
|
||||
tobeclosed # mark it to be closed
|
||||
error # resource is the error object
|
||||
]], newresource)
|
||||
assert(a == false and b[1] == 11)
|
||||
assert(#openresource == 0) -- was closed
|
||||
|
||||
local function check (n)
|
||||
assert(#openresource == n)
|
||||
end
|
||||
|
||||
-- closing resources with 'settop'
|
||||
local a = T.testC([[
|
||||
pushvalue 2
|
||||
call 0 1 # create resource
|
||||
tobeclosed # mark it to be closed
|
||||
pushvalue 2
|
||||
call 0 1 # create another resource
|
||||
tobeclosed # mark it to be closed
|
||||
pushvalue 3
|
||||
pushint 2 # there should be two open resources
|
||||
call 1 0
|
||||
pop 1 # pop second resource from the stack
|
||||
pushvalue 3
|
||||
pushint 1 # there should be one open resource
|
||||
call 1 0
|
||||
pop 1 # pop second resource from the stack
|
||||
pushint *
|
||||
return 1 # return stack size
|
||||
]], newresource, check)
|
||||
assert(a == 3) -- no extra items left in the stack
|
||||
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
-- testing memory limits
|
||||
|
Loading…
Reference in New Issue
Block a user