diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-11-07 14:42:05 -0200 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-11-07 14:42:05 -0200 |
commit | 7f6f70853c8a2730fca2e95d5968ad52cf470bda (patch) | |
tree | 948147a9cf6a5c5eb34232e7547c310eb06eadea | |
parent | b8fed93215a23a3f443c5b0126f0de1725771b44 (diff) | |
download | lua-7f6f70853c8a2730fca2e95d5968ad52cf470bda.tar.gz lua-7f6f70853c8a2730fca2e95d5968ad52cf470bda.tar.bz2 lua-7f6f70853c8a2730fca2e95d5968ad52cf470bda.zip |
To-be-closed variable in 'for' loop separated from the state
The variable to be closed in a generic 'for' loop now is the
4th value produced in the loop initialization, instead of being
the loop state (the 2nd value produced). That allows a loop to
use a state with a '__toclose' metamethod but do not close it.
(As an example, 'f:lines()' might use the file 'f' as a state
for the loop, but it should not close the file when the loop ends.)
-rw-r--r-- | liolib.c | 6 | ||||
-rw-r--r-- | lopcodes.h | 6 | ||||
-rw-r--r-- | lparser.c | 25 | ||||
-rw-r--r-- | lvm.c | 23 | ||||
-rw-r--r-- | testes/files.lua | 37 | ||||
-rw-r--r-- | testes/locals.lua | 8 |
6 files changed, 76 insertions, 29 deletions
@@ -386,8 +386,10 @@ static int io_lines (lua_State *L) { | |||
386 | } | 386 | } |
387 | aux_lines(L, toclose); /* push iteration function */ | 387 | aux_lines(L, toclose); /* push iteration function */ |
388 | if (toclose) { | 388 | if (toclose) { |
389 | lua_pushvalue(L, 1); /* file will be second result */ | 389 | lua_pushnil(L); /* state */ |
390 | return 2; | 390 | lua_pushnil(L); /* control */ |
391 | lua_pushvalue(L, 1); /* file is the to-be-closed variable (4th result) */ | ||
392 | return 4; | ||
391 | } | 393 | } |
392 | else | 394 | else |
393 | return 1; | 395 | return 1; |
@@ -279,9 +279,9 @@ OP_FORLOOP,/* A Bx R(A)+=R(A+2); | |||
279 | if R(A) <?= R(A+1) then { pc-=Bx; R(A+3)=R(A) } */ | 279 | if R(A) <?= R(A+1) then { pc-=Bx; R(A+3)=R(A) } */ |
280 | OP_FORPREP,/* A Bx R(A)-=R(A+2); pc+=Bx */ | 280 | OP_FORPREP,/* A Bx R(A)-=R(A+2); pc+=Bx */ |
281 | 281 | ||
282 | OP_TFORPREP,/* A Bx create upvalue A; pc+=Bx */ | 282 | OP_TFORPREP,/* A Bx create upvalue for R(A + 3); pc+=Bx */ |
283 | OP_TFORCALL,/* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */ | 283 | OP_TFORCALL,/* A C R(A+4), ... ,R(A+3+C) := R(A)(R(A+1), R(A+2)); */ |
284 | OP_TFORLOOP,/* A Bx if R(A+1) ~= nil then { R(A)=R(A+1); pc -= Bx } */ | 284 | OP_TFORLOOP,/* A Bx if R(A+2) ~= nil then { R(A)=R(A+2); pc -= Bx } */ |
285 | 285 | ||
286 | OP_SETLIST,/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */ | 286 | OP_SETLIST,/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */ |
287 | 287 | ||
@@ -165,6 +165,9 @@ static int registerlocalvar (LexState *ls, TString *varname) { | |||
165 | } | 165 | } |
166 | 166 | ||
167 | 167 | ||
168 | /* | ||
169 | ** Create a new local variable with the given 'name'. | ||
170 | */ | ||
168 | static void new_localvar (LexState *ls, TString *name) { | 171 | static void new_localvar (LexState *ls, TString *name) { |
169 | FuncState *fs = ls->fs; | 172 | FuncState *fs = ls->fs; |
170 | Dyndata *dyd = ls->dyd; | 173 | Dyndata *dyd = ls->dyd; |
@@ -176,13 +179,8 @@ static void new_localvar (LexState *ls, TString *name) { | |||
176 | dyd->actvar.arr[dyd->actvar.n++].idx = cast(short, reg); | 179 | dyd->actvar.arr[dyd->actvar.n++].idx = cast(short, reg); |
177 | } | 180 | } |
178 | 181 | ||
179 | |||
180 | static void new_localvarliteral_ (LexState *ls, const char *name, size_t sz) { | ||
181 | new_localvar(ls, luaX_newstring(ls, name, sz)); | ||
182 | } | ||
183 | |||
184 | #define new_localvarliteral(ls,v) \ | 182 | #define new_localvarliteral(ls,v) \ |
185 | new_localvarliteral_(ls, "" v, (sizeof(v)/sizeof(char))-1) | 183 | new_localvar(ls, luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char)) - 1)); |
186 | 184 | ||
187 | 185 | ||
188 | static LocVar *getlocvar (FuncState *fs, int i) { | 186 | static LocVar *getlocvar (FuncState *fs, int i) { |
@@ -192,6 +190,9 @@ static LocVar *getlocvar (FuncState *fs, int i) { | |||
192 | } | 190 | } |
193 | 191 | ||
194 | 192 | ||
193 | /* | ||
194 | ** Start the scope for the last 'nvars' created variables. | ||
195 | */ | ||
195 | static void adjustlocalvars (LexState *ls, int nvars) { | 196 | static void adjustlocalvars (LexState *ls, int nvars) { |
196 | FuncState *fs = ls->fs; | 197 | FuncState *fs = ls->fs; |
197 | fs->nactvar = cast_byte(fs->nactvar + nvars); | 198 | fs->nactvar = cast_byte(fs->nactvar + nvars); |
@@ -1357,7 +1358,6 @@ static void forbody (LexState *ls, int base, int line, int nvars, int kind) { | |||
1357 | BlockCnt bl; | 1358 | BlockCnt bl; |
1358 | FuncState *fs = ls->fs; | 1359 | FuncState *fs = ls->fs; |
1359 | int prep, endfor; | 1360 | int prep, endfor; |
1360 | adjustlocalvars(ls, 3); /* control variables */ | ||
1361 | checknext(ls, TK_DO); | 1361 | checknext(ls, TK_DO); |
1362 | prep = luaK_codeABx(fs, forprep[kind], base, 0); | 1362 | prep = luaK_codeABx(fs, forprep[kind], base, 0); |
1363 | enterblock(fs, &bl, 0); /* scope for declared variables */ | 1363 | enterblock(fs, &bl, 0); /* scope for declared variables */ |
@@ -1399,6 +1399,7 @@ static void fornum (LexState *ls, TString *varname, int line) { | |||
1399 | luaK_int(fs, fs->freereg, 1); | 1399 | luaK_int(fs, fs->freereg, 1); |
1400 | luaK_reserveregs(fs, 1); | 1400 | luaK_reserveregs(fs, 1); |
1401 | } | 1401 | } |
1402 | adjustlocalvars(ls, 3); /* control variables */ | ||
1402 | forbody(ls, base, line, 1, basicfor); | 1403 | forbody(ls, base, line, 1, basicfor); |
1403 | } | 1404 | } |
1404 | 1405 | ||
@@ -1407,7 +1408,7 @@ static void forlist (LexState *ls, TString *indexname) { | |||
1407 | /* forlist -> NAME {,NAME} IN explist forbody */ | 1408 | /* forlist -> NAME {,NAME} IN explist forbody */ |
1408 | FuncState *fs = ls->fs; | 1409 | FuncState *fs = ls->fs; |
1409 | expdesc e; | 1410 | expdesc e; |
1410 | int nvars = 4; /* gen, state, control, plus at least one declared var */ | 1411 | int nvars = 5; /* gen, state, control, toclose, 'indexname' */ |
1411 | int line; | 1412 | int line; |
1412 | int base = fs->freereg; | 1413 | int base = fs->freereg; |
1413 | /* create control variables */ | 1414 | /* create control variables */ |
@@ -1415,6 +1416,7 @@ static void forlist (LexState *ls, TString *indexname) { | |||
1415 | new_localvarliteral(ls, "(for state)"); | 1416 | new_localvarliteral(ls, "(for state)"); |
1416 | markupval(fs, fs->nactvar); /* state may create an upvalue */ | 1417 | markupval(fs, fs->nactvar); /* state may create an upvalue */ |
1417 | new_localvarliteral(ls, "(for control)"); | 1418 | new_localvarliteral(ls, "(for control)"); |
1419 | new_localvarliteral(ls, "(for toclose)"); | ||
1418 | /* create declared variables */ | 1420 | /* create declared variables */ |
1419 | new_localvar(ls, indexname); | 1421 | new_localvar(ls, indexname); |
1420 | while (testnext(ls, ',')) { | 1422 | while (testnext(ls, ',')) { |
@@ -1423,9 +1425,10 @@ static void forlist (LexState *ls, TString *indexname) { | |||
1423 | } | 1425 | } |
1424 | checknext(ls, TK_IN); | 1426 | checknext(ls, TK_IN); |
1425 | line = ls->linenumber; | 1427 | line = ls->linenumber; |
1426 | adjust_assign(ls, 3, explist(ls, &e), &e); | 1428 | adjust_assign(ls, 4, explist(ls, &e), &e); |
1429 | adjustlocalvars(ls, 4); /* control variables */ | ||
1427 | luaK_checkstack(fs, 3); /* extra space to call generator */ | 1430 | luaK_checkstack(fs, 3); /* extra space to call generator */ |
1428 | forbody(ls, base, line, nvars - 3, 2); | 1431 | forbody(ls, base, line, nvars - 4, 2); |
1429 | } | 1432 | } |
1430 | 1433 | ||
1431 | 1434 | ||
@@ -1575,9 +1578,9 @@ static void tocloselocalstat (LexState *ls) { | |||
1575 | new_localvar(ls, str_checkname(ls)); | 1578 | new_localvar(ls, str_checkname(ls)); |
1576 | checknext(ls, '='); | 1579 | checknext(ls, '='); |
1577 | exp1(ls, 0); | 1580 | exp1(ls, 0); |
1578 | luaK_codeABC(fs, OP_TBC, fs->nactvar, 0, 0); | ||
1579 | markupval(fs, fs->nactvar); | 1581 | markupval(fs, fs->nactvar); |
1580 | adjustlocalvars(ls, 1); | 1582 | adjustlocalvars(ls, 1); |
1583 | luaK_codeABC(fs, OP_TBC, fs->nactvar - 1, 0, 0); | ||
1581 | } | 1584 | } |
1582 | 1585 | ||
1583 | 1586 | ||
@@ -1654,11 +1654,11 @@ void luaV_execute (lua_State *L, CallInfo *ci) { | |||
1654 | vmbreak; | 1654 | vmbreak; |
1655 | } | 1655 | } |
1656 | vmcase(OP_TFORPREP) { | 1656 | vmcase(OP_TFORPREP) { |
1657 | /* is 'state' a function or has a '__close' metamethod? */ | 1657 | /* is 'toclose' a function or has a '__close' metamethod? */ |
1658 | if (ttisfunction(s2v(ra + 1)) || | 1658 | if (ttisfunction(s2v(ra + 3)) || |
1659 | !ttisnil(luaT_gettmbyobj(L, s2v(ra + 1), TM_CLOSE))) { | 1659 | !ttisnil(luaT_gettmbyobj(L, s2v(ra + 3), TM_CLOSE))) { |
1660 | /* create to-be-closed upvalue for it */ | 1660 | /* create to-be-closed upvalue for it */ |
1661 | halfProtect(luaF_newtbcupval(L, ra + 1)); | 1661 | halfProtect(luaF_newtbcupval(L, ra + 3)); |
1662 | } | 1662 | } |
1663 | pc += GETARG_Bx(i); | 1663 | pc += GETARG_Bx(i); |
1664 | i = *(pc++); /* go to next instruction */ | 1664 | i = *(pc++); /* go to next instruction */ |
@@ -1668,13 +1668,14 @@ void luaV_execute (lua_State *L, CallInfo *ci) { | |||
1668 | vmcase(OP_TFORCALL) { | 1668 | vmcase(OP_TFORCALL) { |
1669 | l_tforcall: | 1669 | l_tforcall: |
1670 | /* 'ra' has the iterator function, 'ra + 1' has the state, | 1670 | /* 'ra' has the iterator function, 'ra + 1' has the state, |
1671 | and 'ra + 2' has the control variable. The call will use | 1671 | 'ra + 2' has the control variable, and 'ra + 3' has the |
1672 | the stack after these values (starting at 'ra + 3') | 1672 | to-be-closed variable. The call will use the stack after |
1673 | these values (starting at 'ra + 4') | ||
1673 | */ | 1674 | */ |
1674 | /* push function, state, and control variable */ | 1675 | /* push function, state, and control variable */ |
1675 | memcpy(ra + 3, ra, 3 * sizeof(*ra)); | 1676 | memcpy(ra + 4, ra, 3 * sizeof(*ra)); |
1676 | L->top = ra + 6; | 1677 | L->top = ra + 4 + 3; |
1677 | Protect(luaD_call(L, ra + 3, GETARG_C(i))); /* do the call */ | 1678 | Protect(luaD_call(L, ra + 4, GETARG_C(i))); /* do the call */ |
1678 | if (trap) { /* stack may have changed? */ | 1679 | if (trap) { /* stack may have changed? */ |
1679 | updatebase(ci); /* keep 'base' correct */ | 1680 | updatebase(ci); /* keep 'base' correct */ |
1680 | ra = RA(i); /* keep 'ra' correct for next instruction */ | 1681 | ra = RA(i); /* keep 'ra' correct for next instruction */ |
@@ -1686,8 +1687,8 @@ void luaV_execute (lua_State *L, CallInfo *ci) { | |||
1686 | } | 1687 | } |
1687 | vmcase(OP_TFORLOOP) { | 1688 | vmcase(OP_TFORLOOP) { |
1688 | l_tforloop: | 1689 | l_tforloop: |
1689 | if (!ttisnil(s2v(ra + 1))) { /* continue loop? */ | 1690 | if (!ttisnil(s2v(ra + 2))) { /* continue loop? */ |
1690 | setobjs2s(L, ra, ra + 1); /* save control variable */ | 1691 | setobjs2s(L, ra, ra + 2); /* save control variable */ |
1691 | pc -= GETARG_Bx(i); /* jump back */ | 1692 | pc -= GETARG_Bx(i); /* jump back */ |
1692 | } | 1693 | } |
1693 | vmbreak; | 1694 | vmbreak; |
diff --git a/testes/files.lua b/testes/files.lua index e68eb9b8..34fcf851 100644 --- a/testes/files.lua +++ b/testes/files.lua | |||
@@ -200,7 +200,7 @@ return x + y * z | |||
200 | assert(f:close()) | 200 | assert(f:close()) |
201 | f = coroutine.wrap(dofile) | 201 | f = coroutine.wrap(dofile) |
202 | assert(f(file) == 10) | 202 | assert(f(file) == 10) |
203 | print(f(100, 101) == 20) | 203 | assert(f(100, 101) == 20) |
204 | assert(f(200) == 100 + 200 * 101) | 204 | assert(f(200) == 100 + 200 * 101) |
205 | assert(os.remove(file)) | 205 | assert(os.remove(file)) |
206 | 206 | ||
@@ -422,6 +422,41 @@ assert(load(io.lines(file, "L"), nil, nil, t))() | |||
422 | assert(t.a == -((10 + 34) * 2)) | 422 | assert(t.a == -((10 + 34) * 2)) |
423 | 423 | ||
424 | 424 | ||
425 | do -- testing closing file in line iteration | ||
426 | |||
427 | -- get the to-be-closed variable from a loop | ||
428 | local function gettoclose (lv) | ||
429 | lv = lv + 1 | ||
430 | for i = 1, math.maxinteger do | ||
431 | local n, v = debug.getlocal(lv, i) | ||
432 | if n == "(for toclose)" then | ||
433 | return v | ||
434 | end | ||
435 | end | ||
436 | end | ||
437 | |||
438 | local f | ||
439 | for l in io.lines(file) do | ||
440 | f = gettoclose(1) | ||
441 | assert(io.type(f) == "file") | ||
442 | break | ||
443 | end | ||
444 | assert(io.type(f) == "closed file") | ||
445 | |||
446 | f = nil | ||
447 | local function foo (name) | ||
448 | for l in io.lines(name) do | ||
449 | f = gettoclose(1) | ||
450 | assert(io.type(f) == "file") | ||
451 | error(f) -- exit loop with an error | ||
452 | end | ||
453 | end | ||
454 | local st, msg = pcall(foo, file) | ||
455 | assert(st == false and io.type(msg) == "closed file") | ||
456 | |||
457 | end | ||
458 | |||
459 | |||
425 | -- test for multipe arguments in 'lines' | 460 | -- test for multipe arguments in 'lines' |
426 | io.output(file); io.write"0123456789\n":close() | 461 | io.output(file); io.write"0123456789\n":close() |
427 | for a,b in io.lines(file, 1, 1) do | 462 | for a,b in io.lines(file, 1, 1) do |
diff --git a/testes/locals.lua b/testes/locals.lua index 869ac1ff..28f88e54 100644 --- a/testes/locals.lua +++ b/testes/locals.lua | |||
@@ -371,6 +371,8 @@ do | |||
371 | x = x - 1 | 371 | x = x - 1 |
372 | if x > 0 then return x end | 372 | if x > 0 then return x end |
373 | end, | 373 | end, |
374 | nil, -- state | ||
375 | nil, -- control variable | ||
374 | function () -- closing function | 376 | function () -- closing function |
375 | numopen = numopen - 1 | 377 | numopen = numopen - 1 |
376 | end | 378 | end |
@@ -392,12 +394,16 @@ do | |||
392 | -- repeat test with '__open' metamethod instead of a function | 394 | -- repeat test with '__open' metamethod instead of a function |
393 | local function open (x) | 395 | local function open (x) |
394 | numopen = numopen + 1 | 396 | numopen = numopen + 1 |
397 | local state = setmetatable({x}, | ||
398 | {__close = function () numopen = numopen - 1 end}) | ||
395 | return | 399 | return |
396 | function (t) -- iteraction function | 400 | function (t) -- iteraction function |
397 | t[1] = t[1] - 1 | 401 | t[1] = t[1] - 1 |
398 | if t[1] > 0 then return t[1] end | 402 | if t[1] > 0 then return t[1] end |
399 | end, | 403 | end, |
400 | setmetatable({x}, {__close = function () numopen = numopen - 1 end}) | 404 | state, |
405 | nil, | ||
406 | state -- to-be-closed | ||
401 | end | 407 | end |
402 | 408 | ||
403 | local s = 0 | 409 | local s = 0 |