Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f645d31

Browse files
committed
To-be-closed variables must be closed on initialization
When initializing a to-be-closed variable, check whether it has a '__close' metamethod (or is a false value) and raise an error if if it hasn't. This produces more accurate error messages. (The check before closing still need to be done: in the C API, the value is not constant; and the object may lose its '__close' metamethod during the block.)
1 parent 35b4efc commit f645d31

File tree

6 files changed

+70
-41
lines changed

6 files changed

+70
-41
lines changed

lfunc.c

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,24 @@ static int prepclosingmethod (lua_State *L, TValue *obj, TValue *err) {
123123
}
124124

125125

126+
/*
127+
** Raise an error with message 'msg', inserting the name of the
128+
** local variable at position 'level' in the stack.
129+
*/
130+
static void varerror (lua_State *L, StkId level, const char *msg) {
131+
int idx = cast_int(level - L->ci->func);
132+
const char *vname = luaG_findlocal(L, L->ci, idx, NULL);
133+
if (vname == NULL) vname = "?";
134+
luaG_runerror(L, msg, vname);
135+
}
136+
137+
126138
/*
127139
** Prepare and call a closing method. If status is OK, code is still
128140
** inside the original protected call, and so any error will be handled
129-
** there. Otherwise, a previous error already activated original
141+
** there. Otherwise, a previous error already activated the original
130142
** protected call, and so the call to the closing method must be
131-
** protected here. (A status = CLOSEPROTECT behaves like a previous
143+
** protected here. (A status == CLOSEPROTECT behaves like a previous
132144
** error, to also run the closing method in protected mode).
133145
** If status is OK, the call to the closing method will be pushed
134146
** at the top of the stack. Otherwise, values are pushed after
@@ -140,12 +152,8 @@ static int callclosemth (lua_State *L, StkId level, int status) {
140152
if (likely(status == LUA_OK)) {
141153
if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */
142154
callclose(L, NULL); /* call closing method */
143-
else if (!ttisnil(uv)) { /* non-closable non-nil value? */
144-
int idx = cast_int(level - L->ci->func);
145-
const char *vname = luaG_findlocal(L, L->ci, idx, NULL);
146-
if (vname == NULL) vname = "?";
147-
luaG_runerror(L, "attempt to close non-closable variable '%s'", vname);
148-
}
155+
else if (!l_isfalse(uv)) /* non-closable non-false value? */
156+
varerror(L, level, "attempt to close non-closable variable '%s'");
149157
}
150158
else { /* must close the object in protected mode */
151159
ptrdiff_t oldtop;
@@ -170,9 +178,7 @@ static int callclosemth (lua_State *L, StkId level, int status) {
170178
** (can raise a memory-allocation error)
171179
*/
172180
static void trynewtbcupval (lua_State *L, void *ud) {
173-
StkId level = cast(StkId, ud);
174-
lua_assert(L->openupval == NULL || uplevel(L->openupval) < level);
175-
newupval(L, 1, level, &L->openupval);
181+
newupval(L, 1, cast(StkId, ud), &L->openupval);
176182
}
177183

178184

@@ -182,13 +188,22 @@ static void trynewtbcupval (lua_State *L, void *ud) {
182188
** as there is no upvalue to call it later.
183189
*/
184190
void luaF_newtbcupval (lua_State *L, StkId level) {
185-
int status = luaD_rawrunprotected(L, trynewtbcupval, level);
186-
if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */
187-
lua_assert(status == LUA_ERRMEM);
188-
luaD_seterrorobj(L, LUA_ERRMEM, level + 1); /* save error message */
189-
if (prepclosingmethod(L, s2v(level), s2v(level + 1)))
191+
TValue *obj = s2v(level);
192+
lua_assert(L->openupval == NULL || uplevel(L->openupval) < level);
193+
if (!l_isfalse(obj)) { /* false doesn't need to be closed */
194+
int status;
195+
const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE);
196+
if (ttisnil(tm)) /* no metamethod? */
197+
varerror(L, level, "variable '%s' got a non-closable value");
198+
status = luaD_rawrunprotected(L, trynewtbcupval, level);
199+
if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */
200+
lua_assert(status == LUA_ERRMEM);
201+
luaD_seterrorobj(L, LUA_ERRMEM, level + 1); /* save error message */
202+
/* next call must succeed, as object is closable */
203+
prepclosingmethod(L, s2v(level), s2v(level + 1));
190204
callclose(L, NULL); /* call closing method */
191-
luaD_throw(L, LUA_ERRMEM); /* throw memory error */
205+
luaD_throw(L, LUA_ERRMEM); /* throw memory error */
206+
}
192207
}
193208
}
194209

ltests.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1572,6 +1572,9 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
15721572
else if EQ("error") {
15731573
lua_error(L1);
15741574
}
1575+
else if EQ("abort") {
1576+
abort();
1577+
}
15751578
else if EQ("throw") {
15761579
#if defined(__cplusplus)
15771580
static struct X { int x; } x;

lvm.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1739,10 +1739,8 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
17391739
vmbreak;
17401740
}
17411741
vmcase(OP_TFORPREP) {
1742-
if (!ttisnil(s2v(ra + 3))) { /* is 'toclose' not nil? */
1743-
/* create to-be-closed upvalue for it */
1744-
halfProtect(luaF_newtbcupval(L, ra + 3));
1745-
}
1742+
/* create to-be-closed upvalue (if needed) */
1743+
halfProtect(luaF_newtbcupval(L, ra + 3));
17461744
pc += GETARG_Bx(i);
17471745
i = *(pc++); /* go to next instruction */
17481746
lua_assert(GET_OPCODE(i) == OP_TFORCALL && ra == RA(i));

manual/manual.of

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1534,15 +1534,17 @@ or exiting by an error.
15341534

15351535
Here, to @emph{close} a value means
15361536
to call its @idx{__close} metamethod.
1537-
If the value is @nil, it is ignored;
1538-
otherwise,
1539-
if it does not have a @idx{__close} metamethod,
1540-
an error is raised.
15411537
When calling the metamethod,
15421538
the value itself is passed as the first argument
1543-
and the error object (if any) is passed as a second argument;
1539+
and the error object that caused the exit (if any)
1540+
is passed as a second argument;
15441541
if there was no error, the second argument is @nil.
15451542

1543+
The value assigned to a to-be-closed variable
1544+
must have a @idx{__close} metamethod
1545+
or be a false value.
1546+
(@nil and @false are ignored as to-be-closed values.)
1547+
15461548
If several to-be-closed variables go out of scope at the same event,
15471549
they are closed in the reverse order that they were declared.
15481550

@@ -2917,8 +2919,9 @@ it is left unchanged.
29172919
@APIEntry{void lua_close (lua_State *L);|
29182920
@apii{0,0,-}
29192921

2920-
Destroys all objects in the given Lua state
2921-
(calling the corresponding garbage-collection metamethods, if any)
2922+
Close all active to-be-closed variables in the main thread,
2923+
release all objects in the given Lua state
2924+
(calling the corresponding garbage-collection metamethods, if any),
29222925
and frees all dynamic memory used by this state.
29232926

29242927
On several platforms, you may not need to call this function,
@@ -4186,7 +4189,7 @@ An index marked as to-be-closed should not be removed from the stack
41864189
by any other function in the API except @Lid{lua_settop} or @Lid{lua_pop}.
41874190

41884191
This function should not be called for an index
4189-
that is equal to or below an already marked to-be-closed index.
4192+
that is equal to or below an active to-be-closed index.
41904193

41914194
This function can raise an out-of-memory error.
41924195
In that case, the value in the given index is immediately closed,

testes/api.lua

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1096,7 +1096,7 @@ do
10961096
assert(type(a[1]) == "string" and a[2][1] == 11)
10971097
assert(#openresource == 0) -- was closed
10981098

1099-
-- error
1099+
-- closing by error
11001100
local a, b = pcall(T.makeCfunc[[
11011101
call 0 1 # create resource
11021102
toclose -1 # mark it to be closed
@@ -1105,6 +1105,15 @@ do
11051105
assert(a == false and b[1] == 11)
11061106
assert(#openresource == 0) -- was closed
11071107

1108+
-- non-closable value
1109+
local a, b = pcall(T.makeCfunc[[
1110+
newtable # create non-closable object
1111+
toclose -1 # mark it to be closed (shoud raise an error)
1112+
abort # will not be executed
1113+
]])
1114+
assert(a == false and
1115+
string.find(b, "non%-closable value"))
1116+
11081117
local function check (n)
11091118
assert(#openresource == n)
11101119
end

testes/locals.lua

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -215,11 +215,13 @@ end
215215
do
216216
local a = {}
217217
do
218+
local b <close> = false -- not to be closed
218219
local x <close> = setmetatable({"x"}, {__close = function (self)
219220
a[#a + 1] = self[1] end})
220221
local w, y <close>, z = func2close(function (self, err)
221222
assert(err == nil); a[#a + 1] = "y"
222223
end, 10, 20)
224+
local c <close> = nil -- not to be closed
223225
a[#a + 1] = "in"
224226
assert(w == 10 and z == 20)
225227
end
@@ -325,24 +327,22 @@ do -- errors in __close
325327
end
326328

327329

328-
do
329-
330-
-- errors due to non-closable values
330+
do -- errors due to non-closable values
331331
local function foo ()
332332
local x <close> = {}
333+
os.exit(false) -- should not run
333334
end
334335
local stat, msg = pcall(foo)
335-
assert(not stat and string.find(msg, "variable 'x'"))
336+
assert(not stat and
337+
string.find(msg, "variable 'x' got a non%-closable value"))
336338

337-
338-
-- with other errors, non-closable values are ignored
339339
local function foo ()
340-
local x <close> = 34
341-
local y <close> = func2close(function () error(32) end)
340+
local xyz <close> = setmetatable({}, {__close = print})
341+
getmetatable(xyz).__close = nil -- remove metamethod
342342
end
343343
local stat, msg = pcall(foo)
344-
assert(not stat and msg == 32)
345-
344+
assert(not stat and
345+
string.find(msg, "attempt to close non%-closable variable 'xyz'"))
346346
end
347347

348348

@@ -519,7 +519,8 @@ end
519519
-- a suspended coroutine should not close its variables when collected
520520
local co
521521
co = coroutine.wrap(function()
522-
local x <close> = function () os.exit(false) end -- should not run
522+
-- should not run
523+
local x <close> = func2close(function () os.exit(false) end)
523524
co = nil
524525
coroutine.yield()
525526
end)

0 commit comments

Comments
 (0)