[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: lua_Debug.namewhat missing on metamethods; implications for luaL_argerror
- From: David Manura <dm.lua@...>
- Date: Sat, 3 Nov 2007 22:35:45 +0000 (UTC)
In Lua 5.1.2, lua_getinfo (or debug.getinfo) does not fill the
lua_Debug.namewhat and lua_Debug.name fields for metamethod calls. One
implication is that luaL_check* calls, which rely on this information, do not
give meaningful error messages when used in metamethods.
As an example,
/* test1.c */
#include <stdio.h>
#include "lua.h"
#include "lauxlib.h"
static int
lnewindex(lua_State * L) {
luaL_checknumber(L, 3);
return 0;
}
static int
test(lua_State * L) {
lua_newtable(L); /* t */
/* setmetatable(t, {__newindex=lnewindex}) */
lua_newtable(L);
lua_pushcfunction(L, lnewindex);
lua_setfield(L, -2, "__newindex");
lua_setmetatable(L, -2);
/* t[key] = value */
#if 1
lua_pushstring(L, "x");
lua_pushboolean(L, 1);
lua_settable(L, -3);
#endif
/* alternative via Lua */
#if 0
luaL_loadstring(L, "local t=...; t.x = true");
lua_pushvalue(L, -2);
lua_call(L, 1, 0);
#endif
return 0;
}
int main() {
lua_State * L = luaL_newstate();
luaL_openlibs(L);
if (lua_cpcall(L, test, NULL) != 0) {
puts(lua_tostring(L, -1));
}
lua_close(L);
return 0;
}
$ ./test1
bad argument #3 to '?' (number expected, got boolean)
As another example but in Lua,
-- test2.lua
local mt = {}
function mt:__newindex(k,v)
local inf = debug.getinfo(1)
print(inf.name, inf.namewhat, inf.func)
end
local t = setmetatable({}, mt)
t.x = 1
$ lua test2.lua
nil function: 0x6862d0
The solution I thought was to patch ldebug.c getfuncname so that metamethod
calls result in namewhat having a new value "meta" and name having the key
passed to __newindex, or probably more generally the name of the metamethod.
This is shown below for __newindex:
diff -ru lua-5.1.2/src/ldebug.c lua-5.1.2-test1/src/ldebug.c
--- lua-5.1.2/src/ldebug.c 2007-03-23 12:06:19.000000000 -0500
+++ lua-5.1.2-test1/src/ldebug.c 2007-11-03 16:36:36.656250000 -0500
@@ -534,6 +534,15 @@
if (GET_OPCODE(i) == OP_CALL || GET_OPCODE(i) == OP_TAILCALL ||
GET_OPCODE(i) == OP_TFORLOOP)
return getobjname(L, ci, GETARG_A(i), name);
+
+ /* PATCHED */
+ else if (GET_OPCODE(i) == OP_SETTABLE) {
+ /* if (isLua(ci)) { TValue * key = (ci+1)->base + (2 - 1);
+ *name = ttisstring(key) ? svalue(key) : NULL; } */
+ *name = getstr(G(L)->tmname[TM_NEWINDEX]); /* __newindex */
+ return "meta";
+ } /* TODO: support other metamethods */
+
else
return NULL; /* no useful name can be found */
}
The metamethod __newindex is deduced as before from opcodes and could probably
be extended to support all other metamethods.
Furthermore, lauxlib.c luaL_argerror is patched to display this information:
diff -ru lua-5.1.2/src/lauxlib.c lua-5.1.2-test1/src/lauxlib.c
--- lua-5.1.2/src/lauxlib.c 2006-03-21 14:31:09.000000000 -0500
+++ lua-5.1.2-test1/src/lauxlib.c 2007-11-03 17:08:09.968750000 -0500
@@ -51,6 +51,23 @@
return luaL_error(L, "calling " LUA_QS " on bad self (%s)",
ar.name, extramsg);
}
+
+ /* PATCHED */
+ else if (strcmp(ar.namewhat, "meta") == 0) {
+ if (strcmp(ar.name, "__newindex") == 0) {
+ const char * fieldname;
+ lua_checkstack(L, 2);
+ lua_getglobal(L, "tostring"); /* ensure baselib loaded? */
+ lua_pushvalue(L, 2);
+ lua_call(L, 1, 1);
+ fieldname = lua_tostring(L, -1);
+ if (fieldname == NULL) fieldname = "?";
+ return luaL_error(L, "bad %s in accessing field %s (%s)",
+ narg==3 ? "value" : narg==2 ? "key" : narg==1 ? "object" : "?",
+ fieldname, extramsg);
+ } /* TODO: support other metamethods */
+ }
+
if (ar.name == NULL)
ar.name = "?";
return luaL_error(L, "bad argument #%d to " LUA_QS " (%s)",
We now have
$ ./test1
bad argument #3 to '?' (number expected, got boolean)
$ lua test2.lua
__newindex meta function: 0x6862d8
As seen, the second example now displays the required information, but the first
example is unchanged. The problem is that this approach of querying opcodes
does not work when called from C. However, if the #ifdef's in the first example
are changed so that the metamethod is called from Lua, we correctly get
$ ./a
[string "local t=...; t.x = true"]:1: bad value in accessing field x
(number expected, got boolean)
Comments? Improvements? Is argument checking in metamethods typically done
without luaL_check* functions? should it be?