summaryrefslogtreecommitdiff
path: root/libs/liblua/src/lparser.c
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2020-07-02 19:37:06 +0300
committerGeorge Hazan <ghazan@miranda.im>2020-07-02 19:37:06 +0300
commitd35fd87e643656a43e1ec19e18ead85839886679 (patch)
treed3c67be211c7e5ff89d339710ab65b82be9ce99f /libs/liblua/src/lparser.c
parentf10699e580b3eead1cb9c250822abbbc626eb3e3 (diff)
fixes #2472 (Update liblua to 5.4)
Diffstat (limited to 'libs/liblua/src/lparser.c')
-rw-r--r--libs/liblua/src/lparser.c1000
1 files changed, 673 insertions, 327 deletions
diff --git a/libs/liblua/src/lparser.c b/libs/liblua/src/lparser.c
index cc54de43c6..bc7d9a4f2d 100644
--- a/libs/liblua/src/lparser.c
+++ b/libs/liblua/src/lparser.c
@@ -1,5 +1,5 @@
/*
-** $Id: lparser.c,v 2.155.1.2 2017/04/29 18:11:40 roberto Exp $
+** $Id: lparser.c $
** Lua Parser
** See Copyright Notice in lua.h
*/
@@ -10,6 +10,7 @@
#include "lprefix.h"
+#include <limits.h>
#include <string.h>
#include "lua.h"
@@ -52,6 +53,7 @@ typedef struct BlockCnt {
lu_byte nactvar; /* # active locals outside the block */
lu_byte upval; /* true if some variable in the block is an upvalue */
lu_byte isloop; /* true if 'block' is a loop */
+ lu_byte insidetbc; /* true if inside the scope of a to-be-closed var. */
} BlockCnt;
@@ -63,13 +65,6 @@ static void statement (LexState *ls);
static void expr (LexState *ls, expdesc *v);
-/* semantic error */
-static l_noret semerror (LexState *ls, const char *msg) {
- ls->t.token = 0; /* remove "near <token>" from final message */
- luaX_syntaxerror(ls, msg);
-}
-
-
static l_noret error_expected (LexState *ls, int token) {
luaX_syntaxerror(ls,
luaO_pushfstring(ls->L, "%s expected", luaX_token2str(ls, token)));
@@ -94,6 +89,9 @@ static void checklimit (FuncState *fs, int v, int l, const char *what) {
}
+/*
+** Test whether next token is 'c'; if so, skip it.
+*/
static int testnext (LexState *ls, int c) {
if (ls->t.token == c) {
luaX_next(ls);
@@ -103,12 +101,18 @@ static int testnext (LexState *ls, int c) {
}
+/*
+** Check that next token is 'c'.
+*/
static void check (LexState *ls, int c) {
if (ls->t.token != c)
error_expected(ls, c);
}
+/*
+** Check that next token is 'c' and skip it.
+*/
static void checknext (LexState *ls, int c) {
check(ls, c);
luaX_next(ls);
@@ -118,11 +122,15 @@ static void checknext (LexState *ls, int c) {
#define check_condition(ls,c,msg) { if (!(c)) luaX_syntaxerror(ls, msg); }
-
+/*
+** Check that next token is 'what' and skip it. In case of error,
+** raise an error that the expected 'what' should match a 'who'
+** in line 'where' (if that is not the current line).
+*/
static void check_match (LexState *ls, int what, int who, int where) {
- if (!testnext(ls, what)) {
- if (where == ls->linenumber)
- error_expected(ls, what);
+ if (unlikely(!testnext(ls, what))) {
+ if (where == ls->linenumber) /* all in the same line? */
+ error_expected(ls, what); /* do not need a complex message */
else {
luaX_syntaxerror(ls, luaO_pushfstring(ls->L,
"%s expected (to close %s at line %d)",
@@ -148,73 +156,189 @@ static void init_exp (expdesc *e, expkind k, int i) {
}
-static void codestring (LexState *ls, expdesc *e, TString *s) {
- init_exp(e, VK, luaK_stringK(ls->fs, s));
+static void codestring (expdesc *e, TString *s) {
+ e->f = e->t = NO_JUMP;
+ e->k = VKSTR;
+ e->u.strval = s;
}
-static void checkname (LexState *ls, expdesc *e) {
- codestring(ls, e, str_checkname(ls));
+static void codename (LexState *ls, expdesc *e) {
+ codestring(e, str_checkname(ls));
}
-static int registerlocalvar (LexState *ls, TString *varname) {
- FuncState *fs = ls->fs;
+/*
+** Register a new local variable in the active 'Proto' (for debug
+** information).
+*/
+static int registerlocalvar (LexState *ls, FuncState *fs, TString *varname) {
Proto *f = fs->f;
int oldsize = f->sizelocvars;
- luaM_growvector(ls->L, f->locvars, fs->nlocvars, f->sizelocvars,
+ luaM_growvector(ls->L, f->locvars, fs->ndebugvars, f->sizelocvars,
LocVar, SHRT_MAX, "local variables");
while (oldsize < f->sizelocvars)
f->locvars[oldsize++].varname = NULL;
- f->locvars[fs->nlocvars].varname = varname;
+ f->locvars[fs->ndebugvars].varname = varname;
+ f->locvars[fs->ndebugvars].startpc = fs->pc;
luaC_objbarrier(ls->L, f, varname);
- return fs->nlocvars++;
+ return fs->ndebugvars++;
}
-static void new_localvar (LexState *ls, TString *name) {
+/*
+** Create a new local variable with the given 'name'. Return its index
+** in the function.
+*/
+static int new_localvar (LexState *ls, TString *name) {
+ lua_State *L = ls->L;
FuncState *fs = ls->fs;
Dyndata *dyd = ls->dyd;
- int reg = registerlocalvar(ls, name);
+ Vardesc *var;
checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal,
- MAXVARS, "local variables");
- luaM_growvector(ls->L, dyd->actvar.arr, dyd->actvar.n + 1,
- dyd->actvar.size, Vardesc, MAX_INT, "local variables");
- dyd->actvar.arr[dyd->actvar.n++].idx = cast(short, reg);
+ MAXVARS, "local variables");
+ luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1,
+ dyd->actvar.size, Vardesc, USHRT_MAX, "local variables");
+ var = &dyd->actvar.arr[dyd->actvar.n++];
+ var->vd.kind = VDKREG; /* default */
+ var->vd.name = name;
+ return dyd->actvar.n - 1 - fs->firstlocal;
}
+#define new_localvarliteral(ls,v) \
+ new_localvar(ls, \
+ luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char)) - 1));
+
-static void new_localvarliteral_ (LexState *ls, const char *name, size_t sz) {
- new_localvar(ls, luaX_newstring(ls, name, sz));
+
+/*
+** Return the "variable description" (Vardesc) of a given variable.
+** (Unless noted otherwise, all variables are referred to by their
+** compiler indices.)
+*/
+static Vardesc *getlocalvardesc (FuncState *fs, int vidx) {
+ return &fs->ls->dyd->actvar.arr[fs->firstlocal + vidx];
}
-#define new_localvarliteral(ls,v) \
- new_localvarliteral_(ls, "" v, (sizeof(v)/sizeof(char))-1)
+
+/*
+** Convert 'nvar', a compiler index level, to it corresponding
+** stack index level. For that, search for the highest variable
+** below that level that is in the stack and uses its stack
+** index ('sidx').
+*/
+static int stacklevel (FuncState *fs, int nvar) {
+ while (nvar-- > 0) {
+ Vardesc *vd = getlocalvardesc(fs, nvar); /* get variable */
+ if (vd->vd.kind != RDKCTC) /* is in the stack? */
+ return vd->vd.sidx + 1;
+ }
+ return 0; /* no variables in the stack */
+}
+
+
+/*
+** Return the number of variables in the stack for function 'fs'
+*/
+int luaY_nvarstack (FuncState *fs) {
+ return stacklevel(fs, fs->nactvar);
+}
-static LocVar *getlocvar (FuncState *fs, int i) {
- int idx = fs->ls->dyd->actvar.arr[fs->firstlocal + i].idx;
- lua_assert(idx < fs->nlocvars);
- return &fs->f->locvars[idx];
+/*
+** Get the debug-information entry for current variable 'vidx'.
+*/
+static LocVar *localdebuginfo (FuncState *fs, int vidx) {
+ Vardesc *vd = getlocalvardesc(fs, vidx);
+ if (vd->vd.kind == RDKCTC)
+ return NULL; /* no debug info. for constants */
+ else {
+ int idx = vd->vd.pidx;
+ lua_assert(idx < fs->ndebugvars);
+ return &fs->f->locvars[idx];
+ }
}
+/*
+** Create an expression representing variable 'vidx'
+*/
+static void init_var (FuncState *fs, expdesc *e, int vidx) {
+ e->f = e->t = NO_JUMP;
+ e->k = VLOCAL;
+ e->u.var.vidx = vidx;
+ e->u.var.sidx = getlocalvardesc(fs, vidx)->vd.sidx;
+}
+
+
+/*
+** Raises an error if variable described by 'e' is read only
+*/
+static void check_readonly (LexState *ls, expdesc *e) {
+ FuncState *fs = ls->fs;
+ TString *varname = NULL; /* to be set if variable is const */
+ switch (e->k) {
+ case VCONST: {
+ varname = ls->dyd->actvar.arr[e->u.info].vd.name;
+ break;
+ }
+ case VLOCAL: {
+ Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx);
+ if (vardesc->vd.kind != VDKREG) /* not a regular variable? */
+ varname = vardesc->vd.name;
+ break;
+ }
+ case VUPVAL: {
+ Upvaldesc *up = &fs->f->upvalues[e->u.info];
+ if (up->kind != VDKREG)
+ varname = up->name;
+ break;
+ }
+ default:
+ return; /* other cases cannot be read-only */
+ }
+ if (varname) {
+ const char *msg = luaO_pushfstring(ls->L,
+ "attempt to assign to const variable '%s'", getstr(varname));
+ luaK_semerror(ls, msg); /* error */
+ }
+}
+
+
+/*
+** Start the scope for the last 'nvars' created variables.
+*/
static void adjustlocalvars (LexState *ls, int nvars) {
FuncState *fs = ls->fs;
- fs->nactvar = cast_byte(fs->nactvar + nvars);
- for (; nvars; nvars--) {
- getlocvar(fs, fs->nactvar - nvars)->startpc = fs->pc;
+ int stklevel = luaY_nvarstack(fs);
+ int i;
+ for (i = 0; i < nvars; i++) {
+ int vidx = fs->nactvar++;
+ Vardesc *var = getlocalvardesc(fs, vidx);
+ var->vd.sidx = stklevel++;
+ var->vd.pidx = registerlocalvar(ls, fs, var->vd.name);
}
}
+/*
+** Close the scope for all variables up to level 'tolevel'.
+** (debug info.)
+*/
static void removevars (FuncState *fs, int tolevel) {
fs->ls->dyd->actvar.n -= (fs->nactvar - tolevel);
- while (fs->nactvar > tolevel)
- getlocvar(fs, --fs->nactvar)->endpc = fs->pc;
+ while (fs->nactvar > tolevel) {
+ LocVar *var = localdebuginfo(fs, --fs->nactvar);
+ if (var) /* does it have debug information? */
+ var->endpc = fs->pc;
+ }
}
+/*
+** Search the upvalues of the function 'fs' for one
+** with the given 'name'.
+*/
static int searchupvalue (FuncState *fs, TString *name) {
int i;
Upvaldesc *up = fs->f->upvalues;
@@ -225,7 +349,7 @@ static int searchupvalue (FuncState *fs, TString *name) {
}
-static int newupvalue (FuncState *fs, TString *name, expdesc *v) {
+static Upvaldesc *allocupvalue (FuncState *fs) {
Proto *f = fs->f;
int oldsize = f->sizeupvalues;
checklimit(fs, fs->nups + 1, MAXUPVAL, "upvalues");
@@ -233,58 +357,87 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) {
Upvaldesc, MAXUPVAL, "upvalues");
while (oldsize < f->sizeupvalues)
f->upvalues[oldsize++].name = NULL;
- f->upvalues[fs->nups].instack = (v->k == VLOCAL);
- f->upvalues[fs->nups].idx = cast_byte(v->u.info);
- f->upvalues[fs->nups].name = name;
- luaC_objbarrier(fs->ls->L, f, name);
- return fs->nups++;
+ return &f->upvalues[fs->nups++];
}
-static int searchvar (FuncState *fs, TString *n) {
+static int newupvalue (FuncState *fs, TString *name, expdesc *v) {
+ Upvaldesc *up = allocupvalue(fs);
+ FuncState *prev = fs->prev;
+ if (v->k == VLOCAL) {
+ up->instack = 1;
+ up->idx = v->u.var.sidx;
+ up->kind = getlocalvardesc(prev, v->u.var.vidx)->vd.kind;
+ lua_assert(eqstr(name, getlocalvardesc(prev, v->u.var.vidx)->vd.name));
+ }
+ else {
+ up->instack = 0;
+ up->idx = cast_byte(v->u.info);
+ up->kind = prev->f->upvalues[v->u.info].kind;
+ lua_assert(eqstr(name, prev->f->upvalues[v->u.info].name));
+ }
+ up->name = name;
+ luaC_objbarrier(fs->ls->L, fs->f, name);
+ return fs->nups - 1;
+}
+
+
+/*
+** Look for an active local variable with the name 'n' in the
+** function 'fs'. If found, initialize 'var' with it and return
+** its expression kind; otherwise return -1.
+*/
+static int searchvar (FuncState *fs, TString *n, expdesc *var) {
int i;
for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) {
- if (eqstr(n, getlocvar(fs, i)->varname))
- return i;
+ Vardesc *vd = getlocalvardesc(fs, i);
+ if (eqstr(n, vd->vd.name)) { /* found? */
+ if (vd->vd.kind == RDKCTC) /* compile-time constant? */
+ init_exp(var, VCONST, fs->firstlocal + i);
+ else /* real variable */
+ init_var(fs, var, i);
+ return var->k;
+ }
}
return -1; /* not found */
}
/*
- Mark block where variable at given level was defined
- (to emit close instructions later).
+** Mark block where variable at given level was defined
+** (to emit close instructions later).
*/
static void markupval (FuncState *fs, int level) {
BlockCnt *bl = fs->bl;
while (bl->nactvar > level)
bl = bl->previous;
bl->upval = 1;
+ fs->needclose = 1;
}
/*
- Find variable with given name 'n'. If it is an upvalue, add this
- upvalue into all intermediate functions.
+** Find a variable with the given name 'n'. If it is an upvalue, add
+** this upvalue into all intermediate functions. If it is a global, set
+** 'var' as 'void' as a flag.
*/
static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
if (fs == NULL) /* no more levels? */
init_exp(var, VVOID, 0); /* default is global */
else {
- int v = searchvar(fs, n); /* look up locals at current level */
+ int v = searchvar(fs, n, var); /* look up locals at current level */
if (v >= 0) { /* found? */
- init_exp(var, VLOCAL, v); /* variable is local */
- if (!base)
- markupval(fs, v); /* local will be used as an upval */
+ if (v == VLOCAL && !base)
+ markupval(fs, var->u.var.vidx); /* local will be used as an upval */
}
else { /* not found as local at current level; try upvalues */
int idx = searchupvalue(fs, n); /* try existing upvalues */
if (idx < 0) { /* not found? */
singlevaraux(fs->prev, n, var, 0); /* try upper levels */
- if (var->k == VVOID) /* not found? */
- return; /* it is a global */
- /* else was LOCAL or UPVAL */
- idx = newupvalue(fs, n, var); /* will be a new upvalue */
+ if (var->k == VLOCAL || var->k == VUPVAL) /* local or upvalue? */
+ idx = newupvalue(fs, n, var); /* will be a new upvalue */
+ else /* it is a global or a constant */
+ return; /* don't need to do anything at this level */
}
init_exp(var, VUPVAL, idx); /* new or old upvalue */
}
@@ -292,6 +445,10 @@ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
}
+/*
+** Find a variable with the given name 'n', handling global variables
+** too.
+*/
static void singlevar (LexState *ls, expdesc *var) {
TString *varname = str_checkname(ls);
FuncState *fs = ls->fs;
@@ -300,88 +457,96 @@ static void singlevar (LexState *ls, expdesc *var) {
expdesc key;
singlevaraux(fs, ls->envn, var, 1); /* get environment variable */
lua_assert(var->k != VVOID); /* this one must exist */
- codestring(ls, &key, varname); /* key is variable name */
+ codestring(&key, varname); /* key is variable name */
luaK_indexed(fs, var, &key); /* env[varname] */
}
}
+/*
+** Adjust the number of results from an expression list 'e' with 'nexps'
+** expressions to 'nvars' values.
+*/
static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) {
FuncState *fs = ls->fs;
- int extra = nvars - nexps;
- if (hasmultret(e->k)) {
- extra++; /* includes call itself */
- if (extra < 0) extra = 0;
+ int needed = nvars - nexps; /* extra values needed */
+ if (hasmultret(e->k)) { /* last expression has multiple returns? */
+ int extra = needed + 1; /* discount last expression itself */
+ if (extra < 0)
+ extra = 0;
luaK_setreturns(fs, e, extra); /* last exp. provides the difference */
- if (extra > 1) luaK_reserveregs(fs, extra-1);
}
else {
- if (e->k != VVOID) luaK_exp2nextreg(fs, e); /* close last expression */
- if (extra > 0) {
- int reg = fs->freereg;
- luaK_reserveregs(fs, extra);
- luaK_nil(fs, reg, extra);
- }
+ if (e->k != VVOID) /* at least one expression? */
+ luaK_exp2nextreg(fs, e); /* close last expression */
+ if (needed > 0) /* missing values? */
+ luaK_nil(fs, fs->freereg, needed); /* complete with nils */
}
- if (nexps > nvars)
- ls->fs->freereg -= nexps - nvars; /* remove extra values */
+ if (needed > 0)
+ luaK_reserveregs(fs, needed); /* registers for extra values */
+ else /* adding 'needed' is actually a subtraction */
+ fs->freereg += needed; /* remove extra values */
}
-static void enterlevel (LexState *ls) {
- lua_State *L = ls->L;
- ++L->nCcalls;
- checklimit(ls->fs, L->nCcalls, LUAI_MAXCCALLS, "C levels");
-}
+/*
+** Macros to limit the maximum recursion depth while parsing
+*/
+#define enterlevel(ls) luaE_enterCcall((ls)->L)
+
+#define leavelevel(ls) luaE_exitCcall((ls)->L)
-#define leavelevel(ls) ((ls)->L->nCcalls--)
+/*
+** Generates an error that a goto jumps into the scope of some
+** local variable.
+*/
+static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) {
+ const char *varname = getstr(getlocalvardesc(ls->fs, gt->nactvar)->vd.name);
+ const char *msg = "<goto %s> at line %d jumps into the scope of local '%s'";
+ msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line, varname);
+ luaK_semerror(ls, msg); /* raise the error */
+}
-static void closegoto (LexState *ls, int g, Labeldesc *label) {
+/*
+** Solves the goto at index 'g' to given 'label' and removes it
+** from the list of pending goto's.
+** If it jumps into the scope of some variable, raises an error.
+*/
+static void solvegoto (LexState *ls, int g, Labeldesc *label) {
int i;
- FuncState *fs = ls->fs;
- Labellist *gl = &ls->dyd->gt;
- Labeldesc *gt = &gl->arr[g];
+ Labellist *gl = &ls->dyd->gt; /* list of goto's */
+ Labeldesc *gt = &gl->arr[g]; /* goto to be resolved */
lua_assert(eqstr(gt->name, label->name));
- if (gt->nactvar < label->nactvar) {
- TString *vname = getlocvar(fs, gt->nactvar)->varname;
- const char *msg = luaO_pushfstring(ls->L,
- "<goto %s> at line %d jumps into the scope of local '%s'",
- getstr(gt->name), gt->line, getstr(vname));
- semerror(ls, msg);
- }
- luaK_patchlist(fs, gt->pc, label->pc);
- /* remove goto from pending list */
- for (i = g; i < gl->n - 1; i++)
+ if (unlikely(gt->nactvar < label->nactvar)) /* enter some scope? */
+ jumpscopeerror(ls, gt);
+ luaK_patchlist(ls->fs, gt->pc, label->pc);
+ for (i = g; i < gl->n - 1; i++) /* remove goto from pending list */
gl->arr[i] = gl->arr[i + 1];
gl->n--;
}
/*
-** try to close a goto with existing labels; this solves backward jumps
+** Search for an active label with the given name.
*/
-static int findlabel (LexState *ls, int g) {
+static Labeldesc *findlabel (LexState *ls, TString *name) {
int i;
- BlockCnt *bl = ls->fs->bl;
Dyndata *dyd = ls->dyd;
- Labeldesc *gt = &dyd->gt.arr[g];
- /* check labels in current block for a match */
- for (i = bl->firstlabel; i < dyd->label.n; i++) {
+ /* check labels in current function for a match */
+ for (i = ls->fs->firstlabel; i < dyd->label.n; i++) {
Labeldesc *lb = &dyd->label.arr[i];
- if (eqstr(lb->name, gt->name)) { /* correct label? */
- if (gt->nactvar > lb->nactvar &&
- (bl->upval || dyd->label.n > bl->firstlabel))
- luaK_patchclose(ls->fs, gt->pc, lb->nactvar);
- closegoto(ls, g, lb); /* close it */
- return 1;
- }
+ if (eqstr(lb->name, name)) /* correct label? */
+ return lb;
}
- return 0; /* label not found; cannot close goto */
+ return NULL; /* label not found */
}
+/*
+** Adds a new label/goto in the corresponding list.
+*/
static int newlabelentry (LexState *ls, Labellist *l, TString *name,
int line, int pc) {
int n = l->n;
@@ -390,48 +555,76 @@ static int newlabelentry (LexState *ls, Labellist *l, TString *name,
l->arr[n].name = name;
l->arr[n].line = line;
l->arr[n].nactvar = ls->fs->nactvar;
+ l->arr[n].close = 0;
l->arr[n].pc = pc;
l->n = n + 1;
return n;
}
+static int newgotoentry (LexState *ls, TString *name, int line, int pc) {
+ return newlabelentry(ls, &ls->dyd->gt, name, line, pc);
+}
+
+
/*
-** check whether new label 'lb' matches any pending gotos in current
-** block; solves forward jumps
+** Solves forward jumps. Check whether new label 'lb' matches any
+** pending gotos in current block and solves them. Return true
+** if any of the goto's need to close upvalues.
*/
-static void findgotos (LexState *ls, Labeldesc *lb) {
+static int solvegotos (LexState *ls, Labeldesc *lb) {
Labellist *gl = &ls->dyd->gt;
int i = ls->fs->bl->firstgoto;
+ int needsclose = 0;
while (i < gl->n) {
- if (eqstr(gl->arr[i].name, lb->name))
- closegoto(ls, i, lb);
+ if (eqstr(gl->arr[i].name, lb->name)) {
+ needsclose |= gl->arr[i].close;
+ solvegoto(ls, i, lb); /* will remove 'i' from the list */
+ }
else
i++;
}
+ return needsclose;
}
/*
-** export pending gotos to outer level, to check them against
-** outer labels; if the block being exited has upvalues, and
-** the goto exits the scope of any variable (which can be the
-** upvalue), close those variables being exited.
+** Create a new label with the given 'name' at the given 'line'.
+** 'last' tells whether label is the last non-op statement in its
+** block. Solves all pending goto's to this new label and adds
+** a close instruction if necessary.
+** Returns true iff it added a close instruction.
+*/
+static int createlabel (LexState *ls, TString *name, int line,
+ int last) {
+ FuncState *fs = ls->fs;
+ Labellist *ll = &ls->dyd->label;
+ int l = newlabelentry(ls, ll, name, line, luaK_getlabel(fs));
+ if (last) { /* label is last no-op statement in the block? */
+ /* assume that locals are already out of scope */
+ ll->arr[l].nactvar = fs->bl->nactvar;
+ }
+ if (solvegotos(ls, &ll->arr[l])) { /* need close? */
+ luaK_codeABC(fs, OP_CLOSE, luaY_nvarstack(fs), 0, 0);
+ return 1;
+ }
+ return 0;
+}
+
+
+/*
+** Adjust pending gotos to outer level of a block.
*/
static void movegotosout (FuncState *fs, BlockCnt *bl) {
- int i = bl->firstgoto;
+ int i;
Labellist *gl = &fs->ls->dyd->gt;
- /* correct pending gotos to current block and try to close it
- with visible labels */
- while (i < gl->n) {
+ /* correct pending gotos to current block */
+ for (i = bl->firstgoto; i < gl->n; i++) { /* for each pending goto */
Labeldesc *gt = &gl->arr[i];
- if (gt->nactvar > bl->nactvar) {
- if (bl->upval)
- luaK_patchclose(fs, gt->pc, bl->nactvar);
- gt->nactvar = bl->nactvar;
- }
- if (!findlabel(fs->ls, i))
- i++; /* move to next one */
+ /* leaving a variable scope? */
+ if (stacklevel(fs, gt->nactvar) > stacklevel(fs, bl->nactvar))
+ gt->close |= bl->upval; /* jump may need a close */
+ gt->nactvar = bl->nactvar; /* update goto level */
}
}
@@ -442,54 +635,50 @@ static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) {
bl->firstlabel = fs->ls->dyd->label.n;
bl->firstgoto = fs->ls->dyd->gt.n;
bl->upval = 0;
+ bl->insidetbc = (fs->bl != NULL && fs->bl->insidetbc);
bl->previous = fs->bl;
fs->bl = bl;
- lua_assert(fs->freereg == fs->nactvar);
+ lua_assert(fs->freereg == luaY_nvarstack(fs));
}
/*
-** create a label named 'break' to resolve break statements
-*/
-static void breaklabel (LexState *ls) {
- TString *n = luaS_new(ls->L, "break");
- int l = newlabelentry(ls, &ls->dyd->label, n, 0, ls->fs->pc);
- findgotos(ls, &ls->dyd->label.arr[l]);
-}
-
-/*
-** generates an error for an undefined 'goto'; choose appropriate
-** message when label name is a reserved word (which can only be 'break')
+** generates an error for an undefined 'goto'.
*/
static l_noret undefgoto (LexState *ls, Labeldesc *gt) {
- const char *msg = isreserved(gt->name)
- ? "<%s> at line %d not inside a loop"
- : "no visible label '%s' for <goto> at line %d";
- msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line);
- semerror(ls, msg);
+ const char *msg;
+ if (eqstr(gt->name, luaS_newliteral(ls->L, "break"))) {
+ msg = "break outside loop at line %d";
+ msg = luaO_pushfstring(ls->L, msg, gt->line);
+ }
+ else {
+ msg = "no visible label '%s' for <goto> at line %d";
+ msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line);
+ }
+ luaK_semerror(ls, msg);
}
static void leaveblock (FuncState *fs) {
BlockCnt *bl = fs->bl;
LexState *ls = fs->ls;
- if (bl->previous && bl->upval) {
- /* create a 'jump to here' to close upvalues */
- int j = luaK_jump(fs);
- luaK_patchclose(fs, j, bl->nactvar);
- luaK_patchtohere(fs, j);
- }
- if (bl->isloop)
- breaklabel(ls); /* close pending breaks */
+ int hasclose = 0;
+ int stklevel = stacklevel(fs, bl->nactvar); /* level outside the block */
+ if (bl->isloop) /* fix pending breaks? */
+ hasclose = createlabel(ls, luaS_newliteral(ls->L, "break"), 0, 0);
+ if (!hasclose && bl->previous && bl->upval)
+ luaK_codeABC(fs, OP_CLOSE, stklevel, 0, 0);
fs->bl = bl->previous;
removevars(fs, bl->nactvar);
lua_assert(bl->nactvar == fs->nactvar);
- fs->freereg = fs->nactvar; /* free registers */
+ fs->freereg = stklevel; /* free registers */
ls->dyd->label.n = bl->firstlabel; /* remove local labels */
if (bl->previous) /* inner block? */
movegotosout(fs, bl); /* update pending gotos to outer block */
- else if (bl->firstgoto < ls->dyd->gt.n) /* pending gotos in outer block? */
- undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */
+ else {
+ if (bl->firstgoto < ls->dyd->gt.n) /* pending gotos in outer block? */
+ undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */
+ }
}
@@ -515,35 +704,40 @@ static Proto *addprototype (LexState *ls) {
/*
** codes instruction to create new closure in parent function.
-** The OP_CLOSURE instruction must use the last available register,
+** The OP_CLOSURE instruction uses the last available register,
** so that, if it invokes the GC, the GC knows which registers
** are in use at that time.
+
*/
static void codeclosure (LexState *ls, expdesc *v) {
FuncState *fs = ls->fs->prev;
- init_exp(v, VRELOCABLE, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np - 1));
+ init_exp(v, VRELOC, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np - 1));
luaK_exp2nextreg(fs, v); /* fix it at the last register */
}
static void open_func (LexState *ls, FuncState *fs, BlockCnt *bl) {
- Proto *f;
+ Proto *f = fs->f;
fs->prev = ls->fs; /* linked list of funcstates */
fs->ls = ls;
ls->fs = fs;
fs->pc = 0;
+ fs->previousline = f->linedefined;
+ fs->iwthabs = 0;
fs->lasttarget = 0;
- fs->jpc = NO_JUMP;
fs->freereg = 0;
fs->nk = 0;
+ fs->nabslineinfo = 0;
fs->np = 0;
fs->nups = 0;
- fs->nlocvars = 0;
+ fs->ndebugvars = 0;
fs->nactvar = 0;
+ fs->needclose = 0;
fs->firstlocal = ls->dyd->actvar.n;
+ fs->firstlabel = ls->dyd->label.n;
fs->bl = NULL;
- f = fs->f;
f->source = ls->source;
+ luaC_objbarrier(ls->L, f, f->source);
f->maxstacksize = 2; /* registers 0/1 are always valid */
enterblock(fs, bl, 0);
}
@@ -553,21 +747,18 @@ static void close_func (LexState *ls) {
lua_State *L = ls->L;
FuncState *fs = ls->fs;
Proto *f = fs->f;
- luaK_ret(fs, 0, 0); /* final return */
+ luaK_ret(fs, luaY_nvarstack(fs), 0); /* final return */
leaveblock(fs);
- luaM_reallocvector(L, f->code, f->sizecode, fs->pc, Instruction);
- f->sizecode = fs->pc;
- luaM_reallocvector(L, f->lineinfo, f->sizelineinfo, fs->pc, int);
- f->sizelineinfo = fs->pc;
- luaM_reallocvector(L, f->k, f->sizek, fs->nk, TValue);
- f->sizek = fs->nk;
- luaM_reallocvector(L, f->p, f->sizep, fs->np, Proto *);
- f->sizep = fs->np;
- luaM_reallocvector(L, f->locvars, f->sizelocvars, fs->nlocvars, LocVar);
- f->sizelocvars = fs->nlocvars;
- luaM_reallocvector(L, f->upvalues, f->sizeupvalues, fs->nups, Upvaldesc);
- f->sizeupvalues = fs->nups;
lua_assert(fs->bl == NULL);
+ luaK_finish(fs);
+ luaM_shrinkvector(L, f->code, f->sizecode, fs->pc, Instruction);
+ luaM_shrinkvector(L, f->lineinfo, f->sizelineinfo, fs->pc, ls_byte);
+ luaM_shrinkvector(L, f->abslineinfo, f->sizeabslineinfo,
+ fs->nabslineinfo, AbsLineInfo);
+ luaM_shrinkvector(L, f->k, f->sizek, fs->nk, TValue);
+ luaM_shrinkvector(L, f->p, f->sizep, fs->np, Proto *);
+ luaM_shrinkvector(L, f->locvars, f->sizelocvars, fs->ndebugvars, LocVar);
+ luaM_shrinkvector(L, f->upvalues, f->sizeupvalues, fs->nups, Upvaldesc);
ls->fs = fs->prev;
luaC_checkGC(L);
}
@@ -613,7 +804,7 @@ static void fieldsel (LexState *ls, expdesc *v) {
expdesc key;
luaK_exp2anyregup(fs, v);
luaX_next(ls); /* skip the dot or colon */
- checkname(ls, &key);
+ codename(ls, &key);
luaK_indexed(fs, v, &key);
}
@@ -634,48 +825,49 @@ static void yindex (LexState *ls, expdesc *v) {
*/
-struct ConsControl {
+typedef struct ConsControl {
expdesc v; /* last list item read */
expdesc *t; /* table descriptor */
int nh; /* total number of 'record' elements */
- int na; /* total number of array elements */
+ int na; /* number of array elements already stored */
int tostore; /* number of array elements pending to be stored */
-};
+} ConsControl;
-static void recfield (LexState *ls, struct ConsControl *cc) {
- /* recfield -> (NAME | '['exp1']') = exp1 */
+static void recfield (LexState *ls, ConsControl *cc) {
+ /* recfield -> (NAME | '['exp']') = exp */
FuncState *fs = ls->fs;
int reg = ls->fs->freereg;
- expdesc key, val;
- int rkkey;
+ expdesc tab, key, val;
if (ls->t.token == TK_NAME) {
checklimit(fs, cc->nh, MAX_INT, "items in a constructor");
- checkname(ls, &key);
+ codename(ls, &key);
}
else /* ls->t.token == '[' */
yindex(ls, &key);
cc->nh++;
checknext(ls, '=');
- rkkey = luaK_exp2RK(fs, &key);
+ tab = *cc->t;
+ luaK_indexed(fs, &tab, &key);
expr(ls, &val);
- luaK_codeABC(fs, OP_SETTABLE, cc->t->u.info, rkkey, luaK_exp2RK(fs, &val));
+ luaK_storevar(fs, &tab, &val);
fs->freereg = reg; /* free registers */
}
-static void closelistfield (FuncState *fs, struct ConsControl *cc) {
+static void closelistfield (FuncState *fs, ConsControl *cc) {
if (cc->v.k == VVOID) return; /* there is no list item */
luaK_exp2nextreg(fs, &cc->v);
cc->v.k = VVOID;
if (cc->tostore == LFIELDS_PER_FLUSH) {
luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore); /* flush */
+ cc->na += cc->tostore;
cc->tostore = 0; /* no more items pending */
}
}
-static void lastlistfield (FuncState *fs, struct ConsControl *cc) {
+static void lastlistfield (FuncState *fs, ConsControl *cc) {
if (cc->tostore == 0) return;
if (hasmultret(cc->v.k)) {
luaK_setmultret(fs, &cc->v);
@@ -687,19 +879,18 @@ static void lastlistfield (FuncState *fs, struct ConsControl *cc) {
luaK_exp2nextreg(fs, &cc->v);
luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore);
}
+ cc->na += cc->tostore;
}
-static void listfield (LexState *ls, struct ConsControl *cc) {
+static void listfield (LexState *ls, ConsControl *cc) {
/* listfield -> exp */
expr(ls, &cc->v);
- checklimit(ls->fs, cc->na, MAX_INT, "items in a constructor");
- cc->na++;
cc->tostore++;
}
-static void field (LexState *ls, struct ConsControl *cc) {
+static void field (LexState *ls, ConsControl *cc) {
/* field -> listfield | recfield */
switch(ls->t.token) {
case TK_NAME: { /* may be 'listfield' or 'recfield' */
@@ -727,12 +918,13 @@ static void constructor (LexState *ls, expdesc *t) {
FuncState *fs = ls->fs;
int line = ls->linenumber;
int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0);
- struct ConsControl cc;
+ ConsControl cc;
+ luaK_code(fs, 0); /* space for extra arg. */
cc.na = cc.nh = cc.tostore = 0;
cc.t = t;
- init_exp(t, VRELOCABLE, pc);
+ init_exp(t, VNONRELOC, fs->freereg); /* table will be at stack top */
+ luaK_reserveregs(fs, 1);
init_exp(&cc.v, VVOID, 0); /* no value (yet) */
- luaK_exp2nextreg(ls->fs, t); /* fix it at stack top */
checknext(ls, '{');
do {
lua_assert(cc.v.k == VVOID || cc.tostore > 0);
@@ -742,20 +934,24 @@ static void constructor (LexState *ls, expdesc *t) {
} while (testnext(ls, ',') || testnext(ls, ';'));
check_match(ls, '}', '{', line);
lastlistfield(fs, &cc);
- SETARG_B(fs->f->code[pc], luaO_int2fb(cc.na)); /* set initial array size */
- SETARG_C(fs->f->code[pc], luaO_int2fb(cc.nh)); /* set initial table size */
+ luaK_settablesize(fs, pc, t->u.info, cc.na, cc.nh);
}
/* }====================================================================== */
+static void setvararg (FuncState *fs, int nparams) {
+ fs->f->is_vararg = 1;
+ luaK_codeABC(fs, OP_VARARGPREP, nparams, 0, 0);
+}
+
static void parlist (LexState *ls) {
/* parlist -> [ param { ',' param } ] */
FuncState *fs = ls->fs;
Proto *f = fs->f;
int nparams = 0;
- f->is_vararg = 0;
+ int isvararg = 0;
if (ls->t.token != ')') { /* is 'parlist' not empty? */
do {
switch (ls->t.token) {
@@ -766,16 +962,18 @@ static void parlist (LexState *ls) {
}
case TK_DOTS: { /* param -> '...' */
luaX_next(ls);
- f->is_vararg = 1; /* declared vararg */
+ isvararg = 1;
break;
}
default: luaX_syntaxerror(ls, "<name> or '...' expected");
}
- } while (!f->is_vararg && testnext(ls, ','));
+ } while (!isvararg && testnext(ls, ','));
}
adjustlocalvars(ls, nparams);
f->numparams = cast_byte(fs->nactvar);
- luaK_reserveregs(fs, fs->nactvar); /* reserve register for parameters */
+ if (isvararg)
+ setvararg(fs, f->numparams); /* declared vararg */
+ luaK_reserveregs(fs, fs->nactvar); /* reserve registers for parameters */
}
@@ -825,7 +1023,8 @@ static void funcargs (LexState *ls, expdesc *f, int line) {
args.k = VVOID;
else {
explist(ls, &args);
- luaK_setmultret(fs, &args);
+ if (hasmultret(args.k))
+ luaK_setmultret(fs, &args);
}
check_match(ls, ')', '(', line);
break;
@@ -835,7 +1034,7 @@ static void funcargs (LexState *ls, expdesc *f, int line) {
break;
}
case TK_STRING: { /* funcargs -> STRING */
- codestring(ls, &args, ls->t.seminfo.ts);
+ codestring(&args, ls->t.seminfo.ts);
luaX_next(ls); /* must use 'seminfo' before 'next' */
break;
}
@@ -902,7 +1101,7 @@ static void suffixedexp (LexState *ls, expdesc *v) {
fieldsel(ls, v);
break;
}
- case '[': { /* '[' exp1 ']' */
+ case '[': { /* '[' exp ']' */
expdesc key;
luaK_exp2anyregup(fs, v);
yindex(ls, &key);
@@ -912,7 +1111,7 @@ static void suffixedexp (LexState *ls, expdesc *v) {
case ':': { /* ':' NAME funcargs */
expdesc key;
luaX_next(ls);
- checkname(ls, &key);
+ codename(ls, &key);
luaK_self(fs, v, &key);
funcargs(ls, v, line);
break;
@@ -943,7 +1142,7 @@ static void simpleexp (LexState *ls, expdesc *v) {
break;
}
case TK_STRING: {
- codestring(ls, v, ls->t.seminfo.ts);
+ codestring(v, ls->t.seminfo.ts);
break;
}
case TK_NIL: {
@@ -962,7 +1161,7 @@ static void simpleexp (LexState *ls, expdesc *v) {
FuncState *fs = ls->fs;
check_condition(ls, fs->f->is_vararg,
"cannot use '...' outside a vararg function");
- init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 1, 0));
+ init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 0, 1));
break;
}
case '{': { /* constructor */
@@ -1022,6 +1221,9 @@ static BinOpr getbinopr (int op) {
}
+/*
+** Priority table for binary operators.
+*/
static const struct {
lu_byte left; /* left priority for each binary operator */
lu_byte right; /* right priority */
@@ -1050,9 +1252,9 @@ static BinOpr subexpr (LexState *ls, expdesc *v, int limit) {
UnOpr uop;
enterlevel(ls);
uop = getunopr(ls->t.token);
- if (uop != OPR_NOUNOPR) {
+ if (uop != OPR_NOUNOPR) { /* prefix (unary) operator? */
int line = ls->linenumber;
- luaX_next(ls);
+ luaX_next(ls); /* skip operator */
subexpr(ls, v, UNARY_PRIORITY);
luaK_prefix(ls->fs, uop, v, line);
}
@@ -1063,7 +1265,7 @@ static BinOpr subexpr (LexState *ls, expdesc *v, int limit) {
expdesc v2;
BinOpr nextop;
int line = ls->linenumber;
- luaX_next(ls);
+ luaX_next(ls); /* skip operator */
luaK_infix(ls->fs, op, v);
/* read sub-expression with higher priority */
nextop = subexpr(ls, &v2, priority[op].right);
@@ -1121,43 +1323,60 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
int extra = fs->freereg; /* eventual position to save local variable */
int conflict = 0;
for (; lh; lh = lh->prev) { /* check all previous assignments */
- if (lh->v.k == VINDEXED) { /* assigning to a table? */
- /* table is the upvalue/local being assigned now? */
- if (lh->v.u.ind.vt == v->k && lh->v.u.ind.t == v->u.info) {
- conflict = 1;
- lh->v.u.ind.vt = VLOCAL;
- lh->v.u.ind.t = extra; /* previous assignment will use safe copy */
+ if (vkisindexed(lh->v.k)) { /* assignment to table field? */
+ if (lh->v.k == VINDEXUP) { /* is table an upvalue? */
+ if (v->k == VUPVAL && lh->v.u.ind.t == v->u.info) {
+ conflict = 1; /* table is the upvalue being assigned now */
+ lh->v.k = VINDEXSTR;
+ lh->v.u.ind.t = extra; /* assignment will use safe copy */
+ }
}
- /* index is the local being assigned? (index cannot be upvalue) */
- if (v->k == VLOCAL && lh->v.u.ind.idx == v->u.info) {
- conflict = 1;
- lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */
+ else { /* table is a register */
+ if (v->k == VLOCAL && lh->v.u.ind.t == v->u.var.sidx) {
+ conflict = 1; /* table is the local being assigned now */
+ lh->v.u.ind.t = extra; /* assignment will use safe copy */
+ }
+ /* is index the local being assigned? */
+ if (lh->v.k == VINDEXED && v->k == VLOCAL &&
+ lh->v.u.ind.idx == v->u.var.sidx) {
+ conflict = 1;
+ lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */
+ }
}
}
}
if (conflict) {
/* copy upvalue/local value to a temporary (in position 'extra') */
- OpCode op = (v->k == VLOCAL) ? OP_MOVE : OP_GETUPVAL;
- luaK_codeABC(fs, op, extra, v->u.info, 0);
+ if (v->k == VLOCAL)
+ luaK_codeABC(fs, OP_MOVE, extra, v->u.var.sidx, 0);
+ else
+ luaK_codeABC(fs, OP_GETUPVAL, extra, v->u.info, 0);
luaK_reserveregs(fs, 1);
}
}
-
-static void assignment (LexState *ls, struct LHS_assign *lh, int nvars) {
+/*
+** Parse and compile a multiple assignment. The first "variable"
+** (a 'suffixedexp') was already read by the caller.
+**
+** assignment -> suffixedexp restassign
+** restassign -> ',' suffixedexp restassign | '=' explist
+*/
+static void restassign (LexState *ls, struct LHS_assign *lh, int nvars) {
expdesc e;
check_condition(ls, vkisvar(lh->v.k), "syntax error");
- if (testnext(ls, ',')) { /* assignment -> ',' suffixedexp assignment */
+ check_readonly(ls, &lh->v);
+ if (testnext(ls, ',')) { /* restassign -> ',' suffixedexp restassign */
struct LHS_assign nv;
nv.prev = lh;
suffixedexp(ls, &nv.v);
- if (nv.v.k != VINDEXED)
+ if (!vkisindexed(nv.v.k))
check_conflict(ls, lh, &nv.v);
- checklimit(ls->fs, nvars + ls->L->nCcalls, LUAI_MAXCCALLS,
- "C levels");
- assignment(ls, &nv, nvars+1);
+ enterlevel(ls); /* control recursion depth */
+ restassign(ls, &nv, nvars+1);
+ leavelevel(ls);
}
- else { /* assignment -> '=' explist */
+ else { /* restassign -> '=' explist */
int nexps;
checknext(ls, '=');
nexps = explist(ls, &e);
@@ -1184,57 +1403,55 @@ static int cond (LexState *ls) {
}
-static void gotostat (LexState *ls, int pc) {
+static void gotostat (LexState *ls) {
+ FuncState *fs = ls->fs;
int line = ls->linenumber;
- TString *label;
- int g;
- if (testnext(ls, TK_GOTO))
- label = str_checkname(ls);
- else {
- luaX_next(ls); /* skip break */
- label = luaS_new(ls->L, "break");
+ TString *name = str_checkname(ls); /* label's name */
+ Labeldesc *lb = findlabel(ls, name);
+ if (lb == NULL) /* no label? */
+ /* forward jump; will be resolved when the label is declared */
+ newgotoentry(ls, name, line, luaK_jump(fs));
+ else { /* found a label */
+ /* backward jump; will be resolved here */
+ int lblevel = stacklevel(fs, lb->nactvar); /* label level */
+ if (luaY_nvarstack(fs) > lblevel) /* leaving the scope of a variable? */
+ luaK_codeABC(fs, OP_CLOSE, lblevel, 0, 0);
+ /* create jump and link it to the label */
+ luaK_patchlist(fs, luaK_jump(fs), lb->pc);
}
- g = newlabelentry(ls, &ls->dyd->gt, label, line, pc);
- findlabel(ls, g); /* close it if label already defined */
}
-/* check for repeated labels on the same block */
-static void checkrepeated (FuncState *fs, Labellist *ll, TString *label) {
- int i;
- for (i = fs->bl->firstlabel; i < ll->n; i++) {
- if (eqstr(label, ll->arr[i].name)) {
- const char *msg = luaO_pushfstring(fs->ls->L,
- "label '%s' already defined on line %d",
- getstr(label), ll->arr[i].line);
- semerror(fs->ls, msg);
- }
- }
+/*
+** Break statement. Semantically equivalent to "goto break".
+*/
+static void breakstat (LexState *ls) {
+ int line = ls->linenumber;
+ luaX_next(ls); /* skip break */
+ newgotoentry(ls, luaS_newliteral(ls->L, "break"), line, luaK_jump(ls->fs));
}
-/* skip no-op statements */
-static void skipnoopstat (LexState *ls) {
- while (ls->t.token == ';' || ls->t.token == TK_DBCOLON)
- statement(ls);
+/*
+** Check whether there is already a label with the given 'name'.
+*/
+static void checkrepeated (LexState *ls, TString *name) {
+ Labeldesc *lb = findlabel(ls, name);
+ if (unlikely(lb != NULL)) { /* already defined? */
+ const char *msg = "label '%s' already defined on line %d";
+ msg = luaO_pushfstring(ls->L, msg, getstr(name), lb->line);
+ luaK_semerror(ls, msg); /* error */
+ }
}
-static void labelstat (LexState *ls, TString *label, int line) {
+static void labelstat (LexState *ls, TString *name, int line) {
/* label -> '::' NAME '::' */
- FuncState *fs = ls->fs;
- Labellist *ll = &ls->dyd->label;
- int l; /* index of new label being created */
- checkrepeated(fs, ll, label); /* check for repeated labels */
checknext(ls, TK_DBCOLON); /* skip double colon */
- /* create new entry for this label */
- l = newlabelentry(ls, ll, label, line, luaK_getlabel(fs));
- skipnoopstat(ls); /* skip other no-op statements */
- if (block_follow(ls, 0)) { /* label is last no-op statement in the block? */
- /* assume that locals are already out of scope */
- ll->arr[l].nactvar = fs->bl->nactvar;
- }
- findgotos(ls, &ll->arr[l]);
+ while (ls->t.token == ';' || ls->t.token == TK_DBCOLON)
+ statement(ls); /* skip other no-op statements */
+ checkrepeated(ls, name); /* check for repeated labels */
+ createlabel(ls, name, line, block_follow(ls, 0));
}
@@ -1269,58 +1486,83 @@ static void repeatstat (LexState *ls, int line) {
statlist(ls);
check_match(ls, TK_UNTIL, TK_REPEAT, line);
condexit = cond(ls); /* read condition (inside scope block) */
- if (bl2.upval) /* upvalues? */
- luaK_patchclose(fs, condexit, bl2.nactvar);
leaveblock(fs); /* finish scope */
+ if (bl2.upval) { /* upvalues? */
+ int exit = luaK_jump(fs); /* normal exit must jump over fix */
+ luaK_patchtohere(fs, condexit); /* repetition must close upvalues */
+ luaK_codeABC(fs, OP_CLOSE, stacklevel(fs, bl2.nactvar), 0, 0);
+ condexit = luaK_jump(fs); /* repeat after closing upvalues */
+ luaK_patchtohere(fs, exit); /* normal exit comes to here */
+ }
luaK_patchlist(fs, condexit, repeat_init); /* close the loop */
leaveblock(fs); /* finish loop */
}
-static int exp1 (LexState *ls) {
+/*
+** Read an expression and generate code to put its results in next
+** stack slot.
+**
+*/
+static void exp1 (LexState *ls) {
expdesc e;
- int reg;
expr(ls, &e);
luaK_exp2nextreg(ls->fs, &e);
lua_assert(e.k == VNONRELOC);
- reg = e.u.info;
- return reg;
}
-static void forbody (LexState *ls, int base, int line, int nvars, int isnum) {
+/*
+** Fix for instruction at position 'pc' to jump to 'dest'.
+** (Jump addresses are relative in Lua). 'back' true means
+** a back jump.
+*/
+static void fixforjump (FuncState *fs, int pc, int dest, int back) {
+ Instruction *jmp = &fs->f->code[pc];
+ int offset = dest - (pc + 1);
+ if (back)
+ offset = -offset;
+ if (unlikely(offset > MAXARG_Bx))
+ luaX_syntaxerror(fs->ls, "control structure too long");
+ SETARG_Bx(*jmp, offset);
+}
+
+
+/*
+** Generate code for a 'for' loop.
+*/
+static void forbody (LexState *ls, int base, int line, int nvars, int isgen) {
/* forbody -> DO block */
+ static const OpCode forprep[2] = {OP_FORPREP, OP_TFORPREP};
+ static const OpCode forloop[2] = {OP_FORLOOP, OP_TFORLOOP};
BlockCnt bl;
FuncState *fs = ls->fs;
int prep, endfor;
- adjustlocalvars(ls, 3); /* control variables */
checknext(ls, TK_DO);
- prep = isnum ? luaK_codeAsBx(fs, OP_FORPREP, base, NO_JUMP) : luaK_jump(fs);
+ prep = luaK_codeABx(fs, forprep[isgen], base, 0);
enterblock(fs, &bl, 0); /* scope for declared variables */
adjustlocalvars(ls, nvars);
luaK_reserveregs(fs, nvars);
block(ls);
leaveblock(fs); /* end of scope for declared variables */
- luaK_patchtohere(fs, prep);
- if (isnum) /* numeric for? */
- endfor = luaK_codeAsBx(fs, OP_FORLOOP, base, NO_JUMP);
- else { /* generic for */
+ fixforjump(fs, prep, luaK_getlabel(fs), 0);
+ if (isgen) { /* generic for? */
luaK_codeABC(fs, OP_TFORCALL, base, 0, nvars);
luaK_fixline(fs, line);
- endfor = luaK_codeAsBx(fs, OP_TFORLOOP, base + 2, NO_JUMP);
}
- luaK_patchlist(fs, endfor, prep + 1);
+ endfor = luaK_codeABx(fs, forloop[isgen], base, 0);
+ fixforjump(fs, endfor, prep + 1, 1);
luaK_fixline(fs, line);
}
static void fornum (LexState *ls, TString *varname, int line) {
- /* fornum -> NAME = exp1,exp1[,exp1] forbody */
+ /* fornum -> NAME = exp,exp[,exp] forbody */
FuncState *fs = ls->fs;
int base = fs->freereg;
- new_localvarliteral(ls, "(for index)");
- new_localvarliteral(ls, "(for limit)");
- new_localvarliteral(ls, "(for step)");
+ new_localvarliteral(ls, "(for state)");
+ new_localvarliteral(ls, "(for state)");
+ new_localvarliteral(ls, "(for state)");
new_localvar(ls, varname);
checknext(ls, '=');
exp1(ls); /* initial value */
@@ -1329,10 +1571,11 @@ static void fornum (LexState *ls, TString *varname, int line) {
if (testnext(ls, ','))
exp1(ls); /* optional step */
else { /* default step = 1 */
- luaK_codek(fs, fs->freereg, luaK_intK(fs, 1));
+ luaK_int(fs, fs->freereg, 1);
luaK_reserveregs(fs, 1);
}
- forbody(ls, base, line, 1, 1);
+ adjustlocalvars(ls, 3); /* control variables */
+ forbody(ls, base, line, 1, 0);
}
@@ -1340,13 +1583,14 @@ static void forlist (LexState *ls, TString *indexname) {
/* forlist -> NAME {,NAME} IN explist forbody */
FuncState *fs = ls->fs;
expdesc e;
- int nvars = 4; /* gen, state, control, plus at least one declared var */
+ int nvars = 5; /* gen, state, control, toclose, 'indexname' */
int line;
int base = fs->freereg;
/* create control variables */
- new_localvarliteral(ls, "(for generator)");
new_localvarliteral(ls, "(for state)");
- new_localvarliteral(ls, "(for control)");
+ new_localvarliteral(ls, "(for state)");
+ new_localvarliteral(ls, "(for state)");
+ new_localvarliteral(ls, "(for state)");
/* create declared variables */
new_localvar(ls, indexname);
while (testnext(ls, ',')) {
@@ -1355,9 +1599,11 @@ static void forlist (LexState *ls, TString *indexname) {
}
checknext(ls, TK_IN);
line = ls->linenumber;
- adjust_assign(ls, 3, explist(ls, &e), &e);
+ adjust_assign(ls, 4, explist(ls, &e), &e);
+ adjustlocalvars(ls, 4); /* control variables */
+ markupval(fs, fs->nactvar); /* last control var. must be closed */
luaK_checkstack(fs, 3); /* extra space to call generator */
- forbody(ls, base, line, nvars - 3, 0);
+ forbody(ls, base, line, nvars - 4, 1);
}
@@ -1379,28 +1625,68 @@ static void forstat (LexState *ls, int line) {
}
+/*
+** Check whether next instruction is a single jump (a 'break', a 'goto'
+** to a forward label, or a 'goto' to a backward label with no variable
+** to close). If so, set the name of the 'label' it is jumping to
+** ("break" for a 'break') or to where it is jumping to ('target') and
+** return true. If not a single jump, leave input unchanged, to be
+** handled as a regular statement.
+*/
+static int issinglejump (LexState *ls, TString **label, int *target) {
+ if (testnext(ls, TK_BREAK)) { /* a break? */
+ *label = luaS_newliteral(ls->L, "break");
+ return 1;
+ }
+ else if (ls->t.token != TK_GOTO || luaX_lookahead(ls) != TK_NAME)
+ return 0; /* not a valid goto */
+ else {
+ TString *lname = ls->lookahead.seminfo.ts; /* label's id */
+ Labeldesc *lb = findlabel(ls, lname);
+ if (lb) { /* a backward jump? */
+ /* does it need to close variables? */
+ if (luaY_nvarstack(ls->fs) > stacklevel(ls->fs, lb->nactvar))
+ return 0; /* not a single jump; cannot optimize */
+ *target = lb->pc;
+ }
+ else /* jump forward */
+ *label = lname;
+ luaX_next(ls); /* skip goto */
+ luaX_next(ls); /* skip name */
+ return 1;
+ }
+}
+
+
static void test_then_block (LexState *ls, int *escapelist) {
/* test_then_block -> [IF | ELSEIF] cond THEN block */
BlockCnt bl;
+ int line;
FuncState *fs = ls->fs;
+ TString *jlb = NULL;
+ int target = NO_JUMP;
expdesc v;
int jf; /* instruction to skip 'then' code (if condition is false) */
luaX_next(ls); /* skip IF or ELSEIF */
expr(ls, &v); /* read condition */
checknext(ls, TK_THEN);
- if (ls->t.token == TK_GOTO || ls->t.token == TK_BREAK) {
+ line = ls->linenumber;
+ if (issinglejump(ls, &jlb, &target)) { /* 'if x then goto' ? */
luaK_goiffalse(ls->fs, &v); /* will jump to label if condition is true */
enterblock(fs, &bl, 0); /* must enter block before 'goto' */
- gotostat(ls, v.t); /* handle goto/break */
- while (testnext(ls, ';')) {} /* skip colons */
- if (block_follow(ls, 0)) { /* 'goto' is the entire block? */
+ if (jlb != NULL) /* forward jump? */
+ newgotoentry(ls, jlb, line, v.t); /* will be resolved later */
+ else /* backward jump */
+ luaK_patchlist(fs, v.t, target); /* jump directly to 'target' */
+ while (testnext(ls, ';')) {} /* skip semicolons */
+ if (block_follow(ls, 0)) { /* jump is the entire block? */
leaveblock(fs);
return; /* and that is it */
}
else /* must skip over 'then' part if condition is false */
jf = luaK_jump(fs);
}
- else { /* regular case (not goto/break) */
+ else { /* regular case (not a jump) */
luaK_goiftrue(ls->fs, &v); /* skip over block if condition is false */
enterblock(fs, &bl, 0);
jf = v.f;
@@ -1431,21 +1717,60 @@ static void ifstat (LexState *ls, int line) {
static void localfunc (LexState *ls) {
expdesc b;
FuncState *fs = ls->fs;
+ int fvar = fs->nactvar; /* function's variable index */
new_localvar(ls, str_checkname(ls)); /* new local variable */
adjustlocalvars(ls, 1); /* enter its scope */
body(ls, &b, 0, ls->linenumber); /* function created in next register */
/* debug information will only see the variable after this point! */
- getlocvar(fs, b.u.info)->startpc = fs->pc;
+ localdebuginfo(fs, fvar)->startpc = fs->pc;
+}
+
+
+static int getlocalattribute (LexState *ls) {
+ /* ATTRIB -> ['<' Name '>'] */
+ if (testnext(ls, '<')) {
+ const char *attr = getstr(str_checkname(ls));
+ checknext(ls, '>');
+ if (strcmp(attr, "const") == 0)
+ return RDKCONST; /* read-only variable */
+ else if (strcmp(attr, "close") == 0)
+ return RDKTOCLOSE; /* to-be-closed variable */
+ else
+ luaK_semerror(ls,
+ luaO_pushfstring(ls->L, "unknown attribute '%s'", attr));
+ }
+ return VDKREG; /* regular variable */
+}
+
+
+static void checktoclose (LexState *ls, int level) {
+ if (level != -1) { /* is there a to-be-closed variable? */
+ FuncState *fs = ls->fs;
+ markupval(fs, level + 1);
+ fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */
+ luaK_codeABC(fs, OP_TBC, stacklevel(fs, level), 0, 0);
+ }
}
static void localstat (LexState *ls) {
- /* stat -> LOCAL NAME {',' NAME} ['=' explist] */
+ /* stat -> LOCAL ATTRIB NAME {',' ATTRIB NAME} ['=' explist] */
+ FuncState *fs = ls->fs;
+ int toclose = -1; /* index of to-be-closed variable (if any) */
+ Vardesc *var; /* last variable */
+ int vidx, kind; /* index and kind of last variable */
int nvars = 0;
int nexps;
expdesc e;
do {
- new_localvar(ls, str_checkname(ls));
+ vidx = new_localvar(ls, str_checkname(ls));
+ kind = getlocalattribute(ls);
+ getlocalvardesc(fs, vidx)->vd.kind = kind;
+ if (kind == RDKTOCLOSE) { /* to-be-closed? */
+ if (toclose != -1) /* one already present? */
+ luaK_semerror(ls, "multiple to-be-closed variables in local list");
+ toclose = fs->nactvar + nvars;
+ }
nvars++;
} while (testnext(ls, ','));
if (testnext(ls, '='))
@@ -1454,8 +1779,19 @@ static void localstat (LexState *ls) {
e.k = VVOID;
nexps = 0;
}
- adjust_assign(ls, nvars, nexps, &e);
- adjustlocalvars(ls, nvars);
+ var = getlocalvardesc(fs, vidx); /* get last variable */
+ if (nvars == nexps && /* no adjustments? */
+ var->vd.kind == RDKCONST && /* last variable is const? */
+ luaK_exp2const(fs, &e, &var->k)) { /* compile-time constant? */
+ var->vd.kind = RDKCTC; /* variable is a compile-time constant */
+ adjustlocalvars(ls, nvars - 1); /* exclude last variable */
+ fs->nactvar++; /* but count it */
+ }
+ else {
+ adjust_assign(ls, nvars, nexps, &e);
+ adjustlocalvars(ls, nvars);
+ }
+ checktoclose(ls, toclose);
}
@@ -1492,11 +1828,13 @@ static void exprstat (LexState *ls) {
suffixedexp(ls, &v.v);
if (ls->t.token == '=' || ls->t.token == ',') { /* stat -> assignment ? */
v.prev = NULL;
- assignment(ls, &v, 1);
+ restassign(ls, &v, 1);
}
else { /* stat -> func */
+ Instruction *inst;
check_condition(ls, v.v.k == VCALL, "syntax error");
- SETARG_C(getinstruction(fs, &v.v), 1); /* call statement uses no results */
+ inst = &getinstruction(fs, &v.v);
+ SETARG_C(*inst, 1); /* call statement uses no results */
}
}
@@ -1505,26 +1843,25 @@ static void retstat (LexState *ls) {
/* stat -> RETURN [explist] [';'] */
FuncState *fs = ls->fs;
expdesc e;
- int first, nret; /* registers with returned values */
+ int nret; /* number of values being returned */
+ int first = luaY_nvarstack(fs); /* first slot to be returned */
if (block_follow(ls, 1) || ls->t.token == ';')
- first = nret = 0; /* return no values */
+ nret = 0; /* return no values */
else {
nret = explist(ls, &e); /* optional return values */
if (hasmultret(e.k)) {
luaK_setmultret(fs, &e);
- if (e.k == VCALL && nret == 1) { /* tail call? */
+ if (e.k == VCALL && nret == 1 && !fs->bl->insidetbc) { /* tail call? */
SET_OPCODE(getinstruction(fs,&e), OP_TAILCALL);
- lua_assert(GETARG_A(getinstruction(fs,&e)) == fs->nactvar);
+ lua_assert(GETARG_A(getinstruction(fs,&e)) == luaY_nvarstack(fs));
}
- first = fs->nactvar;
nret = LUA_MULTRET; /* return all values */
}
else {
if (nret == 1) /* only one single value? */
- first = luaK_exp2anyreg(fs, &e);
- else {
- luaK_exp2nextreg(fs, &e); /* values must go to the stack */
- first = fs->nactvar; /* return all active values */
+ first = luaK_exp2anyreg(fs, &e); /* can use original slot */
+ else { /* values must go to the top of the stack */
+ luaK_exp2nextreg(fs, &e);
lua_assert(nret == fs->freereg - first);
}
}
@@ -1586,9 +1923,13 @@ static void statement (LexState *ls) {
retstat(ls);
break;
}
- case TK_BREAK: /* stat -> breakstat */
+ case TK_BREAK: { /* stat -> breakstat */
+ breakstat(ls);
+ break;
+ }
case TK_GOTO: { /* stat -> 'goto' NAME */
- gotostat(ls, luaK_jump(ls->fs));
+ luaX_next(ls); /* skip 'goto' */
+ gotostat(ls);
break;
}
default: { /* stat -> func | assignment */
@@ -1597,8 +1938,8 @@ static void statement (LexState *ls) {
}
}
lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg &&
- ls->fs->freereg >= ls->fs->nactvar);
- ls->fs->freereg = ls->fs->nactvar; /* free registers */
+ ls->fs->freereg >= luaY_nvarstack(ls->fs));
+ ls->fs->freereg = luaY_nvarstack(ls->fs); /* free registers */
leavelevel(ls);
}
@@ -1611,11 +1952,15 @@ static void statement (LexState *ls) {
*/
static void mainfunc (LexState *ls, FuncState *fs) {
BlockCnt bl;
- expdesc v;
+ Upvaldesc *env;
open_func(ls, fs, &bl);
- fs->f->is_vararg = 1; /* main function is always declared vararg */
- init_exp(&v, VLOCAL, 0); /* create and... */
- newupvalue(fs, ls->envn, &v); /* ...set environment upvalue */
+ setvararg(fs, 0); /* main function is always declared vararg */
+ env = allocupvalue(fs); /* ...set environment upvalue */
+ env->instack = 1;
+ env->idx = 0;
+ env->kind = VDKREG;
+ env->name = ls->envn;
+ luaC_objbarrier(ls->L, fs->f, env->name);
luaX_next(ls); /* read first token */
statlist(ls); /* parse main body */
check(ls, TK_EOS);
@@ -1628,14 +1973,15 @@ LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
LexState lexstate;
FuncState funcstate;
LClosure *cl = luaF_newLclosure(L, 1); /* create main closure */
- setclLvalue(L, L->top, cl); /* anchor it (to avoid being collected) */
+ setclLvalue2s(L, L->top, cl); /* anchor it (to avoid being collected) */
luaD_inctop(L);
lexstate.h = luaH_new(L); /* create table for scanner */
- sethvalue(L, L->top, lexstate.h); /* anchor it */
+ sethvalue2s(L, L->top, lexstate.h); /* anchor it */
luaD_inctop(L);
funcstate.f = cl->p = luaF_newproto(L);
+ luaC_objbarrier(L, cl, cl->p);
funcstate.f->source = luaS_new(L, name); /* create and anchor TString */
- lua_assert(iswhite(funcstate.f)); /* do not need barrier here */
+ luaC_objbarrier(L, funcstate.f, funcstate.f->source);
lexstate.buff = buff;
lexstate.dyd = dyd;
dyd->actvar.n = dyd->gt.n = dyd->label.n = 0;