Browse Source

New implementation for constants

VLOCAL expressions keep a reference to their corresponding 'Vardesc',
and 'Upvaldesc' (for upvalues) has a field 'ro' (read-only). So, it is
easier to check whether a variable is read-only. The decoupling in
VLOCAL between 'vidx' ('Vardesc' index) and 'sidx' (stack index)
should also help the forthcoming implementation of compile-time
constant propagation.
pull/23/head
Roberto Ierusalimschy 5 years ago
parent
commit
54f7b46c1e
  1. 60
      lcode.c
  2. 1
      lcode.h
  3. 1
      ldump.c
  4. 1
      lobject.h
  5. 158
      lparser.c
  6. 16
      lparser.h
  7. 1
      lundump.c
  8. 18
      testes/locals.lua

60
lcode.c

@ -52,7 +52,7 @@ l_noret luaK_semerror (LexState *ls, const char *msg) {
** If expression is a numeric constant, fills 'v' with its value ** If expression is a numeric constant, fills 'v' with its value
** and returns 1. Otherwise, returns 0. ** and returns 1. Otherwise, returns 0.
*/ */
int luaK_tonumeral (FuncState *fs, const expdesc *e, TValue *v) { static int tonumeral (const expdesc *e, TValue *v) {
if (hasjumps(e)) if (hasjumps(e))
return 0; /* not a numeral */ return 0; /* not a numeral */
switch (e->k) { switch (e->k) {
@ -62,41 +62,11 @@ int luaK_tonumeral (FuncState *fs, const expdesc *e, TValue *v) {
case VKFLT: case VKFLT:
if (v) setfltvalue(v, e->u.nval); if (v) setfltvalue(v, e->u.nval);
return 1; return 1;
case VUPVAL: { /* may be a constant */
Vardesc *vd = luaY_getvardesc(&fs, e);
if (v && vd && !ttisnil(&vd->val)) {
setobj(fs->ls->L, v, &vd->val);
return 1;
} /* else */
} /* FALLTHROUGH */
default: return 0; default: return 0;
} }
} }
/*
** If expression 'e' is a constant, change 'e' to represent
** the constant value.
*/
static int const2exp (FuncState *fs, expdesc *e) {
Vardesc *vd = luaY_getvardesc(&fs, e);
if (vd) {
TValue *v = &vd->val;
switch (ttypetag(v)) {
case LUA_TNUMINT:
e->k = VKINT;
e->u.ival = ivalue(v);
return 1;
case LUA_TNUMFLT:
e->k = VKFLT;
e->u.nval = fltvalue(v);
return 1;
}
}
return 0;
}
/* /*
** Return the previous instruction of the current code. If there ** Return the previous instruction of the current code. If there
** may be a jump target between the current instruction and the ** may be a jump target between the current instruction and the
@ -708,15 +678,13 @@ void luaK_setoneret (FuncState *fs, expdesc *e) {
void luaK_dischargevars (FuncState *fs, expdesc *e) { void luaK_dischargevars (FuncState *fs, expdesc *e) {
switch (e->k) { switch (e->k) {
case VLOCAL: { /* already in a register */ case VLOCAL: { /* already in a register */
e->u.info = e->u.var.idx; e->u.info = e->u.var.sidx;
e->k = VNONRELOC; /* becomes a non-relocatable value */ e->k = VNONRELOC; /* becomes a non-relocatable value */
break; break;
} }
case VUPVAL: { /* move value to some (pending) register */ case VUPVAL: { /* move value to some (pending) register */
if (!const2exp(fs, e)) { e->u.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.info, 0);
e->u.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.var.idx, 0); e->k = VRELOC;
e->k = VRELOC;
}
break; break;
} }
case VINDEXUP: { case VINDEXUP: {
@ -971,12 +939,12 @@ void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {
switch (var->k) { switch (var->k) {
case VLOCAL: { case VLOCAL: {
freeexp(fs, ex); freeexp(fs, ex);
exp2reg(fs, ex, var->u.var.idx); /* compute 'ex' into proper place */ exp2reg(fs, ex, var->u.var.sidx); /* compute 'ex' into proper place */
return; return;
} }
case VUPVAL: { case VUPVAL: {
int e = luaK_exp2anyreg(fs, ex); int e = luaK_exp2anyreg(fs, ex);
luaK_codeABC(fs, OP_SETUPVAL, e, var->u.var.idx, 0); luaK_codeABC(fs, OP_SETUPVAL, e, var->u.info, 0);
break; break;
} }
case VINDEXUP: { case VINDEXUP: {
@ -1203,13 +1171,13 @@ void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non string? */ if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non string? */
luaK_exp2anyreg(fs, t); /* put it in a register */ luaK_exp2anyreg(fs, t); /* put it in a register */
if (t->k == VUPVAL) { if (t->k == VUPVAL) {
t->u.ind.t = t->u.var.idx; /* upvalue index */ t->u.ind.t = t->u.info; /* upvalue index */
t->u.ind.idx = k->u.info; /* literal string */ t->u.ind.idx = k->u.info; /* literal string */
t->k = VINDEXUP; t->k = VINDEXUP;
} }
else { else {
/* register index of the table */ /* register index of the table */
t->u.ind.t = (t->k == VLOCAL) ? t->u.var.idx: t->u.info; t->u.ind.t = (t->k == VLOCAL) ? t->u.var.sidx: t->u.info;
if (isKstr(fs, k)) { if (isKstr(fs, k)) {
t->u.ind.idx = k->u.info; /* literal string */ t->u.ind.idx = k->u.info; /* literal string */
t->k = VINDEXSTR; t->k = VINDEXSTR;
@ -1252,9 +1220,7 @@ static int validop (int op, TValue *v1, TValue *v2) {
static int constfolding (FuncState *fs, int op, expdesc *e1, static int constfolding (FuncState *fs, int op, expdesc *e1,
const expdesc *e2) { const expdesc *e2) {
TValue v1, v2, res; TValue v1, v2, res;
if (!luaK_tonumeral(fs, e1, &v1) || if (!tonumeral(e1, &v1) || !tonumeral(e2, &v2) || !validop(op, &v1, &v2))
!luaK_tonumeral(fs, e2, &v2) ||
!validop(op, &v1, &v2))
return 0; /* non-numeric operands or not safe to fold */ return 0; /* non-numeric operands or not safe to fold */
luaO_rawarith(fs->ls->L, op, &v1, &v2, &res); /* does operation */ luaO_rawarith(fs->ls->L, op, &v1, &v2, &res); /* does operation */
if (ttisinteger(&res)) { if (ttisinteger(&res)) {
@ -1341,7 +1307,7 @@ static void codearith (FuncState *fs, OpCode op,
expdesc *e1, expdesc *e2, int flip, int line) { expdesc *e1, expdesc *e2, int flip, int line) {
if (isSCint(e2)) /* immediate operand? */ if (isSCint(e2)) /* immediate operand? */
codebini(fs, cast(OpCode, op - OP_ADD + OP_ADDI), e1, e2, flip, line); codebini(fs, cast(OpCode, op - OP_ADD + OP_ADDI), e1, e2, flip, line);
else if (luaK_tonumeral(fs, e2, NULL) && luaK_exp2K(fs, e2)) { /* K operand? */ else if (tonumeral(e2, NULL) && luaK_exp2K(fs, e2)) { /* K operand? */
int v2 = e2->u.info; /* K index */ int v2 = e2->u.info; /* K index */
op = cast(OpCode, op - OP_ADD + OP_ADDK); op = cast(OpCode, op - OP_ADD + OP_ADDK);
finishbinexpval(fs, e1, e2, op, v2, flip, line); finishbinexpval(fs, e1, e2, op, v2, flip, line);
@ -1362,7 +1328,7 @@ static void codearith (FuncState *fs, OpCode op,
static void codecommutative (FuncState *fs, OpCode op, static void codecommutative (FuncState *fs, OpCode op,
expdesc *e1, expdesc *e2, int line) { expdesc *e1, expdesc *e2, int line) {
int flip = 0; int flip = 0;
if (luaK_tonumeral(fs, e1, NULL)) { /* is first operand a numeric constant? */ if (tonumeral(e1, NULL)) { /* is first operand a numeric constant? */
swapexps(e1, e2); /* change order */ swapexps(e1, e2); /* change order */
flip = 1; flip = 1;
} }
@ -1519,13 +1485,13 @@ void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) {
case OPR_MOD: case OPR_POW: case OPR_MOD: case OPR_POW:
case OPR_BAND: case OPR_BOR: case OPR_BXOR: case OPR_BAND: case OPR_BOR: case OPR_BXOR:
case OPR_SHL: case OPR_SHR: { case OPR_SHL: case OPR_SHR: {
if (!luaK_tonumeral(fs, v, NULL)) if (!tonumeral(v, NULL))
luaK_exp2anyreg(fs, v); luaK_exp2anyreg(fs, v);
/* else keep numeral, which may be folded with 2nd operand */ /* else keep numeral, which may be folded with 2nd operand */
break; break;
} }
case OPR_EQ: case OPR_NE: { case OPR_EQ: case OPR_NE: {
if (!luaK_tonumeral(fs, v, NULL)) if (!tonumeral(v, NULL))
luaK_exp2RK(fs, v); luaK_exp2RK(fs, v);
/* else keep numeral, which may be an immediate operand */ /* else keep numeral, which may be an immediate operand */
break; break;

1
lcode.h

@ -51,7 +51,6 @@ typedef enum UnOpr { OPR_MINUS, OPR_BNOT, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr;
#define luaK_jumpto(fs,t) luaK_patchlist(fs, luaK_jump(fs), t) #define luaK_jumpto(fs,t) luaK_patchlist(fs, luaK_jump(fs), t)
LUAI_FUNC int luaK_tonumeral (FuncState *fs, const expdesc *e, TValue *v);
LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx); LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx);
LUAI_FUNC int luaK_codeAsBx (FuncState *fs, OpCode o, int A, int Bx); LUAI_FUNC int luaK_codeAsBx (FuncState *fs, OpCode o, int A, int Bx);
LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A, LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A,

1
ldump.c

@ -149,6 +149,7 @@ static void DumpUpvalues (const Proto *f, DumpState *D) {
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
DumpByte(f->upvalues[i].instack, D); DumpByte(f->upvalues[i].instack, D);
DumpByte(f->upvalues[i].idx, D); DumpByte(f->upvalues[i].idx, D);
DumpByte(f->upvalues[i].ro, D);
} }
} }

1
lobject.h

@ -460,6 +460,7 @@ typedef struct Upvaldesc {
TString *name; /* upvalue name (for debug information) */ TString *name; /* upvalue name (for debug information) */
lu_byte instack; /* whether it is in stack (register) */ lu_byte instack; /* whether it is in stack (register) */
lu_byte idx; /* index of upvalue (in stack or in outer function's list) */ lu_byte idx; /* index of upvalue (in stack or in outer function's list) */
lu_byte ro; /* true if upvalue is read-only (const) */
} Upvaldesc; } Upvaldesc;

158
lparser.c

@ -156,13 +156,6 @@ static void init_exp (expdesc *e, expkind k, int i) {
} }
static void init_var (expdesc *e, expkind k, int i) {
e->f = e->t = NO_JUMP;
e->k = k;
e->u.var.idx = i;
}
static void codestring (LexState *ls, expdesc *e, TString *s) { static void codestring (LexState *ls, expdesc *e, TString *s) {
init_exp(e, VK, luaK_stringK(ls->fs, s)); init_exp(e, VK, luaK_stringK(ls->fs, s));
} }
@ -177,16 +170,15 @@ static void codename (LexState *ls, expdesc *e) {
** Register a new local variable in the active 'Proto' (for debug ** Register a new local variable in the active 'Proto' (for debug
** information). ** information).
*/ */
static int registerlocalvar (LexState *ls, TString *varname) { static int registerlocalvar (lua_State *L, FuncState *fs, TString *varname) {
FuncState *fs = ls->fs;
Proto *f = fs->f; Proto *f = fs->f;
int oldsize = f->sizelocvars; int oldsize = f->sizelocvars;
luaM_growvector(ls->L, f->locvars, fs->nlocvars, f->sizelocvars, luaM_growvector(L, f->locvars, fs->nlocvars, f->sizelocvars,
LocVar, SHRT_MAX, "local variables"); LocVar, SHRT_MAX, "local variables");
while (oldsize < f->sizelocvars) while (oldsize < f->sizelocvars)
f->locvars[oldsize++].varname = NULL; f->locvars[oldsize++].varname = NULL;
f->locvars[fs->nlocvars].varname = varname; f->locvars[fs->nlocvars].varname = varname;
luaC_objbarrier(ls->L, f, varname); luaC_objbarrier(L, f, varname);
return fs->nlocvars++; return fs->nlocvars++;
} }
@ -195,18 +187,19 @@ static int registerlocalvar (LexState *ls, TString *varname) {
** Create a new local variable with the given 'name'. ** Create a new local variable with the given 'name'.
*/ */
static Vardesc *new_localvar (LexState *ls, TString *name) { static Vardesc *new_localvar (LexState *ls, TString *name) {
lua_State *L = ls->L;
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
Dyndata *dyd = ls->dyd; Dyndata *dyd = ls->dyd;
Vardesc *var; Vardesc *var;
int reg = registerlocalvar(ls, name); int reg = registerlocalvar(L, fs, name);
checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal, checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal,
MAXVARS, "local variables"); MAXVARS, "local variables");
luaM_growvector(ls->L, dyd->actvar.arr, dyd->actvar.n + 1, luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1,
dyd->actvar.size, Vardesc, MAX_INT, "local variables"); dyd->actvar.size, Vardesc, USHRT_MAX, "local variables");
var = &dyd->actvar.arr[dyd->actvar.n++]; var = &dyd->actvar.arr[dyd->actvar.n++];
var->idx = cast(short, reg); var->pidx = cast(short, reg);
var->ro = 0; var->ro = 0;
setnilvalue(&var->val); setnilvalue(var);
return var; return var;
} }
@ -223,50 +216,47 @@ static Vardesc *getlocalvardesc (FuncState *fs, int i) {
return &fs->ls->dyd->actvar.arr[fs->firstlocal + i]; return &fs->ls->dyd->actvar.arr[fs->firstlocal + i];
} }
/* /*
** Get the debug-information entry for current variable 'i'. ** Get the debug-information entry for current variable 'i'.
*/ */
static LocVar *getlocvar (FuncState *fs, int i) { static LocVar *localdebuginfo (FuncState *fs, int i) {
int idx = getlocalvardesc(fs, i)->idx; int idx = getlocalvardesc(fs, i)->pidx;
lua_assert(idx < fs->nlocvars); lua_assert(idx < fs->nlocvars);
return &fs->f->locvars[idx]; return &fs->f->locvars[idx];
} }
/* static void init_var (FuncState *fs, expdesc *e, int i) {
** Return the "variable description" (Vardesc) of a given e->f = e->t = NO_JUMP;
** local variable and update 'fs' to point to the function e->k = VLOCAL;
** where that variable was defined. Return NULL if expression e->u.var.vidx = i;
** is neither a local variable nor an upvalue. e->u.var.sidx = getlocalvardesc(fs, i)->sidx;
*/
Vardesc *luaY_getvardesc (FuncState **fs, const expdesc *e) {
if (e->k == VLOCAL)
return getlocalvardesc(*fs, e->u.var.idx);
else if (e->k != VUPVAL)
return NULL; /* not a local variable */
else { /* upvalue: must go up all levels up to the original local */
int idx = e->u.var.idx;
for (;;) {
Upvaldesc *up = &(*fs)->f->upvalues[idx];
*fs = (*fs)->prev; /* must look at the previous level */
idx = up->idx; /* at this index */
if (*fs == NULL) /* no more levels? (can happen only with _ENV) */
return NULL;
else if (up->instack) /* got to the original level? */
return getlocalvardesc(*fs, idx);
/* else repeat for previous level */
}
}
} }
static void check_readonly (LexState *ls, expdesc *e) { static void check_readonly (LexState *ls, expdesc *e) {
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
Vardesc *vardesc = luaY_getvardesc(&fs, e); TString *varname = NULL; /* to be set if variable is const */
if (vardesc && vardesc->ro) { /* is variable local and const? */ switch (e->k) {
case VLOCAL: {
Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx);
if (vardesc->ro)
varname = fs->f->locvars[vardesc->pidx].varname;
break;
}
case VUPVAL: {
Upvaldesc *up = &fs->f->upvalues[e->u.info];
if (up->ro)
varname = up->name;
break;
}
default:
return; /* other cases cannot be read-only */
}
if (varname) {
const char *msg = luaO_pushfstring(ls->L, const char *msg = luaO_pushfstring(ls->L,
"attempt to assign to const variable '%s'", "attempt to assign to const variable '%s'", getstr(varname));
getstr(fs->f->locvars[vardesc->idx].varname));
luaK_semerror(ls, msg); /* error */ luaK_semerror(ls, msg); /* error */
} }
} }
@ -274,13 +264,15 @@ static void check_readonly (LexState *ls, expdesc *e) {
/* /*
** Start the scope for the last 'nvars' created variables. ** Start the scope for the last 'nvars' created variables.
** (debug info.)
*/ */
static void adjustlocalvars (LexState *ls, int nvars) { static void adjustlocalvars (LexState *ls, int nvars) {
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
fs->nactvar = cast_byte(fs->nactvar + nvars); int i;
for (; nvars; nvars--) { for (i = 0; i < nvars; i++) {
getlocvar(fs, fs->nactvar - nvars)->startpc = fs->pc; int varidx = fs->nactvar++;
Vardesc *var = getlocalvardesc(fs, varidx);
var->sidx = varidx;
fs->f->locvars[var->pidx].startpc = fs->pc;
} }
} }
@ -292,7 +284,7 @@ static void adjustlocalvars (LexState *ls, int nvars) {
static void removevars (FuncState *fs, int tolevel) { static void removevars (FuncState *fs, int tolevel) {
fs->ls->dyd->actvar.n -= (fs->nactvar - tolevel); fs->ls->dyd->actvar.n -= (fs->nactvar - tolevel);
while (fs->nactvar > tolevel) while (fs->nactvar > tolevel)
getlocvar(fs, --fs->nactvar)->endpc = fs->pc; localdebuginfo(fs, --fs->nactvar)->endpc = fs->pc;
} }
@ -310,7 +302,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; Proto *f = fs->f;
int oldsize = f->sizeupvalues; int oldsize = f->sizeupvalues;
checklimit(fs, fs->nups + 1, MAXUPVAL, "upvalues"); checklimit(fs, fs->nups + 1, MAXUPVAL, "upvalues");
@ -318,11 +310,28 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) {
Upvaldesc, MAXUPVAL, "upvalues"); Upvaldesc, MAXUPVAL, "upvalues");
while (oldsize < f->sizeupvalues) while (oldsize < f->sizeupvalues)
f->upvalues[oldsize++].name = NULL; f->upvalues[oldsize++].name = NULL;
f->upvalues[fs->nups].instack = (v->k == VLOCAL); return &f->upvalues[fs->nups++];
f->upvalues[fs->nups].idx = cast_byte(v->u.var.idx); }
f->upvalues[fs->nups].name = name;
luaC_objbarrier(fs->ls->L, f, name);
return fs->nups++; 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->ro = getlocalvardesc(prev, v->u.var.vidx)->ro;
lua_assert(eqstr(name, localdebuginfo(prev, v->u.var.vidx)->varname));
}
else {
up->instack = 0;
up->idx = cast_byte(v->u.info);
up->ro = prev->f->upvalues[v->u.info].ro;
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;
} }
@ -333,7 +342,7 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) {
static int searchvar (FuncState *fs, TString *n) { static int searchvar (FuncState *fs, TString *n) {
int i; int i;
for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) { for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) {
if (eqstr(n, getlocvar(fs, i)->varname)) if (eqstr(n, localdebuginfo(fs, i)->varname))
return i; return i;
} }
return -1; /* not found */ return -1; /* not found */
@ -364,9 +373,9 @@ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
else { else {
int v = searchvar(fs, n); /* look up locals at current level */ int v = searchvar(fs, n); /* look up locals at current level */
if (v >= 0) { /* found? */ if (v >= 0) { /* found? */
init_var(var, VLOCAL, v); /* variable is local */ init_var(fs, var, v); /* variable is local */
if (!base) if (!base)
markupval(fs, v); /* local will be used as an upval */ markupval(fs, var->u.var.sidx); /* local will be used as an upval */
} }
else { /* not found as local at current level; try upvalues */ else { /* not found as local at current level; try upvalues */
int idx = searchupvalue(fs, n); /* try existing upvalues */ int idx = searchupvalue(fs, n); /* try existing upvalues */
@ -377,7 +386,7 @@ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
/* else was LOCAL or UPVAL */ /* else was LOCAL or UPVAL */
idx = newupvalue(fs, n, var); /* will be a new upvalue */ idx = newupvalue(fs, n, var); /* will be a new upvalue */
} }
init_var(var, VUPVAL, idx); /* new or old upvalue */ init_exp(var, VUPVAL, idx); /* new or old upvalue */
} }
} }
} }
@ -440,7 +449,7 @@ static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) {
** local variable. ** local variable.
*/ */
static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) { static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) {
const char *varname = getstr(getlocvar(ls->fs, gt->nactvar)->varname); const char *varname = getstr(localdebuginfo(ls->fs, gt->nactvar)->varname);
const char *msg = "<goto %s> at line %d jumps into the scope of local '%s'"; 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); msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line, varname);
luaK_semerror(ls, msg); /* raise the error */ luaK_semerror(ls, msg); /* raise the error */
@ -1259,20 +1268,20 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
for (; lh; lh = lh->prev) { /* check all previous assignments */ for (; lh; lh = lh->prev) { /* check all previous assignments */
if (vkisindexed(lh->v.k)) { /* assignment to table field? */ if (vkisindexed(lh->v.k)) { /* assignment to table field? */
if (lh->v.k == VINDEXUP) { /* is table an upvalue? */ if (lh->v.k == VINDEXUP) { /* is table an upvalue? */
if (v->k == VUPVAL && lh->v.u.ind.t == v->u.var.idx) { if (v->k == VUPVAL && lh->v.u.ind.t == v->u.info) {
conflict = 1; /* table is the upvalue being assigned now */ conflict = 1; /* table is the upvalue being assigned now */
lh->v.k = VINDEXSTR; lh->v.k = VINDEXSTR;
lh->v.u.ind.t = extra; /* assignment will use safe copy */ lh->v.u.ind.t = extra; /* assignment will use safe copy */
} }
} }
else { /* table is a register */ else { /* table is a register */
if (v->k == VLOCAL && lh->v.u.ind.t == v->u.var.idx) { if (v->k == VLOCAL && lh->v.u.ind.t == v->u.var.sidx) {
conflict = 1; /* table is the local being assigned now */ conflict = 1; /* table is the local being assigned now */
lh->v.u.ind.t = extra; /* assignment will use safe copy */ lh->v.u.ind.t = extra; /* assignment will use safe copy */
} }
/* is index the local being assigned? */ /* is index the local being assigned? */
if (lh->v.k == VINDEXED && v->k == VLOCAL && if (lh->v.k == VINDEXED && v->k == VLOCAL &&
lh->v.u.ind.idx == v->u.var.idx) { lh->v.u.ind.idx == v->u.var.sidx) {
conflict = 1; conflict = 1;
lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */ lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */
} }
@ -1281,14 +1290,16 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
} }
if (conflict) { if (conflict) {
/* copy upvalue/local value to a temporary (in position 'extra') */ /* copy upvalue/local value to a temporary (in position 'extra') */
OpCode op = (v->k == VLOCAL) ? OP_MOVE : OP_GETUPVAL; if (v->k == VLOCAL)
luaK_codeABC(fs, op, extra, v->u.var.idx, 0); 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); luaK_reserveregs(fs, 1);
} }
} }
/* /*
** Parse and compile a mulitple assignment. The first "variable" ** Parse and compile a multiple assignment. The first "variable"
** (a 'suffixedexp') was already read by the caller. ** (a 'suffixedexp') was already read by the caller.
** **
** assignment -> suffixedexp restassign ** assignment -> suffixedexp restassign
@ -1652,7 +1663,7 @@ static void localfunc (LexState *ls) {
adjustlocalvars(ls, 1); /* enter its scope */ adjustlocalvars(ls, 1); /* enter its scope */
body(ls, &b, 0, ls->linenumber); /* function created in next register */ body(ls, &b, 0, ls->linenumber); /* function created in next register */
/* debug information will only see the variable after this point! */ /* debug information will only see the variable after this point! */
getlocvar(fs, b.u.info)->startpc = fs->pc; localdebuginfo(fs, b.u.info)->startpc = fs->pc;
} }
@ -1870,11 +1881,14 @@ static void statement (LexState *ls) {
*/ */
static void mainfunc (LexState *ls, FuncState *fs) { static void mainfunc (LexState *ls, FuncState *fs) {
BlockCnt bl; BlockCnt bl;
expdesc v; Upvaldesc *env;
open_func(ls, fs, &bl); open_func(ls, fs, &bl);
setvararg(fs, 0); /* main function is always declared vararg */ setvararg(fs, 0); /* main function is always declared vararg */
init_var(&v, VLOCAL, 0); /* create and... */ env = allocupvalue(fs); /* ...set environment upvalue */
newupvalue(fs, ls->envn, &v); /* ...set environment upvalue */ env->instack = 1;
env->idx = 0;
env->ro = 0;
env->name = ls->envn;
luaX_next(ls); /* read first token */ luaX_next(ls); /* read first token */
statlist(ls); /* parse main body */ statlist(ls); /* parse main body */
check(ls, TK_EOS); check(ls, TK_EOS);

16
lparser.h

@ -33,8 +33,9 @@ typedef enum {
VKINT, /* integer constant; nval = numerical integer value */ VKINT, /* integer constant; nval = numerical integer value */
VNONRELOC, /* expression has its value in a fixed register; VNONRELOC, /* expression has its value in a fixed register;
info = result register */ info = result register */
VLOCAL, /* local variable; var.idx = local register */ VLOCAL, /* local variable; var.ridx = local register;
VUPVAL, /* upvalue variable; var.idx = index of upvalue in 'upvalues' */ var.vidx = index in 'actvar.arr' */
VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */
VINDEXED, /* indexed variable; VINDEXED, /* indexed variable;
ind.t = table register; ind.t = table register;
ind.idx = key's R index */ ind.idx = key's R index */
@ -70,8 +71,9 @@ typedef struct expdesc {
short idx; /* index (R or "long" K) */ short idx; /* index (R or "long" K) */
lu_byte t; /* table (register or upvalue) */ lu_byte t; /* table (register or upvalue) */
} ind; } ind;
struct { /* for local variables and upvalues */ struct { /* for local variables */
lu_byte idx; /* index of the variable */ lu_byte sidx; /* index in the stack */
unsigned short vidx; /* index in 'actvar.arr' */
} var; } var;
} u; } u;
int t; /* patch list of 'exit when true' */ int t; /* patch list of 'exit when true' */
@ -81,9 +83,10 @@ typedef struct expdesc {
/* description of an active local variable */ /* description of an active local variable */
typedef struct Vardesc { typedef struct Vardesc {
TValue val; /* constant value (if variable is 'const') */ TValuefields; /* constant value (if variable is 'const') */
short idx; /* index of the variable in the Proto's 'locvars' array */
lu_byte ro; /* true if variable is 'const' */ lu_byte ro; /* true if variable is 'const' */
lu_byte sidx; /* index of the variable in the stack */
short pidx; /* index of the variable in the Proto's 'locvars' array */
} Vardesc; } Vardesc;
@ -144,7 +147,6 @@ typedef struct FuncState {
} FuncState; } FuncState;
LUAI_FUNC Vardesc *luaY_getvardesc (FuncState **fs, const expdesc *e);
LUAI_FUNC LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, LUAI_FUNC LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
Dyndata *dyd, const char *name, int firstchar); Dyndata *dyd, const char *name, int firstchar);

1
lundump.c

@ -203,6 +203,7 @@ static void LoadUpvalues (LoadState *S, Proto *f) {
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
f->upvalues[i].instack = LoadByte(S); f->upvalues[i].instack = LoadByte(S);
f->upvalues[i].idx = LoadByte(S); f->upvalues[i].idx = LoadByte(S);
f->upvalues[i].ro = LoadByte(S);
} }
} }

18
testes/locals.lua

@ -177,14 +177,24 @@ do -- constants
local <const> a, b, <const> c = 10, 20, 30 local <const> a, b, <const> c = 10, 20, 30
b = a + c + b -- 'b' is not constant b = a + c + b -- 'b' is not constant
assert(a == 10 and b == 60 and c == 30) assert(a == 10 and b == 60 and c == 30)
local function checkro (code, name) local function checkro (name, code)
local st, msg = load(code) local st, msg = load(code)
local gab = string.format("attempt to assign to const variable '%s'", name) local gab = string.format("attempt to assign to const variable '%s'", name)
assert(not st and string.find(msg, gab)) assert(not st and string.find(msg, gab))
end end
checkro("local x, <const> y, z = 10, 20, 30; x = 11; y = 12", "y") checkro("y", "local x, <const> y, z = 10, 20, 30; x = 11; y = 12")
checkro("local <const> x, y, <const> z = 10, 20, 30; x = 11", "x") checkro("x", "local <const> x, y, <const> z = 10, 20, 30; x = 11")
checkro("local <const> x, y, <const> z = 10, 20, 30; y = 10; z = 11", "z") checkro("z", "local <const> x, y, <const> z = 10, 20, 30; y = 10; z = 11")
checkro("z", [[
local a, <const> z, b = 10;
function foo() a = 20; z = 32; end
]])
checkro("var1", [[
local a, <const> var1 = 10;
function foo() a = 20; z = function () var1 = 12; end end
]])
end end

Loading…
Cancel
Save