'__close' gets no error object if there is no error
Instead of receiving nil as a second argument, __close metamethods are called with just one argument when there are no errors.
This commit is contained in:
4
ldo.c
4
ldo.c
@@ -111,10 +111,6 @@ void luaD_seterrorobj (lua_State *L, TStatus errcode, StkId oldtop) {
|
|||||||
setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling"));
|
setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LUA_OK: { /* special case only for closing upvalues */
|
|
||||||
setnilvalue(s2v(oldtop)); /* no error message */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
default: {
|
||||||
lua_assert(errorstatus(errcode)); /* real error */
|
lua_assert(errorstatus(errcode)); /* real error */
|
||||||
setobjs2s(L, oldtop, L->top.p - 1); /* error message on current top */
|
setobjs2s(L, oldtop, L->top.p - 1); /* error message on current top */
|
||||||
|
|||||||
32
lfunc.c
32
lfunc.c
@@ -100,21 +100,23 @@ UpVal *luaF_findupval (lua_State *L, StkId level) {
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Call closing method for object 'obj' with error message 'err'. The
|
** Call closing method for object 'obj' with error object 'err'. The
|
||||||
** boolean 'yy' controls whether the call is yieldable.
|
** boolean 'yy' controls whether the call is yieldable.
|
||||||
** (This function assumes EXTRA_STACK.)
|
** (This function assumes EXTRA_STACK.)
|
||||||
*/
|
*/
|
||||||
static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) {
|
static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) {
|
||||||
StkId top = L->top.p;
|
StkId top = L->top.p;
|
||||||
|
StkId func = top;
|
||||||
const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE);
|
const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE);
|
||||||
setobj2s(L, top, tm); /* will call metamethod... */
|
setobj2s(L, top++, tm); /* will call metamethod... */
|
||||||
setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */
|
setobj2s(L, top++, obj); /* with 'self' as the 1st argument */
|
||||||
setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */
|
if (err != NULL) /* if there was an error... */
|
||||||
L->top.p = top + 3; /* add function and arguments */
|
setobj2s(L, top++, err); /* then error object will be 2nd argument */
|
||||||
|
L->top.p = top; /* add function and arguments */
|
||||||
if (yy)
|
if (yy)
|
||||||
luaD_call(L, top, 0);
|
luaD_call(L, func, 0);
|
||||||
else
|
else
|
||||||
luaD_callnoyield(L, top, 0);
|
luaD_callnoyield(L, func, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -144,11 +146,17 @@ static void prepcallclosemth (lua_State *L, StkId level, TStatus status,
|
|||||||
int yy) {
|
int yy) {
|
||||||
TValue *uv = s2v(level); /* value being closed */
|
TValue *uv = s2v(level); /* value being closed */
|
||||||
TValue *errobj;
|
TValue *errobj;
|
||||||
if (status == CLOSEKTOP)
|
switch (status) {
|
||||||
errobj = &G(L)->nilvalue; /* error object is nil */
|
case LUA_OK:
|
||||||
else { /* 'luaD_seterrorobj' will set top to level + 2 */
|
L->top.p = level + 1; /* call will be at this level */
|
||||||
errobj = s2v(level + 1); /* error object goes after 'uv' */
|
/* FALLTHROUGH */
|
||||||
luaD_seterrorobj(L, status, level + 1); /* set error object */
|
case CLOSEKTOP: /* don't need to change top */
|
||||||
|
errobj = NULL; /* no error object */
|
||||||
|
break;
|
||||||
|
default: /* 'luaD_seterrorobj' will set top to level + 2 */
|
||||||
|
errobj = s2v(level + 1); /* error object goes after 'uv' */
|
||||||
|
luaD_seterrorobj(L, status, level + 1); /* set error object */
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
callclosemethod(L, uv, errobj, yy);
|
callclosemethod(L, uv, errobj, yy);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1612,10 +1612,11 @@ or exiting by an error.
|
|||||||
Here, to @emph{close} a value means
|
Here, to @emph{close} a value means
|
||||||
to call its @idx{__close} metamethod.
|
to call its @idx{__close} metamethod.
|
||||||
When calling the metamethod,
|
When calling the metamethod,
|
||||||
the value itself is passed as the first argument
|
the value itself is passed as the first argument.
|
||||||
and the error object that caused the exit (if any)
|
If there was an error,
|
||||||
|
the error object that caused the exit
|
||||||
is passed as a second argument;
|
is passed as a second argument;
|
||||||
if there was no error, the second argument is @nil.
|
otherwise, there is no second argument.
|
||||||
|
|
||||||
The value assigned to a to-be-closed variable
|
The value assigned to a to-be-closed variable
|
||||||
must have a @idx{__close} metamethod
|
must have a @idx{__close} metamethod
|
||||||
|
|||||||
@@ -280,6 +280,32 @@ do
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
do -- testing presence of second argument
|
||||||
|
local function foo (howtoclose, obj, n)
|
||||||
|
local ca -- copy of 'a' visible inside its close metamethod
|
||||||
|
do
|
||||||
|
local a <close> = func2close(function (...)
|
||||||
|
local t = table.pack(...)
|
||||||
|
assert(select("#", ...) == n)
|
||||||
|
assert(t.n == n and t[1] == ca and (t.n < 2 or t[2] == obj))
|
||||||
|
ca = 15 -- final value to be returned if howtoclose=="scope"
|
||||||
|
end)
|
||||||
|
ca = a
|
||||||
|
if howtoclose == "ret" then return obj -- 'a' closed by return
|
||||||
|
elseif howtoclose == "err" then error(obj) -- 'a' closed by error
|
||||||
|
end
|
||||||
|
end -- 'a' closed by end of scope
|
||||||
|
return ca -- ca now should be 15
|
||||||
|
end
|
||||||
|
-- with no errors, closing methods receive no extra argument
|
||||||
|
assert(foo("scope", nil, 1) == 15) -- close by end of scope
|
||||||
|
assert(foo("ret", 32, 1) == 32) -- close by return
|
||||||
|
-- with errors, they do
|
||||||
|
local st, msg = pcall(foo, "err", 23, 2) -- close by error
|
||||||
|
assert(not st and msg == 23)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
-- testing to-be-closed x compile-time constants
|
-- testing to-be-closed x compile-time constants
|
||||||
-- (there were some bugs here in Lua 5.4-rc3, due to a confusion
|
-- (there were some bugs here in Lua 5.4-rc3, due to a confusion
|
||||||
-- between compile levels and stack levels of variables)
|
-- between compile levels and stack levels of variables)
|
||||||
@@ -865,8 +891,10 @@ do
|
|||||||
if extra then
|
if extra then
|
||||||
extrares = co() -- runs until first (extra) yield
|
extrares = co() -- runs until first (extra) yield
|
||||||
end
|
end
|
||||||
local res = table.pack(co()) -- runs until yield inside '__close'
|
local res = table.pack(co()) -- runs until "regular" yield
|
||||||
assert(res.n == 2 and res[2] == nil)
|
-- regular yield will yield all values passed to the close function;
|
||||||
|
-- without errors, that is only the object being closed.
|
||||||
|
assert(res.n == 1 and type(res[1]) == "table")
|
||||||
local res2 = table.pack(co()) -- runs until end of function
|
local res2 = table.pack(co()) -- runs until end of function
|
||||||
assert(res2.n == t.n)
|
assert(res2.n == t.n)
|
||||||
for i = 1, #t do
|
for i = 1, #t do
|
||||||
@@ -879,10 +907,10 @@ do
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function foo ()
|
local function foo ()
|
||||||
local x <close> = func2close(coroutine.yield)
|
local x <close> = func2close(coroutine.yield) -- "regular" yield
|
||||||
local extra <close> = func2close(function (self)
|
local extra <close> = func2close(function (self)
|
||||||
assert(self == extrares)
|
assert(self == extrares)
|
||||||
coroutine.yield(100)
|
coroutine.yield(100) -- first (extra) yield
|
||||||
end)
|
end)
|
||||||
extrares = extra
|
extrares = extra
|
||||||
return table.unpack{10, x, 30}
|
return table.unpack{10, x, 30}
|
||||||
@@ -891,21 +919,21 @@ do
|
|||||||
assert(extrares == 100)
|
assert(extrares == 100)
|
||||||
|
|
||||||
local function foo ()
|
local function foo ()
|
||||||
local x <close> = func2close(coroutine.yield)
|
local x <close> = func2close(coroutine.yield) -- "regular" yield
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
check(foo, false)
|
check(foo, false)
|
||||||
|
|
||||||
local function foo ()
|
local function foo ()
|
||||||
local x <close> = func2close(coroutine.yield)
|
local x <close> = func2close(coroutine.yield) -- "regular" yield
|
||||||
local y, z = 20, 30
|
local y, z = 20, 30
|
||||||
return x
|
return x
|
||||||
end
|
end
|
||||||
check(foo, false, "x")
|
check(foo, false, "x")
|
||||||
|
|
||||||
local function foo ()
|
local function foo ()
|
||||||
local x <close> = func2close(coroutine.yield)
|
local x <close> = func2close(coroutine.yield) -- "regular" yield
|
||||||
local extra <close> = func2close(coroutine.yield)
|
local extra <close> = func2close(coroutine.yield) -- extra yield
|
||||||
return table.unpack({}, 1, 100) -- 100 nils
|
return table.unpack({}, 1, 100) -- 100 nils
|
||||||
end
|
end
|
||||||
check(foo, true, table.unpack({}, 1, 100))
|
check(foo, true, table.unpack({}, 1, 100))
|
||||||
|
|||||||
Reference in New Issue
Block a user