Kaynağa Gözat

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.
Roberto Ierusalimschy 8 ay önce
ebeveyn
işleme
50c7c915ee
9 değiştirilmiş dosya ile 83 ekleme ve 12 silme
  1. 16 8
      lauxlib.c
  2. 3 1
      ldblib.c
  3. 9 1
      ldebug.c
  4. 4 0
      ltests.c
  5. 1 0
      lua.h
  6. 11 2
      manual/manual.of
  7. 11 0
      testes/calls.lua
  8. 3 0
      testes/db.lua
  9. 25 0
      testes/errors.lua

+ 16 - 8
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);
-  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)
   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)",
-                        arg, ar.name, extramsg);
+  return luaL_error(L, "bad %s #%d to '%s' (%s)",
+                       argword, arg, ar.name, extramsg);
 }
 }
 
 
 
 

+ 3 - 1
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'))

+ 9 - 1
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 - 0
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 - 0
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 */

+ 11 - 2
manual/manual.of

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

+ 11 - 0
testes/calls.lua

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

+ 3 - 0
testes/db.lua

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

+ 25 - 0
testes/errors.lua

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