mirror of
https://github.com/lua/lua.git
synced 2024-11-26 19:53:53 +08:00
Debug information about extra arguments from __call
'debug.getinfo' can return number of extra arguments added to a call by a chain of __call metavalues. That information is being used to improve error messages about errors in these extra arguments.
This commit is contained in:
parent
b117bdb344
commit
50c7c915ee
24
lauxlib.c
24
lauxlib.c
@ -170,19 +170,27 @@ LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1,
|
||||
|
||||
LUALIB_API int luaL_argerror (lua_State *L, int arg, const char *extramsg) {
|
||||
lua_Debug ar;
|
||||
const char *argword;
|
||||
if (!lua_getstack(L, 0, &ar)) /* no stack frame? */
|
||||
return luaL_error(L, "bad argument #%d (%s)", arg, extramsg);
|
||||
lua_getinfo(L, "n", &ar);
|
||||
if (strcmp(ar.namewhat, "method") == 0) {
|
||||
arg--; /* do not count 'self' */
|
||||
if (arg == 0) /* error is in the self argument itself? */
|
||||
return luaL_error(L, "calling '%s' on bad self (%s)",
|
||||
ar.name, extramsg);
|
||||
lua_getinfo(L, "nt", &ar);
|
||||
if (arg <= ar.extraargs) /* error in an extra argument? */
|
||||
argword = "extra argument";
|
||||
else {
|
||||
arg -= ar.extraargs; /* do not count extra arguments */
|
||||
if (strcmp(ar.namewhat, "method") == 0) { /* colon syntax? */
|
||||
arg--; /* do not count (extra) self argument */
|
||||
if (arg == 0) /* error in self argument? */
|
||||
return luaL_error(L, "calling '%s' on bad self (%s)",
|
||||
ar.name, extramsg);
|
||||
/* else go through; error in a regular argument */
|
||||
}
|
||||
argword = "argument";
|
||||
}
|
||||
if (ar.name == NULL)
|
||||
ar.name = (pushglobalfuncname(L, &ar)) ? lua_tostring(L, -1) : "?";
|
||||
return luaL_error(L, "bad argument #%d to '%s' (%s)",
|
||||
arg, ar.name, extramsg);
|
||||
return luaL_error(L, "bad %s #%d to '%s' (%s)",
|
||||
argword, arg, ar.name, extramsg);
|
||||
}
|
||||
|
||||
|
||||
|
4
ldblib.c
4
ldblib.c
@ -191,8 +191,10 @@ static int db_getinfo (lua_State *L) {
|
||||
settabsi(L, "ftransfer", ar.ftransfer);
|
||||
settabsi(L, "ntransfer", ar.ntransfer);
|
||||
}
|
||||
if (strchr(options, 't'))
|
||||
if (strchr(options, 't')) {
|
||||
settabsb(L, "istailcall", ar.istailcall);
|
||||
settabsi(L, "extraargs", ar.extraargs);
|
||||
}
|
||||
if (strchr(options, 'L'))
|
||||
treatstackoption(L, L1, "activelines");
|
||||
if (strchr(options, 'f'))
|
||||
|
10
ldebug.c
10
ldebug.c
@ -352,7 +352,15 @@ static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar,
|
||||
break;
|
||||
}
|
||||
case 't': {
|
||||
ar->istailcall = (ci != NULL && (ci->callstatus & CIST_TAIL));
|
||||
if (ci != NULL) {
|
||||
ar->istailcall = !!(ci->callstatus & CIST_TAIL);
|
||||
ar->extraargs =
|
||||
cast_uchar((ci->callstatus & MAX_CCMT) >> CIST_CCMT);
|
||||
}
|
||||
else {
|
||||
ar->istailcall = 0;
|
||||
ar->extraargs = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'n': {
|
||||
|
4
ltests.c
4
ltests.c
@ -1900,6 +1900,10 @@ static struct X { int x; } x;
|
||||
else if EQ("closeslot") {
|
||||
lua_closeslot(L1, getnum);
|
||||
}
|
||||
else if EQ("argerror") {
|
||||
int arg = getnum;
|
||||
luaL_argerror(L1, arg, getstring);
|
||||
}
|
||||
else luaL_error(L, "unknown instruction %s", buff);
|
||||
}
|
||||
return 0;
|
||||
|
1
lua.h
1
lua.h
@ -504,6 +504,7 @@ struct lua_Debug {
|
||||
unsigned char nups; /* (u) number of upvalues */
|
||||
unsigned char nparams;/* (u) number of parameters */
|
||||
char isvararg; /* (u) */
|
||||
unsigned char extraargs; /* (t) number of extra arguments */
|
||||
char istailcall; /* (t) */
|
||||
int ftransfer; /* (r) index of first value transferred */
|
||||
int ntransfer; /* (r) number of transferred values */
|
||||
|
@ -4850,6 +4850,7 @@ typedef struct lua_Debug {
|
||||
unsigned char nups; /* (u) number of upvalues */
|
||||
unsigned char nparams; /* (u) number of parameters */
|
||||
char isvararg; /* (u) */
|
||||
unsigned char extraargs; /* (t) number of extra arguments */
|
||||
char istailcall; /* (t) */
|
||||
int ftransfer; /* (r) index of first value transferred */
|
||||
int ntransfer; /* (r) number of transferred values */
|
||||
@ -4938,6 +4939,14 @@ true if this function invocation was called by a tail call.
|
||||
In this case, the caller of this level is not in the stack.
|
||||
}
|
||||
|
||||
@item{@id{extraargs}|
|
||||
The number of extra arguments added by the call
|
||||
to functions called through @idx{__call} metamethods.
|
||||
(Each @idx{__call} metavalue adds a single extra argument,
|
||||
the object being called,
|
||||
but there may be a chain of @idx{__call} metavalues.)
|
||||
}
|
||||
|
||||
@item{@id{nups}|
|
||||
the number of upvalues of the function.
|
||||
}
|
||||
@ -5045,7 +5054,7 @@ fills in the fields @id{source}, @id{short_src},
|
||||
@id{linedefined}, @id{lastlinedefined}, and @id{what};
|
||||
}
|
||||
|
||||
@item{@Char{t}| fills in the field @id{istailcall};
|
||||
@item{@Char{t}| fills in the fields @id{istailcall} and @id{extraargs};
|
||||
}
|
||||
|
||||
@item{@Char{u}| fills in the fields
|
||||
@ -7993,7 +8002,7 @@ returns @fail plus the position of the first invalid byte.
|
||||
|
||||
@LibEntry{utf8.offset (s, n [, i])|
|
||||
|
||||
Returns the the position of the @id{n}-th character of @id{s}
|
||||
Returns the position of the @id{n}-th character of @id{s}
|
||||
(counting from byte position @id{i}) as two integers:
|
||||
The index (in bytes) where its encoding starts and the
|
||||
index (in bytes) where it ends.
|
||||
|
@ -204,6 +204,17 @@ do print"testing chains of '__call'"
|
||||
assert(Res[i][1] == i)
|
||||
end
|
||||
assert(Res[N + 1] == "a" and Res[N + 2] == "b" and Res[N + 3] == "c")
|
||||
|
||||
local function u (...)
|
||||
local n = debug.getinfo(1, 't').extraargs
|
||||
assert(select("#", ...) == n)
|
||||
return n
|
||||
end
|
||||
|
||||
for i = 0, N do
|
||||
assert(u() == i)
|
||||
u = setmetatable({}, {__call = u})
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
@ -624,6 +624,9 @@ local function f (x)
|
||||
end
|
||||
end
|
||||
|
||||
assert(debug.getinfo(print, 't').istailcall == false)
|
||||
assert(debug.getinfo(print, 't').extraargs == 0)
|
||||
|
||||
function g(x) return f(x) end
|
||||
|
||||
function g1(x) g(x) end
|
||||
|
@ -117,6 +117,31 @@ else
|
||||
return 1
|
||||
]]
|
||||
assert(string.find(res, "xuxu.-main chunk"))
|
||||
|
||||
do -- tests for error messages about extra arguments from __call
|
||||
local function createobj (n)
|
||||
-- function that raises an error on its n-th argument
|
||||
local code = string.format("argerror %d 'msg'", n)
|
||||
local func = T.makeCfunc(code)
|
||||
-- create a chain of 2 __call objects
|
||||
local M = setmetatable({}, {__call = func})
|
||||
M = setmetatable({}, {__call = M})
|
||||
-- put it as a method for a new object
|
||||
return {foo = M}
|
||||
end
|
||||
|
||||
_G.a = createobj(1) -- error in first (extra) argument
|
||||
checkmessage("a:foo()", "bad extra argument #1")
|
||||
|
||||
_G.a = createobj(2) -- error in second (extra) argument
|
||||
checkmessage("a:foo()", "bad extra argument #2")
|
||||
|
||||
_G.a = createobj(3) -- error in self (after two extra arguments)
|
||||
checkmessage("a:foo()", "bad self")
|
||||
|
||||
_G.a = createobj(4) -- error in first regular argument (after self)
|
||||
checkmessage("a:foo()", "bad argument #1")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user