diff options
| author | Roberto I <roberto@inf.puc-rio.br> | 2025-09-24 18:33:08 -0300 |
|---|---|---|
| committer | Roberto I <roberto@inf.puc-rio.br> | 2025-09-24 18:33:08 -0300 |
| commit | 25c54fe60e22d05cdfaa48c64372d354efa59547 (patch) | |
| tree | 3ccaeded5e4363db358f73b7c8fc6b9f414a2f2a | |
| parent | 0cc3c9447cca9abae9738ee77c24d88801c3916c (diff) | |
| download | lua-25c54fe60e22d05cdfaa48c64372d354efa59547.tar.gz lua-25c54fe60e22d05cdfaa48c64372d354efa59547.tar.bz2 lua-25c54fe60e22d05cdfaa48c64372d354efa59547.zip | |
Optimization for vararg tables
A vararg table can be virtual. If the vararg table is used only as
a base in indexing expressions, the code does not need to create an
actual table for it. Instead, it compiles the indexing expressions
into direct accesses to the internal vararg data.
Diffstat (limited to '')
| -rw-r--r-- | lcode.c | 57 | ||||
| -rw-r--r-- | ljumptab.h | 3 | ||||
| -rw-r--r-- | lopcodes.c | 1 | ||||
| -rw-r--r-- | lopcodes.h | 2 | ||||
| -rw-r--r-- | lopnames.h | 1 | ||||
| -rw-r--r-- | lparser.c | 10 | ||||
| -rw-r--r-- | lparser.h | 2 | ||||
| -rw-r--r-- | ltm.c | 22 | ||||
| -rw-r--r-- | ltm.h | 1 | ||||
| -rw-r--r-- | lvm.c | 6 | ||||
| -rw-r--r-- | manual/manual.of | 91 | ||||
| -rw-r--r-- | testes/locals.lua | 6 | ||||
| -rw-r--r-- | testes/vararg.lua | 47 |
13 files changed, 186 insertions, 63 deletions
| @@ -842,6 +842,12 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) { | |||
| 842 | e->k = VRELOC; | 842 | e->k = VRELOC; |
| 843 | break; | 843 | break; |
| 844 | } | 844 | } |
| 845 | case VVARGIND: { | ||
| 846 | freeregs(fs, e->u.ind.t, e->u.ind.idx); | ||
| 847 | e->u.info = luaK_codeABC(fs, OP_GETVARG, 0, e->u.ind.t, e->u.ind.idx); | ||
| 848 | e->k = VRELOC; | ||
| 849 | break; | ||
| 850 | } | ||
| 845 | case VVARARG: case VCALL: { | 851 | case VVARARG: case VCALL: { |
| 846 | luaK_setoneret(fs, e); | 852 | luaK_setoneret(fs, e); |
| 847 | break; | 853 | break; |
| @@ -1004,11 +1010,11 @@ int luaK_exp2anyreg (FuncState *fs, expdesc *e) { | |||
| 1004 | 1010 | ||
| 1005 | 1011 | ||
| 1006 | /* | 1012 | /* |
| 1007 | ** Ensures final expression result is either in a register | 1013 | ** Ensures final expression result is either in a register, |
| 1008 | ** or in an upvalue. | 1014 | ** in an upvalue, or it is the vararg parameter. |
| 1009 | */ | 1015 | */ |
| 1010 | void luaK_exp2anyregup (FuncState *fs, expdesc *e) { | 1016 | void luaK_exp2anyregup (FuncState *fs, expdesc *e) { |
| 1011 | if (e->k != VUPVAL || hasjumps(e)) | 1017 | if ((e->k != VUPVAL && e->k != VVARGVAR) || hasjumps(e)) |
| 1012 | luaK_exp2anyreg(fs, e); | 1018 | luaK_exp2anyreg(fs, e); |
| 1013 | } | 1019 | } |
| 1014 | 1020 | ||
| @@ -1314,6 +1320,13 @@ void luaK_self (FuncState *fs, expdesc *e, expdesc *key) { | |||
| 1314 | } | 1320 | } |
| 1315 | 1321 | ||
| 1316 | 1322 | ||
| 1323 | /* auxiliary function to define indexing expressions */ | ||
| 1324 | static void fillidxk (expdesc *t, int idx, expkind k) { | ||
| 1325 | t->u.ind.idx = cast_byte(idx); | ||
| 1326 | t->k = k; | ||
| 1327 | } | ||
| 1328 | |||
| 1329 | |||
| 1317 | /* | 1330 | /* |
| 1318 | ** Create expression 't[k]'. 't' must have its final result already in a | 1331 | ** Create expression 't[k]'. 't' must have its final result already in a |
| 1319 | ** register or upvalue. Upvalues can only be indexed by literal strings. | 1332 | ** register or upvalue. Upvalues can only be indexed by literal strings. |
| @@ -1325,31 +1338,30 @@ void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) { | |||
| 1325 | if (k->k == VKSTR) | 1338 | if (k->k == VKSTR) |
| 1326 | keystr = str2K(fs, k); | 1339 | keystr = str2K(fs, k); |
| 1327 | lua_assert(!hasjumps(t) && | 1340 | lua_assert(!hasjumps(t) && |
| 1328 | (t->k == VLOCAL || t->k == VNONRELOC || t->k == VUPVAL)); | 1341 | (t->k == VLOCAL || t->k == VVARGVAR || |
| 1342 | t->k == VNONRELOC || t->k == VUPVAL)); | ||
| 1329 | if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non 'Kstr'? */ | 1343 | if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non 'Kstr'? */ |
| 1330 | luaK_exp2anyreg(fs, t); /* put it in a register */ | 1344 | luaK_exp2anyreg(fs, t); /* put it in a register */ |
| 1331 | if (t->k == VUPVAL) { | 1345 | if (t->k == VUPVAL) { |
| 1332 | lu_byte temp = cast_byte(t->u.info); /* upvalue index */ | 1346 | lu_byte temp = cast_byte(t->u.info); /* upvalue index */ |
| 1333 | t->u.ind.t = temp; /* (can't do a direct assignment; values overlap) */ | 1347 | t->u.ind.t = temp; /* (can't do a direct assignment; values overlap) */ |
| 1334 | lua_assert(isKstr(fs, k)); | 1348 | lua_assert(isKstr(fs, k)); |
| 1335 | t->u.ind.idx = cast_short(k->u.info); /* literal short string */ | 1349 | fillidxk(t, k->u.info, VINDEXUP); /* literal short string */ |
| 1336 | t->k = VINDEXUP; | 1350 | } |
| 1351 | else if (t->k == VVARGVAR) { /* indexing the vararg parameter? */ | ||
| 1352 | lua_assert(t->u.ind.t == fs->f->numparams); | ||
| 1353 | t->u.ind.t = cast_byte(t->u.var.ridx); | ||
| 1354 | fillidxk(t, luaK_exp2anyreg(fs, k), VVARGIND); /* register */ | ||
| 1337 | } | 1355 | } |
| 1338 | else { | 1356 | else { |
| 1339 | /* register index of the table */ | 1357 | /* register index of the table */ |
| 1340 | t->u.ind.t = cast_byte((t->k == VLOCAL) ? t->u.var.ridx: t->u.info); | 1358 | t->u.ind.t = cast_byte((t->k == VLOCAL) ? t->u.var.ridx: t->u.info); |
| 1341 | if (isKstr(fs, k)) { | 1359 | if (isKstr(fs, k)) |
| 1342 | t->u.ind.idx = cast_short(k->u.info); /* literal short string */ | 1360 | fillidxk(t, k->u.info, VINDEXSTR); /* literal short string */ |
| 1343 | t->k = VINDEXSTR; | 1361 | else if (isCint(k)) /* int. constant in proper range? */ |
| 1344 | } | 1362 | fillidxk(t, cast_int(k->u.ival), VINDEXI); |
| 1345 | else if (isCint(k)) { /* int. constant in proper range? */ | 1363 | else |
| 1346 | t->u.ind.idx = cast_short(k->u.ival); | 1364 | fillidxk(t, luaK_exp2anyreg(fs, k), VINDEXED); /* register */ |
| 1347 | t->k = VINDEXI; | ||
| 1348 | } | ||
| 1349 | else { | ||
| 1350 | t->u.ind.idx = cast_short(luaK_exp2anyreg(fs, k)); /* register */ | ||
| 1351 | t->k = VINDEXED; | ||
| 1352 | } | ||
| 1353 | } | 1365 | } |
| 1354 | t->u.ind.keystr = keystr; /* string index in 'k' */ | 1366 | t->u.ind.keystr = keystr; /* string index in 'k' */ |
| 1355 | t->u.ind.ro = 0; /* by default, not read-only */ | 1367 | t->u.ind.ro = 0; /* by default, not read-only */ |
| @@ -1913,9 +1925,14 @@ void luaK_finish (FuncState *fs) { | |||
| 1913 | SETARG_C(*pc, p->numparams + 1); /* signal that it is vararg */ | 1925 | SETARG_C(*pc, p->numparams + 1); /* signal that it is vararg */ |
| 1914 | break; | 1926 | break; |
| 1915 | } | 1927 | } |
| 1916 | case OP_JMP: { | 1928 | case OP_GETVARG: { |
| 1929 | if (p->flag & PF_VATAB) /* function has a vararg table? */ | ||
| 1930 | SET_OPCODE(*pc, OP_GETTABLE); /* must get vararg there */ | ||
| 1931 | break; | ||
| 1932 | } | ||
| 1933 | case OP_JMP: { /* to optimize jumps to jumps */ | ||
| 1917 | int target = finaltarget(p->code, i); | 1934 | int target = finaltarget(p->code, i); |
| 1918 | fixjump(fs, i, target); | 1935 | fixjump(fs, i, target); /* jump directly to final target */ |
| 1919 | break; | 1936 | break; |
| 1920 | } | 1937 | } |
| 1921 | default: break; | 1938 | default: break; |
| @@ -21,7 +21,7 @@ static const void *const disptab[NUM_OPCODES] = { | |||
| 21 | #if 0 | 21 | #if 0 |
| 22 | ** you can update the following list with this command: | 22 | ** you can update the following list with this command: |
| 23 | ** | 23 | ** |
| 24 | ** sed -n '/^OP_/\!d; s/OP_/\&\&L_OP_/ ; s/,.*/,/ ; s/\/.*// ; p' lopcodes.h | 24 | ** sed -n '/^OP_/!d; s/OP_/\&\&L_OP_/ ; s/,.*/,/ ; s/\/.*// ; p' lopcodes.h |
| 25 | ** | 25 | ** |
| 26 | #endif | 26 | #endif |
| 27 | 27 | ||
| @@ -106,6 +106,7 @@ static const void *const disptab[NUM_OPCODES] = { | |||
| 106 | &&L_OP_SETLIST, | 106 | &&L_OP_SETLIST, |
| 107 | &&L_OP_CLOSURE, | 107 | &&L_OP_CLOSURE, |
| 108 | &&L_OP_VARARG, | 108 | &&L_OP_VARARG, |
| 109 | &&L_OP_GETVARG, | ||
| 109 | &&L_OP_VARARGPREP, | 110 | &&L_OP_VARARGPREP, |
| 110 | &&L_OP_EXTRAARG | 111 | &&L_OP_EXTRAARG |
| 111 | 112 | ||
| @@ -102,6 +102,7 @@ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = { | |||
| 102 | ,opmode(0, 0, 1, 0, 0, ivABC) /* OP_SETLIST */ | 102 | ,opmode(0, 0, 1, 0, 0, ivABC) /* OP_SETLIST */ |
| 103 | ,opmode(0, 0, 0, 0, 1, iABx) /* OP_CLOSURE */ | 103 | ,opmode(0, 0, 0, 0, 1, iABx) /* OP_CLOSURE */ |
| 104 | ,opmode(0, 1, 0, 0, 1, iABC) /* OP_VARARG */ | 104 | ,opmode(0, 1, 0, 0, 1, iABC) /* OP_VARARG */ |
| 105 | ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETVARG */ | ||
| 105 | ,opmode(0, 0, 1, 0, 1, iABC) /* OP_VARARGPREP */ | 106 | ,opmode(0, 0, 1, 0, 1, iABC) /* OP_VARARGPREP */ |
| 106 | ,opmode(0, 0, 0, 0, 0, iAx) /* OP_EXTRAARG */ | 107 | ,opmode(0, 0, 0, 0, 0, iAx) /* OP_EXTRAARG */ |
| 107 | }; | 108 | }; |
| @@ -338,6 +338,8 @@ OP_CLOSURE,/* A Bx R[A] := closure(KPROTO[Bx]) */ | |||
| 338 | 338 | ||
| 339 | OP_VARARG,/* A C R[A], R[A+1], ..., R[A+C-2] = vararg */ | 339 | OP_VARARG,/* A C R[A], R[A+1], ..., R[A+C-2] = vararg */ |
| 340 | 340 | ||
| 341 | OP_GETVARG, /* A B C R[A] := R[B][R[C]], R[B] is vararg parameter */ | ||
| 342 | |||
| 341 | OP_VARARGPREP,/* (adjust vararg parameters) */ | 343 | OP_VARARGPREP,/* (adjust vararg parameters) */ |
| 342 | 344 | ||
| 343 | OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */ | 345 | OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */ |
| @@ -94,6 +94,7 @@ static const char *const opnames[] = { | |||
| 94 | "SETLIST", | 94 | "SETLIST", |
| 95 | "CLOSURE", | 95 | "CLOSURE", |
| 96 | "VARARG", | 96 | "VARARG", |
| 97 | "GETVARG", | ||
| 97 | "VARARGPREP", | 98 | "VARARGPREP", |
| 98 | "EXTRAARG", | 99 | "EXTRAARG", |
| 99 | NULL | 100 | NULL |
| @@ -279,7 +279,9 @@ static void init_var (FuncState *fs, expdesc *e, int vidx) { | |||
| 279 | 279 | ||
| 280 | 280 | ||
| 281 | /* | 281 | /* |
| 282 | ** Raises an error if variable described by 'e' is read only | 282 | ** Raises an error if variable described by 'e' is read only; moreover, |
| 283 | ** if 'e' is t[exp] where t is the vararg parameter, change it to index | ||
| 284 | ** a real table. (Virtual vararg tables cannot be changed.) | ||
| 283 | */ | 285 | */ |
| 284 | static void check_readonly (LexState *ls, expdesc *e) { | 286 | static void check_readonly (LexState *ls, expdesc *e) { |
| 285 | FuncState *fs = ls->fs; | 287 | FuncState *fs = ls->fs; |
| @@ -301,6 +303,10 @@ static void check_readonly (LexState *ls, expdesc *e) { | |||
| 301 | varname = up->name; | 303 | varname = up->name; |
| 302 | break; | 304 | break; |
| 303 | } | 305 | } |
| 306 | case VVARGIND: { | ||
| 307 | fs->f->flag |= PF_VATAB; /* function will need a vararg table */ | ||
| 308 | e->k = VINDEXED; | ||
| 309 | } /* FALLTHROUGH */ | ||
| 304 | case VINDEXUP: case VINDEXSTR: case VINDEXED: { /* global variable */ | 310 | case VINDEXUP: case VINDEXSTR: case VINDEXED: { /* global variable */ |
| 305 | if (e->u.ind.ro) /* read-only? */ | 311 | if (e->u.ind.ro) /* read-only? */ |
| 306 | varname = tsvalue(&fs->f->k[e->u.ind.keystr]); | 312 | varname = tsvalue(&fs->f->k[e->u.ind.keystr]); |
| @@ -1073,7 +1079,7 @@ static void parlist (LexState *ls) { | |||
| 1073 | case TK_DOTS: { | 1079 | case TK_DOTS: { |
| 1074 | varargk |= PF_ISVARARG; | 1080 | varargk |= PF_ISVARARG; |
| 1075 | luaX_next(ls); | 1081 | luaX_next(ls); |
| 1076 | if (testnext(ls, '=')) { | 1082 | if (testnext(ls, '|')) { |
| 1077 | new_varkind(ls, str_checkname(ls), RDKVAVAR); | 1083 | new_varkind(ls, str_checkname(ls), RDKVAVAR); |
| 1078 | varargk |= PF_VAVAR; | 1084 | varargk |= PF_VAVAR; |
| 1079 | } | 1085 | } |
| @@ -51,6 +51,8 @@ typedef enum { | |||
| 51 | ind.ro = true if it represents a read-only global; | 51 | ind.ro = true if it represents a read-only global; |
| 52 | ind.keystr = if key is a string, index in 'k' of that string; | 52 | ind.keystr = if key is a string, index in 'k' of that string; |
| 53 | -1 if key is not a string */ | 53 | -1 if key is not a string */ |
| 54 | VVARGIND, /* indexed vararg parameter; | ||
| 55 | ind.* as in VINDEXED */ | ||
| 54 | VINDEXUP, /* indexed upvalue; | 56 | VINDEXUP, /* indexed upvalue; |
| 55 | ind.idx = key's K index; | 57 | ind.idx = key's K index; |
| 56 | ind.* as in VINDEXED */ | 58 | ind.* as in VINDEXED */ |
| @@ -277,6 +277,28 @@ void luaT_adjustvarargs (lua_State *L, CallInfo *ci, const Proto *p) { | |||
| 277 | } | 277 | } |
| 278 | 278 | ||
| 279 | 279 | ||
| 280 | void luaT_getvararg (CallInfo *ci, StkId ra, TValue *rc) { | ||
| 281 | int nextra = ci->u.l.nextraargs; | ||
| 282 | lua_Integer n; | ||
| 283 | if (tointegerns(rc, &n)) { /* integral value? */ | ||
| 284 | if (l_castS2U(n) - 1 < cast_uint(nextra)) { | ||
| 285 | StkId slot = ci->func.p - nextra + cast_int(n) - 1; | ||
| 286 | setobjs2s(((lua_State*)NULL), ra, slot); | ||
| 287 | return; | ||
| 288 | } | ||
| 289 | } | ||
| 290 | else if (ttisshrstring(rc)) { /* short-string value? */ | ||
| 291 | size_t len; | ||
| 292 | const char *s = getlstr(tsvalue(rc), len); | ||
| 293 | if (len == 1 && s[0] == 'n') { /* key is "n"? */ | ||
| 294 | setivalue(s2v(ra), nextra); | ||
| 295 | return; | ||
| 296 | } | ||
| 297 | } | ||
| 298 | setnilvalue(s2v(ra)); /* else produce nil */ | ||
| 299 | } | ||
| 300 | |||
| 301 | |||
| 280 | void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted) { | 302 | void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted) { |
| 281 | int i; | 303 | int i; |
| 282 | int nextra = ci->u.l.nextraargs; | 304 | int nextra = ci->u.l.nextraargs; |
| @@ -97,6 +97,7 @@ LUAI_FUNC int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2, | |||
| 97 | 97 | ||
| 98 | LUAI_FUNC void luaT_adjustvarargs (lua_State *L, struct CallInfo *ci, | 98 | LUAI_FUNC void luaT_adjustvarargs (lua_State *L, struct CallInfo *ci, |
| 99 | const Proto *p); | 99 | const Proto *p); |
| 100 | LUAI_FUNC void luaT_getvararg (CallInfo *ci, StkId ra, TValue *rc); | ||
| 100 | LUAI_FUNC void luaT_getvarargs (lua_State *L, struct CallInfo *ci, | 101 | LUAI_FUNC void luaT_getvarargs (lua_State *L, struct CallInfo *ci, |
| 101 | StkId where, int wanted); | 102 | StkId where, int wanted); |
| 102 | 103 | ||
| @@ -1926,6 +1926,12 @@ void luaV_execute (lua_State *L, CallInfo *ci) { | |||
| 1926 | Protect(luaT_getvarargs(L, ci, ra, n)); | 1926 | Protect(luaT_getvarargs(L, ci, ra, n)); |
| 1927 | vmbreak; | 1927 | vmbreak; |
| 1928 | } | 1928 | } |
| 1929 | vmcase(OP_GETVARG) { | ||
| 1930 | StkId ra = RA(i); | ||
| 1931 | TValue *rc = vRC(i); | ||
| 1932 | luaT_getvararg(ci, ra, rc); | ||
| 1933 | vmbreak; | ||
| 1934 | } | ||
| 1929 | vmcase(OP_VARARGPREP) { | 1935 | vmcase(OP_VARARGPREP) { |
| 1930 | ProtectNT(luaT_adjustvarargs(L, ci, cl->p)); | 1936 | ProtectNT(luaT_adjustvarargs(L, ci, cl->p)); |
| 1931 | if (l_unlikely(trap)) { /* previous "Protect" updated trap */ | 1937 | if (l_unlikely(trap)) { /* previous "Protect" updated trap */ |
diff --git a/manual/manual.of b/manual/manual.of index 3c704118..beea41f9 100644 --- a/manual/manual.of +++ b/manual/manual.of | |||
| @@ -2262,7 +2262,7 @@ return x or f(x) -- results adjusted to 1 | |||
| 2262 | 2262 | ||
| 2263 | @sect3{func-def| @title{Function Definitions} | 2263 | @sect3{func-def| @title{Function Definitions} |
| 2264 | 2264 | ||
| 2265 | The syntax for function definition is | 2265 | The syntax for a function definition is |
| 2266 | @Produc{ | 2266 | @Produc{ |
| 2267 | @producname{functiondef}@producbody{@Rw{function} funcbody} | 2267 | @producname{functiondef}@producbody{@Rw{function} funcbody} |
| 2268 | @producname{funcbody}@producbody{@bnfter{(} @bnfopt{parlist} @bnfter{)} block @Rw{end}} | 2268 | @producname{funcbody}@producbody{@bnfter{(} @bnfopt{parlist} @bnfter{)} block @Rw{end}} |
| @@ -2315,6 +2315,18 @@ translates to | |||
| 2315 | global f; f = function () @rep{body} end | 2315 | global f; f = function () @rep{body} end |
| 2316 | } | 2316 | } |
| 2317 | 2317 | ||
| 2318 | The @emphx{colon} syntax | ||
| 2319 | is used to emulate @def{methods}, | ||
| 2320 | adding an implicit extra parameter @idx{self} to the function. | ||
| 2321 | Thus, the statement | ||
| 2322 | @verbatim{ | ||
| 2323 | function t.a.b.c:f (@rep{params}) @rep{body} end | ||
| 2324 | } | ||
| 2325 | is syntactic sugar for | ||
| 2326 | @verbatim{ | ||
| 2327 | t.a.b.c.f = function (self, @rep{params}) @rep{body} end | ||
| 2328 | } | ||
| 2329 | |||
| 2318 | A function definition is an executable expression, | 2330 | A function definition is an executable expression, |
| 2319 | whose value has type @emph{function}. | 2331 | whose value has type @emph{function}. |
| 2320 | When Lua precompiles a chunk, | 2332 | When Lua precompiles a chunk, |
| @@ -2325,11 +2337,25 @@ the function is @emph{instantiated} (or @emph{closed}). | |||
| 2325 | This function instance, or @emphx{closure}, | 2337 | This function instance, or @emphx{closure}, |
| 2326 | is the final value of the expression. | 2338 | is the final value of the expression. |
| 2327 | 2339 | ||
| 2340 | Results are returned using the @Rw{return} statement @see{control}. | ||
| 2341 | If control reaches the end of a function | ||
| 2342 | without encountering a @Rw{return} statement, | ||
| 2343 | then the function returns with no results. | ||
| 2344 | |||
| 2345 | @index{multiple return} | ||
| 2346 | There is a system-dependent limit on the number of values | ||
| 2347 | that a function may return. | ||
| 2348 | This limit is guaranteed to be at least 1000. | ||
| 2349 | |||
| 2350 | @sect4{@title{Parameters} | ||
| 2351 | |||
| 2328 | Parameters act as local variables that are | 2352 | Parameters act as local variables that are |
| 2329 | initialized with the argument values: | 2353 | initialized with the argument values: |
| 2330 | @Produc{ | 2354 | @Produc{ |
| 2331 | @producname{parlist}@producbody{namelist @bnfopt{@bnfter{,} @bnfter{...}} @Or | 2355 | @producname{parlist}@producbody{namelist @bnfopt{@bnfter{,} varargparam} @Or |
| 2332 | @bnfter{...}} | 2356 | varargparam} |
| 2357 | @producname{varargparam}@producbody{@bnfter{...} | ||
| 2358 | @bnfopt{@bnfter{|} @bnfNter{Name}}} | ||
| 2333 | } | 2359 | } |
| 2334 | When a Lua function is called, | 2360 | When a Lua function is called, |
| 2335 | it adjusts its list of @x{arguments} to | 2361 | it adjusts its list of @x{arguments} to |
| @@ -2339,11 +2365,12 @@ which is indicated by three dots (@Char{...}) | |||
| 2339 | at the end of its parameter list. | 2365 | at the end of its parameter list. |
| 2340 | A variadic function does not adjust its argument list; | 2366 | A variadic function does not adjust its argument list; |
| 2341 | instead, it collects all extra arguments and supplies them | 2367 | instead, it collects all extra arguments and supplies them |
| 2342 | to the function through a @def{vararg expression}, | 2368 | to the function through a @def{vararg expression} and, |
| 2343 | which is also written as three dots. | 2369 | if present, a @def{vararg table}. |
| 2344 | The value of this expression is a list of all actual extra arguments, | ||
| 2345 | similar to a function with multiple results @see{multires}. | ||
| 2346 | 2370 | ||
| 2371 | A vararg expression is also written as three dots, | ||
| 2372 | and its value is a list of all actual extra arguments, | ||
| 2373 | similar to a function with multiple results @see{multires}. | ||
| 2347 | 2374 | ||
| 2348 | As an example, consider the following definitions: | 2375 | As an example, consider the following definitions: |
| 2349 | @verbatim{ | 2376 | @verbatim{ |
| @@ -2368,26 +2395,27 @@ g(3, 4, 5, 8) a=3, b=4, ... -> 5 8 | |||
| 2368 | g(5, r()) a=5, b=1, ... -> 2 3 | 2395 | g(5, r()) a=5, b=1, ... -> 2 3 |
| 2369 | } | 2396 | } |
| 2370 | 2397 | ||
| 2371 | Results are returned using the @Rw{return} statement @see{control}. | 2398 | The presence of a varag table in a variadic function is indicated |
| 2372 | If control reaches the end of a function | 2399 | by the @T{|name} syntax after the three dots. |
| 2373 | without encountering a @Rw{return} statement, | 2400 | When present, |
| 2374 | then the function returns with no results. | 2401 | a vararg table behaves like a read-only local variable |
| 2375 | 2402 | with the given name that is initialized with a table. | |
| 2376 | @index{multiple return} | 2403 | In that table, |
| 2377 | There is a system-dependent limit on the number of values | 2404 | the values at indices 1, 2, etc. are the extra arguments, |
| 2378 | that a function may return. | 2405 | and the value at index @St{n} is the number of extra arguments. |
| 2379 | This limit is guaranteed to be at least 1000. | 2406 | In other words, the code behaves as if the function started with |
| 2380 | 2407 | the following statement, | |
| 2381 | The @emphx{colon} syntax | 2408 | assuming the standard behavior of @Lid{table.pack}: |
| 2382 | is used to emulate @def{methods}, | ||
| 2383 | adding an implicit extra parameter @idx{self} to the function. | ||
| 2384 | Thus, the statement | ||
| 2385 | @verbatim{ | 2409 | @verbatim{ |
| 2386 | function t.a.b.c:f (@rep{params}) @rep{body} end | 2410 | local <const> name = table.pack(...) |
| 2387 | } | 2411 | } |
| 2388 | is syntactic sugar for | 2412 | |
| 2389 | @verbatim{ | 2413 | As an optimization, |
| 2390 | t.a.b.c.f = function (self, @rep{params}) @rep{body} end | 2414 | if the vararg table is used only as a base in indexing expressions |
| 2415 | (the @T{t} in @T{t[exp]} or @T{t.id}) and it is not an upvalue, | ||
| 2416 | the code does not create an actual table and instead translates | ||
| 2417 | the indexing expressions into accesses to the internal vararg data. | ||
| 2418 | |||
| 2391 | } | 2419 | } |
| 2392 | 2420 | ||
| 2393 | } | 2421 | } |
| @@ -2422,7 +2450,7 @@ for instance @T{foo(e1, e2, e3)} @see{functioncall}.} | |||
| 2422 | for instance @T{a , b, c = e1, e2, e3} @see{assignment}.} | 2450 | for instance @T{a , b, c = e1, e2, e3} @see{assignment}.} |
| 2423 | 2451 | ||
| 2424 | @item{A local or global declaration, | 2452 | @item{A local or global declaration, |
| 2425 | which is a special case of multiple assignment.} | 2453 | which is similar to a multiple assignment.} |
| 2426 | 2454 | ||
| 2427 | @item{The initial values in a generic @rw{for} loop, | 2455 | @item{The initial values in a generic @rw{for} loop, |
| 2428 | for instance @T{for k in e1, e2, e3 do ... end} @see{for}.} | 2456 | for instance @T{for k in e1, e2, e3 do ... end} @see{for}.} |
| @@ -3016,7 +3044,7 @@ typedef void * (*lua_Alloc) (void *ud, | |||
| 3016 | size_t osize, | 3044 | size_t osize, |
| 3017 | size_t nsize);| | 3045 | size_t nsize);| |
| 3018 | 3046 | ||
| 3019 | The type of the @x{memory-allocation function} used by Lua states. | 3047 | The type of the @x{memory-allocator function} used by Lua states. |
| 3020 | The allocator function must provide a | 3048 | The allocator function must provide a |
| 3021 | functionality similar to @id{realloc}, | 3049 | functionality similar to @id{realloc}, |
| 3022 | but not exactly the same. | 3050 | but not exactly the same. |
| @@ -3482,7 +3510,7 @@ This function should not be called by a finalizer. | |||
| 3482 | @APIEntry{lua_Alloc lua_getallocf (lua_State *L, void **ud);| | 3510 | @APIEntry{lua_Alloc lua_getallocf (lua_State *L, void **ud);| |
| 3483 | @apii{0,0,-} | 3511 | @apii{0,0,-} |
| 3484 | 3512 | ||
| 3485 | Returns the @x{memory-allocation function} of a given state. | 3513 | Returns the @x{memory-allocator function} of a given state. |
| 3486 | If @id{ud} is not @id{NULL}, Lua stores in @T{*ud} the | 3514 | If @id{ud} is not @id{NULL}, Lua stores in @T{*ud} the |
| 3487 | opaque pointer given when the memory-allocator function was set. | 3515 | opaque pointer given when the memory-allocator function was set. |
| 3488 | 3516 | ||
| @@ -9732,8 +9760,11 @@ and @bnfNter{LiteralString}, see @See{lexical}.) | |||
| 9732 | 9760 | ||
| 9733 | @producname{funcbody}@producbody{@bnfter{(} @bnfopt{parlist} @bnfter{)} block @Rw{end}} | 9761 | @producname{funcbody}@producbody{@bnfter{(} @bnfopt{parlist} @bnfter{)} block @Rw{end}} |
| 9734 | 9762 | ||
| 9735 | @producname{parlist}@producbody{namelist @bnfopt{@bnfter{,} @bnfter{...}} | 9763 | @producname{parlist}@producbody{namelist @bnfopt{@bnfter{,} varargparam} @Or |
| 9736 | @Or @bnfter{...}} | 9764 | varargparam} |
| 9765 | |||
| 9766 | @producname{varargparam}@producbody{@bnfter{...} | ||
| 9767 | @bnfopt{@bnfter{|} @bnfNter{Name}}} | ||
| 9737 | 9768 | ||
| 9738 | @producname{tableconstructor}@producbody{@bnfter{@Open} @bnfopt{fieldlist} @bnfter{@Close}} | 9769 | @producname{tableconstructor}@producbody{@bnfter{@Open} @bnfopt{fieldlist} @bnfter{@Close}} |
| 9739 | 9770 | ||
diff --git a/testes/locals.lua b/testes/locals.lua index 02f41980..5222802f 100644 --- a/testes/locals.lua +++ b/testes/locals.lua | |||
| @@ -310,8 +310,7 @@ do -- testing presence of second argument | |||
| 310 | local function foo (howtoclose, obj, n) | 310 | local function foo (howtoclose, obj, n) |
| 311 | local ca -- copy of 'a' visible inside its close metamethod | 311 | local ca -- copy of 'a' visible inside its close metamethod |
| 312 | do | 312 | do |
| 313 | local a <close> = func2close(function (...) | 313 | local a <close> = func2close(function (... | t) |
| 314 | local t = table.pack(...) | ||
| 315 | assert(select("#", ...) == n) | 314 | assert(select("#", ...) == n) |
| 316 | assert(t.n == n and t[1] == ca and (t.n < 2 or t[2] == obj)) | 315 | assert(t.n == n and t[1] == ca and (t.n < 2 or t[2] == obj)) |
| 317 | ca = 15 -- final value to be returned if howtoclose=="scope" | 316 | ca = 15 -- final value to be returned if howtoclose=="scope" |
| @@ -911,8 +910,7 @@ do | |||
| 911 | 910 | ||
| 912 | local extrares -- result from extra yield (if any) | 911 | local extrares -- result from extra yield (if any) |
| 913 | 912 | ||
| 914 | local function check (body, extra, ...) | 913 | local function check (body, extra, ...|t) |
| 915 | local t = table.pack(...) -- expected returns | ||
| 916 | local co = coroutine.wrap(body) | 914 | local co = coroutine.wrap(body) |
| 917 | if extra then | 915 | if extra then |
| 918 | extrares = co() -- runs until first (extra) yield | 916 | extrares = co() -- runs until first (extra) yield |
diff --git a/testes/vararg.lua b/testes/vararg.lua index 92f720cb..5711f78b 100644 --- a/testes/vararg.lua +++ b/testes/vararg.lua | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | print('testing vararg') | 4 | print('testing vararg') |
| 5 | 5 | ||
| 6 | local function f (a, ...=t) | 6 | local function f (a, ...|t) |
| 7 | local x = {n = select('#', ...), ...} | 7 | local x = {n = select('#', ...), ...} |
| 8 | assert(x.n == t.n) | 8 | assert(x.n == t.n) |
| 9 | for i = 1, x.n do | 9 | for i = 1, x.n do |
| @@ -20,7 +20,7 @@ local function c12 (...) | |||
| 20 | return res, 2 | 20 | return res, 2 |
| 21 | end | 21 | end |
| 22 | 22 | ||
| 23 | local function vararg (...=t) return t end | 23 | local function vararg (... | t) return t end |
| 24 | 24 | ||
| 25 | local call = function (f, args) return f(table.unpack(args, 1, args.n)) end | 25 | local call = function (f, args) return f(table.unpack(args, 1, args.n)) end |
| 26 | 26 | ||
| @@ -153,8 +153,8 @@ end | |||
| 153 | 153 | ||
| 154 | 154 | ||
| 155 | do -- vararg parameter used in nested functions | 155 | do -- vararg parameter used in nested functions |
| 156 | local function foo (... = tab1) | 156 | local function foo (... | tab1) |
| 157 | return function (... = tab2) | 157 | return function (... | tab2) |
| 158 | return {tab1, tab2} | 158 | return {tab1, tab2} |
| 159 | end | 159 | end |
| 160 | end | 160 | end |
| @@ -165,16 +165,51 @@ do -- vararg parameter used in nested functions | |||
| 165 | end | 165 | end |
| 166 | 166 | ||
| 167 | do -- vararg parameter is read-only | 167 | do -- vararg parameter is read-only |
| 168 | local st, msg = load("return function (... = t) t = 10 end") | 168 | local st, msg = load("return function (... | t) t = 10 end") |
| 169 | assert(string.find(msg, "const variable 't'")) | 169 | assert(string.find(msg, "const variable 't'")) |
| 170 | 170 | ||
| 171 | local st, msg = load[[ | 171 | local st, msg = load[[ |
| 172 | local function foo (... = extra) | 172 | local function foo (... | extra) |
| 173 | return function (...) extra = nil end | 173 | return function (...) extra = nil end |
| 174 | end | 174 | end |
| 175 | ]] | 175 | ]] |
| 176 | assert(string.find(msg, "const variable 'extra'")) | 176 | assert(string.find(msg, "const variable 'extra'")) |
| 177 | end | 177 | end |
| 178 | 178 | ||
| 179 | |||
| 180 | do -- _ENV as vararg parameter | ||
| 181 | local st, msg = load[[ | ||
| 182 | local function aux (... | _ENV) | ||
| 183 | global <const> a | ||
| 184 | a = 10 | ||
| 185 | end ]] | ||
| 186 | assert(string.find(msg, "const variable 'a'")) | ||
| 187 | end | ||
| 188 | |||
| 189 | |||
| 190 | do -- access to vararg parameter | ||
| 191 | local function notab (keys, t, ... | v) | ||
| 192 | for _, k in pairs(keys) do | ||
| 193 | assert(t[k] == v[k]) | ||
| 194 | end | ||
| 195 | assert(t.n == v.n) | ||
| 196 | end | ||
| 197 | |||
| 198 | local t = table.pack(10, 20, 30) | ||
| 199 | local keys = {-1, 0, 1, t.n, t.n + 1, 1.0, 1.1, "n", print, "k", "1"} | ||
| 200 | notab(keys, t, 10, 20, 30) -- ensure stack space | ||
| 201 | local m = collectgarbage"count" | ||
| 202 | notab(keys, t, 10, 20, 30) | ||
| 203 | -- 'notab' does not create any table/object | ||
| 204 | assert(m == collectgarbage"count") | ||
| 205 | |||
| 206 | -- writing to the vararg table | ||
| 207 | local function foo (... | t) | ||
| 208 | t[1] = t[1] + 10 | ||
| 209 | return t[1] | ||
| 210 | end | ||
| 211 | assert(foo(10, 30) == 20) | ||
| 212 | end | ||
| 213 | |||
| 179 | print('OK') | 214 | print('OK') |
| 180 | 215 | ||
