To-be-closed variable in 'for' loop separated from the state · lua/lua@7f6f708 · GitHub
Skip to content

Commit 7f6f708

Browse files
committed
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.)
1 parent b8fed93 commit 7f6f708

6 files changed

Lines changed: 76 additions & 29 deletions

File tree

‎liolib.c‎

Lines changed: 4 additions & 2 deletions

‎lopcodes.h‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -279,9 +279,9 @@ OP_FORLOOP,/* A Bx R(A)+=R(A+2);
279279
if R(A) <?= R(A+1) then { pc-=Bx; R(A+3)=R(A) } */
280280
OP_FORPREP,/* A Bx R(A)-=R(A+2); pc+=Bx */
281281

282-
OP_TFORPREP,/* A Bx create upvalue A; pc+=Bx */
283-
OP_TFORCALL,/* A C R(A+3), ... ,R(A+2+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 } */
282+
OP_TFORPREP,/* A Bx create upvalue for R(A + 3); pc+=Bx */
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+2) ~= nil then { R(A)=R(A+2); pc -= Bx } */
285285

286286
OP_SETLIST,/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
287287

‎lparser.c‎

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@ static int registerlocalvar (LexState *ls, TString *varname) {
165165
}
166166

167167

168+
/*
169+
** Create a new local variable with the given 'name'.
170+
*/
168171
static void new_localvar (LexState *ls, TString *name) {
169172
FuncState *fs = ls->fs;
170173
Dyndata *dyd = ls->dyd;
@@ -176,13 +179,8 @@ static void new_localvar (LexState *ls, TString *name) {
176179
dyd->actvar.arr[dyd->actvar.n++].idx = cast(short, reg);
177180
}
178181

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-
184182
#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));
186184

187185

188186
static LocVar *getlocvar (FuncState *fs, int i) {
@@ -192,6 +190,9 @@ static LocVar *getlocvar (FuncState *fs, int i) {
192190
}
193191

194192

193+
/*
194+
** Start the scope for the last 'nvars' created variables.
195+
*/
195196
static void adjustlocalvars (LexState *ls, int nvars) {
196197
FuncState *fs = ls->fs;
197198
fs->nactvar = cast_byte(fs->nactvar + nvars);
@@ -1357,7 +1358,6 @@ static void forbody (LexState *ls, int base, int line, int nvars, int kind) {
13571358
BlockCnt bl;
13581359
FuncState *fs = ls->fs;
13591360
int prep, endfor;
1360-
adjustlocalvars(ls, 3); /* control variables */
13611361
checknext(ls, TK_DO);
13621362
prep = luaK_codeABx(fs, forprep[kind], base, 0);
13631363
enterblock(fs, &bl, 0); /* scope for declared variables */
@@ -1399,6 +1399,7 @@ static void fornum (LexState *ls, TString *varname, int line) {
13991399
luaK_int(fs, fs->freereg, 1);
14001400
luaK_reserveregs(fs, 1);
14011401
}
1402+
adjustlocalvars(ls, 3); /* control variables */
14021403
forbody(ls, base, line, 1, basicfor);
14031404
}
14041405

@@ -1407,14 +1408,15 @@ static void forlist (LexState *ls, TString *indexname) {
14071408
/* forlist -> NAME {,NAME} IN explist forbody */
14081409
FuncState *fs = ls->fs;
14091410
expdesc e;
1410-
int nvars = 4; /* gen, state, control, plus at least one declared var */
1411+
int nvars = 5; /* gen, state, control, toclose, 'indexname' */
14111412
int line;
14121413
int base = fs->freereg;
14131414
/* create control variables */
14141415
new_localvarliteral(ls, "(for generator)");
14151416
new_localvarliteral(ls, "(for state)");
14161417
markupval(fs, fs->nactvar); /* state may create an upvalue */
14171418
new_localvarliteral(ls, "(for control)");
1419+
new_localvarliteral(ls, "(for toclose)");
14181420
/* create declared variables */
14191421
new_localvar(ls, indexname);
14201422
while (testnext(ls, ',')) {
@@ -1423,9 +1425,10 @@ static void forlist (LexState *ls, TString *indexname) {
14231425
}
14241426
checknext(ls, TK_IN);
14251427
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 */
14271430
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);
14291432
}
14301433

14311434

@@ -1575,9 +1578,9 @@ static void tocloselocalstat (LexState *ls) {
15751578
new_localvar(ls, str_checkname(ls));
15761579
checknext(ls, '=');
15771580
exp1(ls, 0);
1578-
luaK_codeABC(fs, OP_TBC, fs->nactvar, 0, 0);
15791581
markupval(fs, fs->nactvar);
15801582
adjustlocalvars(ls, 1);
1583+
luaK_codeABC(fs, OP_TBC, fs->nactvar - 1, 0, 0);
15811584
}
15821585

15831586

‎lvm.c‎

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1654,11 +1654,11 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
16541654
vmbreak;
16551655
}
16561656
vmcase(OP_TFORPREP) {
1657-
/* is 'state' a function or has a '__close' metamethod? */
1658-
if (ttisfunction(s2v(ra + 1)) ||
1659-
!ttisnil(luaT_gettmbyobj(L, s2v(ra + 1), TM_CLOSE))) {
1657+
/* is 'toclose' a function or has a '__close' metamethod? */
1658+
if (ttisfunction(s2v(ra + 3)) ||
1659+
!ttisnil(luaT_gettmbyobj(L, s2v(ra + 3), TM_CLOSE))) {
16601660
/* create to-be-closed upvalue for it */
1661-
halfProtect(luaF_newtbcupval(L, ra + 1));
1661+
halfProtect(luaF_newtbcupval(L, ra + 3));
16621662
}
16631663
pc += GETARG_Bx(i);
16641664
i = *(pc++); /* go to next instruction */
@@ -1668,13 +1668,14 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
16681668
vmcase(OP_TFORCALL) {
16691669
l_tforcall:
16701670
/* 'ra' has the iterator function, 'ra + 1' has the state,
1671-
and 'ra + 2' has the control variable. The call will use
1672-
the stack after these values (starting at 'ra + 3')
1671+
'ra + 2' has the control variable, and 'ra + 3' has the
1672+
to-be-closed variable. The call will use the stack after
1673+
these values (starting at 'ra + 4')
16731674
*/
16741675
/* push function, state, and control variable */
1675-
memcpy(ra + 3, ra, 3 * sizeof(*ra));
1676-
L->top = ra + 6;
1677-
Protect(luaD_call(L, ra + 3, GETARG_C(i))); /* do the call */
1676+
memcpy(ra + 4, ra, 3 * sizeof(*ra));
1677+
L->top = ra + 4 + 3;
1678+
Protect(luaD_call(L, ra + 4, GETARG_C(i))); /* do the call */
16781679
if (trap) { /* stack may have changed? */
16791680
updatebase(ci); /* keep 'base' correct */
16801681
ra = RA(i); /* keep 'ra' correct for next instruction */
@@ -1686,8 +1687,8 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
16861687
}
16871688
vmcase(OP_TFORLOOP) {
16881689
l_tforloop:
1689-
if (!ttisnil(s2v(ra + 1))) { /* continue loop? */
1690-
setobjs2s(L, ra, ra + 1); /* save control variable */
1690+
if (!ttisnil(s2v(ra + 2))) { /* continue loop? */
1691+
setobjs2s(L, ra, ra + 2); /* save control variable */
16911692
pc -= GETARG_Bx(i); /* jump back */
16921693
}
16931694
vmbreak;

‎testes/files.lua‎

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ return x + y * z
200200
assert(f:close())
201201
f = coroutine.wrap(dofile)
202202
assert(f(file) == 10)
203-
print(f(100, 101) == 20)
203+
assert(f(100, 101) == 20)
204204
assert(f(200) == 100 + 200 * 101)
205205
assert(os.remove(file))
206206

@@ -422,6 +422,41 @@ assert(load(io.lines(file, "L"), nil, nil, t))()
422422
assert(t.a == -((10 + 34) * 2))
423423

424424

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+
425460
-- test for multipe arguments in 'lines'
426461
io.output(file); io.write"0123456789\n":close()
427462
for a,b in io.lines(file, 1, 1) do

‎testes/locals.lua‎

Lines changed: 7 additions & 1 deletion

0 commit comments

Comments
 (0)