diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2019-07-09 10:43:17 -0300 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2019-07-09 10:43:17 -0300 |
| commit | 54f7b46c1e8a0188e1649046a3a72522f2d769f4 (patch) | |
| tree | 28e973e10e2bc82de4d7d1e7a9f6673b028f7ad2 | |
| parent | e888976bc6ba5592fb8ab8ecc04a8f63e217aa74 (diff) | |
| download | lua-54f7b46c1e8a0188e1649046a3a72522f2d769f4.tar.gz lua-54f7b46c1e8a0188e1649046a3a72522f2d769f4.tar.bz2 lua-54f7b46c1e8a0188e1649046a3a72522f2d769f4.zip | |
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.
Diffstat (limited to '')
| -rw-r--r-- | lcode.c | 60 | ||||
| -rw-r--r-- | lcode.h | 1 | ||||
| -rw-r--r-- | ldump.c | 1 | ||||
| -rw-r--r-- | lobject.h | 1 | ||||
| -rw-r--r-- | lparser.c | 158 | ||||
| -rw-r--r-- | lparser.h | 16 | ||||
| -rw-r--r-- | lundump.c | 1 | ||||
| -rw-r--r-- | testes/locals.lua | 18 |
8 files changed, 125 insertions, 131 deletions
| @@ -52,7 +52,7 @@ l_noret luaK_semerror (LexState *ls, const char *msg) { | |||
| 52 | ** If expression is a numeric constant, fills 'v' with its value | 52 | ** If expression is a numeric constant, fills 'v' with its value |
| 53 | ** and returns 1. Otherwise, returns 0. | 53 | ** and returns 1. Otherwise, returns 0. |
| 54 | */ | 54 | */ |
| 55 | int luaK_tonumeral (FuncState *fs, const expdesc *e, TValue *v) { | 55 | static int tonumeral (const expdesc *e, TValue *v) { |
| 56 | if (hasjumps(e)) | 56 | if (hasjumps(e)) |
| 57 | return 0; /* not a numeral */ | 57 | return 0; /* not a numeral */ |
| 58 | switch (e->k) { | 58 | switch (e->k) { |
| @@ -62,42 +62,12 @@ int luaK_tonumeral (FuncState *fs, const expdesc *e, TValue *v) { | |||
| 62 | case VKFLT: | 62 | case VKFLT: |
| 63 | if (v) setfltvalue(v, e->u.nval); | 63 | if (v) setfltvalue(v, e->u.nval); |
| 64 | return 1; | 64 | return 1; |
| 65 | case VUPVAL: { /* may be a constant */ | ||
| 66 | Vardesc *vd = luaY_getvardesc(&fs, e); | ||
| 67 | if (v && vd && !ttisnil(&vd->val)) { | ||
| 68 | setobj(fs->ls->L, v, &vd->val); | ||
| 69 | return 1; | ||
| 70 | } /* else */ | ||
| 71 | } /* FALLTHROUGH */ | ||
| 72 | default: return 0; | 65 | default: return 0; |
| 73 | } | 66 | } |
| 74 | } | 67 | } |
| 75 | 68 | ||
| 76 | 69 | ||
| 77 | /* | 70 | /* |
| 78 | ** If expression 'e' is a constant, change 'e' to represent | ||
| 79 | ** the constant value. | ||
| 80 | */ | ||
| 81 | static int const2exp (FuncState *fs, expdesc *e) { | ||
| 82 | Vardesc *vd = luaY_getvardesc(&fs, e); | ||
| 83 | if (vd) { | ||
| 84 | TValue *v = &vd->val; | ||
| 85 | switch (ttypetag(v)) { | ||
| 86 | case LUA_TNUMINT: | ||
| 87 | e->k = VKINT; | ||
| 88 | e->u.ival = ivalue(v); | ||
| 89 | return 1; | ||
| 90 | case LUA_TNUMFLT: | ||
| 91 | e->k = VKFLT; | ||
| 92 | e->u.nval = fltvalue(v); | ||
| 93 | return 1; | ||
| 94 | } | ||
| 95 | } | ||
| 96 | return 0; | ||
| 97 | } | ||
| 98 | |||
| 99 | |||
| 100 | /* | ||
| 101 | ** Return the previous instruction of the current code. If there | 71 | ** Return the previous instruction of the current code. If there |
| 102 | ** may be a jump target between the current instruction and the | 72 | ** may be a jump target between the current instruction and the |
| 103 | ** previous one, return an invalid instruction (to avoid wrong | 73 | ** previous one, return an invalid instruction (to avoid wrong |
| @@ -708,15 +678,13 @@ void luaK_setoneret (FuncState *fs, expdesc *e) { | |||
| 708 | void luaK_dischargevars (FuncState *fs, expdesc *e) { | 678 | void luaK_dischargevars (FuncState *fs, expdesc *e) { |
| 709 | switch (e->k) { | 679 | switch (e->k) { |
| 710 | case VLOCAL: { /* already in a register */ | 680 | case VLOCAL: { /* already in a register */ |
| 711 | e->u.info = e->u.var.idx; | 681 | e->u.info = e->u.var.sidx; |
| 712 | e->k = VNONRELOC; /* becomes a non-relocatable value */ | 682 | e->k = VNONRELOC; /* becomes a non-relocatable value */ |
| 713 | break; | 683 | break; |
| 714 | } | 684 | } |
| 715 | case VUPVAL: { /* move value to some (pending) register */ | 685 | case VUPVAL: { /* move value to some (pending) register */ |
| 716 | if (!const2exp(fs, e)) { | 686 | e->u.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.info, 0); |
| 717 | e->u.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.var.idx, 0); | 687 | e->k = VRELOC; |
| 718 | e->k = VRELOC; | ||
| 719 | } | ||
| 720 | break; | 688 | break; |
| 721 | } | 689 | } |
| 722 | case VINDEXUP: { | 690 | case VINDEXUP: { |
| @@ -971,12 +939,12 @@ void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) { | |||
| 971 | switch (var->k) { | 939 | switch (var->k) { |
| 972 | case VLOCAL: { | 940 | case VLOCAL: { |
| 973 | freeexp(fs, ex); | 941 | freeexp(fs, ex); |
| 974 | exp2reg(fs, ex, var->u.var.idx); /* compute 'ex' into proper place */ | 942 | exp2reg(fs, ex, var->u.var.sidx); /* compute 'ex' into proper place */ |
| 975 | return; | 943 | return; |
| 976 | } | 944 | } |
| 977 | case VUPVAL: { | 945 | case VUPVAL: { |
| 978 | int e = luaK_exp2anyreg(fs, ex); | 946 | int e = luaK_exp2anyreg(fs, ex); |
| 979 | luaK_codeABC(fs, OP_SETUPVAL, e, var->u.var.idx, 0); | 947 | luaK_codeABC(fs, OP_SETUPVAL, e, var->u.info, 0); |
| 980 | break; | 948 | break; |
| 981 | } | 949 | } |
| 982 | case VINDEXUP: { | 950 | case VINDEXUP: { |
| @@ -1203,13 +1171,13 @@ void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) { | |||
| 1203 | if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non string? */ | 1171 | if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non string? */ |
| 1204 | luaK_exp2anyreg(fs, t); /* put it in a register */ | 1172 | luaK_exp2anyreg(fs, t); /* put it in a register */ |
| 1205 | if (t->k == VUPVAL) { | 1173 | if (t->k == VUPVAL) { |
| 1206 | t->u.ind.t = t->u.var.idx; /* upvalue index */ | 1174 | t->u.ind.t = t->u.info; /* upvalue index */ |
| 1207 | t->u.ind.idx = k->u.info; /* literal string */ | 1175 | t->u.ind.idx = k->u.info; /* literal string */ |
| 1208 | t->k = VINDEXUP; | 1176 | t->k = VINDEXUP; |
| 1209 | } | 1177 | } |
| 1210 | else { | 1178 | else { |
| 1211 | /* register index of the table */ | 1179 | /* register index of the table */ |
| 1212 | t->u.ind.t = (t->k == VLOCAL) ? t->u.var.idx: t->u.info; | 1180 | t->u.ind.t = (t->k == VLOCAL) ? t->u.var.sidx: t->u.info; |
| 1213 | if (isKstr(fs, k)) { | 1181 | if (isKstr(fs, k)) { |
| 1214 | t->u.ind.idx = k->u.info; /* literal string */ | 1182 | t->u.ind.idx = k->u.info; /* literal string */ |
| 1215 | t->k = VINDEXSTR; | 1183 | t->k = VINDEXSTR; |
| @@ -1252,9 +1220,7 @@ static int validop (int op, TValue *v1, TValue *v2) { | |||
| 1252 | static int constfolding (FuncState *fs, int op, expdesc *e1, | 1220 | static int constfolding (FuncState *fs, int op, expdesc *e1, |
| 1253 | const expdesc *e2) { | 1221 | const expdesc *e2) { |
| 1254 | TValue v1, v2, res; | 1222 | TValue v1, v2, res; |
| 1255 | if (!luaK_tonumeral(fs, e1, &v1) || | 1223 | if (!tonumeral(e1, &v1) || !tonumeral(e2, &v2) || !validop(op, &v1, &v2)) |
| 1256 | !luaK_tonumeral(fs, e2, &v2) || | ||
| 1257 | !validop(op, &v1, &v2)) | ||
| 1258 | return 0; /* non-numeric operands or not safe to fold */ | 1224 | return 0; /* non-numeric operands or not safe to fold */ |
| 1259 | luaO_rawarith(fs->ls->L, op, &v1, &v2, &res); /* does operation */ | 1225 | luaO_rawarith(fs->ls->L, op, &v1, &v2, &res); /* does operation */ |
| 1260 | if (ttisinteger(&res)) { | 1226 | if (ttisinteger(&res)) { |
| @@ -1341,7 +1307,7 @@ static void codearith (FuncState *fs, OpCode op, | |||
| 1341 | expdesc *e1, expdesc *e2, int flip, int line) { | 1307 | expdesc *e1, expdesc *e2, int flip, int line) { |
| 1342 | if (isSCint(e2)) /* immediate operand? */ | 1308 | if (isSCint(e2)) /* immediate operand? */ |
| 1343 | codebini(fs, cast(OpCode, op - OP_ADD + OP_ADDI), e1, e2, flip, line); | 1309 | codebini(fs, cast(OpCode, op - OP_ADD + OP_ADDI), e1, e2, flip, line); |
| 1344 | else if (luaK_tonumeral(fs, e2, NULL) && luaK_exp2K(fs, e2)) { /* K operand? */ | 1310 | else if (tonumeral(e2, NULL) && luaK_exp2K(fs, e2)) { /* K operand? */ |
| 1345 | int v2 = e2->u.info; /* K index */ | 1311 | int v2 = e2->u.info; /* K index */ |
| 1346 | op = cast(OpCode, op - OP_ADD + OP_ADDK); | 1312 | op = cast(OpCode, op - OP_ADD + OP_ADDK); |
| 1347 | finishbinexpval(fs, e1, e2, op, v2, flip, line); | 1313 | finishbinexpval(fs, e1, e2, op, v2, flip, line); |
| @@ -1362,7 +1328,7 @@ static void codearith (FuncState *fs, OpCode op, | |||
| 1362 | static void codecommutative (FuncState *fs, OpCode op, | 1328 | static void codecommutative (FuncState *fs, OpCode op, |
| 1363 | expdesc *e1, expdesc *e2, int line) { | 1329 | expdesc *e1, expdesc *e2, int line) { |
| 1364 | int flip = 0; | 1330 | int flip = 0; |
| 1365 | if (luaK_tonumeral(fs, e1, NULL)) { /* is first operand a numeric constant? */ | 1331 | if (tonumeral(e1, NULL)) { /* is first operand a numeric constant? */ |
| 1366 | swapexps(e1, e2); /* change order */ | 1332 | swapexps(e1, e2); /* change order */ |
| 1367 | flip = 1; | 1333 | flip = 1; |
| 1368 | } | 1334 | } |
| @@ -1519,13 +1485,13 @@ void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) { | |||
| 1519 | case OPR_MOD: case OPR_POW: | 1485 | case OPR_MOD: case OPR_POW: |
| 1520 | case OPR_BAND: case OPR_BOR: case OPR_BXOR: | 1486 | case OPR_BAND: case OPR_BOR: case OPR_BXOR: |
| 1521 | case OPR_SHL: case OPR_SHR: { | 1487 | case OPR_SHL: case OPR_SHR: { |
| 1522 | if (!luaK_tonumeral(fs, v, NULL)) | 1488 | if (!tonumeral(v, NULL)) |
| 1523 | luaK_exp2anyreg(fs, v); | 1489 | luaK_exp2anyreg(fs, v); |
| 1524 | /* else keep numeral, which may be folded with 2nd operand */ | 1490 | /* else keep numeral, which may be folded with 2nd operand */ |
| 1525 | break; | 1491 | break; |
| 1526 | } | 1492 | } |
| 1527 | case OPR_EQ: case OPR_NE: { | 1493 | case OPR_EQ: case OPR_NE: { |
| 1528 | if (!luaK_tonumeral(fs, v, NULL)) | 1494 | if (!tonumeral(v, NULL)) |
| 1529 | luaK_exp2RK(fs, v); | 1495 | luaK_exp2RK(fs, v); |
| 1530 | /* else keep numeral, which may be an immediate operand */ | 1496 | /* else keep numeral, which may be an immediate operand */ |
| 1531 | break; | 1497 | break; |
| @@ -51,7 +51,6 @@ typedef enum UnOpr { OPR_MINUS, OPR_BNOT, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr; | |||
| 51 | 51 | ||
| 52 | #define luaK_jumpto(fs,t) luaK_patchlist(fs, luaK_jump(fs), t) | 52 | #define luaK_jumpto(fs,t) luaK_patchlist(fs, luaK_jump(fs), t) |
| 53 | 53 | ||
| 54 | LUAI_FUNC int luaK_tonumeral (FuncState *fs, const expdesc *e, TValue *v); | ||
| 55 | LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx); | 54 | LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx); |
| 56 | LUAI_FUNC int luaK_codeAsBx (FuncState *fs, OpCode o, int A, int Bx); | 55 | LUAI_FUNC int luaK_codeAsBx (FuncState *fs, OpCode o, int A, int Bx); |
| 57 | LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A, | 56 | LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A, |
| @@ -149,6 +149,7 @@ static void DumpUpvalues (const Proto *f, DumpState *D) { | |||
| 149 | for (i = 0; i < n; i++) { | 149 | for (i = 0; i < n; i++) { |
| 150 | DumpByte(f->upvalues[i].instack, D); | 150 | DumpByte(f->upvalues[i].instack, D); |
| 151 | DumpByte(f->upvalues[i].idx, D); | 151 | DumpByte(f->upvalues[i].idx, D); |
| 152 | DumpByte(f->upvalues[i].ro, D); | ||
| 152 | } | 153 | } |
| 153 | } | 154 | } |
| 154 | 155 | ||
| @@ -460,6 +460,7 @@ typedef struct Upvaldesc { | |||
| 460 | TString *name; /* upvalue name (for debug information) */ | 460 | TString *name; /* upvalue name (for debug information) */ |
| 461 | lu_byte instack; /* whether it is in stack (register) */ | 461 | lu_byte instack; /* whether it is in stack (register) */ |
| 462 | lu_byte idx; /* index of upvalue (in stack or in outer function's list) */ | 462 | lu_byte idx; /* index of upvalue (in stack or in outer function's list) */ |
| 463 | lu_byte ro; /* true if upvalue is read-only (const) */ | ||
| 463 | } Upvaldesc; | 464 | } Upvaldesc; |
| 464 | 465 | ||
| 465 | 466 | ||
| @@ -156,13 +156,6 @@ static void init_exp (expdesc *e, expkind k, int i) { | |||
| 156 | } | 156 | } |
| 157 | 157 | ||
| 158 | 158 | ||
| 159 | static void init_var (expdesc *e, expkind k, int i) { | ||
| 160 | e->f = e->t = NO_JUMP; | ||
| 161 | e->k = k; | ||
| 162 | e->u.var.idx = i; | ||
| 163 | } | ||
| 164 | |||
| 165 | |||
| 166 | static void codestring (LexState *ls, expdesc *e, TString *s) { | 159 | static void codestring (LexState *ls, expdesc *e, TString *s) { |
| 167 | init_exp(e, VK, luaK_stringK(ls->fs, s)); | 160 | init_exp(e, VK, luaK_stringK(ls->fs, s)); |
| 168 | } | 161 | } |
| @@ -177,16 +170,15 @@ static void codename (LexState *ls, expdesc *e) { | |||
| 177 | ** Register a new local variable in the active 'Proto' (for debug | 170 | ** Register a new local variable in the active 'Proto' (for debug |
| 178 | ** information). | 171 | ** information). |
| 179 | */ | 172 | */ |
| 180 | static int registerlocalvar (LexState *ls, TString *varname) { | 173 | static int registerlocalvar (lua_State *L, FuncState *fs, TString *varname) { |
| 181 | FuncState *fs = ls->fs; | ||
| 182 | Proto *f = fs->f; | 174 | Proto *f = fs->f; |
| 183 | int oldsize = f->sizelocvars; | 175 | int oldsize = f->sizelocvars; |
| 184 | luaM_growvector(ls->L, f->locvars, fs->nlocvars, f->sizelocvars, | 176 | luaM_growvector(L, f->locvars, fs->nlocvars, f->sizelocvars, |
| 185 | LocVar, SHRT_MAX, "local variables"); | 177 | LocVar, SHRT_MAX, "local variables"); |
| 186 | while (oldsize < f->sizelocvars) | 178 | while (oldsize < f->sizelocvars) |
| 187 | f->locvars[oldsize++].varname = NULL; | 179 | f->locvars[oldsize++].varname = NULL; |
| 188 | f->locvars[fs->nlocvars].varname = varname; | 180 | f->locvars[fs->nlocvars].varname = varname; |
| 189 | luaC_objbarrier(ls->L, f, varname); | 181 | luaC_objbarrier(L, f, varname); |
| 190 | return fs->nlocvars++; | 182 | return fs->nlocvars++; |
| 191 | } | 183 | } |
| 192 | 184 | ||
| @@ -195,18 +187,19 @@ static int registerlocalvar (LexState *ls, TString *varname) { | |||
| 195 | ** Create a new local variable with the given 'name'. | 187 | ** Create a new local variable with the given 'name'. |
| 196 | */ | 188 | */ |
| 197 | static Vardesc *new_localvar (LexState *ls, TString *name) { | 189 | static Vardesc *new_localvar (LexState *ls, TString *name) { |
| 190 | lua_State *L = ls->L; | ||
| 198 | FuncState *fs = ls->fs; | 191 | FuncState *fs = ls->fs; |
| 199 | Dyndata *dyd = ls->dyd; | 192 | Dyndata *dyd = ls->dyd; |
| 200 | Vardesc *var; | 193 | Vardesc *var; |
| 201 | int reg = registerlocalvar(ls, name); | 194 | int reg = registerlocalvar(L, fs, name); |
| 202 | checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal, | 195 | checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal, |
| 203 | MAXVARS, "local variables"); | 196 | MAXVARS, "local variables"); |
| 204 | luaM_growvector(ls->L, dyd->actvar.arr, dyd->actvar.n + 1, | 197 | luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1, |
| 205 | dyd->actvar.size, Vardesc, MAX_INT, "local variables"); | 198 | dyd->actvar.size, Vardesc, USHRT_MAX, "local variables"); |
| 206 | var = &dyd->actvar.arr[dyd->actvar.n++]; | 199 | var = &dyd->actvar.arr[dyd->actvar.n++]; |
| 207 | var->idx = cast(short, reg); | 200 | var->pidx = cast(short, reg); |
| 208 | var->ro = 0; | 201 | var->ro = 0; |
| 209 | setnilvalue(&var->val); | 202 | setnilvalue(var); |
| 210 | return var; | 203 | return var; |
| 211 | } | 204 | } |
| 212 | 205 | ||
| @@ -223,50 +216,47 @@ static Vardesc *getlocalvardesc (FuncState *fs, int i) { | |||
| 223 | return &fs->ls->dyd->actvar.arr[fs->firstlocal + i]; | 216 | return &fs->ls->dyd->actvar.arr[fs->firstlocal + i]; |
| 224 | } | 217 | } |
| 225 | 218 | ||
| 219 | |||
| 226 | /* | 220 | /* |
| 227 | ** Get the debug-information entry for current variable 'i'. | 221 | ** Get the debug-information entry for current variable 'i'. |
| 228 | */ | 222 | */ |
| 229 | static LocVar *getlocvar (FuncState *fs, int i) { | 223 | static LocVar *localdebuginfo (FuncState *fs, int i) { |
| 230 | int idx = getlocalvardesc(fs, i)->idx; | 224 | int idx = getlocalvardesc(fs, i)->pidx; |
| 231 | lua_assert(idx < fs->nlocvars); | 225 | lua_assert(idx < fs->nlocvars); |
| 232 | return &fs->f->locvars[idx]; | 226 | return &fs->f->locvars[idx]; |
| 233 | } | 227 | } |
| 234 | 228 | ||
| 235 | 229 | ||
| 236 | /* | 230 | static void init_var (FuncState *fs, expdesc *e, int i) { |
| 237 | ** Return the "variable description" (Vardesc) of a given | 231 | e->f = e->t = NO_JUMP; |
| 238 | ** local variable and update 'fs' to point to the function | 232 | e->k = VLOCAL; |
| 239 | ** where that variable was defined. Return NULL if expression | 233 | e->u.var.vidx = i; |
| 240 | ** is neither a local variable nor an upvalue. | 234 | e->u.var.sidx = getlocalvardesc(fs, i)->sidx; |
| 241 | */ | ||
| 242 | Vardesc *luaY_getvardesc (FuncState **fs, const expdesc *e) { | ||
| 243 | if (e->k == VLOCAL) | ||
| 244 | return getlocalvardesc(*fs, e->u.var.idx); | ||
| 245 | else if (e->k != VUPVAL) | ||
| 246 | return NULL; /* not a local variable */ | ||
| 247 | else { /* upvalue: must go up all levels up to the original local */ | ||
| 248 | int idx = e->u.var.idx; | ||
| 249 | for (;;) { | ||
| 250 | Upvaldesc *up = &(*fs)->f->upvalues[idx]; | ||
| 251 | *fs = (*fs)->prev; /* must look at the previous level */ | ||
| 252 | idx = up->idx; /* at this index */ | ||
| 253 | if (*fs == NULL) /* no more levels? (can happen only with _ENV) */ | ||
| 254 | return NULL; | ||
| 255 | else if (up->instack) /* got to the original level? */ | ||
| 256 | return getlocalvardesc(*fs, idx); | ||
| 257 | /* else repeat for previous level */ | ||
| 258 | } | ||
| 259 | } | ||
| 260 | } | 235 | } |
| 261 | 236 | ||
| 262 | 237 | ||
| 263 | static void check_readonly (LexState *ls, expdesc *e) { | 238 | static void check_readonly (LexState *ls, expdesc *e) { |
| 264 | FuncState *fs = ls->fs; | 239 | FuncState *fs = ls->fs; |
| 265 | Vardesc *vardesc = luaY_getvardesc(&fs, e); | 240 | TString *varname = NULL; /* to be set if variable is const */ |
| 266 | if (vardesc && vardesc->ro) { /* is variable local and const? */ | 241 | switch (e->k) { |
| 242 | case VLOCAL: { | ||
| 243 | Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx); | ||
| 244 | if (vardesc->ro) | ||
| 245 | varname = fs->f->locvars[vardesc->pidx].varname; | ||
| 246 | break; | ||
| 247 | } | ||
| 248 | case VUPVAL: { | ||
| 249 | Upvaldesc *up = &fs->f->upvalues[e->u.info]; | ||
| 250 | if (up->ro) | ||
| 251 | varname = up->name; | ||
| 252 | break; | ||
| 253 | } | ||
| 254 | default: | ||
| 255 | return; /* other cases cannot be read-only */ | ||
| 256 | } | ||
| 257 | if (varname) { | ||
| 267 | const char *msg = luaO_pushfstring(ls->L, | 258 | const char *msg = luaO_pushfstring(ls->L, |
| 268 | "attempt to assign to const variable '%s'", | 259 | "attempt to assign to const variable '%s'", getstr(varname)); |
| 269 | getstr(fs->f->locvars[vardesc->idx].varname)); | ||
| 270 | luaK_semerror(ls, msg); /* error */ | 260 | luaK_semerror(ls, msg); /* error */ |
| 271 | } | 261 | } |
| 272 | } | 262 | } |
| @@ -274,13 +264,15 @@ static void check_readonly (LexState *ls, expdesc *e) { | |||
| 274 | 264 | ||
| 275 | /* | 265 | /* |
| 276 | ** Start the scope for the last 'nvars' created variables. | 266 | ** Start the scope for the last 'nvars' created variables. |
| 277 | ** (debug info.) | ||
| 278 | */ | 267 | */ |
| 279 | static void adjustlocalvars (LexState *ls, int nvars) { | 268 | static void adjustlocalvars (LexState *ls, int nvars) { |
| 280 | FuncState *fs = ls->fs; | 269 | FuncState *fs = ls->fs; |
| 281 | fs->nactvar = cast_byte(fs->nactvar + nvars); | 270 | int i; |
| 282 | for (; nvars; nvars--) { | 271 | for (i = 0; i < nvars; i++) { |
| 283 | getlocvar(fs, fs->nactvar - nvars)->startpc = fs->pc; | 272 | int varidx = fs->nactvar++; |
| 273 | Vardesc *var = getlocalvardesc(fs, varidx); | ||
| 274 | var->sidx = varidx; | ||
| 275 | fs->f->locvars[var->pidx].startpc = fs->pc; | ||
| 284 | } | 276 | } |
| 285 | } | 277 | } |
| 286 | 278 | ||
| @@ -292,7 +284,7 @@ static void adjustlocalvars (LexState *ls, int nvars) { | |||
| 292 | static void removevars (FuncState *fs, int tolevel) { | 284 | static void removevars (FuncState *fs, int tolevel) { |
| 293 | fs->ls->dyd->actvar.n -= (fs->nactvar - tolevel); | 285 | fs->ls->dyd->actvar.n -= (fs->nactvar - tolevel); |
| 294 | while (fs->nactvar > tolevel) | 286 | while (fs->nactvar > tolevel) |
| 295 | getlocvar(fs, --fs->nactvar)->endpc = fs->pc; | 287 | localdebuginfo(fs, --fs->nactvar)->endpc = fs->pc; |
| 296 | } | 288 | } |
| 297 | 289 | ||
| 298 | 290 | ||
| @@ -310,7 +302,7 @@ static int searchupvalue (FuncState *fs, TString *name) { | |||
| 310 | } | 302 | } |
| 311 | 303 | ||
| 312 | 304 | ||
| 313 | static int newupvalue (FuncState *fs, TString *name, expdesc *v) { | 305 | static Upvaldesc *allocupvalue (FuncState *fs) { |
| 314 | Proto *f = fs->f; | 306 | Proto *f = fs->f; |
| 315 | int oldsize = f->sizeupvalues; | 307 | int oldsize = f->sizeupvalues; |
| 316 | checklimit(fs, fs->nups + 1, MAXUPVAL, "upvalues"); | 308 | checklimit(fs, fs->nups + 1, MAXUPVAL, "upvalues"); |
| @@ -318,11 +310,28 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) { | |||
| 318 | Upvaldesc, MAXUPVAL, "upvalues"); | 310 | Upvaldesc, MAXUPVAL, "upvalues"); |
| 319 | while (oldsize < f->sizeupvalues) | 311 | while (oldsize < f->sizeupvalues) |
| 320 | f->upvalues[oldsize++].name = NULL; | 312 | f->upvalues[oldsize++].name = NULL; |
| 321 | f->upvalues[fs->nups].instack = (v->k == VLOCAL); | 313 | return &f->upvalues[fs->nups++]; |
| 322 | f->upvalues[fs->nups].idx = cast_byte(v->u.var.idx); | 314 | } |
| 323 | f->upvalues[fs->nups].name = name; | 315 | |
| 324 | luaC_objbarrier(fs->ls->L, f, name); | 316 | |
| 325 | return fs->nups++; | 317 | static int newupvalue (FuncState *fs, TString *name, expdesc *v) { |
| 318 | Upvaldesc *up = allocupvalue(fs); | ||
| 319 | FuncState *prev = fs->prev; | ||
| 320 | if (v->k == VLOCAL) { | ||
| 321 | up->instack = 1; | ||
| 322 | up->idx = v->u.var.sidx; | ||
| 323 | up->ro = getlocalvardesc(prev, v->u.var.vidx)->ro; | ||
| 324 | lua_assert(eqstr(name, localdebuginfo(prev, v->u.var.vidx)->varname)); | ||
| 325 | } | ||
| 326 | else { | ||
| 327 | up->instack = 0; | ||
| 328 | up->idx = cast_byte(v->u.info); | ||
| 329 | up->ro = prev->f->upvalues[v->u.info].ro; | ||
| 330 | lua_assert(eqstr(name, prev->f->upvalues[v->u.info].name)); | ||
| 331 | } | ||
| 332 | up->name = name; | ||
| 333 | luaC_objbarrier(fs->ls->L, fs->f, name); | ||
| 334 | return fs->nups - 1; | ||
| 326 | } | 335 | } |
| 327 | 336 | ||
| 328 | 337 | ||
| @@ -333,7 +342,7 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) { | |||
| 333 | static int searchvar (FuncState *fs, TString *n) { | 342 | static int searchvar (FuncState *fs, TString *n) { |
| 334 | int i; | 343 | int i; |
| 335 | for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) { | 344 | for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) { |
| 336 | if (eqstr(n, getlocvar(fs, i)->varname)) | 345 | if (eqstr(n, localdebuginfo(fs, i)->varname)) |
| 337 | return i; | 346 | return i; |
| 338 | } | 347 | } |
| 339 | return -1; /* not found */ | 348 | return -1; /* not found */ |
| @@ -364,9 +373,9 @@ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) { | |||
| 364 | else { | 373 | else { |
| 365 | int v = searchvar(fs, n); /* look up locals at current level */ | 374 | int v = searchvar(fs, n); /* look up locals at current level */ |
| 366 | if (v >= 0) { /* found? */ | 375 | if (v >= 0) { /* found? */ |
| 367 | init_var(var, VLOCAL, v); /* variable is local */ | 376 | init_var(fs, var, v); /* variable is local */ |
| 368 | if (!base) | 377 | if (!base) |
| 369 | markupval(fs, v); /* local will be used as an upval */ | 378 | markupval(fs, var->u.var.sidx); /* local will be used as an upval */ |
| 370 | } | 379 | } |
| 371 | else { /* not found as local at current level; try upvalues */ | 380 | else { /* not found as local at current level; try upvalues */ |
| 372 | int idx = searchupvalue(fs, n); /* try existing upvalues */ | 381 | int idx = searchupvalue(fs, n); /* try existing upvalues */ |
| @@ -377,7 +386,7 @@ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) { | |||
| 377 | /* else was LOCAL or UPVAL */ | 386 | /* else was LOCAL or UPVAL */ |
| 378 | idx = newupvalue(fs, n, var); /* will be a new upvalue */ | 387 | idx = newupvalue(fs, n, var); /* will be a new upvalue */ |
| 379 | } | 388 | } |
| 380 | init_var(var, VUPVAL, idx); /* new or old upvalue */ | 389 | init_exp(var, VUPVAL, idx); /* new or old upvalue */ |
| 381 | } | 390 | } |
| 382 | } | 391 | } |
| 383 | } | 392 | } |
| @@ -440,7 +449,7 @@ static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) { | |||
| 440 | ** local variable. | 449 | ** local variable. |
| 441 | */ | 450 | */ |
| 442 | static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) { | 451 | static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) { |
| 443 | const char *varname = getstr(getlocvar(ls->fs, gt->nactvar)->varname); | 452 | const char *varname = getstr(localdebuginfo(ls->fs, gt->nactvar)->varname); |
| 444 | const char *msg = "<goto %s> at line %d jumps into the scope of local '%s'"; | 453 | const char *msg = "<goto %s> at line %d jumps into the scope of local '%s'"; |
| 445 | msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line, varname); | 454 | msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line, varname); |
| 446 | luaK_semerror(ls, msg); /* raise the error */ | 455 | luaK_semerror(ls, msg); /* raise the error */ |
| @@ -1259,20 +1268,20 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) { | |||
| 1259 | for (; lh; lh = lh->prev) { /* check all previous assignments */ | 1268 | for (; lh; lh = lh->prev) { /* check all previous assignments */ |
| 1260 | if (vkisindexed(lh->v.k)) { /* assignment to table field? */ | 1269 | if (vkisindexed(lh->v.k)) { /* assignment to table field? */ |
| 1261 | if (lh->v.k == VINDEXUP) { /* is table an upvalue? */ | 1270 | if (lh->v.k == VINDEXUP) { /* is table an upvalue? */ |
| 1262 | if (v->k == VUPVAL && lh->v.u.ind.t == v->u.var.idx) { | 1271 | if (v->k == VUPVAL && lh->v.u.ind.t == v->u.info) { |
| 1263 | conflict = 1; /* table is the upvalue being assigned now */ | 1272 | conflict = 1; /* table is the upvalue being assigned now */ |
| 1264 | lh->v.k = VINDEXSTR; | 1273 | lh->v.k = VINDEXSTR; |
| 1265 | lh->v.u.ind.t = extra; /* assignment will use safe copy */ | 1274 | lh->v.u.ind.t = extra; /* assignment will use safe copy */ |
| 1266 | } | 1275 | } |
| 1267 | } | 1276 | } |
| 1268 | else { /* table is a register */ | 1277 | else { /* table is a register */ |
| 1269 | if (v->k == VLOCAL && lh->v.u.ind.t == v->u.var.idx) { | 1278 | if (v->k == VLOCAL && lh->v.u.ind.t == v->u.var.sidx) { |
| 1270 | conflict = 1; /* table is the local being assigned now */ | 1279 | conflict = 1; /* table is the local being assigned now */ |
| 1271 | lh->v.u.ind.t = extra; /* assignment will use safe copy */ | 1280 | lh->v.u.ind.t = extra; /* assignment will use safe copy */ |
| 1272 | } | 1281 | } |
| 1273 | /* is index the local being assigned? */ | 1282 | /* is index the local being assigned? */ |
| 1274 | if (lh->v.k == VINDEXED && v->k == VLOCAL && | 1283 | if (lh->v.k == VINDEXED && v->k == VLOCAL && |
| 1275 | lh->v.u.ind.idx == v->u.var.idx) { | 1284 | lh->v.u.ind.idx == v->u.var.sidx) { |
| 1276 | conflict = 1; | 1285 | conflict = 1; |
| 1277 | lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */ | 1286 | lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */ |
| 1278 | } | 1287 | } |
| @@ -1281,14 +1290,16 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) { | |||
| 1281 | } | 1290 | } |
| 1282 | if (conflict) { | 1291 | if (conflict) { |
| 1283 | /* copy upvalue/local value to a temporary (in position 'extra') */ | 1292 | /* copy upvalue/local value to a temporary (in position 'extra') */ |
| 1284 | OpCode op = (v->k == VLOCAL) ? OP_MOVE : OP_GETUPVAL; | 1293 | if (v->k == VLOCAL) |
| 1285 | luaK_codeABC(fs, op, extra, v->u.var.idx, 0); | 1294 | luaK_codeABC(fs, OP_MOVE, extra, v->u.var.sidx, 0); |
| 1295 | else | ||
| 1296 | luaK_codeABC(fs, OP_GETUPVAL, extra, v->u.info, 0); | ||
| 1286 | luaK_reserveregs(fs, 1); | 1297 | luaK_reserveregs(fs, 1); |
| 1287 | } | 1298 | } |
| 1288 | } | 1299 | } |
| 1289 | 1300 | ||
| 1290 | /* | 1301 | /* |
| 1291 | ** Parse and compile a mulitple assignment. The first "variable" | 1302 | ** Parse and compile a multiple assignment. The first "variable" |
| 1292 | ** (a 'suffixedexp') was already read by the caller. | 1303 | ** (a 'suffixedexp') was already read by the caller. |
| 1293 | ** | 1304 | ** |
| 1294 | ** assignment -> suffixedexp restassign | 1305 | ** assignment -> suffixedexp restassign |
| @@ -1652,7 +1663,7 @@ static void localfunc (LexState *ls) { | |||
| 1652 | adjustlocalvars(ls, 1); /* enter its scope */ | 1663 | adjustlocalvars(ls, 1); /* enter its scope */ |
| 1653 | body(ls, &b, 0, ls->linenumber); /* function created in next register */ | 1664 | body(ls, &b, 0, ls->linenumber); /* function created in next register */ |
| 1654 | /* debug information will only see the variable after this point! */ | 1665 | /* debug information will only see the variable after this point! */ |
| 1655 | getlocvar(fs, b.u.info)->startpc = fs->pc; | 1666 | localdebuginfo(fs, b.u.info)->startpc = fs->pc; |
| 1656 | } | 1667 | } |
| 1657 | 1668 | ||
| 1658 | 1669 | ||
| @@ -1870,11 +1881,14 @@ static void statement (LexState *ls) { | |||
| 1870 | */ | 1881 | */ |
| 1871 | static void mainfunc (LexState *ls, FuncState *fs) { | 1882 | static void mainfunc (LexState *ls, FuncState *fs) { |
| 1872 | BlockCnt bl; | 1883 | BlockCnt bl; |
| 1873 | expdesc v; | 1884 | Upvaldesc *env; |
| 1874 | open_func(ls, fs, &bl); | 1885 | open_func(ls, fs, &bl); |
| 1875 | setvararg(fs, 0); /* main function is always declared vararg */ | 1886 | setvararg(fs, 0); /* main function is always declared vararg */ |
| 1876 | init_var(&v, VLOCAL, 0); /* create and... */ | 1887 | env = allocupvalue(fs); /* ...set environment upvalue */ |
| 1877 | newupvalue(fs, ls->envn, &v); /* ...set environment upvalue */ | 1888 | env->instack = 1; |
| 1889 | env->idx = 0; | ||
| 1890 | env->ro = 0; | ||
| 1891 | env->name = ls->envn; | ||
| 1878 | luaX_next(ls); /* read first token */ | 1892 | luaX_next(ls); /* read first token */ |
| 1879 | statlist(ls); /* parse main body */ | 1893 | statlist(ls); /* parse main body */ |
| 1880 | check(ls, TK_EOS); | 1894 | check(ls, TK_EOS); |
| @@ -33,8 +33,9 @@ typedef enum { | |||
| 33 | VKINT, /* integer constant; nval = numerical integer value */ | 33 | VKINT, /* integer constant; nval = numerical integer value */ |
| 34 | VNONRELOC, /* expression has its value in a fixed register; | 34 | VNONRELOC, /* expression has its value in a fixed register; |
| 35 | info = result register */ | 35 | info = result register */ |
| 36 | VLOCAL, /* local variable; var.idx = local register */ | 36 | VLOCAL, /* local variable; var.ridx = local register; |
| 37 | VUPVAL, /* upvalue variable; var.idx = index of upvalue in 'upvalues' */ | 37 | var.vidx = index in 'actvar.arr' */ |
| 38 | VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */ | ||
| 38 | VINDEXED, /* indexed variable; | 39 | VINDEXED, /* indexed variable; |
| 39 | ind.t = table register; | 40 | ind.t = table register; |
| 40 | ind.idx = key's R index */ | 41 | ind.idx = key's R index */ |
| @@ -70,8 +71,9 @@ typedef struct expdesc { | |||
| 70 | short idx; /* index (R or "long" K) */ | 71 | short idx; /* index (R or "long" K) */ |
| 71 | lu_byte t; /* table (register or upvalue) */ | 72 | lu_byte t; /* table (register or upvalue) */ |
| 72 | } ind; | 73 | } ind; |
| 73 | struct { /* for local variables and upvalues */ | 74 | struct { /* for local variables */ |
| 74 | lu_byte idx; /* index of the variable */ | 75 | lu_byte sidx; /* index in the stack */ |
| 76 | unsigned short vidx; /* index in 'actvar.arr' */ | ||
| 75 | } var; | 77 | } var; |
| 76 | } u; | 78 | } u; |
| 77 | int t; /* patch list of 'exit when true' */ | 79 | int t; /* patch list of 'exit when true' */ |
| @@ -81,9 +83,10 @@ typedef struct expdesc { | |||
| 81 | 83 | ||
| 82 | /* description of an active local variable */ | 84 | /* description of an active local variable */ |
| 83 | typedef struct Vardesc { | 85 | typedef struct Vardesc { |
| 84 | TValue val; /* constant value (if variable is 'const') */ | 86 | TValuefields; /* constant value (if variable is 'const') */ |
| 85 | short idx; /* index of the variable in the Proto's 'locvars' array */ | ||
| 86 | lu_byte ro; /* true if variable is 'const' */ | 87 | lu_byte ro; /* true if variable is 'const' */ |
| 88 | lu_byte sidx; /* index of the variable in the stack */ | ||
| 89 | short pidx; /* index of the variable in the Proto's 'locvars' array */ | ||
| 87 | } Vardesc; | 90 | } Vardesc; |
| 88 | 91 | ||
| 89 | 92 | ||
| @@ -144,7 +147,6 @@ typedef struct FuncState { | |||
| 144 | } FuncState; | 147 | } FuncState; |
| 145 | 148 | ||
| 146 | 149 | ||
| 147 | LUAI_FUNC Vardesc *luaY_getvardesc (FuncState **fs, const expdesc *e); | ||
| 148 | LUAI_FUNC LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, | 150 | LUAI_FUNC LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, |
| 149 | Dyndata *dyd, const char *name, int firstchar); | 151 | Dyndata *dyd, const char *name, int firstchar); |
| 150 | 152 | ||
| @@ -203,6 +203,7 @@ static void LoadUpvalues (LoadState *S, Proto *f) { | |||
| 203 | for (i = 0; i < n; i++) { | 203 | for (i = 0; i < n; i++) { |
| 204 | f->upvalues[i].instack = LoadByte(S); | 204 | f->upvalues[i].instack = LoadByte(S); |
| 205 | f->upvalues[i].idx = LoadByte(S); | 205 | f->upvalues[i].idx = LoadByte(S); |
| 206 | f->upvalues[i].ro = LoadByte(S); | ||
| 206 | } | 207 | } |
| 207 | } | 208 | } |
| 208 | 209 | ||
diff --git a/testes/locals.lua b/testes/locals.lua index 50230a27..0de00a98 100644 --- a/testes/locals.lua +++ b/testes/locals.lua | |||
| @@ -177,14 +177,24 @@ do -- constants | |||
| 177 | local <const> a, b, <const> c = 10, 20, 30 | 177 | local <const> a, b, <const> c = 10, 20, 30 |
| 178 | b = a + c + b -- 'b' is not constant | 178 | b = a + c + b -- 'b' is not constant |
| 179 | assert(a == 10 and b == 60 and c == 30) | 179 | assert(a == 10 and b == 60 and c == 30) |
| 180 | local function checkro (code, name) | 180 | local function checkro (name, code) |
| 181 | local st, msg = load(code) | 181 | local st, msg = load(code) |
| 182 | local gab = string.format("attempt to assign to const variable '%s'", name) | 182 | local gab = string.format("attempt to assign to const variable '%s'", name) |
| 183 | assert(not st and string.find(msg, gab)) | 183 | assert(not st and string.find(msg, gab)) |
| 184 | end | 184 | end |
| 185 | checkro("local x, <const> y, z = 10, 20, 30; x = 11; y = 12", "y") | 185 | checkro("y", "local x, <const> y, z = 10, 20, 30; x = 11; y = 12") |
| 186 | checkro("local <const> x, y, <const> z = 10, 20, 30; x = 11", "x") | 186 | checkro("x", "local <const> x, y, <const> z = 10, 20, 30; x = 11") |
| 187 | checkro("local <const> x, y, <const> z = 10, 20, 30; y = 10; z = 11", "z") | 187 | checkro("z", "local <const> x, y, <const> z = 10, 20, 30; y = 10; z = 11") |
| 188 | |||
| 189 | checkro("z", [[ | ||
| 190 | local a, <const> z, b = 10; | ||
| 191 | function foo() a = 20; z = 32; end | ||
| 192 | ]]) | ||
| 193 | |||
| 194 | checkro("var1", [[ | ||
| 195 | local a, <const> var1 = 10; | ||
| 196 | function foo() a = 20; z = function () var1 = 12; end end | ||
| 197 | ]]) | ||
| 188 | end | 198 | end |
| 189 | 199 | ||
| 190 | 200 | ||
