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 | ||