aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-10-08 10:42:07 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-10-08 10:42:07 -0300
commit4cd1f4aac01184765818e0cebf02da454ccf6590 (patch)
treec7e6398095afccc9987ed42598477094b6ee2aa6
parentb114c7d4871051cbdd7af185a61f35fe4028da79 (diff)
downloadlua-4cd1f4aac01184765818e0cebf02da454ccf6590.tar.gz
lua-4cd1f4aac01184765818e0cebf02da454ccf6590.tar.bz2
lua-4cd1f4aac01184765818e0cebf02da454ccf6590.zip
Towards "to closed" local variables
Start of the implementation of "scoped variables" or "to be closed" variables, local variables whose '__close' (or themselves) are called when they go out of scope. This commit implements the syntax, the opcode, and the creation of the corresponding upvalue, but it still does not call the finalizations when the variable goes out of scope (the most important part). Currently, the syntax is 'local scoped name = exp', but that will probably change.
-rw-r--r--lcode.c4
-rw-r--r--ldo.c3
-rw-r--r--lgc.c4
-rw-r--r--ljumptab.h1
-rw-r--r--lobject.h4
-rw-r--r--lopcodes.c1
-rw-r--r--lopcodes.h1
-rw-r--r--lopnames.h1
-rw-r--r--lparser.c34
-rw-r--r--lparser.h1
-rw-r--r--lstate.h3
-rw-r--r--ltests.c25
-rw-r--r--lvm.c6
-rw-r--r--testes/code.lua6
-rw-r--r--testes/locals.lua9
15 files changed, 81 insertions, 22 deletions
diff --git a/lcode.c b/lcode.c
index d00038dd..e84b85ac 100644
--- a/lcode.c
+++ b/lcode.c
@@ -1673,13 +1673,13 @@ void luaK_finish (FuncState *fs) {
1673 lua_assert(i == 0 || isOT(*(pc - 1)) == isIT(*pc)); 1673 lua_assert(i == 0 || isOT(*(pc - 1)) == isIT(*pc));
1674 switch (GET_OPCODE(*pc)) { 1674 switch (GET_OPCODE(*pc)) {
1675 case OP_RETURN0: case OP_RETURN1: { 1675 case OP_RETURN0: case OP_RETURN1: {
1676 if (p->sizep == 0 && !p->is_vararg) 1676 if (!(fs->needclose || p->is_vararg))
1677 break; /* no extra work */ 1677 break; /* no extra work */
1678 /* else use OP_RETURN to do the extra work */ 1678 /* else use OP_RETURN to do the extra work */
1679 SET_OPCODE(*pc, OP_RETURN); 1679 SET_OPCODE(*pc, OP_RETURN);
1680 } /* FALLTHROUGH */ 1680 } /* FALLTHROUGH */
1681 case OP_RETURN: case OP_TAILCALL: { 1681 case OP_RETURN: case OP_TAILCALL: {
1682 if (p->sizep > 0 || p->is_vararg) { 1682 if (fs->needclose || p->is_vararg) {
1683 SETARG_C(*pc, p->is_vararg ? p->numparams + 1 : 0); 1683 SETARG_C(*pc, p->is_vararg ? p->numparams + 1 : 0);
1684 SETARG_k(*pc, 1); /* signal that there is extra work */ 1684 SETARG_k(*pc, 1); /* signal that there is extra work */
1685 } 1685 }
diff --git a/ldo.c b/ldo.c
index 0d68d36c..2349aaed 100644
--- a/ldo.c
+++ b/ldo.c
@@ -91,8 +91,7 @@ struct lua_longjmp {
91static void seterrorobj (lua_State *L, int errcode, StkId oldtop) { 91static void seterrorobj (lua_State *L, int errcode, StkId oldtop) {
92 switch (errcode) { 92 switch (errcode) {
93 case LUA_ERRMEM: { /* memory error? */ 93 case LUA_ERRMEM: { /* memory error? */
94 TString *memerrmsg = luaS_newliteral(L, MEMERRMSG); 94 setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */
95 setsvalue2s(L, oldtop, memerrmsg); /* reuse preregistered msg. */
96 break; 95 break;
97 } 96 }
98 case LUA_ERRERR: { 97 case LUA_ERRERR: {
diff --git a/lgc.c b/lgc.c
index e8429e1b..39b3ab73 100644
--- a/lgc.c
+++ b/lgc.c
@@ -293,7 +293,8 @@ static void reallymarkobject (global_State *g, GCObject *o) {
293 gray2black(o); 293 gray2black(o);
294 break; 294 break;
295 } 295 }
296 case LUA_TUPVAL: { 296 case LUA_TUPVAL:
297 case LUA_TUPVALTBC: {
297 UpVal *uv = gco2upv(o); 298 UpVal *uv = gco2upv(o);
298 if (!upisopen(uv)) /* open upvalues are kept gray */ 299 if (!upisopen(uv)) /* open upvalues are kept gray */
299 gray2black(o); 300 gray2black(o);
@@ -760,6 +761,7 @@ static void freeobj (lua_State *L, GCObject *o) {
760 luaF_freeproto(L, gco2p(o)); 761 luaF_freeproto(L, gco2p(o));
761 break; 762 break;
762 case LUA_TUPVAL: 763 case LUA_TUPVAL:
764 case LUA_TUPVALTBC:
763 freeupval(L, gco2upv(o)); 765 freeupval(L, gco2upv(o));
764 break; 766 break;
765 case LUA_TLCL: 767 case LUA_TLCL:
diff --git a/ljumptab.h b/ljumptab.h
index c775f10a..da4cf7b7 100644
--- a/ljumptab.h
+++ b/ljumptab.h
@@ -74,6 +74,7 @@ static void *disptab[] = {
74&&L_OP_LEN, 74&&L_OP_LEN,
75&&L_OP_CONCAT, 75&&L_OP_CONCAT,
76&&L_OP_CLOSE, 76&&L_OP_CLOSE,
77&&L_OP_TBC,
77&&L_OP_JMP, 78&&L_OP_JMP,
78&&L_OP_EQ, 79&&L_OP_EQ,
79&&L_OP_LT, 80&&L_OP_LT,
diff --git a/lobject.h b/lobject.h
index ddcf609c..ea3511de 100644
--- a/lobject.h
+++ b/lobject.h
@@ -588,6 +588,10 @@ typedef struct UpVal {
588} UpVal; 588} UpVal;
589 589
590 590
591/* variant for "To Be Closed" upvalues */
592#define LUA_TUPVALTBC (LUA_TUPVAL | (1 << 4))
593
594
591#define ClosureHeader \ 595#define ClosureHeader \
592 CommonHeader; lu_byte nupvalues; GCObject *gclist 596 CommonHeader; lu_byte nupvalues; GCObject *gclist
593 597
diff --git a/lopcodes.c b/lopcodes.c
index 95347b27..f6915beb 100644
--- a/lopcodes.c
+++ b/lopcodes.c
@@ -68,6 +68,7 @@ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = {
68 ,opmode(0, 0, 0, 1, iABC) /* OP_LEN */ 68 ,opmode(0, 0, 0, 1, iABC) /* OP_LEN */
69 ,opmode(0, 0, 0, 1, iABC) /* OP_CONCAT */ 69 ,opmode(0, 0, 0, 1, iABC) /* OP_CONCAT */
70 ,opmode(0, 0, 0, 0, iABC) /* OP_CLOSE */ 70 ,opmode(0, 0, 0, 0, iABC) /* OP_CLOSE */
71 ,opmode(0, 0, 0, 0, iABC) /* OP_TBC */
71 ,opmode(0, 0, 0, 0, isJ) /* OP_JMP */ 72 ,opmode(0, 0, 0, 0, isJ) /* OP_JMP */
72 ,opmode(0, 0, 1, 0, iABC) /* OP_EQ */ 73 ,opmode(0, 0, 1, 0, iABC) /* OP_EQ */
73 ,opmode(0, 0, 1, 0, iABC) /* OP_LT */ 74 ,opmode(0, 0, 1, 0, iABC) /* OP_LT */
diff --git a/lopcodes.h b/lopcodes.h
index 9442a336..4d144000 100644
--- a/lopcodes.h
+++ b/lopcodes.h
@@ -251,6 +251,7 @@ OP_LEN,/* A B R(A) := length of R(B) */
251OP_CONCAT,/* A B R(A) := R(A).. ... ..R(A + B - 1) */ 251OP_CONCAT,/* A B R(A) := R(A).. ... ..R(A + B - 1) */
252 252
253OP_CLOSE,/* A close all upvalues >= R(A) */ 253OP_CLOSE,/* A close all upvalues >= R(A) */
254OP_TBC,/* A mark variable A "to be closed" */
254OP_JMP,/* k sJ pc += sJ (k is used in code generation) */ 255OP_JMP,/* k sJ pc += sJ (k is used in code generation) */
255OP_EQ,/* A B if ((R(A) == R(B)) ~= k) then pc++ */ 256OP_EQ,/* A B if ((R(A) == R(B)) ~= k) then pc++ */
256OP_LT,/* A B if ((R(A) < R(B)) ~= k) then pc++ */ 257OP_LT,/* A B if ((R(A) < R(B)) ~= k) then pc++ */
diff --git a/lopnames.h b/lopnames.h
index c40eaeae..304d3b6c 100644
--- a/lopnames.h
+++ b/lopnames.h
@@ -59,6 +59,7 @@ static const char *const opnames[] = {
59 "LEN", 59 "LEN",
60 "CONCAT", 60 "CONCAT",
61 "CLOSE", 61 "CLOSE",
62 "TBC",
62 "JMP", 63 "JMP",
63 "EQ", 64 "EQ",
64 "LT", 65 "LT",
diff --git a/lparser.c b/lparser.c
index 32500b02..84abeb90 100644
--- a/lparser.c
+++ b/lparser.c
@@ -255,6 +255,7 @@ static void markupval (FuncState *fs, int level) {
255 while (bl->nactvar > level) 255 while (bl->nactvar > level)
256 bl = bl->previous; 256 bl = bl->previous;
257 bl->upval = 1; 257 bl->upval = 1;
258 fs->needclose = 1;
258} 259}
259 260
260 261
@@ -547,6 +548,7 @@ static void open_func (LexState *ls, FuncState *fs, BlockCnt *bl) {
547 fs->nups = 0; 548 fs->nups = 0;
548 fs->nlocvars = 0; 549 fs->nlocvars = 0;
549 fs->nactvar = 0; 550 fs->nactvar = 0;
551 fs->needclose = 0;
550 fs->firstlocal = ls->dyd->actvar.n; 552 fs->firstlocal = ls->dyd->actvar.n;
551 fs->bl = NULL; 553 fs->bl = NULL;
552 f->source = ls->source; 554 f->source = ls->source;
@@ -1509,15 +1511,16 @@ static void localfunc (LexState *ls) {
1509} 1511}
1510 1512
1511 1513
1512static void localstat (LexState *ls) { 1514static void commonlocalstat (LexState *ls, TString *firstvar) {
1513 /* stat -> LOCAL NAME {',' NAME} ['=' explist] */ 1515 /* stat -> LOCAL NAME {',' NAME} ['=' explist] */
1514 int nvars = 0; 1516 int nvars = 1;
1515 int nexps; 1517 int nexps;
1516 expdesc e; 1518 expdesc e;
1517 do { 1519 new_localvar(ls, firstvar);
1520 while (testnext(ls, ',')) {
1518 new_localvar(ls, str_checkname(ls)); 1521 new_localvar(ls, str_checkname(ls));
1519 nvars++; 1522 nvars++;
1520 } while (testnext(ls, ',')); 1523 }
1521 if (testnext(ls, '=')) 1524 if (testnext(ls, '='))
1522 nexps = explist(ls, &e); 1525 nexps = explist(ls, &e);
1523 else { 1526 else {
@@ -1529,6 +1532,29 @@ static void localstat (LexState *ls) {
1529} 1532}
1530 1533
1531 1534
1535static void scopedlocalstat (LexState *ls) {
1536 FuncState *fs = ls->fs;
1537 new_localvar(ls, str_checkname(ls));
1538 checknext(ls, '=');
1539 luaK_codeABC(fs, OP_TBC, fs->nactvar, 0, 0);
1540 markupval(fs, fs->nactvar);
1541 exp1(ls, 0);
1542 adjustlocalvars(ls, 1);
1543}
1544
1545
1546static void localstat (LexState *ls) {
1547 /* stat -> LOCAL NAME {',' NAME} ['=' explist]
1548 | LOCAL SCOPED NAME '=' exp */
1549 TString *firstvar = str_checkname(ls);
1550 if (ls->t.token == TK_NAME &&
1551 eqshrstr(firstvar, luaS_newliteral(ls->L, "scoped")))
1552 scopedlocalstat(ls);
1553 else
1554 commonlocalstat(ls, firstvar);
1555}
1556
1557
1532static int funcname (LexState *ls, expdesc *v) { 1558static int funcname (LexState *ls, expdesc *v) {
1533 /* funcname -> NAME {fieldsel} [':' NAME] */ 1559 /* funcname -> NAME {fieldsel} [':' NAME] */
1534 int ismethod = 0; 1560 int ismethod = 0;
diff --git a/lparser.h b/lparser.h
index e158c9d9..1b94a97a 100644
--- a/lparser.h
+++ b/lparser.h
@@ -133,6 +133,7 @@ typedef struct FuncState {
133 lu_byte nups; /* number of upvalues */ 133 lu_byte nups; /* number of upvalues */
134 lu_byte freereg; /* first free register */ 134 lu_byte freereg; /* first free register */
135 lu_byte iwthabs; /* instructions issued since last absolute line info */ 135 lu_byte iwthabs; /* instructions issued since last absolute line info */
136 lu_byte needclose; /* function needs to close upvalues when returning */
136} FuncState; 137} FuncState;
137 138
138 139
diff --git a/lstate.h b/lstate.h
index 5461b291..f08c2355 100644
--- a/lstate.h
+++ b/lstate.h
@@ -267,7 +267,8 @@ union GCUnion {
267#define gco2t(o) check_exp((o)->tt == LUA_TTABLE, &((cast_u(o))->h)) 267#define gco2t(o) check_exp((o)->tt == LUA_TTABLE, &((cast_u(o))->h))
268#define gco2p(o) check_exp((o)->tt == LUA_TPROTO, &((cast_u(o))->p)) 268#define gco2p(o) check_exp((o)->tt == LUA_TPROTO, &((cast_u(o))->p))
269#define gco2th(o) check_exp((o)->tt == LUA_TTHREAD, &((cast_u(o))->th)) 269#define gco2th(o) check_exp((o)->tt == LUA_TTHREAD, &((cast_u(o))->th))
270#define gco2upv(o) check_exp((o)->tt == LUA_TUPVAL, &((cast_u(o))->upv)) 270#define gco2upv(o) \
271 check_exp(novariant((o)->tt) == LUA_TUPVAL, &((cast_u(o))->upv))
271 272
272 273
273/* 274/*
diff --git a/ltests.c b/ltests.c
index bc71d937..ff962543 100644
--- a/ltests.c
+++ b/ltests.c
@@ -357,7 +357,8 @@ static void checkrefs (global_State *g, GCObject *o) {
357 checkudata(g, gco2u(o)); 357 checkudata(g, gco2u(o));
358 break; 358 break;
359 } 359 }
360 case LUA_TUPVAL: { 360 case LUA_TUPVAL:
361 case LUA_TUPVALTBC: {
361 checkvalref(g, o, gco2upv(o)->v); 362 checkvalref(g, o, gco2upv(o)->v);
362 break; 363 break;
363 } 364 }
@@ -522,35 +523,37 @@ int lua_checkmemory (lua_State *L) {
522 523
523 524
524static char *buildop (Proto *p, int pc, char *buff) { 525static char *buildop (Proto *p, int pc, char *buff) {
526 char *obuff = buff;
525 Instruction i = p->code[pc]; 527 Instruction i = p->code[pc];
526 OpCode o = GET_OPCODE(i); 528 OpCode o = GET_OPCODE(i);
527 const char *name = opnames[o]; 529 const char *name = opnames[o];
528 int line = luaG_getfuncline(p, pc); 530 int line = luaG_getfuncline(p, pc);
529 int lineinfo = (p->lineinfo != NULL) ? p->lineinfo[pc] : 0; 531 int lineinfo = (p->lineinfo != NULL) ? p->lineinfo[pc] : 0;
530 sprintf(buff, "(%2d - %4d) %4d - ", lineinfo, line, pc); 532 if (lineinfo == ABSLINEINFO)
533 buff += sprintf(buff, "(__");
534 else
535 buff += sprintf(buff, "(%2d", lineinfo);
536 buff += sprintf(buff, " - %4d) %4d - ", line, pc);
531 switch (getOpMode(o)) { 537 switch (getOpMode(o)) {
532 case iABC: 538 case iABC:
533 sprintf(buff+strlen(buff), "%-12s%4d %4d %4d%s", name, 539 sprintf(buff, "%-12s%4d %4d %4d%s", name,
534 GETARG_A(i), GETARG_B(i), GETARG_C(i), 540 GETARG_A(i), GETARG_B(i), GETARG_C(i),
535 GETARG_k(i) ? " (k)" : ""); 541 GETARG_k(i) ? " (k)" : "");
536 break; 542 break;
537 case iABx: 543 case iABx:
538 sprintf(buff+strlen(buff), "%-12s%4d %4d", name, GETARG_A(i), 544 sprintf(buff, "%-12s%4d %4d", name, GETARG_A(i), GETARG_Bx(i));
539 GETARG_Bx(i));
540 break; 545 break;
541 case iAsBx: 546 case iAsBx:
542 sprintf(buff+strlen(buff), "%-12s%4d %4d", name, GETARG_A(i), 547 sprintf(buff, "%-12s%4d %4d", name, GETARG_A(i), GETARG_sBx(i));
543 GETARG_sBx(i));
544 break; 548 break;
545 case iAx: 549 case iAx:
546 sprintf(buff+strlen(buff), "%-12s%4d", name, GETARG_Ax(i)); 550 sprintf(buff, "%-12s%4d", name, GETARG_Ax(i));
547 break; 551 break;
548 case isJ: 552 case isJ:
549 sprintf(buff+strlen(buff), "%-12s%4d (%1d)", name, GETARG_sJ(i), 553 sprintf(buff, "%-12s%4d (%1d)", name, GETARG_sJ(i), !!GETARG_m(i));
550 !!GETARG_m(i));
551 break; 554 break;
552 } 555 }
553 return buff; 556 return obuff;
554} 557}
555 558
556 559
diff --git a/lvm.c b/lvm.c
index dd6a660b..fdd99a42 100644
--- a/lvm.c
+++ b/lvm.c
@@ -1455,6 +1455,12 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
1455 luaF_close(L, ra); 1455 luaF_close(L, ra);
1456 vmbreak; 1456 vmbreak;
1457 } 1457 }
1458 vmcase(OP_TBC) {
1459 UpVal *up = luaF_findupval(L, ra); /* create new upvalue */
1460 up->tt = LUA_TUPVALTBC; /* mark it to be closed */
1461 setnilvalue(s2v(ra)); /* intialize it with nil */
1462 vmbreak;
1463 }
1458 vmcase(OP_JMP) { 1464 vmcase(OP_JMP) {
1459 dojump(ci, i, 0); 1465 dojump(ci, i, 0);
1460 vmbreak; 1466 vmbreak;
diff --git a/testes/code.lua b/testes/code.lua
index 6bd6ebfa..ad484485 100644
--- a/testes/code.lua
+++ b/testes/code.lua
@@ -64,8 +64,12 @@ end
64 64
65 65
66-- some basic instructions 66-- some basic instructions
67check(function () 67check(function () -- function does not create upvalues
68 (function () end){f()} 68 (function () end){f()}
69end, 'CLOSURE', 'NEWTABLE', 'GETTABUP', 'CALL', 'SETLIST', 'CALL', 'RETURN0')
70
71check(function (x) -- function creates upvalues
72 (function () return x end){f()}
69end, 'CLOSURE', 'NEWTABLE', 'GETTABUP', 'CALL', 'SETLIST', 'CALL', 'RETURN') 73end, 'CLOSURE', 'NEWTABLE', 'GETTABUP', 'CALL', 'SETLIST', 'CALL', 'RETURN')
70 74
71 75
diff --git a/testes/locals.lua b/testes/locals.lua
index 14e49a7c..20ecae4b 100644
--- a/testes/locals.lua
+++ b/testes/locals.lua
@@ -173,6 +173,15 @@ end
173assert(x==20) 173assert(x==20)
174 174
175 175
176-- tests for to-be-closed variables
177do
178 local scoped x = 3
179 local a
180 local scoped y = 5
181 assert(x == 3 and y == 5)
182end
183
184
176print('OK') 185print('OK')
177 186
178return 5,f 187return 5,f