CallInfo bit CIST_CLSRET broken in two
Since commit f407b3c4a, it was being used for two distinct (and
incompatible) meanings:
A: Function has TBC variables (now bit CIST_TBC)
B: Interpreter is closing TBC variables (original bit CIST_CLSRET)
B implies A, but A does not imply B.
This commit is contained in:
6
lapi.c
6
lapi.c
@@ -195,7 +195,7 @@ LUA_API void lua_settop (lua_State *L, int idx) {
|
|||||||
}
|
}
|
||||||
newtop = L->top.p + diff;
|
newtop = L->top.p + diff;
|
||||||
if (diff < 0 && L->tbclist.p >= newtop) {
|
if (diff < 0 && L->tbclist.p >= newtop) {
|
||||||
lua_assert(ci->callstatus & CIST_CLSRET);
|
lua_assert(ci->callstatus & CIST_TBC);
|
||||||
newtop = luaF_close(L, newtop, CLOSEKTOP, 0);
|
newtop = luaF_close(L, newtop, CLOSEKTOP, 0);
|
||||||
}
|
}
|
||||||
L->top.p = newtop; /* correct top only after closing any upvalue */
|
L->top.p = newtop; /* correct top only after closing any upvalue */
|
||||||
@@ -207,7 +207,7 @@ LUA_API void lua_closeslot (lua_State *L, int idx) {
|
|||||||
StkId level;
|
StkId level;
|
||||||
lua_lock(L);
|
lua_lock(L);
|
||||||
level = index2stack(L, idx);
|
level = index2stack(L, idx);
|
||||||
api_check(L, (L->ci->callstatus & CIST_CLSRET) && L->tbclist.p == level,
|
api_check(L, (L->ci->callstatus & CIST_TBC) && (L->tbclist.p == level),
|
||||||
"no variable to close at given level");
|
"no variable to close at given level");
|
||||||
level = luaF_close(L, level, CLOSEKTOP, 0);
|
level = luaF_close(L, level, CLOSEKTOP, 0);
|
||||||
setnilvalue(s2v(level));
|
setnilvalue(s2v(level));
|
||||||
@@ -1280,7 +1280,7 @@ LUA_API void lua_toclose (lua_State *L, int idx) {
|
|||||||
o = index2stack(L, idx);
|
o = index2stack(L, idx);
|
||||||
api_check(L, L->tbclist.p < o, "given index below or equal a marked one");
|
api_check(L, L->tbclist.p < o, "given index below or equal a marked one");
|
||||||
luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */
|
luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */
|
||||||
L->ci->callstatus |= CIST_CLSRET; /* mark that function has TBC slots */
|
L->ci->callstatus |= CIST_TBC; /* mark that function has TBC slots */
|
||||||
lua_unlock(L);
|
lua_unlock(L);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
12
ldo.c
12
ldo.c
@@ -505,7 +505,7 @@ l_sinline void genmoveresults (lua_State *L, StkId res, int nres,
|
|||||||
** Given 'nres' results at 'firstResult', move 'fwanted-1' of them
|
** Given 'nres' results at 'firstResult', move 'fwanted-1' of them
|
||||||
** to 'res'. Handle most typical cases (zero results for commands,
|
** to 'res'. Handle most typical cases (zero results for commands,
|
||||||
** one result for expressions, multiple results for tail calls/single
|
** one result for expressions, multiple results for tail calls/single
|
||||||
** parameters) separated. The flag CIST_CLSRET in 'fwanted', if set,
|
** parameters) separated. The flag CIST_TBC in 'fwanted', if set,
|
||||||
** forces the swicth to go to the default case.
|
** forces the swicth to go to the default case.
|
||||||
*/
|
*/
|
||||||
l_sinline void moveresults (lua_State *L, StkId res, int nres,
|
l_sinline void moveresults (lua_State *L, StkId res, int nres,
|
||||||
@@ -526,8 +526,9 @@ l_sinline void moveresults (lua_State *L, StkId res, int nres,
|
|||||||
break;
|
break;
|
||||||
default: { /* two/more results and/or to-be-closed variables */
|
default: { /* two/more results and/or to-be-closed variables */
|
||||||
int wanted = get_nresults(fwanted);
|
int wanted = get_nresults(fwanted);
|
||||||
if (fwanted & CIST_CLSRET) { /* to-be-closed variables? */
|
if (fwanted & CIST_TBC) { /* to-be-closed variables? */
|
||||||
L->ci->u2.nres = nres;
|
L->ci->u2.nres = nres;
|
||||||
|
L->ci->callstatus |= CIST_CLSRET; /* in case of yields */
|
||||||
res = luaF_close(L, res, CLOSEKTOP, 1);
|
res = luaF_close(L, res, CLOSEKTOP, 1);
|
||||||
L->ci->callstatus &= ~CIST_CLSRET;
|
L->ci->callstatus &= ~CIST_CLSRET;
|
||||||
if (L->hookmask) { /* if needed, call hook after '__close's */
|
if (L->hookmask) { /* if needed, call hook after '__close's */
|
||||||
@@ -552,8 +553,8 @@ l_sinline void moveresults (lua_State *L, StkId res, int nres,
|
|||||||
** that.
|
** that.
|
||||||
*/
|
*/
|
||||||
void luaD_poscall (lua_State *L, CallInfo *ci, int nres) {
|
void luaD_poscall (lua_State *L, CallInfo *ci, int nres) {
|
||||||
l_uint32 fwanted = ci->callstatus & (CIST_CLSRET | CIST_NRESULTS);
|
l_uint32 fwanted = ci->callstatus & (CIST_TBC | CIST_NRESULTS);
|
||||||
if (l_unlikely(L->hookmask) && !(fwanted & CIST_CLSRET))
|
if (l_unlikely(L->hookmask) && !(fwanted & CIST_TBC))
|
||||||
rethook(L, ci, nres);
|
rethook(L, ci, nres);
|
||||||
/* move results to proper place */
|
/* move results to proper place */
|
||||||
moveresults(L, ci->func.p, nres, fwanted);
|
moveresults(L, ci->func.p, nres, fwanted);
|
||||||
@@ -785,7 +786,8 @@ static int finishpcallk (lua_State *L, CallInfo *ci) {
|
|||||||
*/
|
*/
|
||||||
static void finishCcall (lua_State *L, CallInfo *ci) {
|
static void finishCcall (lua_State *L, CallInfo *ci) {
|
||||||
int n; /* actual number of results from C function */
|
int n; /* actual number of results from C function */
|
||||||
if (ci->callstatus & CIST_CLSRET) { /* was returning? */
|
if (ci->callstatus & CIST_CLSRET) { /* was closing TBC variable? */
|
||||||
|
lua_assert(ci->callstatus & CIST_TBC);
|
||||||
n = ci->u2.nres; /* just redo 'luaD_poscall' */
|
n = ci->u2.nres; /* just redo 'luaD_poscall' */
|
||||||
/* don't need to reset CIST_CLSRET, as it will be set again anyway */
|
/* don't need to reset CIST_CLSRET, as it will be set again anyway */
|
||||||
}
|
}
|
||||||
|
|||||||
4
lstate.h
4
lstate.h
@@ -235,8 +235,10 @@ struct CallInfo {
|
|||||||
#define CIST_FRESH cast(l_uint32, CIST_C << 1)
|
#define CIST_FRESH cast(l_uint32, CIST_C << 1)
|
||||||
/* function is closing tbc variables */
|
/* function is closing tbc variables */
|
||||||
#define CIST_CLSRET (CIST_FRESH << 1)
|
#define CIST_CLSRET (CIST_FRESH << 1)
|
||||||
|
/* function has tbc variables to close */
|
||||||
|
#define CIST_TBC (CIST_CLSRET << 1)
|
||||||
/* original value of 'allowhook' */
|
/* original value of 'allowhook' */
|
||||||
#define CIST_OAH (CIST_CLSRET << 1)
|
#define CIST_OAH (CIST_TBC << 1)
|
||||||
/* call is running a debug hook */
|
/* call is running a debug hook */
|
||||||
#define CIST_HOOKED (CIST_OAH << 1)
|
#define CIST_HOOKED (CIST_OAH << 1)
|
||||||
/* doing a yieldable protected call */
|
/* doing a yieldable protected call */
|
||||||
|
|||||||
@@ -515,7 +515,7 @@ else
|
|||||||
print "testing yields inside hooks"
|
print "testing yields inside hooks"
|
||||||
|
|
||||||
local turn
|
local turn
|
||||||
|
|
||||||
local function fact (t, x)
|
local function fact (t, x)
|
||||||
assert(turn == t)
|
assert(turn == t)
|
||||||
if x == 0 then return 1
|
if x == 0 then return 1
|
||||||
@@ -642,7 +642,7 @@ else
|
|||||||
|
|
||||||
|
|
||||||
print "testing coroutine API"
|
print "testing coroutine API"
|
||||||
|
|
||||||
-- reusing a thread
|
-- reusing a thread
|
||||||
assert(T.testC([[
|
assert(T.testC([[
|
||||||
newthread # create thread
|
newthread # create thread
|
||||||
@@ -920,7 +920,7 @@ do -- a few more tests for comparison operators
|
|||||||
until res ~= 10
|
until res ~= 10
|
||||||
return res
|
return res
|
||||||
end
|
end
|
||||||
|
|
||||||
local function test ()
|
local function test ()
|
||||||
local a1 = setmetatable({x=1}, mt1)
|
local a1 = setmetatable({x=1}, mt1)
|
||||||
local a2 = setmetatable({x=2}, mt2)
|
local a2 = setmetatable({x=2}, mt2)
|
||||||
@@ -932,7 +932,7 @@ do -- a few more tests for comparison operators
|
|||||||
assert(2 >= a2)
|
assert(2 >= a2)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
run(test)
|
run(test)
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -1037,6 +1037,31 @@ f = T.makeCfunc([[
|
|||||||
return *
|
return *
|
||||||
]], 23, "huu")
|
]], 23, "huu")
|
||||||
|
|
||||||
|
|
||||||
|
do -- testing bug introduced in commit f407b3c4a
|
||||||
|
local X = false -- flag "to be closed"
|
||||||
|
local coro = coroutine.wrap(T.testC)
|
||||||
|
-- runs it until 'pcallk' (that yields)
|
||||||
|
-- 4th argument (at index 4): object to be closed
|
||||||
|
local res1, res2 = coro(
|
||||||
|
[[
|
||||||
|
toclose 3 # this could break the next 'pcallk'
|
||||||
|
pushvalue 2 # push function 'yield' to call it
|
||||||
|
pushint 22; pushint 33 # arguments to yield
|
||||||
|
# calls 'yield' (2 args; 2 results; continuation function at index 4)
|
||||||
|
pcallk 2 2 4
|
||||||
|
invalid command (should not arrive here)
|
||||||
|
]], -- 1st argument (at index 1): code;
|
||||||
|
coroutine.yield, -- (at index 2): function to be called
|
||||||
|
func2close(function () X = true end), -- (index 3): TBC slot
|
||||||
|
"pushint 43; return 3" -- (index 4): code for continuation function
|
||||||
|
)
|
||||||
|
|
||||||
|
assert(res1 == 22 and res2 == 33 and not X)
|
||||||
|
local res1, res2, res3 = coro(34, "hi") -- runs continuation function
|
||||||
|
assert(res1 == 34 and res2 == "hi" and res3 == 43 and X)
|
||||||
|
end
|
||||||
|
|
||||||
x = coroutine.wrap(f)
|
x = coroutine.wrap(f)
|
||||||
assert(x() == 102)
|
assert(x() == 102)
|
||||||
eqtab({x()}, {23, "huu"})
|
eqtab({x()}, {23, "huu"})
|
||||||
@@ -1094,11 +1119,11 @@ co = coroutine.wrap(function (...) return
|
|||||||
cannot be here!
|
cannot be here!
|
||||||
]],
|
]],
|
||||||
[[ # 1st continuation
|
[[ # 1st continuation
|
||||||
yieldk 0 3
|
yieldk 0 3
|
||||||
cannot be here!
|
cannot be here!
|
||||||
]],
|
]],
|
||||||
[[ # 2nd continuation
|
[[ # 2nd continuation
|
||||||
yieldk 0 4
|
yieldk 0 4
|
||||||
cannot be here!
|
cannot be here!
|
||||||
]],
|
]],
|
||||||
[[ # 3th continuation
|
[[ # 3th continuation
|
||||||
|
|||||||
Reference in New Issue
Block a user