Преглед изворни кода

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.
Roberto I пре 2 месеци
родитељ
комит
a07f6a8241
9 измењених фајлова са 70 додато и 43 уклоњено
  1. 7 5
      lcode.c
  2. 4 4
      ldebug.c
  3. 1 1
      ldo.c
  4. 9 1
      lobject.h
  5. 17 14
      lopcodes.h
  6. 3 3
      lparser.c
  7. 25 14
      ltm.c
  8. 1 1
      manual/manual.of
  9. 3 0
      testes/db.lua

+ 7 - 5
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: {

+ 4 - 4
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 */

+ 1 - 1
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' */

+ 9 - 1
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

+ 17 - 14
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

+ 3 - 3
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;

+ 25 - 14
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);
 }
 
 

+ 1 - 1
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.
 
 }

+ 3 - 0
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")