mirror of
https://github.com/lua/lua.git
synced 2025-02-04 06:13:04 +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
20
lauxlib.c
20
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) {
|
LUALIB_API int luaL_argerror (lua_State *L, int arg, const char *extramsg) {
|
||||||
lua_Debug ar;
|
lua_Debug ar;
|
||||||
|
const char *argword;
|
||||||
if (!lua_getstack(L, 0, &ar)) /* no stack frame? */
|
if (!lua_getstack(L, 0, &ar)) /* no stack frame? */
|
||||||
return luaL_error(L, "bad argument #%d (%s)", arg, extramsg);
|
return luaL_error(L, "bad argument #%d (%s)", arg, extramsg);
|
||||||
lua_getinfo(L, "n", &ar);
|
lua_getinfo(L, "nt", &ar);
|
||||||
if (strcmp(ar.namewhat, "method") == 0) {
|
if (arg <= ar.extraargs) /* error in an extra argument? */
|
||||||
arg--; /* do not count 'self' */
|
argword = "extra argument";
|
||||||
if (arg == 0) /* error is in the self argument itself? */
|
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)",
|
return luaL_error(L, "calling '%s' on bad self (%s)",
|
||||||
ar.name, extramsg);
|
ar.name, extramsg);
|
||||||
|
/* else go through; error in a regular argument */
|
||||||
|
}
|
||||||
|
argword = "argument";
|
||||||
}
|
}
|
||||||
if (ar.name == NULL)
|
if (ar.name == NULL)
|
||||||
ar.name = (pushglobalfuncname(L, &ar)) ? lua_tostring(L, -1) : "?";
|
ar.name = (pushglobalfuncname(L, &ar)) ? lua_tostring(L, -1) : "?";
|
||||||
return luaL_error(L, "bad argument #%d to '%s' (%s)",
|
return luaL_error(L, "bad %s #%d to '%s' (%s)",
|
||||||
arg, ar.name, extramsg);
|
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, "ftransfer", ar.ftransfer);
|
||||||
settabsi(L, "ntransfer", ar.ntransfer);
|
settabsi(L, "ntransfer", ar.ntransfer);
|
||||||
}
|
}
|
||||||
if (strchr(options, 't'))
|
if (strchr(options, 't')) {
|
||||||
settabsb(L, "istailcall", ar.istailcall);
|
settabsb(L, "istailcall", ar.istailcall);
|
||||||
|
settabsi(L, "extraargs", ar.extraargs);
|
||||||
|
}
|
||||||
if (strchr(options, 'L'))
|
if (strchr(options, 'L'))
|
||||||
treatstackoption(L, L1, "activelines");
|
treatstackoption(L, L1, "activelines");
|
||||||
if (strchr(options, 'f'))
|
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;
|
break;
|
||||||
}
|
}
|
||||||
case 't': {
|
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;
|
break;
|
||||||
}
|
}
|
||||||
case 'n': {
|
case 'n': {
|
||||||
|
4
ltests.c
4
ltests.c
@ -1900,6 +1900,10 @@ static struct X { int x; } x;
|
|||||||
else if EQ("closeslot") {
|
else if EQ("closeslot") {
|
||||||
lua_closeslot(L1, getnum);
|
lua_closeslot(L1, getnum);
|
||||||
}
|
}
|
||||||
|
else if EQ("argerror") {
|
||||||
|
int arg = getnum;
|
||||||
|
luaL_argerror(L1, arg, getstring);
|
||||||
|
}
|
||||||
else luaL_error(L, "unknown instruction %s", buff);
|
else luaL_error(L, "unknown instruction %s", buff);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
1
lua.h
1
lua.h
@ -504,6 +504,7 @@ struct lua_Debug {
|
|||||||
unsigned char nups; /* (u) number of upvalues */
|
unsigned char nups; /* (u) number of upvalues */
|
||||||
unsigned char nparams;/* (u) number of parameters */
|
unsigned char nparams;/* (u) number of parameters */
|
||||||
char isvararg; /* (u) */
|
char isvararg; /* (u) */
|
||||||
|
unsigned char extraargs; /* (t) number of extra arguments */
|
||||||
char istailcall; /* (t) */
|
char istailcall; /* (t) */
|
||||||
int ftransfer; /* (r) index of first value transferred */
|
int ftransfer; /* (r) index of first value transferred */
|
||||||
int ntransfer; /* (r) number of transferred values */
|
int ntransfer; /* (r) number of transferred values */
|
||||||
|
@ -4850,6 +4850,7 @@ typedef struct lua_Debug {
|
|||||||
unsigned char nups; /* (u) number of upvalues */
|
unsigned char nups; /* (u) number of upvalues */
|
||||||
unsigned char nparams; /* (u) number of parameters */
|
unsigned char nparams; /* (u) number of parameters */
|
||||||
char isvararg; /* (u) */
|
char isvararg; /* (u) */
|
||||||
|
unsigned char extraargs; /* (t) number of extra arguments */
|
||||||
char istailcall; /* (t) */
|
char istailcall; /* (t) */
|
||||||
int ftransfer; /* (r) index of first value transferred */
|
int ftransfer; /* (r) index of first value transferred */
|
||||||
int ntransfer; /* (r) number of transferred values */
|
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.
|
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}|
|
@item{@id{nups}|
|
||||||
the number of upvalues of the function.
|
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};
|
@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
|
@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])|
|
@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:
|
(counting from byte position @id{i}) as two integers:
|
||||||
The index (in bytes) where its encoding starts and the
|
The index (in bytes) where its encoding starts and the
|
||||||
index (in bytes) where it ends.
|
index (in bytes) where it ends.
|
||||||
|
@ -204,6 +204,17 @@ do print"testing chains of '__call'"
|
|||||||
assert(Res[i][1] == i)
|
assert(Res[i][1] == i)
|
||||||
end
|
end
|
||||||
assert(Res[N + 1] == "a" and Res[N + 2] == "b" and Res[N + 3] == "c")
|
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
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -624,6 +624,9 @@ local function f (x)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
assert(debug.getinfo(print, 't').istailcall == false)
|
||||||
|
assert(debug.getinfo(print, 't').extraargs == 0)
|
||||||
|
|
||||||
function g(x) return f(x) end
|
function g(x) return f(x) end
|
||||||
|
|
||||||
function g1(x) g(x) end
|
function g1(x) g(x) end
|
||||||
|
@ -117,6 +117,31 @@ else
|
|||||||
return 1
|
return 1
|
||||||
]]
|
]]
|
||||||
assert(string.find(res, "xuxu.-main chunk"))
|
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
|
end
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user