From a07f6a824197d7dc01c321599d3bc71936a2590e Mon Sep 17 00:00:00 2001 From: Roberto I Date: Fri, 28 Nov 2025 15:12:51 -0300 Subject: Functions with vararg tables don't need hidden args. Vararg functions with vararg tables don't use the arguments hidden in the stack; therfore, it doesn't need to build/keep them. --- lcode.c | 12 +++++++----- ldebug.c | 8 ++++---- ldo.c | 2 +- lobject.h | 10 +++++++++- lopcodes.h | 31 +++++++++++++++++-------------- lparser.c | 6 +++--- ltm.c | 39 +++++++++++++++++++++++++-------------- manual/manual.of | 2 +- testes/db.lua | 3 +++ 9 files changed, 70 insertions(+), 43 deletions(-) diff --git a/lcode.c b/lcode.c index afed05d1..b0a81421 100644 --- a/lcode.c +++ b/lcode.c @@ -806,7 +806,7 @@ void luaK_setoneret (FuncState *fs, expdesc *e) { ** Change a vararg parameter into a regular local variable */ void luaK_vapar2local (FuncState *fs, expdesc *var) { - fs->f->flag |= PF_VATAB; /* function will need a vararg table */ + needvatab(fs->f); /* function will need a vararg table */ /* now a vararg parameter is equivalent to a regular local variable */ var->k = VLOCAL; } @@ -1127,7 +1127,7 @@ void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) { break; } case VVARGIND: { - fs->f->flag |= PF_VATAB; /* function will need a vararg table */ + needvatab(fs->f); /* function will need a vararg table */ /* now, assignment is to a regular table */ } /* FALLTHROUGH */ case VINDEXED: { @@ -1927,6 +1927,8 @@ static int finaltarget (Instruction *code, int i) { void luaK_finish (FuncState *fs) { int i; Proto *p = fs->f; + if (p->flag & PF_VATAB) /* will it use a vararg table? */ + p->flag &= cast_byte(~PF_VAHID); /* then it will not use hidden args. */ for (i = 0; i < fs->pc; i++) { Instruction *pc = &p->code[i]; /* avoid "not used" warnings when assert is off (for 'onelua.c') */ @@ -1934,7 +1936,7 @@ void luaK_finish (FuncState *fs) { lua_assert(i == 0 || luaP_isOT(*(pc - 1)) == luaP_isIT(*pc)); switch (GET_OPCODE(*pc)) { case OP_RETURN0: case OP_RETURN1: { - if (!(fs->needclose || (p->flag & PF_ISVARARG))) + if (!(fs->needclose || (p->flag & PF_VAHID))) break; /* no extra work */ /* else use OP_RETURN to do the extra work */ SET_OPCODE(*pc, OP_RETURN); @@ -1942,8 +1944,8 @@ void luaK_finish (FuncState *fs) { case OP_RETURN: case OP_TAILCALL: { if (fs->needclose) SETARG_k(*pc, 1); /* signal that it needs to close */ - if (p->flag & PF_ISVARARG) - SETARG_C(*pc, p->numparams + 1); /* signal that it is vararg */ + if (p->flag & PF_VAHID) /* does it use hidden arguments? */ + SETARG_C(*pc, p->numparams + 1); /* signal that */ break; } case OP_GETVARG: { diff --git a/ldebug.c b/ldebug.c index abead91c..8df5f5f2 100644 --- a/ldebug.c +++ b/ldebug.c @@ -184,7 +184,7 @@ static const char *upvalname (const Proto *p, int uv) { static const char *findvararg (CallInfo *ci, int n, StkId *pos) { - if (clLvalue(s2v(ci->func.p))->p->flag & PF_ISVARARG) { + if (clLvalue(s2v(ci->func.p))->p->flag & PF_VAHID) { int nextra = ci->u.l.nextraargs; if (n >= -nextra) { /* 'n' is negative */ *pos = ci->func.p - nextra - (n + 1); @@ -304,7 +304,7 @@ static void collectvalidlines (lua_State *L, Closure *f) { int i; TValue v; setbtvalue(&v); /* boolean 'true' to be the value of all indices */ - if (!(p->flag & PF_ISVARARG)) /* regular function? */ + if (!(isvararg(p))) /* regular function? */ i = 0; /* consider all instructions */ else { /* vararg function */ lua_assert(GET_OPCODE(p->code[0]) == OP_VARARGPREP); @@ -348,7 +348,7 @@ static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar, ar->nparams = 0; } else { - ar->isvararg = (f->l.p->flag & PF_ISVARARG) ? 1 : 0; + ar->isvararg = (isvararg(f->l.p)) ? 1 : 0; ar->nparams = f->l.p->numparams; } break; @@ -912,7 +912,7 @@ int luaG_tracecall (lua_State *L) { Proto *p = ci_func(ci)->p; ci->u.l.trap = 1; /* ensure hooks will be checked */ if (ci->u.l.savedpc == p->code) { /* first instruction (not resuming)? */ - if (p->flag & PF_ISVARARG) + if (isvararg(p)) return 0; /* hooks will start at VARARGPREP instruction */ else if (!(ci->callstatus & CIST_HOOKYIELD)) /* not yielded? */ luaD_hookcall(L, ci); /* check 'call' hook */ diff --git a/ldo.c b/ldo.c index 44937068..75ce1488 100644 --- a/ldo.c +++ b/ldo.c @@ -487,7 +487,7 @@ static void rethook (lua_State *L, CallInfo *ci, int nres) { int ftransfer; if (isLua(ci)) { Proto *p = ci_func(ci)->p; - if (p->flag & PF_ISVARARG) + if (p->flag & PF_VAHID) delta = ci->u.l.nextraargs + p->numparams + 1; } ci->func.p += delta; /* if vararg, back to virtual 'func' */ diff --git a/lobject.h b/lobject.h index 070f12a4..156c942f 100644 --- a/lobject.h +++ b/lobject.h @@ -583,10 +583,18 @@ typedef struct AbsLineInfo { /* ** Flags in Prototypes */ -#define PF_ISVARARG 1 /* function is vararg */ +#define PF_VAHID 1 /* function has hidden vararg arguments */ #define PF_VATAB 2 /* function has vararg table */ #define PF_FIXED 4 /* prototype has parts in fixed memory */ +/* a vararg function either has hidden args. or a vararg table */ +#define isvararg(p) ((p)->flag & (PF_VAHID | PF_VATAB)) + +/* +** mark that a function needs a vararg table. (The flag PF_VAHID will +** be cleared later.) +*/ +#define needvatab(p) ((p)->flag |= PF_VATAB) /* ** Function Prototypes diff --git a/lopcodes.h b/lopcodes.h index fac87da2..b6bd182e 100644 --- a/lopcodes.h +++ b/lopcodes.h @@ -224,8 +224,8 @@ enum OpMode {iABC, ivABC, iABx, iAsBx, iAx, isJ}; /* -** Grep "ORDER OP" if you change these enums. Opcodes marked with a (*) -** has extra descriptions in the notes after the enumeration. +** Grep "ORDER OP" if you change this enum. +** See "Notes" below for more information about some instructions. */ typedef enum { @@ -238,7 +238,7 @@ OP_LOADF,/* A sBx R[A] := (lua_Number)sBx */ OP_LOADK,/* A Bx R[A] := K[Bx] */ OP_LOADKX,/* A R[A] := K[extra arg] */ OP_LOADFALSE,/* A R[A] := false */ -OP_LFALSESKIP,/*A R[A] := false; pc++ (*) */ +OP_LFALSESKIP,/*A R[A] := false; pc++ */ OP_LOADTRUE,/* A R[A] := true */ OP_LOADNIL,/* A B R[A], R[A+1], ..., R[A+B] := nil */ OP_GETUPVAL,/* A B R[A] := UpValue[B] */ @@ -289,7 +289,7 @@ OP_BXOR,/* A B C R[A] := R[B] ~ R[C] */ OP_SHL,/* A B C R[A] := R[B] << R[C] */ OP_SHR,/* A B C R[A] := R[B] >> R[C] */ -OP_MMBIN,/* A B C call C metamethod over R[A] and R[B] (*) */ +OP_MMBIN,/* A B C call C metamethod over R[A] and R[B] */ OP_MMBINI,/* A sB C k call C metamethod over R[A] and sB */ OP_MMBINK,/* A B C k call C metamethod over R[A] and K[B] */ @@ -315,12 +315,12 @@ OP_GTI,/* A sB k if ((R[A] > sB) ~= k) then pc++ */ OP_GEI,/* A sB k if ((R[A] >= sB) ~= k) then pc++ */ OP_TEST,/* A k if (not R[A] == k) then pc++ */ -OP_TESTSET,/* A B k if (not R[B] == k) then pc++ else R[A] := R[B] (*) */ +OP_TESTSET,/* A B k if (not R[B] == k) then pc++ else R[A] := R[B] */ OP_CALL,/* A B C R[A], ... ,R[A+C-2] := R[A](R[A+1], ... ,R[A+B-1]) */ OP_TAILCALL,/* A B C k return R[A](R[A+1], ... ,R[A+B-1]) */ -OP_RETURN,/* A B C k return R[A], ... ,R[A+B-2] (see note) */ +OP_RETURN,/* A B C k return R[A], ... ,R[A+B-2] */ OP_RETURN0,/* return */ OP_RETURN1,/* A return R[A] */ @@ -336,13 +336,13 @@ OP_SETLIST,/* A vB vC k R[A][vC+i] := R[A+i], 1 <= i <= vB */ OP_CLOSURE,/* A Bx R[A] := closure(KPROTO[Bx]) */ -OP_VARARG,/* A C R[A], ..., R[A+C-2] = vararg, R[B] is vararg param. */ +OP_VARARG,/* A B C k R[A], ..., R[A+C-2] = varargs */ OP_GETVARG, /* A B C R[A] := R[B][R[C]], R[B] is vararg parameter */ OP_ERRNNIL,/* A Bx raise error if R[A] ~= nil (K[Bx - 1] is global name)*/ -OP_VARARGPREP,/* (adjust vararg parameters) */ +OP_VARARGPREP,/* (adjust varargs) */ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */ } OpCode; @@ -371,7 +371,8 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */ OP_RETURN*, OP_SETLIST) may use 'top'. (*) In OP_VARARG, if (C == 0) then use actual number of varargs and - set top (like in OP_CALL with C == 0). + set top (like in OP_CALL with C == 0). 'k' means function has a + vararg table, which is in R[B]. (*) In OP_RETURN, if (B == 0) then return up to 'top'. @@ -387,20 +388,22 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */ is vC. Otherwise, the array size is EXTRAARG _ vC. (*) In OP_ERRNNIL, (Bx == 0) means index of global name doesn't - fit in Bx. (So, that name is not available for the instruction.) + fit in Bx. (So, that name is not available for the error message.) (*) For comparisons, k specifies what condition the test should accept (true or false). (*) In OP_MMBINI/OP_MMBINK, k means the arguments were flipped - (the constant is the first operand). + (the constant is the first operand). - (*) All 'skips' (pc++) assume that next instruction is a jump. + (*) All comparison and test instructions assume that the instruction + being skipped (pc++) is a jump. (*) In instructions OP_RETURN/OP_TAILCALL, 'k' specifies that the function builds upvalues, which may need to be closed. C > 0 means - the function is vararg, so that its 'func' must be corrected before - returning; in this case, (C - 1) is its number of fixed parameters. + the function has hidden vararg arguments, so that its 'func' must be + corrected before returning; in this case, (C - 1) is its number of + fixed parameters. (*) In comparisons with an immediate operand, C signals whether the original operand was a float. (It must be corrected in case of diff --git a/lparser.c b/lparser.c index a07044b8..e015dfc5 100644 --- a/lparser.c +++ b/lparser.c @@ -304,7 +304,7 @@ static void check_readonly (LexState *ls, expdesc *e) { break; } case VVARGIND: { - fs->f->flag |= PF_VATAB; /* function will need a vararg table */ + needvatab(fs->f); /* function will need a vararg table */ e->k = VINDEXED; } /* FALLTHROUGH */ case VINDEXUP: case VINDEXSTR: case VINDEXED: { /* global variable */ @@ -1057,7 +1057,7 @@ static void constructor (LexState *ls, expdesc *t) { static void setvararg (FuncState *fs) { - fs->f->flag |= PF_ISVARARG; + fs->f->flag |= PF_VAHID; /* by default, use hidden vararg arguments */ luaK_codeABC(fs, OP_VARARGPREP, 0, 0, 0); } @@ -1283,7 +1283,7 @@ static void simpleexp (LexState *ls, expdesc *v) { } case TK_DOTS: { /* vararg */ FuncState *fs = ls->fs; - check_condition(ls, fs->f->flag & PF_ISVARARG, + check_condition(ls, isvararg(fs->f), "cannot use '...' outside a vararg function"); init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, fs->f->numparams, 1)); break; diff --git a/ltm.c b/ltm.c index 39ac59d4..f2a373f8 100644 --- a/ltm.c +++ b/ltm.c @@ -250,31 +250,42 @@ static void createvarargtab (lua_State *L, StkId f, int n) { ** initial stack: func arg1 ... argn extra1 ... ** ^ ci->func ^ L->top ** final stack: func nil ... nil extra1 ... func arg1 ... argn -** ^ ci->func ^ L->top +** ^ ci->func */ -void luaT_adjustvarargs (lua_State *L, CallInfo *ci, const Proto *p) { +static void buildhiddenargs (lua_State *L, CallInfo *ci, const Proto *p, + int totalargs, int nfixparams, int nextra) { int i; - int totalargs = cast_int(L->top.p - ci->func.p) - 1; - int nfixparams = p->numparams; - int nextra = totalargs - nfixparams; /* number of extra arguments */ ci->u.l.nextraargs = nextra; luaD_checkstack(L, p->maxstacksize + 1); - /* copy function to the top of the stack */ + /* copy function to the top of the stack, after extra arguments */ setobjs2s(L, L->top.p++, ci->func.p); - /* move fixed parameters to the top of the stack */ + /* move fixed parameters to after the copied function */ for (i = 1; i <= nfixparams; i++) { setobjs2s(L, L->top.p++, ci->func.p + i); setnilvalue(s2v(ci->func.p + i)); /* erase original parameter (for GC) */ } - if (p->flag & PF_VATAB) /* does it need a vararg table? */ + ci->func.p += totalargs + 1; /* 'func' now lives after hidden arguments */ + ci->top.p += totalargs + 1; +} + + +void luaT_adjustvarargs (lua_State *L, CallInfo *ci, const Proto *p) { + int totalargs = cast_int(L->top.p - ci->func.p) - 1; + int nfixparams = p->numparams; + int nextra = totalargs - nfixparams; /* number of extra arguments */ + if (p->flag & PF_VATAB) { /* does it need a vararg table? */ + lua_assert(!(p->flag & PF_VAHID)); createvarargtab(L, ci->func.p + nfixparams + 1, nextra); - else { /* no table; set parameter to nil */ - setnilvalue(s2v(L->top.p)); - L->top.p++; + /* move table to proper place (last parameter) */ + setobjs2s(L, ci->func.p + nfixparams + 1, L->top.p - 1); + } + else { /* no table */ + lua_assert(p->flag & PF_VAHID); + buildhiddenargs(L, ci, p, totalargs, nfixparams, nextra); + /* set vararg parameter to nil */ + setnilvalue(s2v(ci->func.p + nfixparams + 1)); + lua_assert(L->top.p <= ci->top.p && ci->top.p <= L->stack_last.p); } - ci->func.p += totalargs + 1; - ci->top.p += totalargs + 1; - lua_assert(L->top.p <= ci->top.p && ci->top.p <= L->stack_last.p); } diff --git a/manual/manual.of b/manual/manual.of index 9b8e144d..54f67b3e 100644 --- a/manual/manual.of +++ b/manual/manual.of @@ -2425,7 +2425,7 @@ The conditions are as follows: If the vararg table has a name, that name is not an upvalue in a nested function and it is used only as the base table -in the syntactic constructions @T{t[exp]} or @T{t.id}). +in the syntactic constructions @T{t[exp]} or @T{t.id}. Note that an anonymous vararg table always satisfy these conditions. } diff --git a/testes/db.lua b/testes/db.lua index 4220b68b..e15a5be6 100644 --- a/testes/db.lua +++ b/testes/db.lua @@ -726,6 +726,9 @@ assert(t.isvararg == false and t.nparams == 3 and t.nups == 0) t = debug.getinfo(function (a,b,...) return t[a] end, "u") assert(t.isvararg == true and t.nparams == 2 and t.nups == 1) +t = debug.getinfo(function (a,b,...t) t.n = 2; return t[a] end, "u") +assert(t.isvararg == true and t.nparams == 2 and t.nups == 0) + t = debug.getinfo(1) -- main assert(t.isvararg == true and t.nparams == 0 and t.nups == 1 and debug.getupvalue(t.func, 1) == "_ENV") -- cgit v1.2.3-55-g6feb