New function 'resetCI'
New function 'resetCI' resets the CallInfo list of a thread, ensuring a proper state when creating a new thread, closing a thread, or closing a state, so that we can run code after that. (When closing a thread, we need to run its __close metamethods; when closing a state, we need to run its __close metamethods and its finalizers.)
This commit is contained in:
39
lstate.c
39
lstate.c
@@ -143,25 +143,29 @@ LUAI_FUNC void luaE_incCstack (lua_State *L) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void resetCI (lua_State *L) {
|
||||||
|
CallInfo *ci = L->ci = &L->base_ci;
|
||||||
|
ci->func.p = L->stack.p;
|
||||||
|
setnilvalue(s2v(ci->func.p)); /* 'function' entry for basic 'ci' */
|
||||||
|
ci->top.p = ci->func.p + 1 + LUA_MINSTACK; /* +1 for 'function' entry */
|
||||||
|
ci->u.c.k = NULL;
|
||||||
|
ci->callstatus = CIST_C;
|
||||||
|
L->status = LUA_OK;
|
||||||
|
L->errfunc = 0; /* stack unwind can "throw away" the error function */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void stack_init (lua_State *L1, lua_State *L) {
|
static void stack_init (lua_State *L1, lua_State *L) {
|
||||||
int i; CallInfo *ci;
|
int i;
|
||||||
/* initialize stack array */
|
/* initialize stack array */
|
||||||
L1->stack.p = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue);
|
L1->stack.p = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue);
|
||||||
L1->tbclist.p = L1->stack.p;
|
L1->tbclist.p = L1->stack.p;
|
||||||
for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++)
|
for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++)
|
||||||
setnilvalue(s2v(L1->stack.p + i)); /* erase new stack */
|
setnilvalue(s2v(L1->stack.p + i)); /* erase new stack */
|
||||||
L1->top.p = L1->stack.p;
|
|
||||||
L1->stack_last.p = L1->stack.p + BASIC_STACK_SIZE;
|
L1->stack_last.p = L1->stack.p + BASIC_STACK_SIZE;
|
||||||
/* initialize first ci */
|
/* initialize first ci */
|
||||||
ci = &L1->base_ci;
|
resetCI(L1);
|
||||||
ci->next = ci->previous = NULL;
|
L1->top.p = L1->stack.p + 1; /* +1 for 'function' entry */
|
||||||
ci->callstatus = CIST_C;
|
|
||||||
ci->func.p = L1->top.p;
|
|
||||||
ci->u.c.k = NULL;
|
|
||||||
setnilvalue(s2v(L1->top.p)); /* 'function' entry for this 'ci' */
|
|
||||||
L1->top.p++;
|
|
||||||
ci->top.p = L1->top.p + LUA_MINSTACK;
|
|
||||||
L1->ci = ci;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -235,6 +239,7 @@ static void preinit_thread (lua_State *L, global_State *g) {
|
|||||||
L->status = LUA_OK;
|
L->status = LUA_OK;
|
||||||
L->errfunc = 0;
|
L->errfunc = 0;
|
||||||
L->oldpc = 0;
|
L->oldpc = 0;
|
||||||
|
L->base_ci.previous = L->base_ci.next = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -252,8 +257,9 @@ static void close_state (lua_State *L) {
|
|||||||
if (!completestate(g)) /* closing a partially built state? */
|
if (!completestate(g)) /* closing a partially built state? */
|
||||||
luaC_freeallobjects(L); /* just collect its objects */
|
luaC_freeallobjects(L); /* just collect its objects */
|
||||||
else { /* closing a fully built state */
|
else { /* closing a fully built state */
|
||||||
L->ci = &L->base_ci; /* unwind CallInfo list */
|
resetCI(L);
|
||||||
luaD_closeprotected(L, 1, LUA_OK); /* close all upvalues */
|
luaD_closeprotected(L, 1, LUA_OK); /* close all upvalues */
|
||||||
|
L->top.p = L->stack.p + 1; /* empty the stack to run finalizers */
|
||||||
luaC_freeallobjects(L); /* collect all objects */
|
luaC_freeallobjects(L); /* collect all objects */
|
||||||
luai_userstateclose(L);
|
luai_userstateclose(L);
|
||||||
}
|
}
|
||||||
@@ -302,20 +308,15 @@ void luaE_freethread (lua_State *L, lua_State *L1) {
|
|||||||
|
|
||||||
|
|
||||||
TStatus luaE_resetthread (lua_State *L, TStatus status) {
|
TStatus luaE_resetthread (lua_State *L, TStatus status) {
|
||||||
CallInfo *ci = L->ci = &L->base_ci; /* unwind CallInfo list */
|
resetCI(L);
|
||||||
setnilvalue(s2v(L->stack.p)); /* 'function' entry for basic 'ci' */
|
|
||||||
ci->func.p = L->stack.p;
|
|
||||||
ci->callstatus = CIST_C;
|
|
||||||
if (status == LUA_YIELD)
|
if (status == LUA_YIELD)
|
||||||
status = LUA_OK;
|
status = LUA_OK;
|
||||||
L->status = LUA_OK; /* so it can run __close metamethods */
|
|
||||||
status = luaD_closeprotected(L, 1, status);
|
status = luaD_closeprotected(L, 1, status);
|
||||||
if (status != LUA_OK) /* errors? */
|
if (status != LUA_OK) /* errors? */
|
||||||
luaD_seterrorobj(L, status, L->stack.p + 1);
|
luaD_seterrorobj(L, status, L->stack.p + 1);
|
||||||
else
|
else
|
||||||
L->top.p = L->stack.p + 1;
|
L->top.p = L->stack.p + 1;
|
||||||
ci->top.p = L->top.p + LUA_MINSTACK;
|
luaD_reallocstack(L, cast_int(L->ci->top.p - L->stack.p), 0);
|
||||||
luaD_reallocstack(L, cast_int(ci->top.p - L->stack.p), 0);
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -505,6 +505,25 @@ assert(not pcall(a, a))
|
|||||||
a = nil
|
a = nil
|
||||||
|
|
||||||
|
|
||||||
|
do
|
||||||
|
-- bug in 5.4: thread can use message handler higher in the stack
|
||||||
|
-- than the variable being closed
|
||||||
|
local c = coroutine.create(function()
|
||||||
|
local clo <close> = setmetatable({}, {__close=function()
|
||||||
|
local x = 134 -- will overwrite message handler
|
||||||
|
error(x)
|
||||||
|
end})
|
||||||
|
-- yields coroutine but leaves a new message handler for it,
|
||||||
|
-- that would be used when closing the coroutine (except that it
|
||||||
|
-- will be overwritten)
|
||||||
|
xpcall(coroutine.yield, function() return "XXX" end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert(coroutine.resume(c)) -- start coroutine
|
||||||
|
local st, msg = coroutine.close(c)
|
||||||
|
assert(not st and msg == 134)
|
||||||
|
end
|
||||||
|
|
||||||
-- access to locals of erroneous coroutines
|
-- access to locals of erroneous coroutines
|
||||||
local x = coroutine.create (function ()
|
local x = coroutine.create (function ()
|
||||||
local a = 10
|
local a = 10
|
||||||
|
|||||||
Reference in New Issue
Block a user