From f407b3c4a1bc9667867ec51e835c20d97aab55a2 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Fri, 19 Jul 2024 17:34:22 -0300 Subject: [PATCH] Using CIST_CLSRET instead of trick with 'nresults' The callstatus flag CIST_CLSRET is used in all tests for the presence of variables to be closed in C functions. --- lapi.c | 8 +++----- lapi.h | 16 ---------------- ldo.c | 20 +++++++++++--------- testes/api.lua | 17 +++++++++++++++++ 4 files changed, 31 insertions(+), 30 deletions(-) diff --git a/lapi.c b/lapi.c index dbd291d7..70e2a44a 100644 --- a/lapi.c +++ b/lapi.c @@ -207,7 +207,7 @@ LUA_API void lua_settop (lua_State *L, int idx) { } newtop = L->top.p + diff; if (diff < 0 && L->tbclist.p >= newtop) { - lua_assert(hastocloseCfunc(ci->nresults)); + lua_assert(ci->callstatus & CIST_CLSRET); newtop = luaF_close(L, newtop, CLOSEKTOP, 0); } L->top.p = newtop; /* correct top only after closing any upvalue */ @@ -219,7 +219,7 @@ 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->tbclist.p == level, + api_check(L, (L->ci->callstatus & CIST_CLSRET) && L->tbclist.p == level, "no variable to close at given level"); level = luaF_close(L, level, CLOSEKTOP, 0); setnilvalue(s2v(level)); @@ -1287,9 +1287,7 @@ LUA_API void lua_toclose (lua_State *L, int idx) { nresults = L->ci->nresults; 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 */ - if (!hastocloseCfunc(nresults)) /* function not marked yet? */ - L->ci->nresults = codeNresults(nresults); /* mark it */ - lua_assert(hastocloseCfunc(L->ci->nresults)); + L->ci->callstatus |= CIST_CLSRET; /* mark that function has TBC slots */ lua_unlock(L); } diff --git a/lapi.h b/lapi.h index 21be4a24..9b545344 100644 --- a/lapi.h +++ b/lapi.h @@ -62,20 +62,4 @@ L->tbclist.p < L->top.p - (n), \ "not enough free 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) - -/* Map [-1, inf) (range of 'nresults') into (-inf, -2] */ -#define codeNresults(n) (-(n) - 3) -#define decodeNresults(n) (-(n) - 3) - #endif diff --git a/ldo.c b/ldo.c index 34101ba3..6eaa31a0 100644 --- a/ldo.c +++ b/ldo.c @@ -462,22 +462,23 @@ l_sinline 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 */ + case 0 + 1: /* no values needed */ L->top.p = res; return; - case 1: /* one value needed */ + case 1 + 1: /* one value needed */ if (nres == 0) /* no results? */ setnilvalue(s2v(res)); /* adjust with nil */ else /* at least one result */ setobjs2s(L, res, L->top.p - nres); /* move it to proper place */ L->top.p = res + 1; return; - case LUA_MULTRET: + case LUA_MULTRET + 1: wanted = nres; /* we want all results */ break; default: /* two/more results and/or to-be-closed variables */ - if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */ - L->ci->callstatus |= CIST_CLSRET; /* in case of yields */ + if (!(wanted & CIST_CLSRET)) + wanted--; + else { /* to-be-closed variables? */ L->ci->u2.nres = nres; res = luaF_close(L, res, CLOSEKTOP, 1); L->ci->callstatus &= ~CIST_CLSRET; @@ -486,7 +487,7 @@ l_sinline void moveresults (lua_State *L, StkId res, int nres, int wanted) { rethook(L, L->ci, nres); res = restorestack(L, savedres); /* hook can move stack */ } - wanted = decodeNresults(wanted); + wanted = (wanted & ~CIST_CLSRET) - 1; if (wanted == LUA_MULTRET) wanted = nres; /* we want all results */ } @@ -511,8 +512,10 @@ l_sinline void moveresults (lua_State *L, StkId res, int nres, int wanted) { ** that. */ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { - int wanted = ci->nresults; - if (l_unlikely(L->hookmask && !hastocloseCfunc(wanted))) + int wanted = ci->nresults + 1; + if (ci->callstatus & CIST_CLSRET) + wanted |= CIST_CLSRET; /* don't check hook in this case */ + else if (l_unlikely(L->hookmask)) rethook(L, ci, nres); /* move results to proper place */ moveresults(L, ci->func.p, nres, wanted); @@ -736,7 +739,6 @@ static int finishpcallk (lua_State *L, CallInfo *ci) { static void finishCcall (lua_State *L, CallInfo *ci) { int n; /* actual number of results from C function */ if (ci->callstatus & CIST_CLSRET) { /* was returning? */ - lua_assert(hastocloseCfunc(ci->nresults)); n = ci->u2.nres; /* just redo 'luaD_poscall' */ /* don't need to reset CIST_CLSRET, as it will be set again anyway */ } diff --git a/testes/api.lua b/testes/api.lua index dc485240..ae2f82dd 100644 --- a/testes/api.lua +++ b/testes/api.lua @@ -165,6 +165,23 @@ do -- test returning more results than fit in the caller stack end +do -- testing multipe returns + local function foo (n) + if n > 0 then return n, foo(n - 1) end + end + + local t = {T.testC("call 1 10; return 10", foo, 20)} + assert(t[1] == 20 and t[10] == 11 and t[11] == nil) + + local t = table.pack(T.testC("call 1 10; return 10", foo, 2)) + assert(t[1] == 2 and t[2] == 1 and t[3] == nil and t.n == 10) + + local t = {T.testC([[ + checkstack 300 "error"; call 1 250; return 250]], foo, 250)} + assert(t[1] == 250 and t[250] == 1 and t[251] == nil) +end + + -- testing globals _G.AA = 14; _G.BB = "a31" local a = {T.testC[[