aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-10-31 14:54:45 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-10-31 14:54:45 -0300
commit947a372f5860a76fcafb4a2845abc322e440d6fc (patch)
treee35840847207c850af5262f93f863f583d2af76d
parente073cbc2e538369e0611abfc9948f301aea6aef3 (diff)
downloadlua-947a372f5860a76fcafb4a2845abc322e440d6fc.tar.gz
lua-947a372f5860a76fcafb4a2845abc322e440d6fc.tar.bz2
lua-947a372f5860a76fcafb4a2845abc322e440d6fc.zip
State in generic 'for' acts as a to-be-closed variable
The implicit variable 'state' in a generic 'for' is marked as a to-be-closed variable, so that the state will be closed as soon as the loop ends, no matter how. Taking advantage of this new facility, the call 'io.lines(filename)' now returns the open file as a second result. Therefore, an iteraction like 'for l in io.lines(name)...' will close the file even when the loop ends with a break or an error.
-rw-r--r--liolib.c35
-rw-r--r--lparser.c1
-rw-r--r--lvm.c41
-rw-r--r--testes/files.lua8
-rw-r--r--testes/locals.lua50
5 files changed, 111 insertions, 24 deletions
diff --git a/liolib.c b/liolib.c
index 3dc509bd..5881b029 100644
--- a/liolib.c
+++ b/liolib.c
@@ -354,12 +354,22 @@ static int io_readline (lua_State *L);
354*/ 354*/
355#define MAXARGLINE 250 355#define MAXARGLINE 250
356 356
357/*
358** Auxiliar function to create the iteration function for 'lines'.
359** The iteration function is a closure over 'io_readline', with
360** the following upvalues:
361** 1) The file being read (first value in the stack)
362** 2) the number of arguments to read
363** 3) a boolean, true iff file has to be closed when finished ('toclose')
364** *) a variable number of format arguments (rest of the stack)
365*/
357static void aux_lines (lua_State *L, int toclose) { 366static void aux_lines (lua_State *L, int toclose) {
358 int n = lua_gettop(L) - 1; /* number of arguments to read */ 367 int n = lua_gettop(L) - 1; /* number of arguments to read */
359 luaL_argcheck(L, n <= MAXARGLINE, MAXARGLINE + 2, "too many arguments"); 368 luaL_argcheck(L, n <= MAXARGLINE, MAXARGLINE + 2, "too many arguments");
369 lua_pushvalue(L, 1); /* file */
360 lua_pushinteger(L, n); /* number of arguments to read */ 370 lua_pushinteger(L, n); /* number of arguments to read */
361 lua_pushboolean(L, toclose); /* close/not close file when finished */ 371 lua_pushboolean(L, toclose); /* close/not close file when finished */
362 lua_rotate(L, 2, 2); /* move 'n' and 'toclose' to their positions */ 372 lua_rotate(L, 2, 3); /* move the three values to their positions */
363 lua_pushcclosure(L, io_readline, 3 + n); 373 lua_pushcclosure(L, io_readline, 3 + n);
364} 374}
365 375
@@ -371,6 +381,11 @@ static int f_lines (lua_State *L) {
371} 381}
372 382
373 383
384/*
385** Return an iteration function for 'io.lines'. If file has to be
386** closed, also returns the file itself as a second result (to be
387** closed as the state at the exit of a generic for).
388*/
374static int io_lines (lua_State *L) { 389static int io_lines (lua_State *L) {
375 int toclose; 390 int toclose;
376 if (lua_isnone(L, 1)) lua_pushnil(L); /* at least one argument */ 391 if (lua_isnone(L, 1)) lua_pushnil(L); /* at least one argument */
@@ -386,8 +401,13 @@ static int io_lines (lua_State *L) {
386 lua_replace(L, 1); /* put file at index 1 */ 401 lua_replace(L, 1); /* put file at index 1 */
387 toclose = 1; /* close it after iteration */ 402 toclose = 1; /* close it after iteration */
388 } 403 }
389 aux_lines(L, toclose); 404 aux_lines(L, toclose); /* push iteration function */
390 return 1; 405 if (toclose) {
406 lua_pushvalue(L, 1); /* file will be second result */
407 return 2;
408 }
409 else
410 return 1;
391} 411}
392 412
393 413
@@ -453,7 +473,7 @@ static int readdigits (RN *rn, int hex) {
453/* 473/*
454** Read a number: first reads a valid prefix of a numeral into a buffer. 474** Read a number: first reads a valid prefix of a numeral into a buffer.
455** Then it calls 'lua_stringtonumber' to check whether the format is 475** Then it calls 'lua_stringtonumber' to check whether the format is
456** correct and to convert it to a Lua number 476** correct and to convert it to a Lua number.
457*/ 477*/
458static int read_number (lua_State *L, FILE *f) { 478static int read_number (lua_State *L, FILE *f) {
459 RN rn; 479 RN rn;
@@ -604,6 +624,9 @@ static int f_read (lua_State *L) {
604} 624}
605 625
606 626
627/*
628** Iteration function for 'lines'.
629*/
607static int io_readline (lua_State *L) { 630static int io_readline (lua_State *L) {
608 LStream *p = (LStream *)lua_touserdata(L, lua_upvalueindex(1)); 631 LStream *p = (LStream *)lua_touserdata(L, lua_upvalueindex(1));
609 int i; 632 int i;
@@ -624,8 +647,8 @@ static int io_readline (lua_State *L) {
624 return luaL_error(L, "%s", lua_tostring(L, -n + 1)); 647 return luaL_error(L, "%s", lua_tostring(L, -n + 1));
625 } 648 }
626 if (lua_toboolean(L, lua_upvalueindex(3))) { /* generator created file? */ 649 if (lua_toboolean(L, lua_upvalueindex(3))) { /* generator created file? */
627 lua_settop(L, 0); 650 lua_settop(L, 0); /* clear stack */
628 lua_pushvalue(L, lua_upvalueindex(1)); 651 lua_pushvalue(L, lua_upvalueindex(1)); /* push file at index 1 */
629 aux_close(L); /* close it */ 652 aux_close(L); /* close it */
630 } 653 }
631 return 0; 654 return 0;
diff --git a/lparser.c b/lparser.c
index 9419f880..a5b84aa1 100644
--- a/lparser.c
+++ b/lparser.c
@@ -1413,6 +1413,7 @@ static void forlist (LexState *ls, TString *indexname) {
1413 /* create control variables */ 1413 /* create control variables */
1414 new_localvarliteral(ls, "(for generator)"); 1414 new_localvarliteral(ls, "(for generator)");
1415 new_localvarliteral(ls, "(for state)"); 1415 new_localvarliteral(ls, "(for state)");
1416 markupval(fs, fs->nactvar); /* state may create an upvalue */
1416 new_localvarliteral(ls, "(for control)"); 1417 new_localvarliteral(ls, "(for control)");
1417 /* create declared variables */ 1418 /* create declared variables */
1418 new_localvar(ls, indexname); 1419 new_localvar(ls, indexname);
diff --git a/lvm.c b/lvm.c
index aad965d6..1535700f 100644
--- a/lvm.c
+++ b/lvm.c
@@ -866,7 +866,8 @@ void luaV_finishOp (lua_State *L) {
866#define ProtectNT(exp) (savepc(L), (exp), updatetrap(ci)) 866#define ProtectNT(exp) (savepc(L), (exp), updatetrap(ci))
867 867
868/* 868/*
869** Protect code that will finish the loop (returns). 869** Protect code that will finish the loop (returns) or can only raise
870** errors.
870*/ 871*/
871#define halfProtect(exp) (savepc(L), (exp)) 872#define halfProtect(exp) (savepc(L), (exp))
872 873
@@ -1457,7 +1458,8 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
1457 vmbreak; 1458 vmbreak;
1458 } 1459 }
1459 vmcase(OP_TBC) { 1460 vmcase(OP_TBC) {
1460 luaF_newtbcupval(L, ra); /* create new to-be-closed upvalue */ 1461 /* create new to-be-closed upvalue */
1462 halfProtect(luaF_newtbcupval(L, ra));
1461 vmbreak; 1463 vmbreak;
1462 } 1464 }
1463 vmcase(OP_JMP) { 1465 vmcase(OP_JMP) {
@@ -1745,21 +1747,34 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
1745 vmbreak; 1747 vmbreak;
1746 } 1748 }
1747 vmcase(OP_TFORPREP) { 1749 vmcase(OP_TFORPREP) {
1750 /* is 'state' a function or has a '__close' metamethod? */
1751 if (ttisfunction(s2v(ra + 1)) ||
1752 !ttisnil(luaT_gettmbyobj(L, s2v(ra + 1), TM_CLOSE))) {
1753 /* create to-be-closed upvalue for it */
1754 halfProtect(luaF_newtbcupval(L, ra + 1));
1755 }
1748 pc += GETARG_Bx(i); 1756 pc += GETARG_Bx(i);
1749 vmbreak; 1757 i = *(pc++); /* go to next instruction */
1758 lua_assert(GET_OPCODE(i) == OP_TFORCALL && ra == RA(i));
1759 goto l_tforcall;
1750 } 1760 }
1751 vmcase(OP_TFORCALL) { 1761 vmcase(OP_TFORCALL) {
1752 StkId cb = ra + 3; /* call base */ 1762 l_tforcall:
1753 setobjs2s(L, cb+2, ra+2); 1763 /* 'ra' has the iterator function, 'ra + 1' has the state,
1754 setobjs2s(L, cb+1, ra+1); 1764 and 'ra + 2' has the control variable. The call will use
1755 setobjs2s(L, cb, ra); 1765 the stack after these values (starting at 'ra + 3')
1756 L->top = cb + 3; /* func. + 2 args (state and index) */ 1766 */
1757 Protect(luaD_call(L, cb, GETARG_C(i))); 1767 /* push function, state, and control variable */
1758 if (trap) /* keep 'base' correct for next instruction */ 1768 memcpy(ra + 3, ra, 3 * sizeof(*ra));
1759 updatebase(ci); 1769 L->top = ra + 6;
1770 Protect(luaD_call(L, ra + 3, GETARG_C(i))); /* do the call */
1771 if (trap) { /* stack may have changed? */
1772 updatebase(ci); /* keep 'base' correct */
1773 ra = RA(i); /* keep 'ra' correct for next instruction */
1774 }
1760 i = *(pc++); /* go to next instruction */ 1775 i = *(pc++); /* go to next instruction */
1761 ra = RA(i); /* get its 'ra' */ 1776 ra += 2; /* adjust for next instruction */
1762 lua_assert(GET_OPCODE(i) == OP_TFORLOOP); 1777 lua_assert(GET_OPCODE(i) == OP_TFORLOOP && ra == RA(i));
1763 goto l_tforloop; 1778 goto l_tforloop;
1764 } 1779 }
1765 vmcase(OP_TFORLOOP) { 1780 vmcase(OP_TFORLOOP) {
diff --git a/testes/files.lua b/testes/files.lua
index 9aae5913..a11c5e61 100644
--- a/testes/files.lua
+++ b/testes/files.lua
@@ -462,13 +462,13 @@ X
462- y; 462- y;
463]]:close() 463]]:close()
464_G.X = 1 464_G.X = 1
465assert(not load(io.lines(file))) 465assert(not load((io.lines(file))))
466collectgarbage() -- to close file in previous iteration 466collectgarbage() -- to close file in previous iteration
467load(io.lines(file, "L"))() 467load((io.lines(file, "L")))()
468assert(_G.X == 2) 468assert(_G.X == 2)
469load(io.lines(file, 1))() 469load((io.lines(file, 1)))()
470assert(_G.X == 4) 470assert(_G.X == 4)
471load(io.lines(file, 3))() 471load((io.lines(file, 3)))()
472assert(_G.X == 8) 472assert(_G.X == 8)
473 473
474print('+') 474print('+')
diff --git a/testes/locals.lua b/testes/locals.lua
index 65b145db..1e0f525b 100644
--- a/testes/locals.lua
+++ b/testes/locals.lua
@@ -108,7 +108,7 @@ print'+'
108if rawget(_G, "T") then 108if rawget(_G, "T") then
109 -- testing clearing of dead elements from tables 109 -- testing clearing of dead elements from tables
110 collectgarbage("stop") -- stop GC 110 collectgarbage("stop") -- stop GC
111 local a = {[{}] = 4, [3] = 0, alo = 1, 111 local a = {[{}] = 4, [3] = 0, alo = 1,
112 a1234567890123456789012345678901234567890 = 10} 112 a1234567890123456789012345678901234567890 = 10}
113 113
114 local t = T.querytab(a) 114 local t = T.querytab(a)
@@ -360,6 +360,54 @@ end)
360co() -- start coroutine 360co() -- start coroutine
361assert(co == nil) -- eventually it will be collected 361assert(co == nil) -- eventually it will be collected
362 362
363
364-- to-be-closed variables in generic for loops
365do
366 local numopen = 0
367 local function open (x)
368 numopen = numopen + 1
369 return
370 function () -- iteraction function
371 x = x - 1
372 if x > 0 then return x end
373 end,
374 function () -- closing function
375 numopen = numopen - 1
376 end
377 end
378
379 local s = 0
380 for i in open(10) do
381 s = s + i
382 end
383 assert(s == 45 and numopen == 0)
384
385 local s = 0
386 for i in open(10) do
387 if i < 5 then break end
388 s = s + i
389 end
390 assert(s == 35 and numopen == 0)
391
392 -- repeat test with '__open' metamethod instead of a function
393 local function open (x)
394 numopen = numopen + 1
395 return
396 function (t) -- iteraction function
397 t[1] = t[1] - 1
398 if t[1] > 0 then return t[1] end
399 end,
400 setmetatable({x}, {__close = function () numopen = numopen - 1 end})
401 end
402
403 local s = 0
404 for i in open(10) do
405 if (i < 5) then break end
406 s = s + i
407 end
408 assert(s == 35 and numopen == 0)
409end
410
363print('OK') 411print('OK')
364 412
365return 5,f 413return 5,f