diff options
author | Hadriel Kaplan <hadrielk@yahoo.com> | 2014-03-10 15:18:24 -0400 |
---|---|---|
committer | Anders Broman <a.broman58@gmail.com> | 2014-03-11 05:30:58 +0000 |
commit | 9961ee369c97768d5ed906d5ef5d17b718d33d3e (patch) | |
tree | 0c4c556d5949df2e4b99df061e3678729f928f5e /epan | |
parent | f4de2a2dd1142e7d7c9069bc923eeb8c7ac20333 (diff) |
Fix Bug 9870 'Lua: trying to call/get an invalid name results in a get-loop error'
Due to the change I made previously for how methods are accessed, if you try
to access one that doesn't exist (for example mistype it or whatever), you get
an internal Lua error about a loop in table get, as opposed to the right error
message about the field not existing.
That's because I had set the class' metatable __index metamethod to point to
the class table, which of course has the metatable with the __index
metamethod, causing a lookup loop. Blech.
Change-Id: I20d3717feadd45f652c2640e1671846184e7082d
Reviewed-on: https://code.wireshark.org/review/593
Reviewed-by: Alexis La Goutte <alexis.lagoutte@gmail.com>
Reviewed-by: Anders Broman <a.broman58@gmail.com>
Diffstat (limited to 'epan')
-rw-r--r-- | epan/wslua/wslua.h | 6 | ||||
-rw-r--r-- | epan/wslua/wslua_internals.c | 77 |
2 files changed, 74 insertions, 9 deletions
diff --git a/epan/wslua/wslua.h b/epan/wslua/wslua.h index 2ec7456766..9a41a714f0 100644 --- a/epan/wslua/wslua.h +++ b/epan/wslua/wslua.h @@ -343,6 +343,7 @@ typedef struct _wslua_attribute_table { lua_CFunction setfunc; } wslua_attribute_table; extern int wslua_reg_attributes(lua_State *L, const wslua_attribute_table *t, gboolean is_getter); +extern int wslua_set__index(lua_State *L); #define WSLUA_TYPEOF_FIELD "__typeof" @@ -387,9 +388,8 @@ extern int wslua_reg_attributes(lua_State *L, const wslua_attribute_table *t, gb /* setup the meta table */ \ WSLUA_REGISTER_META(C); \ luaL_getmetatable(L, #C); \ - /* push a copy of the class methods table, and set it to be the metatable's __index field */ \ - lua_pushvalue (L, -2); \ - lua_setfield (L, -2, "__index"); \ + /* the following sets the __index metamethod appropriately */ \ + wslua_set__index(L); \ /* set the metatable to be the class's metatable, so scripts can inspect it, and metamethods work for class tables */ \ lua_setmetatable(L, -2); \ /* set the class methods table as the global class table */ \ diff --git a/epan/wslua/wslua_internals.c b/epan/wslua/wslua_internals.c index c8c1614f17..50cd8a3d40 100644 --- a/epan/wslua/wslua_internals.c +++ b/epan/wslua/wslua_internals.c @@ -121,7 +121,6 @@ WSLUA_API void wslua_print_stack(char* s, lua_State* L) { printf("\n"); } - /* C-code function equivalent of the typeof() function we created in Lua. * The Lua one is for Lua scripts to use, this one is for C-code to use. */ @@ -168,7 +167,6 @@ void wslua_assert_table_field_new(lua_State *L, int idx, const gchar *name) { * its functions were populated by the WSLUA_REGISTER_ATTRIBUTES() macro, and * really by wslua_reg_attributes(). */ - static int wslua_attribute_dispatcher (lua_State *L) { lua_CFunction cfunc = NULL; const gchar *fieldname = lua_shiftstring(L,2); /* remove the field name */ @@ -178,7 +176,7 @@ static int wslua_attribute_dispatcher (lua_State *L) { /* the userdata object is at index 1, fieldname was at 2 but no longer, now we get the getter/setter table at upvalue 1 */ if (!lua_istable(L, lua_upvalueindex(1))) - return luaL_error(L, "Accessor dispatcher cannot retrieve the metatable"); + return luaL_error(L, "Accessor dispatcher cannot retrieve the getter/setter table"); lua_rawgetfield(L, lua_upvalueindex(1), fieldname); /* field's cfunction is now at -1 */ @@ -222,9 +220,9 @@ static int wslua_attribute_dispatcher (lua_State *L) { * cfunctions, saving that as an upvalue of a dispatcher cfunction, and using that * dispatcher cfunction as the value of the __index field of the metatable of the wslua object. * - * In some cases, the metatable _index/__newindex will already be a table; for example if - * class methods were registered, then __index will already be a table. In that case, we - * move the existing one to be an upvalue of the attribute dispatcher function. The attribute + * In some cases, the metatable _index/__newindex will already be a function; for example if + * class methods were registered, then __index will already be a function In that case, we + * move the __methods table to be an upvalue of the attribute dispatcher function. The attribute * dispatcher will look for it and return the method, if it doesn't find an attribute field. * The code below makes sure the attribute names don't overlap with method names. * @@ -252,6 +250,17 @@ int wslua_reg_attributes(lua_State *L, const wslua_attribute_table *t, gboolean /* there is one, so make it be the attribute dispatchers upvalue #2 table */ nup = 2; } + else if (lua_iscfunction(L, -1)) { + /* there's a methods __index dispatcher, copy the __methods table */ + lua_pop(L,1); /* pop the cfunction */ + lua_rawgetfield(L, midx, "__methods"); + if (!lua_istable(L, -1)) { + /* oh oh, something's wrong */ + fprintf(stderr, "got a __index cfunction but no __methods table when registering attributes!\n"); + exit(1); + } + nup = 2; + } else { fprintf(stderr, "'%s' field is not a table in the Lua stack when registering attributes!\n", metafield); exit(1); @@ -300,3 +309,59 @@ int wslua_reg_attributes(lua_State *L, const wslua_attribute_table *t, gboolean /* we should now be back to real metatable being on top */ return 0; } + +/* similar to __index metamethod but without triggering more metamethods */ +static int wslua__index(lua_State *L) { + const gchar *fieldname = lua_shiftstring(L,2); /* remove the field name */ + + /* the userdata object or table is at index 1, fieldname was at 2 but no longer, + now we get the metatable, so we can get the methods table */ + if (!lua_getmetatable(L,1)) { + /* this should be impossible */ + return luaL_error(L, "No such '%s' field", fieldname); + } + + lua_rawgetfield(L, 2, "__methods"); /* method table is now at 3 */ + lua_remove(L,2); /* remove metatable, methods table is at 2 */ + + if (!lua_istable(L, -1)) { + const gchar *classname = wslua_typeof(L, 1); + lua_pop(L, 1); /* pop the nil getfield result */ + return luaL_error(L, "No such '%s' field for object type '%s'", fieldname, classname); + } + + lua_rawgetfield(L, 2, fieldname); /* field's value/function is now at 3 */ + lua_remove(L,2); /* remove methods table, field value si at 2 */ + + if (lua_isnil(L, -1)) { + const gchar *classname = wslua_typeof(L, 1); + lua_pop(L, 1); /* pop the nil getfield result */ + return luaL_error(L, "No such '%s' function/method/field for object type '%s'", fieldname, classname); + } + + /* we found a method for Lua to call, or a value of some type, so give it back to Lua */ + return 1; +} + +/* + * This function assumes there's a class methods table at index 1, and its metatable at 2, + * when it's initially called, and leaves them that way when done. + */ +int wslua_set__index(lua_State *L) { + + if (!lua_istable(L, 2) || !lua_istable(L, 1)) { + fprintf(stderr, "No metatable or class table in the Lua stack when registering __index!\n"); + exit(1); + } + + /* push a copy of the class methods table, and set it to be the metatable's __methods field */ + lua_pushvalue (L, 1); + lua_rawsetfield(L, 2, "__methods"); + + /* set the wslua__index to be the __index metamethod */ + lua_pushcfunction(L, wslua__index); + lua_rawsetfield(L, 2, "__index"); + + /* we should now be back to real metatable being on top */ + return 0; +} |