mirror of
https://github.com/lua/lua.git
synced 2026-07-23 06:29:06 +00:00
New conceptual model for vararg
Conceptually, all functions get their vararg arguments in a vararg table. The storing of vararg arguments in the stack is always treated as an optimization.
This commit is contained in:
5
lcode.c
5
lcode.c
@@ -1951,6 +1951,11 @@ void luaK_finish (FuncState *fs) {
|
||||
SET_OPCODE(*pc, OP_GETTABLE); /* must get vararg there */
|
||||
break;
|
||||
}
|
||||
case OP_VARARG: {
|
||||
if (p->flag & PF_VATAB) /* function has a vararg table? */
|
||||
SETARG_k(*pc, 1); /* must get vararg there */
|
||||
break;
|
||||
}
|
||||
case OP_JMP: { /* to optimize jumps to jumps */
|
||||
int target = finaltarget(p->code, i);
|
||||
fixjump(fs, i, target); /* jump directly to final target */
|
||||
|
||||
@@ -584,9 +584,8 @@ typedef struct AbsLineInfo {
|
||||
** Flags in Prototypes
|
||||
*/
|
||||
#define PF_ISVARARG 1 /* function is vararg */
|
||||
#define PF_VAVAR 2 /* function has vararg parameter */
|
||||
#define PF_VATAB 4 /* function has vararg table */
|
||||
#define PF_FIXED 8 /* prototype has parts in fixed memory */
|
||||
#define PF_VATAB 2 /* function has vararg table */
|
||||
#define PF_FIXED 4 /* prototype has parts in fixed memory */
|
||||
|
||||
|
||||
/*
|
||||
|
||||
@@ -336,7 +336,7 @@ 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+1], ..., R[A+C-2] = vararg */
|
||||
OP_VARARG,/* A C R[A], ..., R[A+C-2] = vararg, R[B] is vararg param. */
|
||||
|
||||
OP_GETVARG, /* A B C R[A] := R[B][R[C]], R[B] is vararg parameter */
|
||||
|
||||
|
||||
24
lparser.c
24
lparser.c
@@ -1056,9 +1056,8 @@ static void constructor (LexState *ls, expdesc *t) {
|
||||
/* }====================================================================== */
|
||||
|
||||
|
||||
static void setvararg (FuncState *fs, int kind) {
|
||||
lua_assert(kind & PF_ISVARARG);
|
||||
fs->f->flag |= cast_byte(kind);
|
||||
static void setvararg (FuncState *fs) {
|
||||
fs->f->flag |= PF_ISVARARG;
|
||||
luaK_codeABC(fs, OP_VARARGPREP, 0, 0, 0);
|
||||
}
|
||||
|
||||
@@ -1078,12 +1077,12 @@ static void parlist (LexState *ls) {
|
||||
break;
|
||||
}
|
||||
case TK_DOTS: {
|
||||
varargk |= PF_ISVARARG;
|
||||
varargk = 1;
|
||||
luaX_next(ls); /* skip '...' */
|
||||
if (ls->t.token == TK_NAME) {
|
||||
if (ls->t.token == TK_NAME)
|
||||
new_varkind(ls, str_checkname(ls), RDKVAVAR);
|
||||
varargk |= PF_VAVAR;
|
||||
}
|
||||
else
|
||||
new_localvarliteral(ls, "(vararg table)");
|
||||
break;
|
||||
}
|
||||
default: luaX_syntaxerror(ls, "<name> or '...' expected");
|
||||
@@ -1092,10 +1091,9 @@ static void parlist (LexState *ls) {
|
||||
}
|
||||
adjustlocalvars(ls, nparams);
|
||||
f->numparams = cast_byte(fs->nactvar);
|
||||
if (varargk != 0) {
|
||||
setvararg(fs, varargk); /* declared vararg */
|
||||
if (varargk & PF_VAVAR)
|
||||
adjustlocalvars(ls, 1); /* vararg parameter */
|
||||
if (varargk) {
|
||||
setvararg(fs); /* declared vararg */
|
||||
adjustlocalvars(ls, 1); /* vararg parameter */
|
||||
}
|
||||
/* reserve registers for parameters (plus vararg parameter, if present) */
|
||||
luaK_reserveregs(fs, fs->nactvar);
|
||||
@@ -1287,7 +1285,7 @@ static void simpleexp (LexState *ls, expdesc *v) {
|
||||
FuncState *fs = ls->fs;
|
||||
check_condition(ls, fs->f->flag & PF_ISVARARG,
|
||||
"cannot use '...' outside a vararg function");
|
||||
init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 0, 1));
|
||||
init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, fs->f->numparams, 1));
|
||||
break;
|
||||
}
|
||||
case '{' /*}*/: { /* constructor */
|
||||
@@ -2153,7 +2151,7 @@ static void mainfunc (LexState *ls, FuncState *fs) {
|
||||
BlockCnt bl;
|
||||
Upvaldesc *env;
|
||||
open_func(ls, fs, &bl);
|
||||
setvararg(fs, PF_ISVARARG); /* main function is always vararg */
|
||||
setvararg(fs); /* main function is always vararg */
|
||||
env = allocupvalue(fs); /* ...set environment upvalue */
|
||||
env->instack = 1;
|
||||
env->idx = 0;
|
||||
|
||||
66
ltm.c
66
ltm.c
@@ -242,6 +242,7 @@ static void createvarargtab (lua_State *L, StkId f, int n) {
|
||||
luaH_set(L, t, &key, &value); /* t.n = n */
|
||||
for (i = 0; i < n; i++)
|
||||
luaH_setint(L, t, i + 1, s2v(f + i));
|
||||
luaC_checkGC(L);
|
||||
}
|
||||
|
||||
|
||||
@@ -265,11 +266,11 @@ void luaT_adjustvarargs (lua_State *L, CallInfo *ci, const Proto *p) {
|
||||
setobjs2s(L, L->top.p++, ci->func.p + i);
|
||||
setnilvalue(s2v(ci->func.p + i)); /* erase original parameter (for GC) */
|
||||
}
|
||||
if (p->flag & PF_VAVAR) { /* is there a vararg parameter? */
|
||||
if (p->flag & PF_VATAB) /* does it need a vararg table? */
|
||||
createvarargtab(L, ci->func.p + nfixparams + 1, nextra);
|
||||
else /* no table; set parameter to nil */
|
||||
setnilvalue(s2v(L->top.p));
|
||||
if (p->flag & PF_VATAB) /* does it need a vararg table? */
|
||||
createvarargtab(L, ci->func.p + nfixparams + 1, nextra);
|
||||
else { /* no table; set parameter to nil */
|
||||
setnilvalue(s2v(L->top.p));
|
||||
L->top.p++;
|
||||
}
|
||||
ci->func.p += totalargs + 1;
|
||||
ci->top.p += totalargs + 1;
|
||||
@@ -299,16 +300,53 @@ void luaT_getvararg (CallInfo *ci, StkId ra, TValue *rc) {
|
||||
}
|
||||
|
||||
|
||||
void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted) {
|
||||
int i;
|
||||
int nextra = ci->u.l.nextraargs;
|
||||
if (wanted < 0) {
|
||||
wanted = nextra; /* get all extra arguments available */
|
||||
checkstackp(L, nextra, where); /* ensure stack space */
|
||||
L->top.p = where + nextra; /* next instruction will need top */
|
||||
/*
|
||||
** Get the number of extra arguments in a vararg function. If vararg
|
||||
** table has been optimized away, that number is in the call info.
|
||||
** Otherwise, get the field 'n' from the vararg table and check that it
|
||||
** has a proper value (non-negative integer not larger than the stack
|
||||
** limit).
|
||||
*/
|
||||
static int getnumargs (lua_State *L, CallInfo *ci, Table *h) {
|
||||
if (h == NULL) /* no vararg table? */
|
||||
return ci->u.l.nextraargs;
|
||||
else {
|
||||
TValue res;
|
||||
if (luaH_getshortstr(h, luaS_new(L, "n"), &res) != LUA_VNUMINT ||
|
||||
l_castS2U(ivalue(&res)) > cast_uint(INT_MAX/2))
|
||||
luaG_runerror(L, "vararg table has no proper 'n'");
|
||||
return cast_int(ivalue(&res));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Get 'wanted' vararg arguments and put them in 'where'. 'vatab' is
|
||||
** the register of the vararg table or -1 if there is no vararg table.
|
||||
*/
|
||||
void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted,
|
||||
int vatab) {
|
||||
Table *h = (vatab < 0) ? NULL : hvalue(s2v(ci->func.p + vatab + 1));
|
||||
int nargs = getnumargs(L, ci, h); /* number of available vararg args. */
|
||||
int i, touse; /* 'touse' is minimum between 'wanted' and 'nargs' */
|
||||
if (wanted < 0) {
|
||||
touse = wanted = nargs; /* get all extra arguments available */
|
||||
checkstackp(L, nargs, where); /* ensure stack space */
|
||||
L->top.p = where + nargs; /* next instruction will need top */
|
||||
}
|
||||
else
|
||||
touse = (nargs > wanted) ? wanted : nargs;
|
||||
if (h == NULL) { /* no vararg table? */
|
||||
for (i = 0; i < touse; i++) /* get vararg values from the stack */
|
||||
setobjs2s(L, where + i, ci->func.p - nargs + i);
|
||||
}
|
||||
else { /* get vararg values from vararg table */
|
||||
for (i = 0; i < touse; i++) {
|
||||
lu_byte tag = luaH_getint(h, i + 1, s2v(where + i));
|
||||
if (tagisempty(tag))
|
||||
setnilvalue(s2v(where + i));
|
||||
}
|
||||
}
|
||||
for (i = 0; i < wanted && i < nextra; i++)
|
||||
setobjs2s(L, where + i, ci->func.p - nextra + i);
|
||||
for (; i < wanted; i++) /* complete required results with nil */
|
||||
setnilvalue(s2v(where + i));
|
||||
}
|
||||
|
||||
4
ltm.h
4
ltm.h
@@ -98,8 +98,8 @@ LUAI_FUNC int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
|
||||
LUAI_FUNC void luaT_adjustvarargs (lua_State *L, struct CallInfo *ci,
|
||||
const Proto *p);
|
||||
LUAI_FUNC void luaT_getvararg (CallInfo *ci, StkId ra, TValue *rc);
|
||||
LUAI_FUNC void luaT_getvarargs (lua_State *L, struct CallInfo *ci,
|
||||
StkId where, int wanted);
|
||||
LUAI_FUNC void luaT_getvarargs (lua_State *L, struct CallInfo *ci, StkId where,
|
||||
int wanted, int vatab);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
5
lvm.c
5
lvm.c
@@ -1935,8 +1935,9 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
}
|
||||
vmcase(OP_VARARG) {
|
||||
StkId ra = RA(i);
|
||||
int n = GETARG_C(i) - 1; /* required results */
|
||||
Protect(luaT_getvarargs(L, ci, ra, n));
|
||||
int n = GETARG_C(i) - 1; /* required results (-1 means all) */
|
||||
int vatab = GETARG_k(i) ? GETARG_B(i) : -1;
|
||||
Protect(luaT_getvarargs(L, ci, ra, n, vatab));
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_GETVARG) {
|
||||
|
||||
@@ -2221,7 +2221,7 @@ The form
|
||||
}
|
||||
can be used to emulate methods.
|
||||
A call @T{v:name(@rep{args})}
|
||||
is syntactic sugar for @T{v.name(v,@rep{args})},
|
||||
is syntactic sugar for @T{v.name(v, @rep{args})},
|
||||
except that @id{v} is evaluated only once.
|
||||
|
||||
Arguments have the following syntax:
|
||||
@@ -2372,12 +2372,10 @@ which is indicated by three dots (@Char{...})
|
||||
at the end of its parameter list.
|
||||
A variadic function does not adjust its argument list;
|
||||
instead, it collects all extra arguments and supplies them
|
||||
to the function through a @def{vararg expression} and,
|
||||
if present, a @def{vararg table}.
|
||||
|
||||
A vararg expression is also written as three dots,
|
||||
and its value is a list of all actual extra arguments,
|
||||
similar to a function with multiple results @see{multires}.
|
||||
to the function through a @def{vararg table}.
|
||||
In that table,
|
||||
the values at indices 1, 2, etc. are the extra arguments,
|
||||
and the value at index @St{n} is the number of extra arguments.
|
||||
|
||||
As an example, consider the following definitions:
|
||||
@verbatim{
|
||||
@@ -2386,7 +2384,7 @@ function g(a, b, ...) end
|
||||
function r() return 1,2,3 end
|
||||
}
|
||||
Then, we have the following mapping from arguments to parameters and
|
||||
to the vararg expression:
|
||||
to the vararg table:
|
||||
@verbatim{
|
||||
CALL PARAMETERS
|
||||
|
||||
@@ -2396,33 +2394,39 @@ f(3, 4, 5) a=3, b=4
|
||||
f(r(), 10) a=1, b=10
|
||||
f(r()) a=1, b=2
|
||||
|
||||
g(3) a=3, b=nil, ... -> (nothing)
|
||||
g(3, 4) a=3, b=4, ... -> (nothing)
|
||||
g(3, 4, 5, 8) a=3, b=4, ... -> 5 8
|
||||
g(5, r()) a=5, b=1, ... -> 2 3
|
||||
g(3) a=3, b=nil, va. table -> {n = 0}
|
||||
g(3, 4) a=3, b=4, va. table -> {n = 0}
|
||||
g(3, 4, 5, 8) a=3, b=4, va. table -> {5, 8, n = 2}
|
||||
g(5, r()) a=5, b=1, va. table -> {2, 3, n = 2}
|
||||
}
|
||||
|
||||
The presence of a vararg table in a variadic function is indicated
|
||||
by a name after the three dots.
|
||||
A vararg table in a variadic function can have an optional name,
|
||||
given after the three dots.
|
||||
When present,
|
||||
a vararg table behaves like a read-only local variable
|
||||
with the given name that is initialized with a table.
|
||||
In that table,
|
||||
the values at indices 1, 2, etc. are the extra arguments,
|
||||
and the value at index @St{n} is the number of extra arguments.
|
||||
In other words, the code behaves as if the function started with
|
||||
the following statement,
|
||||
assuming the standard behavior of @Lid{table.pack}:
|
||||
@verbatim{
|
||||
local <const> name = table.pack(...)
|
||||
}
|
||||
that name denotes a read-only local variable that
|
||||
refers to the vararg table.
|
||||
If the vararg table does not have a name,
|
||||
it can only be accessed through a vararg expression.
|
||||
|
||||
A vararg expression is also written as three dots,
|
||||
and its value is a list of the values in the vararg table,
|
||||
from 1 to the integer value at index @St{n}.
|
||||
(Therefore, if the code does not modify the vararg table,
|
||||
this list corresponds to the extra arguments in the function call.)
|
||||
This list behaves like the results from a
|
||||
function with multiple results @see{multires}.
|
||||
|
||||
As an optimization,
|
||||
if the vararg table is used only as the base table
|
||||
in the syntactic constructions @T{t[exp]} or @T{t.id})
|
||||
and it is not an upvalue,
|
||||
if the vararg table satisfies some conditions,
|
||||
the code does not create an actual table and instead translates
|
||||
the indexing expressions into accesses to the internal vararg data.
|
||||
the indexing expressions and the vararg expressions
|
||||
into accesses to the internal vararg data.
|
||||
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}).
|
||||
Note that an anonymous vararg table always satisfy these conditions.
|
||||
|
||||
}
|
||||
|
||||
@@ -3103,7 +3107,7 @@ void *luaL_alloc (void *ud, void *ptr, size_t osize,
|
||||
}
|
||||
Note that @N{ISO C} ensures
|
||||
that @T{free(NULL)} has no effect and that
|
||||
@T{realloc(NULL,size)} is equivalent to @T{malloc(size)}.
|
||||
@T{realloc(NULL, size)} is equivalent to @T{malloc(size)}.
|
||||
|
||||
}
|
||||
|
||||
@@ -9197,6 +9201,10 @@ Compile-time constants may not appear in this listing,
|
||||
if they were optimized away by the compiler.
|
||||
Negative indices refer to vararg arguments;
|
||||
@num{-1} is the first vararg argument.
|
||||
These negative indices are only available when the vararg table
|
||||
has been optimized away;
|
||||
otherwise, the vararg arguments are available in the vararg table.
|
||||
|
||||
The function returns @fail
|
||||
if there is no variable with the given index,
|
||||
and raises an error when called with a level out of range.
|
||||
|
||||
@@ -702,7 +702,9 @@ else
|
||||
assert(t.currentline == t.linedefined + 2)
|
||||
assert(not debug.getinfo(c, 1)) -- no other level
|
||||
assert(coroutine.resume(c)) -- run next line
|
||||
local n,v = debug.getlocal(c, 0, 2) -- check next local
|
||||
local n,v = debug.getlocal(c, 0, 2) -- check vararg table
|
||||
assert(n == "(vararg table)" and v == nil)
|
||||
local n,v = debug.getlocal(c, 0, 3) -- check next local
|
||||
assert(n == "b" and v == 10)
|
||||
v = {coroutine.resume(c)} -- finish coroutine
|
||||
assert(v[1] == true and v[2] == 2 and v[3] == 3 and v[4] == undef)
|
||||
|
||||
@@ -356,8 +356,8 @@ function f(a,b)
|
||||
global assert, g, string
|
||||
local _, y = debug.getlocal(1, 2)
|
||||
assert(x == a and y == b)
|
||||
assert(debug.setlocal(2, 3, "pera") == "AA".."AA")
|
||||
assert(debug.setlocal(2, 4, "manga") == "B")
|
||||
assert(debug.setlocal(2, 4, "pera") == "AA".."AA")
|
||||
assert(debug.setlocal(2, 5, "manga") == "B")
|
||||
x = debug.getinfo(2)
|
||||
assert(x.func == g and x.what == "Lua" and x.name == 'g' and
|
||||
x.nups == 2 and string.find(x.source, "^@.*db%.lua$"))
|
||||
@@ -392,7 +392,7 @@ function g (...)
|
||||
global *
|
||||
local B = 13
|
||||
global<const> assert
|
||||
local x,y = debug.getlocal(1,5)
|
||||
local x,y = debug.getlocal(1,6)
|
||||
assert(x == 'B' and y == 13)
|
||||
end
|
||||
end
|
||||
@@ -458,7 +458,8 @@ local function collectlocals (level)
|
||||
local tab = {}
|
||||
for i = 1, math.huge do
|
||||
local n, v = debug.getlocal(level + 1, i)
|
||||
if not (n and string.find(n, "^[a-zA-Z0-9_]+$")) then
|
||||
if not (n and string.find(n, "^[a-zA-Z0-9_]+$") or
|
||||
n == "(vararg table)") then
|
||||
break -- consider only real variables
|
||||
end
|
||||
tab[n] = v
|
||||
|
||||
@@ -101,6 +101,38 @@ a,b,c,d,e = f(4)
|
||||
assert(a==nil and b==nil and c==nil and d==nil and e==nil)
|
||||
|
||||
|
||||
do -- vararg expressions using unpack
|
||||
local function aux (a, v, ...t)
|
||||
for k, val in pairs(v) do t[k] = val end
|
||||
return ...
|
||||
end
|
||||
|
||||
local t = table.pack(aux(10, {11, [5] = 24}, 1, 2, 3, nil, 4))
|
||||
assert(t.n == 5 and t[1] == 11 and t[2] == 2 and t[3] == 3
|
||||
and t[4] == nil and t[5] == 24)
|
||||
|
||||
local t = table.pack(aux(nil, {1, [20] = "a", [30] = "b", n = 30}))
|
||||
assert(t.n == 30 and t[1] == 1 and t[20] == "a" and t[30] == "b")
|
||||
-- table has only those four elements
|
||||
assert(next(t, next(t, next(t, next(t, next(t, nil))))) == nil)
|
||||
|
||||
local a, b, c, d = aux(nil, {}, 10, 20, 30)
|
||||
assert(a == 10 and b == 20 and c == 30 and d == nil)
|
||||
|
||||
local function aux (a, b, n, ...t) t.n = n; return b, ... end
|
||||
local t = table.pack(aux(10, 1, 10000))
|
||||
assert(t.n == 10001 and t[1] == 1 and #t == 1)
|
||||
|
||||
local function checkerr (emsg, f, ...)
|
||||
local st, msg = pcall(f, ...)
|
||||
assert(not st and string.find(msg, emsg))
|
||||
end
|
||||
checkerr("no proper 'n'", aux, 1, 1, -1)
|
||||
checkerr("no proper 'n'", aux, 1, 1, math.maxinteger)
|
||||
checkerr("no proper 'n'", aux, 1, 1, math.mininteger)
|
||||
checkerr("no proper 'n'", aux, 1, 1, 1.0)
|
||||
end
|
||||
|
||||
-- varargs for main chunks
|
||||
local f = assert(load[[ return {...} ]])
|
||||
local x = f(2,3)
|
||||
@@ -205,6 +237,7 @@ do -- access to vararg parameter
|
||||
assert(t[k] == v[k])
|
||||
end
|
||||
assert(t.n == v.n)
|
||||
return ...
|
||||
end
|
||||
|
||||
local t = table.pack(10, 20, 30)
|
||||
|
||||
Reference in New Issue
Block a user