diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2024-11-19 14:09:18 -0300 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2024-11-19 14:09:18 -0300 |
| commit | 50c7c915ee2fa239043d5456237f5145d064089b (patch) | |
| tree | 386e17e4baa154bb60dc54c1a00c751e3adedde9 | |
| parent | b117bdb3448778d9e7f9a0302791e8ac3bb97ddd (diff) | |
| download | lua-50c7c915ee2fa239043d5456237f5145d064089b.tar.gz lua-50c7c915ee2fa239043d5456237f5145d064089b.tar.bz2 lua-50c7c915ee2fa239043d5456237f5145d064089b.zip | |
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.
| -rw-r--r-- | lauxlib.c | 24 | ||||
| -rw-r--r-- | ldblib.c | 4 | ||||
| -rw-r--r-- | ldebug.c | 10 | ||||
| -rw-r--r-- | ltests.c | 4 | ||||
| -rw-r--r-- | lua.h | 1 | ||||
| -rw-r--r-- | manual/manual.of | 13 | ||||
| -rw-r--r-- | testes/calls.lua | 11 | ||||
| -rw-r--r-- | testes/db.lua | 3 | ||||
| -rw-r--r-- | testes/errors.lua | 25 |
9 files changed, 83 insertions, 12 deletions
| @@ -170,19 +170,27 @@ LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1, | |||
| 170 | 170 | ||
| 171 | LUALIB_API int luaL_argerror (lua_State *L, int arg, const char *extramsg) { | 171 | LUALIB_API int luaL_argerror (lua_State *L, int arg, const char *extramsg) { |
| 172 | lua_Debug ar; | 172 | lua_Debug ar; |
| 173 | const char *argword; | ||
| 173 | if (!lua_getstack(L, 0, &ar)) /* no stack frame? */ | 174 | if (!lua_getstack(L, 0, &ar)) /* no stack frame? */ |
| 174 | return luaL_error(L, "bad argument #%d (%s)", arg, extramsg); | 175 | return luaL_error(L, "bad argument #%d (%s)", arg, extramsg); |
| 175 | lua_getinfo(L, "n", &ar); | 176 | lua_getinfo(L, "nt", &ar); |
| 176 | if (strcmp(ar.namewhat, "method") == 0) { | 177 | if (arg <= ar.extraargs) /* error in an extra argument? */ |
| 177 | arg--; /* do not count 'self' */ | 178 | argword = "extra argument"; |
| 178 | if (arg == 0) /* error is in the self argument itself? */ | 179 | else { |
| 179 | return luaL_error(L, "calling '%s' on bad self (%s)", | 180 | arg -= ar.extraargs; /* do not count extra arguments */ |
| 180 | ar.name, extramsg); | 181 | if (strcmp(ar.namewhat, "method") == 0) { /* colon syntax? */ |
| 182 | arg--; /* do not count (extra) self argument */ | ||
| 183 | if (arg == 0) /* error in self argument? */ | ||
| 184 | return luaL_error(L, "calling '%s' on bad self (%s)", | ||
| 185 | ar.name, extramsg); | ||
| 186 | /* else go through; error in a regular argument */ | ||
| 187 | } | ||
| 188 | argword = "argument"; | ||
| 181 | } | 189 | } |
| 182 | if (ar.name == NULL) | 190 | if (ar.name == NULL) |
| 183 | ar.name = (pushglobalfuncname(L, &ar)) ? lua_tostring(L, -1) : "?"; | 191 | ar.name = (pushglobalfuncname(L, &ar)) ? lua_tostring(L, -1) : "?"; |
| 184 | return luaL_error(L, "bad argument #%d to '%s' (%s)", | 192 | return luaL_error(L, "bad %s #%d to '%s' (%s)", |
| 185 | arg, ar.name, extramsg); | 193 | argword, arg, ar.name, extramsg); |
| 186 | } | 194 | } |
| 187 | 195 | ||
| 188 | 196 | ||
| @@ -191,8 +191,10 @@ static int db_getinfo (lua_State *L) { | |||
| 191 | settabsi(L, "ftransfer", ar.ftransfer); | 191 | settabsi(L, "ftransfer", ar.ftransfer); |
| 192 | settabsi(L, "ntransfer", ar.ntransfer); | 192 | settabsi(L, "ntransfer", ar.ntransfer); |
| 193 | } | 193 | } |
| 194 | if (strchr(options, 't')) | 194 | if (strchr(options, 't')) { |
| 195 | settabsb(L, "istailcall", ar.istailcall); | 195 | settabsb(L, "istailcall", ar.istailcall); |
| 196 | settabsi(L, "extraargs", ar.extraargs); | ||
| 197 | } | ||
| 196 | if (strchr(options, 'L')) | 198 | if (strchr(options, 'L')) |
| 197 | treatstackoption(L, L1, "activelines"); | 199 | treatstackoption(L, L1, "activelines"); |
| 198 | if (strchr(options, 'f')) | 200 | if (strchr(options, 'f')) |
| @@ -352,7 +352,15 @@ static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar, | |||
| 352 | break; | 352 | break; |
| 353 | } | 353 | } |
| 354 | case 't': { | 354 | case 't': { |
| 355 | ar->istailcall = (ci != NULL && (ci->callstatus & CIST_TAIL)); | 355 | if (ci != NULL) { |
| 356 | ar->istailcall = !!(ci->callstatus & CIST_TAIL); | ||
| 357 | ar->extraargs = | ||
| 358 | cast_uchar((ci->callstatus & MAX_CCMT) >> CIST_CCMT); | ||
| 359 | } | ||
| 360 | else { | ||
| 361 | ar->istailcall = 0; | ||
| 362 | ar->extraargs = 0; | ||
| 363 | } | ||
| 356 | break; | 364 | break; |
| 357 | } | 365 | } |
| 358 | case 'n': { | 366 | case 'n': { |
| @@ -1900,6 +1900,10 @@ static struct X { int x; } x; | |||
| 1900 | else if EQ("closeslot") { | 1900 | else if EQ("closeslot") { |
| 1901 | lua_closeslot(L1, getnum); | 1901 | lua_closeslot(L1, getnum); |
| 1902 | } | 1902 | } |
| 1903 | else if EQ("argerror") { | ||
| 1904 | int arg = getnum; | ||
| 1905 | luaL_argerror(L1, arg, getstring); | ||
| 1906 | } | ||
| 1903 | else luaL_error(L, "unknown instruction %s", buff); | 1907 | else luaL_error(L, "unknown instruction %s", buff); |
| 1904 | } | 1908 | } |
| 1905 | return 0; | 1909 | return 0; |
| @@ -504,6 +504,7 @@ struct lua_Debug { | |||
| 504 | unsigned char nups; /* (u) number of upvalues */ | 504 | unsigned char nups; /* (u) number of upvalues */ |
| 505 | unsigned char nparams;/* (u) number of parameters */ | 505 | unsigned char nparams;/* (u) number of parameters */ |
| 506 | char isvararg; /* (u) */ | 506 | char isvararg; /* (u) */ |
| 507 | unsigned char extraargs; /* (t) number of extra arguments */ | ||
| 507 | char istailcall; /* (t) */ | 508 | char istailcall; /* (t) */ |
| 508 | int ftransfer; /* (r) index of first value transferred */ | 509 | int ftransfer; /* (r) index of first value transferred */ |
| 509 | int ntransfer; /* (r) number of transferred values */ | 510 | int ntransfer; /* (r) number of transferred values */ |
diff --git a/manual/manual.of b/manual/manual.of index ce42ff51..a441cea1 100644 --- a/manual/manual.of +++ b/manual/manual.of | |||
| @@ -4850,6 +4850,7 @@ typedef struct lua_Debug { | |||
| 4850 | unsigned char nups; /* (u) number of upvalues */ | 4850 | unsigned char nups; /* (u) number of upvalues */ |
| 4851 | unsigned char nparams; /* (u) number of parameters */ | 4851 | unsigned char nparams; /* (u) number of parameters */ |
| 4852 | char isvararg; /* (u) */ | 4852 | char isvararg; /* (u) */ |
| 4853 | unsigned char extraargs; /* (t) number of extra arguments */ | ||
| 4853 | char istailcall; /* (t) */ | 4854 | char istailcall; /* (t) */ |
| 4854 | int ftransfer; /* (r) index of first value transferred */ | 4855 | int ftransfer; /* (r) index of first value transferred */ |
| 4855 | int ntransfer; /* (r) number of transferred values */ | 4856 | int ntransfer; /* (r) number of transferred values */ |
| @@ -4938,6 +4939,14 @@ true if this function invocation was called by a tail call. | |||
| 4938 | In this case, the caller of this level is not in the stack. | 4939 | In this case, the caller of this level is not in the stack. |
| 4939 | } | 4940 | } |
| 4940 | 4941 | ||
| 4942 | @item{@id{extraargs}| | ||
| 4943 | The number of extra arguments added by the call | ||
| 4944 | to functions called through @idx{__call} metamethods. | ||
| 4945 | (Each @idx{__call} metavalue adds a single extra argument, | ||
| 4946 | the object being called, | ||
| 4947 | but there may be a chain of @idx{__call} metavalues.) | ||
| 4948 | } | ||
| 4949 | |||
| 4941 | @item{@id{nups}| | 4950 | @item{@id{nups}| |
| 4942 | the number of upvalues of the function. | 4951 | the number of upvalues of the function. |
| 4943 | } | 4952 | } |
| @@ -5045,7 +5054,7 @@ fills in the fields @id{source}, @id{short_src}, | |||
| 5045 | @id{linedefined}, @id{lastlinedefined}, and @id{what}; | 5054 | @id{linedefined}, @id{lastlinedefined}, and @id{what}; |
| 5046 | } | 5055 | } |
| 5047 | 5056 | ||
| 5048 | @item{@Char{t}| fills in the field @id{istailcall}; | 5057 | @item{@Char{t}| fills in the fields @id{istailcall} and @id{extraargs}; |
| 5049 | } | 5058 | } |
| 5050 | 5059 | ||
| 5051 | @item{@Char{u}| fills in the fields | 5060 | @item{@Char{u}| fills in the fields |
| @@ -7993,7 +8002,7 @@ returns @fail plus the position of the first invalid byte. | |||
| 7993 | 8002 | ||
| 7994 | @LibEntry{utf8.offset (s, n [, i])| | 8003 | @LibEntry{utf8.offset (s, n [, i])| |
| 7995 | 8004 | ||
| 7996 | Returns the the position of the @id{n}-th character of @id{s} | 8005 | Returns the position of the @id{n}-th character of @id{s} |
| 7997 | (counting from byte position @id{i}) as two integers: | 8006 | (counting from byte position @id{i}) as two integers: |
| 7998 | The index (in bytes) where its encoding starts and the | 8007 | The index (in bytes) where its encoding starts and the |
| 7999 | index (in bytes) where it ends. | 8008 | index (in bytes) where it ends. |
diff --git a/testes/calls.lua b/testes/calls.lua index 12312d60..31028215 100644 --- a/testes/calls.lua +++ b/testes/calls.lua | |||
| @@ -204,6 +204,17 @@ do print"testing chains of '__call'" | |||
| 204 | assert(Res[i][1] == i) | 204 | assert(Res[i][1] == i) |
| 205 | end | 205 | end |
| 206 | assert(Res[N + 1] == "a" and Res[N + 2] == "b" and Res[N + 3] == "c") | 206 | assert(Res[N + 1] == "a" and Res[N + 2] == "b" and Res[N + 3] == "c") |
| 207 | |||
| 208 | local function u (...) | ||
| 209 | local n = debug.getinfo(1, 't').extraargs | ||
| 210 | assert(select("#", ...) == n) | ||
| 211 | return n | ||
| 212 | end | ||
| 213 | |||
| 214 | for i = 0, N do | ||
| 215 | assert(u() == i) | ||
| 216 | u = setmetatable({}, {__call = u}) | ||
| 217 | end | ||
| 207 | end | 218 | end |
| 208 | 219 | ||
| 209 | 220 | ||
diff --git a/testes/db.lua b/testes/db.lua index 49ff8e3e..fc0db9ea 100644 --- a/testes/db.lua +++ b/testes/db.lua | |||
| @@ -624,6 +624,9 @@ local function f (x) | |||
| 624 | end | 624 | end |
| 625 | end | 625 | end |
| 626 | 626 | ||
| 627 | assert(debug.getinfo(print, 't').istailcall == false) | ||
| 628 | assert(debug.getinfo(print, 't').extraargs == 0) | ||
| 629 | |||
| 627 | function g(x) return f(x) end | 630 | function g(x) return f(x) end |
| 628 | 631 | ||
| 629 | function g1(x) g(x) end | 632 | function g1(x) g(x) end |
diff --git a/testes/errors.lua b/testes/errors.lua index 80d91a92..0925fe58 100644 --- a/testes/errors.lua +++ b/testes/errors.lua | |||
| @@ -117,6 +117,31 @@ else | |||
| 117 | return 1 | 117 | return 1 |
| 118 | ]] | 118 | ]] |
| 119 | assert(string.find(res, "xuxu.-main chunk")) | 119 | assert(string.find(res, "xuxu.-main chunk")) |
| 120 | |||
| 121 | do -- tests for error messages about extra arguments from __call | ||
| 122 | local function createobj (n) | ||
| 123 | -- function that raises an error on its n-th argument | ||
| 124 | local code = string.format("argerror %d 'msg'", n) | ||
| 125 | local func = T.makeCfunc(code) | ||
| 126 | -- create a chain of 2 __call objects | ||
| 127 | local M = setmetatable({}, {__call = func}) | ||
| 128 | M = setmetatable({}, {__call = M}) | ||
| 129 | -- put it as a method for a new object | ||
| 130 | return {foo = M} | ||
| 131 | end | ||
| 132 | |||
| 133 | _G.a = createobj(1) -- error in first (extra) argument | ||
| 134 | checkmessage("a:foo()", "bad extra argument #1") | ||
| 135 | |||
| 136 | _G.a = createobj(2) -- error in second (extra) argument | ||
| 137 | checkmessage("a:foo()", "bad extra argument #2") | ||
| 138 | |||
| 139 | _G.a = createobj(3) -- error in self (after two extra arguments) | ||
| 140 | checkmessage("a:foo()", "bad self") | ||
| 141 | |||
| 142 | _G.a = createobj(4) -- error in first regular argument (after self) | ||
| 143 | checkmessage("a:foo()", "bad argument #1") | ||
| 144 | end | ||
| 120 | end | 145 | end |
| 121 | 146 | ||
| 122 | 147 | ||
