diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-10-08 10:42:07 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-10-08 10:42:07 -0300 |
commit | 4cd1f4aac01184765818e0cebf02da454ccf6590 (patch) | |
tree | c7e6398095afccc9987ed42598477094b6ee2aa6 | |
parent | b114c7d4871051cbdd7af185a61f35fe4028da79 (diff) | |
download | lua-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.c | 4 | ||||
-rw-r--r-- | ldo.c | 3 | ||||
-rw-r--r-- | lgc.c | 4 | ||||
-rw-r--r-- | ljumptab.h | 1 | ||||
-rw-r--r-- | lobject.h | 4 | ||||
-rw-r--r-- | lopcodes.c | 1 | ||||
-rw-r--r-- | lopcodes.h | 1 | ||||
-rw-r--r-- | lopnames.h | 1 | ||||
-rw-r--r-- | lparser.c | 34 | ||||
-rw-r--r-- | lparser.h | 1 | ||||
-rw-r--r-- | lstate.h | 3 | ||||
-rw-r--r-- | ltests.c | 25 | ||||
-rw-r--r-- | lvm.c | 6 | ||||
-rw-r--r-- | testes/code.lua | 6 | ||||
-rw-r--r-- | testes/locals.lua | 9 |
15 files changed, 81 insertions, 22 deletions
@@ -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 | } |
@@ -91,8 +91,7 @@ struct lua_longjmp { | |||
91 | static void seterrorobj (lua_State *L, int errcode, StkId oldtop) { | 91 | static 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: { |
@@ -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: |
@@ -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, |
@@ -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 | ||
@@ -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 */ |
@@ -251,6 +251,7 @@ OP_LEN,/* A B R(A) := length of R(B) */ | |||
251 | OP_CONCAT,/* A B R(A) := R(A).. ... ..R(A + B - 1) */ | 251 | OP_CONCAT,/* A B R(A) := R(A).. ... ..R(A + B - 1) */ |
252 | 252 | ||
253 | OP_CLOSE,/* A close all upvalues >= R(A) */ | 253 | OP_CLOSE,/* A close all upvalues >= R(A) */ |
254 | OP_TBC,/* A mark variable A "to be closed" */ | ||
254 | OP_JMP,/* k sJ pc += sJ (k is used in code generation) */ | 255 | OP_JMP,/* k sJ pc += sJ (k is used in code generation) */ |
255 | OP_EQ,/* A B if ((R(A) == R(B)) ~= k) then pc++ */ | 256 | OP_EQ,/* A B if ((R(A) == R(B)) ~= k) then pc++ */ |
256 | OP_LT,/* A B if ((R(A) < R(B)) ~= k) then pc++ */ | 257 | OP_LT,/* A B if ((R(A) < R(B)) ~= k) then pc++ */ |
@@ -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", |
@@ -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 | ||
1512 | static void localstat (LexState *ls) { | 1514 | static 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 | ||
1535 | static 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 | |||
1546 | static 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 | |||
1532 | static int funcname (LexState *ls, expdesc *v) { | 1558 | static int funcname (LexState *ls, expdesc *v) { |
1533 | /* funcname -> NAME {fieldsel} [':' NAME] */ | 1559 | /* funcname -> NAME {fieldsel} [':' NAME] */ |
1534 | int ismethod = 0; | 1560 | int ismethod = 0; |
@@ -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 | ||
@@ -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 | /* |
@@ -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 | ||
524 | static char *buildop (Proto *p, int pc, char *buff) { | 525 | static 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 | ||
@@ -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 |
67 | check(function () | 67 | check(function () -- function does not create upvalues |
68 | (function () end){f()} | 68 | (function () end){f()} |
69 | end, 'CLOSURE', 'NEWTABLE', 'GETTABUP', 'CALL', 'SETLIST', 'CALL', 'RETURN0') | ||
70 | |||
71 | check(function (x) -- function creates upvalues | ||
72 | (function () return x end){f()} | ||
69 | end, 'CLOSURE', 'NEWTABLE', 'GETTABUP', 'CALL', 'SETLIST', 'CALL', 'RETURN') | 73 | end, '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 | |||
173 | assert(x==20) | 173 | assert(x==20) |
174 | 174 | ||
175 | 175 | ||
176 | -- tests for to-be-closed variables | ||
177 | do | ||
178 | local scoped x = 3 | ||
179 | local a | ||
180 | local scoped y = 5 | ||
181 | assert(x == 3 and y == 5) | ||
182 | end | ||
183 | |||
184 | |||
176 | print('OK') | 185 | print('OK') |
177 | 186 | ||
178 | return 5,f | 187 | return 5,f |