diff options
| author | Roberto I <roberto@inf.puc-rio.br> | 2025-09-17 16:07:48 -0300 |
|---|---|---|
| committer | Roberto I <roberto@inf.puc-rio.br> | 2025-09-17 16:07:48 -0300 |
| commit | 8fb1af0e33cd8688f57cd0e3ab86420a8cfe99bd (patch) | |
| tree | 2c199de5e67f395a4f9c94fcfb2ef3f0dad8882a | |
| parent | 140b672e2ee2ac842661ece4b48e1a64f0cd11ea (diff) | |
| download | lua-8fb1af0e33cd8688f57cd0e3ab86420a8cfe99bd.tar.gz lua-8fb1af0e33cd8688f57cd0e3ab86420a8cfe99bd.tar.bz2 lua-8fb1af0e33cd8688f57cd0e3ab86420a8cfe99bd.zip | |
Varag parameter is a new kind of variable
To allow some optimizations on its use.
| -rw-r--r-- | lcode.c | 12 | ||||
| -rw-r--r-- | lcode.h | 1 | ||||
| -rw-r--r-- | lobject.h | 4 | ||||
| -rw-r--r-- | lparser.c | 26 | ||||
| -rw-r--r-- | lparser.h | 4 | ||||
| -rw-r--r-- | ltm.c | 8 | ||||
| -rw-r--r-- | testes/vararg.lua | 26 |
7 files changed, 65 insertions, 16 deletions
| @@ -785,6 +785,15 @@ void luaK_setoneret (FuncState *fs, expdesc *e) { | |||
| 785 | } | 785 | } |
| 786 | } | 786 | } |
| 787 | 787 | ||
| 788 | /* | ||
| 789 | ** Change a vararg parameter into a regular local variable | ||
| 790 | */ | ||
| 791 | void luaK_vapar2local (FuncState *fs, expdesc *var) { | ||
| 792 | fs->f->flag |= PF_VATAB; /* function will need a vararg table */ | ||
| 793 | /* now a vararg parameter is equivalent to a regular local variable */ | ||
| 794 | var->k = VLOCAL; | ||
| 795 | } | ||
| 796 | |||
| 788 | 797 | ||
| 789 | /* | 798 | /* |
| 790 | ** Ensure that expression 'e' is not a variable (nor a <const>). | 799 | ** Ensure that expression 'e' is not a variable (nor a <const>). |
| @@ -796,6 +805,9 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) { | |||
| 796 | const2exp(const2val(fs, e), e); | 805 | const2exp(const2val(fs, e), e); |
| 797 | break; | 806 | break; |
| 798 | } | 807 | } |
| 808 | case VVARGVAR: { | ||
| 809 | luaK_vapar2local(fs, e); /* turn it into a local variable */ | ||
| 810 | } /* FALLTHROUGH */ | ||
| 799 | case VLOCAL: { /* already in a register */ | 811 | case VLOCAL: { /* already in a register */ |
| 800 | int temp = e->u.var.ridx; | 812 | int temp = e->u.var.ridx; |
| 801 | e->u.info = temp; /* (can't do a direct assignment; values overlap) */ | 813 | e->u.info = temp; /* (can't do a direct assignment; values overlap) */ |
| @@ -71,6 +71,7 @@ LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n); | |||
| 71 | LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n); | 71 | LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n); |
| 72 | LUAI_FUNC void luaK_checkstack (FuncState *fs, int n); | 72 | LUAI_FUNC void luaK_checkstack (FuncState *fs, int n); |
| 73 | LUAI_FUNC void luaK_int (FuncState *fs, int reg, lua_Integer n); | 73 | LUAI_FUNC void luaK_int (FuncState *fs, int reg, lua_Integer n); |
| 74 | LUAI_FUNC void luaK_vapar2local (FuncState *fs, expdesc *var); | ||
| 74 | LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e); | 75 | LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e); |
| 75 | LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e); | 76 | LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e); |
| 76 | LUAI_FUNC void luaK_exp2anyregup (FuncState *fs, expdesc *e); | 77 | LUAI_FUNC void luaK_exp2anyregup (FuncState *fs, expdesc *e); |
| @@ -584,8 +584,8 @@ typedef struct AbsLineInfo { | |||
| 584 | ** Flags in Prototypes | 584 | ** Flags in Prototypes |
| 585 | */ | 585 | */ |
| 586 | #define PF_ISVARARG 1 /* function is vararg */ | 586 | #define PF_ISVARARG 1 /* function is vararg */ |
| 587 | #define PF_VATAB 2 /* function is vararg with table */ | 587 | #define PF_VAVAR 2 /* function has vararg parameter */ |
| 588 | #define PF_VAPTAB 4 /* function is vararg with pseudo-table */ | 588 | #define PF_VATAB 4 /* function has vararg table */ |
| 589 | #define PF_FIXED 8 /* prototype has parts in fixed memory */ | 589 | #define PF_FIXED 8 /* prototype has parts in fixed memory */ |
| 590 | 590 | ||
| 591 | 591 | ||
| @@ -289,7 +289,7 @@ static void check_readonly (LexState *ls, expdesc *e) { | |||
| 289 | varname = ls->dyd->actvar.arr[e->u.info].vd.name; | 289 | varname = ls->dyd->actvar.arr[e->u.info].vd.name; |
| 290 | break; | 290 | break; |
| 291 | } | 291 | } |
| 292 | case VLOCAL: { | 292 | case VLOCAL: case VVARGVAR: { |
| 293 | Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx); | 293 | Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx); |
| 294 | if (vardesc->vd.kind != VDKREG) /* not a regular variable? */ | 294 | if (vardesc->vd.kind != VDKREG) /* not a regular variable? */ |
| 295 | varname = vardesc->vd.name; | 295 | varname = vardesc->vd.name; |
| @@ -426,8 +426,11 @@ static int searchvar (FuncState *fs, TString *n, expdesc *var) { | |||
| 426 | else if (eqstr(n, vd->vd.name)) { /* found? */ | 426 | else if (eqstr(n, vd->vd.name)) { /* found? */ |
| 427 | if (vd->vd.kind == RDKCTC) /* compile-time constant? */ | 427 | if (vd->vd.kind == RDKCTC) /* compile-time constant? */ |
| 428 | init_exp(var, VCONST, fs->firstlocal + i); | 428 | init_exp(var, VCONST, fs->firstlocal + i); |
| 429 | else /* local variable */ | 429 | else { /* local variable */ |
| 430 | init_var(fs, var, i); | 430 | init_var(fs, var, i); |
| 431 | if (vd->vd.kind == RDKVAVAR) /* vararg parameter? */ | ||
| 432 | var->k = VVARGVAR; | ||
| 433 | } | ||
| 431 | return cast_int(var->k); | 434 | return cast_int(var->k); |
| 432 | } | 435 | } |
| 433 | } | 436 | } |
| @@ -467,8 +470,13 @@ static void marktobeclosed (FuncState *fs) { | |||
| 467 | static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) { | 470 | static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) { |
| 468 | int v = searchvar(fs, n, var); /* look up variables at current level */ | 471 | int v = searchvar(fs, n, var); /* look up variables at current level */ |
| 469 | if (v >= 0) { /* found? */ | 472 | if (v >= 0) { /* found? */ |
| 470 | if (v == VLOCAL && !base) | 473 | if (!base) { |
| 471 | markupval(fs, var->u.var.vidx); /* local will be used as an upval */ | 474 | if (var->k == VVARGVAR) /* vararg parameter? */ |
| 475 | luaK_vapar2local(fs, var); /* change it to a regular local */ | ||
| 476 | if (var->k == VLOCAL) | ||
| 477 | markupval(fs, var->u.var.vidx); /* will be used as an upvalue */ | ||
| 478 | } | ||
| 479 | /* else nothing else to be done */ | ||
| 472 | } | 480 | } |
| 473 | else { /* not found at current level; try upvalues */ | 481 | else { /* not found at current level; try upvalues */ |
| 474 | int idx = searchupvalue(fs, n); /* try existing upvalues */ | 482 | int idx = searchupvalue(fs, n); /* try existing upvalues */ |
| @@ -1066,8 +1074,8 @@ static void parlist (LexState *ls) { | |||
| 1066 | varargk |= PF_ISVARARG; | 1074 | varargk |= PF_ISVARARG; |
| 1067 | luaX_next(ls); | 1075 | luaX_next(ls); |
| 1068 | if (testnext(ls, '=')) { | 1076 | if (testnext(ls, '=')) { |
| 1069 | new_varkind(ls, str_checkname(ls), RDKVATAB); | 1077 | new_varkind(ls, str_checkname(ls), RDKVAVAR); |
| 1070 | varargk |= PF_VATAB; | 1078 | varargk |= PF_VAVAR; |
| 1071 | } | 1079 | } |
| 1072 | break; | 1080 | break; |
| 1073 | } | 1081 | } |
| @@ -1079,10 +1087,10 @@ static void parlist (LexState *ls) { | |||
| 1079 | f->numparams = cast_byte(fs->nactvar); | 1087 | f->numparams = cast_byte(fs->nactvar); |
| 1080 | if (varargk != 0) { | 1088 | if (varargk != 0) { |
| 1081 | setvararg(fs, varargk); /* declared vararg */ | 1089 | setvararg(fs, varargk); /* declared vararg */ |
| 1082 | if (varargk & PF_VATAB) | 1090 | if (varargk & PF_VAVAR) |
| 1083 | adjustlocalvars(ls, 1); /* vararg table */ | 1091 | adjustlocalvars(ls, 1); /* vararg parameter */ |
| 1084 | } | 1092 | } |
| 1085 | /* reserve registers for parameters (and vararg variable, if present) */ | 1093 | /* reserve registers for parameters (plus vararg parameter, if present) */ |
| 1086 | luaK_reserveregs(fs, fs->nactvar); | 1094 | luaK_reserveregs(fs, fs->nactvar); |
| 1087 | } | 1095 | } |
| 1088 | 1096 | ||
| @@ -37,6 +37,8 @@ typedef enum { | |||
| 37 | info = result register */ | 37 | info = result register */ |
| 38 | VLOCAL, /* local variable; var.ridx = register index; | 38 | VLOCAL, /* local variable; var.ridx = register index; |
| 39 | var.vidx = relative index in 'actvar.arr' */ | 39 | var.vidx = relative index in 'actvar.arr' */ |
| 40 | VVARGVAR, /* vararg parameter; var.ridx = register index; | ||
| 41 | var.vidx = relative index in 'actvar.arr' */ | ||
| 40 | VGLOBAL, /* global variable; | 42 | VGLOBAL, /* global variable; |
| 41 | info = relative index in 'actvar.arr' (or -1 for | 43 | info = relative index in 'actvar.arr' (or -1 for |
| 42 | implicit declaration) */ | 44 | implicit declaration) */ |
| @@ -97,7 +99,7 @@ typedef struct expdesc { | |||
| 97 | /* kinds of variables */ | 99 | /* kinds of variables */ |
| 98 | #define VDKREG 0 /* regular local */ | 100 | #define VDKREG 0 /* regular local */ |
| 99 | #define RDKCONST 1 /* local constant */ | 101 | #define RDKCONST 1 /* local constant */ |
| 100 | #define RDKVATAB 2 /* vararg table */ | 102 | #define RDKVAVAR 2 /* vararg parameter */ |
| 101 | #define RDKTOCLOSE 3 /* to-be-closed */ | 103 | #define RDKTOCLOSE 3 /* to-be-closed */ |
| 102 | #define RDKCTC 4 /* local compile-time constant */ | 104 | #define RDKCTC 4 /* local compile-time constant */ |
| 103 | #define GDKREG 5 /* regular global */ | 105 | #define GDKREG 5 /* regular global */ |
| @@ -265,11 +265,11 @@ void luaT_adjustvarargs (lua_State *L, CallInfo *ci, const Proto *p) { | |||
| 265 | setobjs2s(L, L->top.p++, ci->func.p + i); | 265 | setobjs2s(L, L->top.p++, ci->func.p + i); |
| 266 | setnilvalue(s2v(ci->func.p + i)); /* erase original parameter (for GC) */ | 266 | setnilvalue(s2v(ci->func.p + i)); /* erase original parameter (for GC) */ |
| 267 | } | 267 | } |
| 268 | if (p->flag & (PF_VAPTAB | PF_VATAB)) { /* is there a vararg table? */ | 268 | if (p->flag & PF_VAVAR) { /* is there a vararg parameter? */ |
| 269 | if (p->flag & PF_VAPTAB) /* is vararg table fake? */ | 269 | if (p->flag & PF_VATAB) /* does it need a vararg table? */ |
| 270 | setnilvalue(s2v(L->top.p)); /* initialize it */ | ||
| 271 | else | ||
| 272 | createvarargtab(L, ci->func.p + nfixparams + 1, nextra); | 270 | createvarargtab(L, ci->func.p + nfixparams + 1, nextra); |
| 271 | else /* no table; set parameter to nil */ | ||
| 272 | setnilvalue(s2v(L->top.p)); | ||
| 273 | } | 273 | } |
| 274 | ci->func.p += totalargs + 1; | 274 | ci->func.p += totalargs + 1; |
| 275 | ci->top.p += totalargs + 1; | 275 | ci->top.p += totalargs + 1; |
diff --git a/testes/vararg.lua b/testes/vararg.lua index 4320684e..92f720cb 100644 --- a/testes/vararg.lua +++ b/testes/vararg.lua | |||
| @@ -150,5 +150,31 @@ do | |||
| 150 | local a, b = g() | 150 | local a, b = g() |
| 151 | assert(a == nil and b == 2) | 151 | assert(a == nil and b == 2) |
| 152 | end | 152 | end |
| 153 | |||
| 154 | |||
| 155 | do -- vararg parameter used in nested functions | ||
| 156 | local function foo (... = tab1) | ||
| 157 | return function (... = tab2) | ||
| 158 | return {tab1, tab2} | ||
| 159 | end | ||
| 160 | end | ||
| 161 | local f = foo(10, 20, 30) | ||
| 162 | local t = f("a", "b") | ||
| 163 | assert(t[1].n == 3 and t[1][1] == 10) | ||
| 164 | assert(t[2].n == 2 and t[2][1] == "a") | ||
| 165 | end | ||
| 166 | |||
| 167 | do -- vararg parameter is read-only | ||
| 168 | local st, msg = load("return function (... = t) t = 10 end") | ||
| 169 | assert(string.find(msg, "const variable 't'")) | ||
| 170 | |||
| 171 | local st, msg = load[[ | ||
| 172 | local function foo (... = extra) | ||
| 173 | return function (...) extra = nil end | ||
| 174 | end | ||
| 175 | ]] | ||
| 176 | assert(string.find(msg, "const variable 'extra'")) | ||
| 177 | end | ||
| 178 | |||
| 153 | print('OK') | 179 | print('OK') |
| 154 | 180 | ||
