mirror of https://github.com/rusefi/lua.git
Supressed errors in '__close' generate warnings
This commit is contained in:
parent
a1d8eb2743
commit
ca13be9af7
|
@ -1010,9 +1010,9 @@ static int panic (lua_State *L) {
|
|||
static void warnf (void *ud, const char *message, int tocont) {
|
||||
int *warnstate = (int *)ud;
|
||||
if (*warnstate != 2 && !tocont && *message == '@') { /* control message? */
|
||||
if (strcmp(message + 1, "off") == 0)
|
||||
if (strcmp(message, "@off") == 0)
|
||||
*warnstate = 0;
|
||||
else if (strcmp(message + 1, "on") == 0)
|
||||
else if (strcmp(message, "@on") == 0)
|
||||
*warnstate = 1;
|
||||
return;
|
||||
}
|
||||
|
|
6
lfunc.c
6
lfunc.c
|
@ -164,8 +164,12 @@ static int callclosemth (lua_State *L, StkId level, int status) {
|
|||
int newstatus = luaD_pcall(L, callclose, NULL, oldtop, 0);
|
||||
if (newstatus != LUA_OK && status == CLOSEPROTECT) /* first error? */
|
||||
status = newstatus; /* this will be the new error */
|
||||
else /* leave original error (or nil) on top */
|
||||
else {
|
||||
if (newstatus != LUA_OK) /* supressed error? */
|
||||
luaE_warnerror(L, "__close metamethod");
|
||||
/* leave original error (or nil) on top */
|
||||
L->top = restorestack(L, oldtop);
|
||||
}
|
||||
}
|
||||
/* else no metamethod; ignore this case and keep original error */
|
||||
}
|
||||
|
|
7
lgc.c
7
lgc.c
|
@ -854,12 +854,7 @@ static void GCTM (lua_State *L) {
|
|||
L->allowhook = oldah; /* restore hooks */
|
||||
g->gcrunning = running; /* restore state */
|
||||
if (unlikely(status != LUA_OK)) { /* error while running __gc? */
|
||||
const char *msg = (ttisstring(s2v(L->top - 1)))
|
||||
? svalue(s2v(L->top - 1))
|
||||
: "error object is not a string";
|
||||
luaE_warning(L, "error in __gc metamethod (", 1);
|
||||
luaE_warning(L, msg, 1);
|
||||
luaE_warning(L, ")", 0);
|
||||
luaE_warnerror(L, "__gc metamethod");
|
||||
L->top--; /* pops error object */
|
||||
}
|
||||
}
|
||||
|
|
16
lstate.c
16
lstate.c
|
@ -443,3 +443,19 @@ void luaE_warning (lua_State *L, const char *msg, int tocont) {
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
** Generate a warning from an error message
|
||||
*/
|
||||
void luaE_warnerror (lua_State *L, const char *where) {
|
||||
TValue *errobj = s2v(L->top - 1); /* error object */
|
||||
const char *msg = (ttisstring(errobj))
|
||||
? svalue(errobj)
|
||||
: "error object is not a string";
|
||||
/* produce warning "error in %s (%s)" (where, msg) */
|
||||
luaE_warning(L, "error in ", 1);
|
||||
luaE_warning(L, where, 1);
|
||||
luaE_warning(L, " (", 1);
|
||||
luaE_warning(L, msg, 1);
|
||||
luaE_warning(L, ")", 0);
|
||||
}
|
||||
|
||||
|
|
1
lstate.h
1
lstate.h
|
@ -355,6 +355,7 @@ LUAI_FUNC void luaE_freeCI (lua_State *L);
|
|||
LUAI_FUNC void luaE_shrinkCI (lua_State *L);
|
||||
LUAI_FUNC void luaE_enterCcall (lua_State *L);
|
||||
LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont);
|
||||
LUAI_FUNC void luaE_warnerror (lua_State *L, const char *where);
|
||||
|
||||
|
||||
#define luaE_exitCcall(L) ((L)->nCcalls++)
|
||||
|
|
10
ltests.c
10
ltests.c
|
@ -95,15 +95,15 @@ static void warnf (void *ud, const char *msg, int tocont) {
|
|||
if (!lasttocont && !tocont && *msg == '@') { /* control message? */
|
||||
if (buff[0] != '\0')
|
||||
badexit("Control warning during warning: %s\naborting...\n", msg);
|
||||
if (strcmp(msg + 1, "off") == 0)
|
||||
if (strcmp(msg, "@off") == 0)
|
||||
onoff = 0;
|
||||
else if (strcmp(msg + 1, "on") == 0)
|
||||
else if (strcmp(msg, "@on") == 0)
|
||||
onoff = 1;
|
||||
else if (strcmp(msg + 1, "normal") == 0)
|
||||
else if (strcmp(msg, "@normal") == 0)
|
||||
mode = 0;
|
||||
else if (strcmp(msg + 1, "allow") == 0)
|
||||
else if (strcmp(msg, "@allow") == 0)
|
||||
mode = 1;
|
||||
else if (strcmp(msg + 1, "store") == 0)
|
||||
else if (strcmp(msg, "@store") == 0)
|
||||
mode = 2;
|
||||
else
|
||||
badexit("Invalid control warning in test mode: %s\naborting...\n", msg);
|
||||
|
|
|
@ -1556,7 +1556,7 @@ However, Lua may call the method one more time.
|
|||
After an error,
|
||||
the other pending closing methods will still be called.
|
||||
Errors in these methods
|
||||
interrupt the respective method,
|
||||
interrupt the respective method and generate a warning,
|
||||
but are otherwise ignored;
|
||||
the error reported is only the original one.
|
||||
|
||||
|
|
|
@ -209,12 +209,12 @@ if #msgs > 0 then
|
|||
warn("#tests not performed:\n ", m, "\n")
|
||||
end
|
||||
|
||||
print("(there should be two warnings now)")
|
||||
warn("#This is ", "an expected", " warning")
|
||||
warn("@off")
|
||||
warn("******** THIS WARNING SHOULD NOT APPEAR **********")
|
||||
warn("******** THIS WARNING ALSO SHOULD NOT APPEAR **********")
|
||||
warn("@on")
|
||||
print("(there should be two warnings now)")
|
||||
warn("#This is ", "an expected", " warning")
|
||||
warn("#This is", " another one")
|
||||
|
||||
-- no test module should define 'debug'
|
||||
|
|
|
@ -168,7 +168,7 @@ do
|
|||
local y <close> = func2close(function (self,err)
|
||||
if (err ~= 111) then os.exit(false) end -- should not happen
|
||||
x = 200
|
||||
error(200)
|
||||
error("200")
|
||||
end)
|
||||
local x <close> = func2close(function (self, err)
|
||||
assert(err == nil); error(111)
|
||||
|
@ -177,7 +177,10 @@ do
|
|||
end)
|
||||
coroutine.resume(co)
|
||||
assert(x == 0)
|
||||
_WARN = nil; warn("@off"); warn("@store")
|
||||
local st, msg = coroutine.close(co)
|
||||
warn("@on"); warn("@normal")
|
||||
assert(_WARN == nil or string.find(_WARN, "200"))
|
||||
assert(st == false and coroutine.status(co) == "dead" and msg == 111)
|
||||
assert(x == 200)
|
||||
|
||||
|
|
|
@ -286,57 +286,149 @@ do
|
|||
end
|
||||
|
||||
|
||||
do -- errors in __close
|
||||
local log = {}
|
||||
local function foo (err)
|
||||
-- auxiliary functions for testing warnings in '__close'
|
||||
local function prepwarn ()
|
||||
warn("@off") -- do not show (lots of) warnings
|
||||
if not T then
|
||||
_WARN = "OFF" -- signal that warnings are not being captured
|
||||
else
|
||||
warn("@store") -- to test the warnings
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function endwarn ()
|
||||
assert(T or _WARN == "OFF")
|
||||
warn("@on") -- back to normal
|
||||
warn("@normal")
|
||||
_WARN = nil
|
||||
end
|
||||
|
||||
|
||||
local function checkwarn (msg)
|
||||
assert(_WARN == "OFF" or string.find(_WARN, msg))
|
||||
end
|
||||
|
||||
|
||||
do print("testing errors in __close")
|
||||
|
||||
prepwarn()
|
||||
|
||||
-- original error is in __close
|
||||
local function foo ()
|
||||
|
||||
local x <close> =
|
||||
func2close(function (self, msg) log[#log + 1] = msg; error(1) end)
|
||||
func2close(function (self, msg)
|
||||
assert(string.find(msg, "@z"))
|
||||
error("@x")
|
||||
end)
|
||||
|
||||
local x1 <close> =
|
||||
func2close(function (self, msg) log[#log + 1] = msg; end)
|
||||
func2close(function (self, msg)
|
||||
checkwarn("@y")
|
||||
assert(string.find(msg, "@z"))
|
||||
end)
|
||||
|
||||
local gc <close> = func2close(function () collectgarbage() end)
|
||||
|
||||
local y <close> =
|
||||
func2close(function (self, msg) log[#log + 1] = msg; error(2) end)
|
||||
func2close(function (self, msg)
|
||||
assert(string.find(msg, "@z")) -- error in 'z'
|
||||
error("@y")
|
||||
end)
|
||||
|
||||
local first = true
|
||||
local z <close> =
|
||||
-- 'z' close is called twice
|
||||
func2close(function (self, msg)
|
||||
if first then
|
||||
assert(msg == nil)
|
||||
first = false
|
||||
else
|
||||
assert(string.find(msg, "@z")) -- own error
|
||||
end
|
||||
error("@z")
|
||||
end)
|
||||
|
||||
return 200
|
||||
end
|
||||
|
||||
local stat, msg = pcall(foo, false)
|
||||
assert(string.find(msg, "@z"))
|
||||
checkwarn("@x")
|
||||
|
||||
|
||||
-- original error not in __close
|
||||
local function foo ()
|
||||
|
||||
local x <close> =
|
||||
func2close(function (self, msg)
|
||||
assert(msg == 4)
|
||||
end)
|
||||
|
||||
local x1 <close> =
|
||||
func2close(function (self, msg)
|
||||
checkwarn("@y")
|
||||
assert(msg == 4)
|
||||
error("@x1")
|
||||
end)
|
||||
|
||||
local gc <close> = func2close(function () collectgarbage() end)
|
||||
|
||||
local y <close> =
|
||||
func2close(function (self, msg)
|
||||
assert(msg == 4) -- error in body
|
||||
error("@y")
|
||||
end)
|
||||
|
||||
local first = true
|
||||
local z <close> =
|
||||
func2close(function (self, msg)
|
||||
log[#log + 1] = (msg or 10) + 1;
|
||||
error(3)
|
||||
checkwarn("@z")
|
||||
-- 'z' close is called once
|
||||
assert(first and msg == 4)
|
||||
first = false
|
||||
error("@z")
|
||||
end)
|
||||
if err then error(4) end
|
||||
end
|
||||
local stat, msg = pcall(foo, false)
|
||||
assert(msg == 3)
|
||||
-- 'z' close is called twice
|
||||
assert(log[1] == 11 and log[2] == 4 and log[3] == 3 and log[4] == 3
|
||||
and log[5] == 3 and #log == 5)
|
||||
|
||||
log = {}
|
||||
error(4) -- original error
|
||||
end
|
||||
|
||||
local stat, msg = pcall(foo, true)
|
||||
assert(msg == 4)
|
||||
-- 'z' close is called once
|
||||
assert(log[1] == 5 and log[2] == 4 and log[3] == 4 and log[4] == 4
|
||||
and #log == 4)
|
||||
checkwarn("@x1") -- last error
|
||||
|
||||
-- error leaving a block
|
||||
local function foo (...)
|
||||
do
|
||||
local x1 <close> = func2close(function () error("Y") end)
|
||||
local x123 <close> = func2close(function () error("X") end)
|
||||
local x1 <close> =
|
||||
func2close(function ()
|
||||
checkwarn("@X")
|
||||
error("@Y")
|
||||
end)
|
||||
|
||||
local x123 <close> =
|
||||
func2close(function ()
|
||||
error("@X")
|
||||
end)
|
||||
end
|
||||
os.exit(false) -- should not run
|
||||
end
|
||||
|
||||
local st, msg = xpcall(foo, debug.traceback)
|
||||
assert(string.match(msg, "^[^ ]* X"))
|
||||
assert(string.match(msg, "^[^ ]* @X"))
|
||||
assert(string.find(msg, "in metamethod 'close'"))
|
||||
|
||||
-- error in toclose in vararg function
|
||||
local function foo (...)
|
||||
local x123 <close> = func2close(function () error("X") end)
|
||||
local x123 <close> = func2close(function () error("@X") end)
|
||||
end
|
||||
|
||||
local st, msg = xpcall(foo, debug.traceback)
|
||||
assert(string.match(msg, "^[^ ]* X"))
|
||||
assert(string.match(msg, "^[^ ]* @X"))
|
||||
|
||||
assert(string.find(msg, "in metamethod 'close'"))
|
||||
endwarn()
|
||||
end
|
||||
|
||||
|
||||
|
@ -361,6 +453,8 @@ end
|
|||
|
||||
if rawget(_G, "T") then
|
||||
|
||||
warn("@off")
|
||||
|
||||
-- memory error inside closing function
|
||||
local function foo ()
|
||||
local y <close> = func2close(function () T.alloccount() end)
|
||||
|
@ -437,7 +531,7 @@ if rawget(_G, "T") then
|
|||
|
||||
local s = string.rep("a", lim)
|
||||
|
||||
-- concat this table needs two buffer resizes (one for each 's')
|
||||
-- concat this table needs two buffer resizes (one for each 's')
|
||||
local a = {s, s}
|
||||
|
||||
collectgarbage()
|
||||
|
@ -472,6 +566,8 @@ if rawget(_G, "T") then
|
|||
|
||||
print'+'
|
||||
end
|
||||
|
||||
warn("@on")
|
||||
end
|
||||
|
||||
|
||||
|
@ -501,17 +597,20 @@ end
|
|||
|
||||
|
||||
do
|
||||
prepwarn()
|
||||
|
||||
-- error in a wrapped coroutine raising errors when closing a variable
|
||||
local x = 0
|
||||
local co = coroutine.wrap(function ()
|
||||
local xx <close> = func2close(function () x = x + 1; error("YYY") end)
|
||||
local xv <close> = func2close(function () x = x + 1; error("XXX") end)
|
||||
local xx <close> = func2close(function () x = x + 1; error("@YYY") end)
|
||||
local xv <close> = func2close(function () x = x + 1; error("@XXX") end)
|
||||
coroutine.yield(100)
|
||||
error(200)
|
||||
end)
|
||||
assert(co() == 100); assert(x == 0)
|
||||
local st, msg = pcall(co); assert(x == 2)
|
||||
assert(not st and msg == 200) -- should get first error raised
|
||||
checkwarn("@YYY")
|
||||
|
||||
local x = 0
|
||||
local y = 0
|
||||
|
@ -526,6 +625,9 @@ do
|
|||
assert(x == 2 and y == 1) -- first close is called twice
|
||||
-- should get first error raised
|
||||
assert(not st and string.find(msg, "%w+%.%w+:%d+: XXX"))
|
||||
checkwarn("YYY")
|
||||
|
||||
endwarn()
|
||||
end
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue