aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2024-11-19 14:09:18 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2024-11-19 14:09:18 -0300
commit50c7c915ee2fa239043d5456237f5145d064089b (patch)
tree386e17e4baa154bb60dc54c1a00c751e3adedde9
parentb117bdb3448778d9e7f9a0302791e8ac3bb97ddd (diff)
downloadlua-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.c24
-rw-r--r--ldblib.c4
-rw-r--r--ldebug.c10
-rw-r--r--ltests.c4
-rw-r--r--lua.h1
-rw-r--r--manual/manual.of13
-rw-r--r--testes/calls.lua11
-rw-r--r--testes/db.lua3
-rw-r--r--testes/errors.lua25
9 files changed, 83 insertions, 12 deletions
diff --git a/lauxlib.c b/lauxlib.c
index e4b12587..d37d2f8c 100644
--- a/lauxlib.c
+++ b/lauxlib.c
@@ -170,19 +170,27 @@ LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1,
170 170
171LUALIB_API int luaL_argerror (lua_State *L, int arg, const char *extramsg) { 171LUALIB_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
diff --git a/ldblib.c b/ldblib.c
index a0a06dd7..c7b74812 100644
--- a/ldblib.c
+++ b/ldblib.c
@@ -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'))
diff --git a/ldebug.c b/ldebug.c
index d1b47c56..ee3ac17f 100644
--- a/ldebug.c
+++ b/ldebug.c
@@ -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': {
diff --git a/ltests.c b/ltests.c
index 8191f14a..3edf805e 100644
--- a/ltests.c
+++ b/ltests.c
@@ -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;
diff --git a/lua.h b/lua.h
index 5fbc9d34..aefa3b8c 100644
--- a/lua.h
+++ b/lua.h
@@ -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.
4938In this case, the caller of this level is not in the stack. 4939In this case, the caller of this level is not in the stack.
4939} 4940}
4940 4941
4942@item{@id{extraargs}|
4943The number of extra arguments added by the call
4944to functions called through @idx{__call} metamethods.
4945(Each @idx{__call} metavalue adds a single extra argument,
4946the object being called,
4947but there may be a chain of @idx{__call} metavalues.)
4948}
4949
4941@item{@id{nups}| 4950@item{@id{nups}|
4942the number of upvalues of the function. 4951the 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
7996Returns the the position of the @id{n}-th character of @id{s} 8005Returns 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:
7998The index (in bytes) where its encoding starts and the 8007The index (in bytes) where its encoding starts and the
7999index (in bytes) where it ends. 8008index (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
207end 218end
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
625end 625end
626 626
627assert(debug.getinfo(print, 't').istailcall == false)
628assert(debug.getinfo(print, 't').extraargs == 0)
629
627function g(x) return f(x) end 630function g(x) return f(x) end
628 631
629function g1(x) g(x) end 632function 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
120end 145end
121 146
122 147