diff options
-rw-r--r-- | doc/README.wslua | 50 | ||||
-rw-r--r-- | epan/wslua/wslua.h | 81 | ||||
-rw-r--r-- | epan/wslua/wslua_internals.c | 495 | ||||
-rw-r--r-- | epan/wslua/wslua_pinfo.c | 1 | ||||
-rw-r--r-- | test/lua/globals_2.2.txt | 25 |
5 files changed, 362 insertions, 290 deletions
diff --git a/doc/README.wslua b/doc/README.wslua index acb372c7f2..738535c615 100644 --- a/doc/README.wslua +++ b/doc/README.wslua @@ -150,19 +150,21 @@ explicitly using strings and function names, you should use the WSLUA_METHODS macro name for the array, and use WSLUA_CLASS_FNREG macro for each entry. The WSLUA_META table follows the same behavior, with the WSLUA_CLASS_MTREG macro for each entry. Make sure your C-function names use two underscores -instead of one. +instead of one (for instance, ClassName__tostring). Once you've created the appropriate array tables, define a registration function named 'ClassName_register', where 'ClassName'is your class name, the same one used in WSLUA_CLASS_DEFINE. The make-reg.pl Perl script will search your file for WSLUA_CLASS_DEFINE, and it generates a register_wslua.c which will call your ClassName_register function during Wireshark initialization. -Inside your ClassName_register function, use either the WSLUA_REGISTER_CLASS -or the WSLUA_REGISTER_META macros with the class name as the argument. That -will automatically register the methods/meta tables into Lua. Use -WSLUA_REGISTER_CLASS if your class has methods and optionally metamethods, or -use WSLUA_REGISTER_META if it only has metamethods - do *not* use both. Note -that your class does not need to have a WSLUA_METHODS nor WSLUA_META table. +Define a wslua_class structure which describes the class and register this in +your ClassName_register function using one of: + - wslua_register_classinstance_meta to create a metatable which allows + instances of a class to have methods and attributes. C code can create such + instances by setting the metatable on userdata, the class itself is not + directly visible in the Lua scope. + - wslua_register_class to additionally expose a class with static functions + that is also directly visible in the Lua global scope. Also, you should read the 'Memory management model' section later in this document. @@ -196,11 +198,30 @@ type each provide a __call metamethod as an accessor - I strongly suggest you do NOT do that, as it's not a common model and will confuse people since it doesn't follow the model of the other classes in Wireshark. -The way attribute accessing is handled is a bit too complicated to discuss -here, but is documented in wslua_internals.c above the wslua_reg_attributes -function definition. All you need to know is how to write the C-code to -register attributes, and the code to provide getter/setters for them. To -create them, you create an array table similar to the WSLUA_METHODS and +Attributes are handled internally like this: + + -- invoked on myObj.myAttribute + function myObj.__metatable:__index(key) + if "getter for key exists" then + return getter(self) + elseif "method for key exists" then + -- ensures that myObj.myMethod() works + return method + else + error("no such property error message") + end + end + -- invoked on myObj.myAttribute = 1 + function myObj.__metatable:__newindex(key, value) + if "setter for key exists" then + return setter(self, value) + else + error("no such property error message") + end + end + +To add getters/setters in C, initialize the "attrs" member of the wslua_class +structure. This should contain an array table similar to the WSLUA_METHODS and WSLUA_META tables, except using the macro name WSLUA_ATTRIBUTES. Inside this array, each entry should use one of the following macros: WSLUA_ATTRIBUTE_ROREG, WSLUA_ATTRIBUTE_WOREG, or WSLUA_ATTRIBUTE_RWREG. Those provide the hooks for @@ -213,11 +234,6 @@ the WSLUA_ATTRIBUTE_NAMED_BOOLEAN_GETTER(Foo,bar,choo) macro creates a getter function to get the boolean value of the Class Foo's choo member variable, as the Lua attribute named 'bar'. -To register the attributes, your Class registration function must call the -WSLUA_REGISTER_ATTRIBUTES(ClassName) macro, after it calls either the -WSLUA_REGISTER_META(ClassName) macro or the WSLUA_REGISTER_CLASS(ClassName) -one. - Callback function registration: For some callbacks, there are register_* Lua global functions, which take a diff --git a/epan/wslua/wslua.h b/epan/wslua/wslua.h index 335adbb887..0fa2c9a6bf 100644 --- a/epan/wslua/wslua.h +++ b/epan/wslua/wslua.h @@ -412,57 +412,40 @@ 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" #ifdef HAVE_LUA -#define WSLUA_REGISTER_META(C) { \ - /* check for existing metatable in registry */ \ +/* temporary transition macro to reduce duplication in WSLUA_REGISTER_xxx. */ +#define WSLUA_REGISTER_GC(C) \ luaL_getmetatable(L, #C); \ - if (!lua_isnil (L, -1)) { \ - fprintf(stderr, "ERROR: Attempt to register metatable '%s' which already exists in Lua registry\n", #C); \ - exit(1); \ - } \ - lua_pop (L, 1); \ - /* create a new metatable and register metamethods into it */ \ - luaL_newmetatable (L, #C); \ - wslua_setfuncs (L, C ## _meta, 0); \ - /* add a metatable field named '__typeof' = the class name, this is used by the typeof() Lua func */ \ - lua_pushstring(L, #C); \ - lua_setfield(L, -2, WSLUA_TYPEOF_FIELD); \ /* add the '__gc' metamethod with a C-function named Class__gc */ \ /* this will force ALL wslua classes to have a Class__gc function defined, which is good */ \ lua_pushcfunction(L, C ## __gc); \ lua_setfield(L, -2, "__gc"); \ /* pop the metatable */ \ - lua_pop(L, 1); \ + lua_pop(L, 1) + +#define WSLUA_REGISTER_META(C) { \ + const wslua_class C ## _class = { \ + .name = #C, \ + .instance_meta = C ## _meta, \ + }; \ + wslua_register_classinstance_meta(L, &C ## _class); \ + WSLUA_REGISTER_GC(C); \ } #define WSLUA_REGISTER_CLASS(C) { \ - /* check for existing class table in global */ \ - lua_getglobal (L, #C); \ - if (!lua_isnil (L, -1)) { \ - fprintf(stderr, "ERROR: Attempt to register class '%s' which already exists in global Lua table\n", #C); \ - exit(1); \ - } \ - lua_pop (L, 1); \ - /* create new class method table and 'register' the class methods into it */ \ - lua_newtable (L); \ - wslua_setfuncs (L, C ## _methods, 0); \ - /* add a method-table field named '__typeof' = the class name, this is used by the typeof() Lua func */ \ - lua_pushstring(L, #C); \ - lua_setfield(L, -2, WSLUA_TYPEOF_FIELD); \ - /* setup the meta table */ \ - WSLUA_REGISTER_META(C); \ - luaL_getmetatable(L, #C); \ - /* 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 */ \ - lua_setglobal (L, #C); \ + const wslua_class C ## _class = { \ + .name = #C, \ + .class_methods = C ## _methods, \ + .class_meta = C ## _meta, \ + .instance_methods = C ## _methods, \ + .instance_meta = C ## _meta, \ + }; \ + wslua_register_class(L, &C ## _class); \ + WSLUA_REGISTER_GC(C); \ } /* see comments for wslua_reg_attributes and wslua_attribute_dispatcher to see how this attribute stuff works */ @@ -470,8 +453,7 @@ extern int wslua_set__index(lua_State *L); /* get metatable from Lua registry */ \ luaL_getmetatable(L, #C); \ if (lua_isnil(L, -1)) { \ - fprintf(stderr, "ERROR: Attempt to register attributes without a pre-existing metatable for '%s' in Lua registry\n", #C); \ - exit(1); \ + g_error("Attempt to register attributes without a pre-existing metatable for '%s' in Lua registry\n", #C); \ } \ /* register the getters/setters in their tables */ \ wslua_reg_attributes(L, C##_attributes, TRUE); \ @@ -738,6 +720,25 @@ WSLUA_DECLARE_FUNCTIONS() extern lua_State* wslua_state(void); + +/* wslua_internals.c */ +/** + * @brief Type for defining new classes. + * + * A new class is defined as a Lua table type. Instances of this class are + * created through pushXxx which sets the appropriate metatable. + */ +typedef struct _wslua_class { + const char *name; /**< Class name that is exposed to Lua code. */ + const luaL_Reg *class_methods; /**< Methods for the static class (optional) */ + const luaL_Reg *class_meta; /**< Metatable for the static class (optional) */ + const luaL_Reg *instance_methods; /**< Methods for class instances. (optional) */ + const luaL_Reg *instance_meta; /**< Metatable for class instances (optional) */ + const wslua_attribute_table *attrs; /**< Table of getters/setters for attributes on class instances (optional). */ +} wslua_class; +void wslua_register_classinstance_meta(lua_State *L, const wslua_class *cls_def); +void wslua_register_class(lua_State *L, const wslua_class *cls_def); + extern int wslua__concat(lua_State* L); extern gboolean wslua_toboolean(lua_State* L, int n); extern gboolean wslua_checkboolean(lua_State* L, int n); @@ -746,13 +747,11 @@ extern lua_Integer wslua_tointeger(lua_State* L, int n); extern int wslua_optboolint(lua_State* L, int n, int def); extern const char* wslua_checklstring_only(lua_State* L, int n, size_t *l); extern const char* wslua_checkstring_only(lua_State* L, int n); -extern const gchar* lua_shiftstring(lua_State* L,int idx); extern void wslua_setfuncs(lua_State *L, const luaL_Reg *l, int nup); extern const gchar* wslua_typeof_unknown; extern const gchar* wslua_typeof(lua_State *L, int idx); extern gboolean wslua_get_table(lua_State *L, int idx, const gchar *name); extern gboolean wslua_get_field(lua_State *L, int idx, const gchar *name); -extern void wslua_assert_table_field_new(lua_State *L, int idx, const gchar *name); extern int dissect_lua(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* data); extern int heur_dissect_lua(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* data); extern expert_field* wslua_get_expert_field(const int group, const int severity); diff --git a/epan/wslua/wslua_internals.c b/epan/wslua/wslua_internals.c index ff61a74dd9..f0c0ba81bd 100644 --- a/epan/wslua/wslua_internals.c +++ b/epan/wslua/wslua_internals.c @@ -30,6 +30,11 @@ #include "wslua.h" #include <wsutil/ws_printf.h> /* ws_debug_printf */ +/* Several implementation details (__getters, __setters, __methods) were exposed + * to Lua code. These are normally not used by dissectors, just for debugging + * (and the "wslua global" test). Enable by setting WSLUA_WITH_INTROSPECTION */ +#define WSLUA_WITH_INTROSPECTION + #if LUA_VERSION_NUM == 501 /* Compatibility with Lua 5.1, function was added in 5.2 */ static @@ -146,17 +151,6 @@ WSLUA_API const char* wslua_checkstring_only(lua_State* L, int n) { return wslua_checklstring_only(L, n, NULL); } -WSLUA_API const gchar* lua_shiftstring(lua_State* L, int i) { - const gchar* p = luaL_checkstring(L, i); - - if (p) { - lua_remove(L,i); - return p; - } else { - return NULL; - } -} - /* following is based on the luaL_setfuncs() from Lua 5.2, so we can use it in pre-5.2 */ WSLUA_API void wslua_setfuncs(lua_State *L, const luaL_Reg *l, int nup) { luaL_checkstack(L, nup, "too many upvalues"); @@ -247,230 +241,120 @@ gboolean wslua_get_field(lua_State *L, int idx, const gchar *name) { return result; } -/* This verifies/asserts that field 'name' doesn't already exist in table at location idx. - * If it does, this EXITS wireshark, because this is a fundamental programming error. - * As such, this function is only useful for special circumstances, notably - * those that will happen on application start every time, as opposed to - * something that could happen only if a Lua script makes it happen. - */ -void wslua_assert_table_field_new(lua_State *L, int idx, const gchar *name) { - lua_rawgetfield(L, idx, name); - if (!lua_isnil (L, -1)) { - fprintf(stderr, "ERROR: Field %s already exists!\n", name); - exit(1); - } - lua_pop (L, 1); /* pop the nil */ \ -} - -/* This function is an attribute field __index/__newindex (ie, getter/setter) dispatcher. - * What the heck does that mean? Well, when a Lua script tries to retrieve a - * table/userdata field by doing this: - * local foo = myobj.fieldname - * if 'fieldname' does not exist in the 'myobj' table/userdata, then Lua calls - * the '__index' metamethod of the table/userdata, and puts onto the Lua - * stack the table and fieldname string. So this function here handles that, - * by dispatching that request to the appropriate getter function in the - * __getters table within the metatable of the userdata. That table and - * 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 */ - const gchar *classname = NULL; - const gchar *type = NULL; - - /* 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 getter/setter table"); - - lua_rawgetfield(L, lua_upvalueindex(1), fieldname); /* field's cfunction is now at -1 */ - - if (!lua_iscfunction(L, -1)) { - lua_pop(L,1); /* pop whatever we got before */ - /* check if there's a methods table */ - if (lua_istable(L, lua_upvalueindex(2))) { - lua_rawgetfield(L, lua_upvalueindex(2), fieldname); - if (lua_iscfunction(L,-1)) { - /* we found a method for Lua to call, so give it back to Lua */ - return 1; - } - lua_pop(L,1); /* pop whatever we got before */ - } - classname = wslua_typeof(L, 1); - type = wslua_typeof(L, lua_upvalueindex(1)); - lua_pop(L, 1); /* pop the nil/invalid getfield result */ - return luaL_error(L, "No such '%s' %s attribute/field for object type '%s'", fieldname, type, classname); - } - cfunc = lua_tocfunction(L, -1); - lua_pop(L, 1); /* pop the cfunction */ - - /* the stack is now as if it had been calling the getter/setter c-function directly, so do it */ - return (*cfunc)(L); -} - - -/* This function "registers" attribute functions - i.e., getters/setters for Lua objects. - * This way we don't have to write the __index/__newindex function dispatcher for every - * wslua class. Instead, your class should use WSLUA_REGISTER_ATTRIBUTES(classname), which - * ultimately calls this one - it calls it twice: once to register getters, once to register - * setters. - * - * The way this all works is every wslua class has a metatable. One of the fields of that - * metatable is a __index field, used for "getter" access, and a __newindex field used for - * "setter" access. If the __index field's _value_ is a Lua table, then Lua looks - * up that table, as it does for class methods for example; but if the __index field's - * value is a function/cfunction, Lua calls it instead to get/set the field. So - * we use that behavior to access our getters/setters, by creating a table of getter - * 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 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. - * - * This function assumes there's a class metatable on top of the stack when it's initially called, - * and leaves it on top when done. +/** + * This is a transition function that sets attributes on classes. It must be + * replaced by setting the attributes in the wslua_class structure such that + * WSLUA_REGISTER_ATTRIBUTES can be nuked. */ int wslua_reg_attributes(lua_State *L, const wslua_attribute_table *t, gboolean is_getter) { - int midx = lua_gettop(L); const gchar *metafield = is_getter ? "__index" : "__newindex"; - int idx; - int nup = 1; /* number of upvalues */ - if (!lua_istable(L, midx)) { - fprintf(stderr, "No metatable in the Lua stack when registering attributes!\n"); - exit(1); - } - - /* check if there's a __index/__newindex table already - could be if this class has methods */ - lua_rawgetfield(L, midx, metafield); + /* assume __index/__newindex exists and index 1 is a metatable */ + lua_rawgetfield(L, -1, metafield); if (lua_isnil(L, -1)) { - /* there isn't one, pop the nil */ - lua_pop(L,1); - } - else if (lua_istable(L, -1)) { - /* 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); + g_error("%s expected in metatable, found none!\n", metafield); } + lua_pop(L, 1); - /* make our new getter/setter table - we don't need to pop it later */ - lua_newtable(L); - idx = lua_gettop(L); + /* Find table to add properties. */ + metafield = is_getter ? "__getters" : "__setters"; + lua_rawgetfield(L, -1, metafield); + if (!lua_istable(L, -1)) { + g_error("Property %s is not found in metatable!\n", metafield); + } - /* fill the getter/setter table with given functions */ + /* Fill the getter/setter table with given functions. Being a transition + * function that will be removed later, this does not perform duplicate + * keys detection. */ for (; t->fieldname != NULL; t++) { lua_CFunction cfunc = is_getter ? t->getfunc : t->setfunc; if (cfunc) { - /* if there's a previous methods table, make sure this attribute name doesn't collide */ - if (nup > 1) { - lua_rawgetfield(L, -1, t->fieldname); - if (!lua_isnil(L,-1)) { - fprintf(stderr, "'%s' attribute name already exists as method name for the class\n", t->fieldname); - exit(1); - } - lua_pop(L,1); /* pop the nil */ - } lua_pushcfunction(L, cfunc); - lua_rawsetfield(L, idx, t->fieldname); + lua_rawsetfield(L, -2, t->fieldname); } } - /* push the getter/setter table name into its table, for error reporting purposes */ - lua_pushstring(L, (is_getter ? "getter" : "setter")); - lua_rawsetfield(L, idx, WSLUA_TYPEOF_FIELD); - - /* copy table into the class's metatable, for introspection */ - lua_pushvalue(L, idx); - lua_rawsetfield(L, midx, (is_getter ? "__getters" : "__setters")); - - if (nup > 1) { - /* we've got more than one upvalue, so move the new getter/setter to the bottom-most of those */ - lua_insert(L,-nup); - } - - /* we should now be back to having gettter/setter table at -1 (or -2 if there was a previous methods table) */ - /* create upvalue of getter/setter table for wslua_attribute_dispatcher function */ - lua_pushcclosure(L, wslua_attribute_dispatcher, nup); /* pushes cfunc with upvalue, removes getter/setter table */ - lua_rawsetfield(L, midx, metafield); /* sets this dispatch function as __index/__newindex field of metatable */ - - /* we should now be back to real metatable being on top */ + /* Drop __getters/__setters table */ + lua_pop(L, 1); 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); - } +/** + * The __index metamethod for classes. Expected upvalues: class name. + */ +static int wslua_classmeta_index(lua_State *L) { + const char *fieldname = luaL_checkstring(L, 2); + const char *classname = luaL_checkstring(L, lua_upvalueindex(1)); - lua_rawgetfield(L, 2, "__methods"); /* method table is now at 3 */ - lua_remove(L,2); /* remove metatable, methods table is at 2 */ + return luaL_error(L, "No such '%s' function/property for object type '%s'", fieldname, classname); +} - 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); +/** + * The __index/__newindex metamethod for class instances. Expected upvalues: + * class name, getters/getters, __index/__newindex class instance metamethod, + * class methods (getters only). See wslua_register_classinstance_meta. + * + * It first tries to find an attribute getter/setter, then an instance method + * (getters only), then the __index/__newindex metamethod of the class instance + * metatable and finally it gives up with an error. + * + * Getters are invoked with the table as parameter. Setters are invoked with the + * table and the value as parameter. + */ +static int wslua_instancemeta_index_impl(lua_State *L, gboolean is_getter) +{ + const char *fieldname = luaL_checkstring(L, 2); + const int attr_idx = lua_upvalueindex(2); + const int fallback_idx = lua_upvalueindex(3); + const int methods_idx = lua_upvalueindex(4); + + /* Check for getter/setter */ + if (lua_istable(L, attr_idx)) { + lua_rawgetfield(L, attr_idx, fieldname); + if (lua_iscfunction(L, -1)) { + lua_CFunction cfunc = lua_tocfunction(L, -1); + lua_pop(L, 1); /* Remove cfunction from stack */ + lua_remove(L, 2); /* Remove key from stack */ + /* + * Note: this re-uses the current closure as optimization, exposing + * its upvalues via pseudo-indices. The alternative is to create a + * new C closure (via lua_call), but this is more expensive. + * Callees should not rely on the availability of the upvalues. + */ + return (*cfunc)(L); + } } - 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); + /* If this is a getter, and the getter has methods, try them. */ + if (is_getter && lua_istable(L, methods_idx)) { + lua_rawgetfield(L, methods_idx, fieldname); + if (!lua_isnil(L, -1)) { + /* Return method from methods table. */ + return 1; + } + lua_pop(L, 1); /* Remove nil from stack. */ } - /* 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); + /* Use function from the class instance metatable (if any). */ + if (lua_iscfunction(L, fallback_idx)) { + lua_CFunction cfunc = lua_tocfunction(L, fallback_idx); + /* Note, unlike getters/setters functions, the key must be preserved! */ + return (*cfunc)(L); } - /* 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"); + const char *classname = luaL_checkstring(L, lua_upvalueindex(1)); + return luaL_error(L, "No such '%s' method/field for object type '%s'", fieldname, classname); +} - /* set the wslua__index to be the __index metamethod */ - lua_pushcfunction(L, wslua__index); - lua_rawsetfield(L, 2, "__index"); +static int wslua_instancemeta_index(lua_State *L) +{ + return wslua_instancemeta_index_impl(L, TRUE); +} - /* we should now be back to real metatable being on top */ - return 0; +static int wslua_instancemeta_newindex(lua_State *L) +{ + return wslua_instancemeta_index_impl(L, FALSE); } /* Pushes a hex string of the binary data argument. */ @@ -581,6 +465,205 @@ int wslua_hex2bin(lua_State* L, const char* data, const guint len, const gchar* return 1; } +/** + * Creates a table of getters/setters and pushes it on the Lua stack. + * + * Additionally, a sanity check is performed to detect colliding getters/setters + * and method names. + */ +static void wslua_push_attributes(lua_State *L, const wslua_attribute_table *t, gboolean is_getter, int methods_idx) +{ + if (!t) { + /* No property accessors? Nothing to do. */ + //lua_pushnil(L); + lua_newtable(L); /* wslua_reg_attributes requires a table for the moment. */ + return; + } + + /* If there is a methods table, prepare for a collission check. */ + if (lua_istable(L, methods_idx)) { + methods_idx = lua_absindex(L, methods_idx); + } else { + methods_idx = 0; + } + + lua_newtable(L); + /* Fill the getter/setter table with given functions. */ + for (; t->fieldname != NULL; t++) { + lua_CFunction cfunc = is_getter ? t->getfunc : t->setfunc; + if (cfunc) { + /* if there's a previous methods table, make sure this attribute name doesn't collide */ + if (methods_idx) { + lua_rawgetfield(L, methods_idx, t->fieldname); + if (!lua_isnil(L, -1)) { + g_error("'%s' attribute name already exists as method name for class\n", t->fieldname); + } + lua_pop(L,1); /* pop the nil */ + } + lua_pushcfunction(L, cfunc); + lua_rawsetfield(L, -2, t->fieldname); + } + } +} + +/** + * Registers the metatable for class instances. See the documentation of + * wslua_register_class for the exact metatable. + */ +void wslua_register_classinstance_meta(lua_State *L, const wslua_class *cls_def) +{ + /* Register metatable for use by class instances. STACK = { MT } */ + /* NOTE: the name can be changed as long as luaL_checkudata is also adapted */ + luaL_newmetatable(L, cls_def->name); + if (cls_def->instance_meta) { + wslua_setfuncs(L, cls_def->instance_meta, 0); + } + + /* Set the __typeof attribute to the class name (for use by "typeof" in Lua code). */ + lua_pushstring(L, cls_def->name); + lua_rawsetfield(L, -2, WSLUA_TYPEOF_FIELD); + + /* Create table to store method names. STACK = { MT, methods } */ + if (cls_def->instance_methods) { + lua_newtable(L); + wslua_setfuncs(L, cls_def->instance_methods, 0); + } else { + lua_pushnil(L); + } + + /* Prepare __index method on metatable. */ + lua_pushstring(L, cls_def->name); /* upval 1: class name */ + wslua_push_attributes(L, cls_def->attrs, TRUE, -2); /* upval 2: getters table */ +#ifdef WSLUA_WITH_INTROSPECTION + lua_pushvalue(L, -1), lua_rawsetfield(L, -5, "__getters"); /* set (transition) property on mt, remove later! */ +#endif + lua_rawgetfield(L, -4, "__index"); /* upval 3: fallback __index method from metatable */ + lua_pushvalue(L, -4); /* upval 4: class methods table */ + lua_pushcclosure(L, wslua_instancemeta_index, 4); + lua_rawsetfield(L, -3, "__index"); + + /* Prepare __newindex method on metatable. */ + lua_pushstring(L, cls_def->name); /* upval 1: class name */ + wslua_push_attributes(L, cls_def->attrs, FALSE, -2); /* upval 2: setters table */ +#ifdef WSLUA_WITH_INTROSPECTION + lua_pushvalue(L, -1), lua_rawsetfield(L, -5, "__setters"); /* set (transition) property on mt, remove later! */ +#endif + lua_rawgetfield(L, -4, "__newindex"); /* upval 3: fallback __newindex method from metatable */ + lua_pushcclosure(L, wslua_instancemeta_newindex, 3); + lua_rawsetfield(L, -3, "__newindex"); + + /* Pop metatable + methods table. STACK = { } */ + lua_pop(L, 2); +} + +/** + * Registers a new class for use in Lua with the specified properties. The + * metatable for the class instance is internally registered with the given + * name. + * + * This functions basically creates a class (type table) with this structure: + * + * Class = { class_methods } + * Class.__typeof = "Class" -- NOTE: might be removed in future + * Class.__metatable = { class_meta } + * Class.__metatable.__typeof = "Class" -- NOTE: might be removed in future + * Class.__metatable.__index = function_that_errors_out + * Class.__metatable.__newindex = function_that_errors_out + * + * It also registers another metatable for class instances (type userdata): + * + * mt = { instance_meta } + * mt.__typeof = "Class" + * -- will be passed upvalues (see wslua_instancemeta_index_impl). + * mt.__index = function_that_finds_right_property_or_method_getter + * mt.__newindex = function_thaon_that_finds_right_property_or_method_setter + * + * For backwards compatibility, introspection is still possible (this detail + * might be removed in the future though, do not rely on this!): + * + * Class.__metatable.__methods = Class + * Class.__metatable.__getters = { __typeof = "getter", getter_attrs } + * Class.__metatable.__setters = { __typeof = "setter", setter_attrs } + */ +void wslua_register_class(lua_State *L, const wslua_class *cls_def) +{ + /* Check for existing global variables/classes with the same name. */ + lua_getglobal(L, cls_def->name); + if (!lua_isnil (L, -1)) { + g_error("Attempt to register class '%s' which already exists in global Lua table\n", cls_def->name); + } + lua_pop(L, 1); + + /* Create new table for class. STACK = { table } */ + lua_newtable(L); + if (cls_def->class_methods) { + wslua_setfuncs(L, cls_def->class_methods, 0); + } + +#ifdef WSLUA_WITH_INTROSPECTION + /* Set __typeof to the class name, used by wslua_typeof. Might be removed in + * the future as the type can already be determined from the metatable. */ + lua_pushstring(L, cls_def->name); + lua_rawsetfield(L, -2, WSLUA_TYPEOF_FIELD); +#endif + + /* Create new metatable for class. STACK = { table, CLASSMT } */ + lua_newtable(L); + if (cls_def->class_meta) { + /* Set metamethods on metatable for class. */ + wslua_setfuncs(L, cls_def->class_meta, 0); + } +#ifdef WSLUA_WITH_INTROSPECTION + /* Set __typeof to the class name. Might be removed in the future, a "class" + * is not of the type "(name of class)", instead it is of type "class". + * Instances of this class should be of type "(name of class)". */ + lua_pushstring(L, cls_def->name); + lua_rawsetfield(L, -2, WSLUA_TYPEOF_FIELD); +#endif + + /* For backwards compatibility, error out when a non-existing property is being accessed. */ + lua_pushstring(L, cls_def->name); + lua_pushcclosure(L, wslua_classmeta_index, 1); + lua_rawsetfield(L, -2, "__index"); + + /* Prevent properties from being set on classes. Previously this was always + * forbidden for classes with attributes (such as Listener), this extends + * the restriction to all classes. */ + lua_pushstring(L, cls_def->name); + lua_pushcclosure(L, wslua_classmeta_index, 1); + lua_rawsetfield(L, -2, "__newindex"); + + /* Set metatable on class. STACK = { table } */ + lua_setmetatable(L, -2); + + wslua_register_classinstance_meta(L, cls_def); + +#ifdef WSLUA_WITH_INTROSPECTION + /* XXX remove these? It looks like an internal implementation detail that is + * no longer needed but is added here to pass the wslua tests (API check) */ + lua_getmetatable(L, -1); /* Stack = { table, CLASSMT } */ + luaL_getmetatable(L, cls_def->name); /* Stack = { table, CLASSMT, MT } */ + + lua_rawgetfield(L, -1, "__getters"); /* __getters from instance MT */ + lua_pushstring(L, "getter"); + lua_rawsetfield(L, -2, WSLUA_TYPEOF_FIELD); + lua_rawsetfield(L, -3, "__getters"); /* Set property on class MT */ + + lua_rawgetfield(L, -1, "__setters"); /* setters from instance MT */ + lua_pushstring(L, "setter"); + lua_rawsetfield(L, -2, WSLUA_TYPEOF_FIELD); + lua_rawsetfield(L, -3, "__setters"); /* Set property on class MT */ + lua_pop(L, 1); /* Stack = { table, CLASSMT } */ + + lua_pushvalue(L, -2); + lua_rawsetfield(L, -2, "__methods"); /* CLASSMT.__methods = Class */ + lua_pop(L, 1); /* Stack = { table } */ +#endif + + /* Set the class methods table as global name. STACK = { } */ + lua_setglobal(L, cls_def->name); +} + /* * Editor modelines - http://www.wireshark.org/tools/modelines.html * diff --git a/epan/wslua/wslua_pinfo.c b/epan/wslua/wslua_pinfo.c index b86d9f5b87..3ce89e75be 100644 --- a/epan/wslua/wslua_pinfo.c +++ b/epan/wslua/wslua_pinfo.c @@ -467,7 +467,6 @@ WSLUA_ATTRIBUTES Pinfo_attributes[] = { WSLUA_ATTRIBUTE_ROREG(Pinfo,private), WSLUA_ATTRIBUTE_ROREG(Pinfo,fragmented), WSLUA_ATTRIBUTE_ROREG(Pinfo,in_error_pkt), - WSLUA_ATTRIBUTE_ROREG(Pinfo,fragmented), WSLUA_ATTRIBUTE_ROREG(Pinfo,match_uint), WSLUA_ATTRIBUTE_ROREG(Pinfo,match_string), WSLUA_ATTRIBUTE_WOREG(Pinfo,conversation), diff --git a/test/lua/globals_2.2.txt b/test/lua/globals_2.2.txt index 77b0f85fec..bde560a316 100644 --- a/test/lua/globals_2.2.txt +++ b/test/lua/globals_2.2.txt @@ -6,7 +6,6 @@ ["ipv4"] = '<function 1>', ['<metatable>'] = { ["__eq"] = '<function 2>', - ["__gc"] = '<function 3>', ["__index"] = '<filtered>', ["__le"] = '<function 4>', ["__lt"] = '<function 5>', @@ -33,7 +32,6 @@ ["__call"] = '<function 16>', ["__concat"] = '<function 19>', ["__eq"] = '<function 20>', - ["__gc"] = '<function 21>', ["__index"] = '<filtered>', ["__methods"] = '<table 3>', ["__tostring"] = '<function 22>', @@ -50,7 +48,6 @@ ["preppend"] = '<function 27>', ["set"] = '<function 28>', ['<metatable>'] = { - ["__gc"] = '<function 29>', ["__index"] = '<filtered>', ["__methods"] = '<table 4>', ["__tostring"] = '<function 30>', @@ -73,7 +70,6 @@ ["remove_all"] = '<function 40>', ['<metatable>'] = { ["__call"] = '<function 41>', - ["__gc"] = '<function 42>', ["__index"] = '<filtered>', ["__methods"] = '<table 5>', ["__typeof"] = "Dir" @@ -86,7 +82,6 @@ ["list"] = '<function 45>', ['<metatable>'] = { ["__call"] = '<function 46>', - ["__gc"] = '<function 47>', ["__index"] = '<filtered>', ["__methods"] = '<table 6>', ["__tostring"] = '<function 48>', @@ -106,7 +101,6 @@ ["set"] = '<function 57>', ["try"] = '<function 58>', ['<metatable>'] = { - ["__gc"] = '<function 59>', ["__index"] = '<filtered>', ["__methods"] = '<table 7>', ["__tostring"] = '<function 60>', @@ -122,7 +116,6 @@ ["new"] = '<function 65>', ["new_for_current"] = '<function 66>', ['<metatable>'] = { - ["__gc"] = '<function 67>', ["__index"] = '<filtered>', ["__methods"] = '<table 8>', ["__typeof"] = "Dumper" @@ -184,7 +177,6 @@ ["new"] = '<function 69>', ['<metatable>'] = { ["__call"] = '<function 70>', - ["__gc"] = '<function 71>', ["__index"] = '<filtered>', ["__methods"] = '<table 9>', ["__tostring"] = '<function 72>', @@ -198,7 +190,6 @@ ["seek"] = '<function 75>', ["write"] = '<function 76>', ['<metatable>'] = { - ["__gc"] = '<function 77>', ["__getters"] = { ["__typeof"] = "getter", ["compressed"] = '<function 78>' @@ -217,7 +208,6 @@ ["__typeof"] = "FileHandler", ["new"] = '<function 81>', ['<metatable>'] = { - ["__gc"] = '<function 82>', ["__getters"] = { ["__typeof"] = "getter", ["extensions"] = '<function 83>', @@ -253,7 +243,6 @@ ["__typeof"] = "FrameInfo", ["read_data"] = '<function 103>', ['<metatable>'] = { - ["__gc"] = '<function 104>', ["__getters"] = { ["__typeof"] = "getter", ["captured_length"] = '<function 105>', @@ -287,7 +276,6 @@ ["__typeof"] = "FrameInfoConst", ["write_data"] = '<function 123>', ['<metatable>'] = { - ["__gc"] = '<function 124>', ["__getters"] = { ["__typeof"] = "getter", ["captured_length"] = '<function 125>', @@ -369,7 +357,6 @@ ["__concat"] = '<function 167>', ["__div"] = '<function 168>', ["__eq"] = '<function 169>', - ["__gc"] = '<function 170>', ["__index"] = '<filtered>', ["__le"] = '<function 171>', ["__lt"] = '<function 172>', @@ -389,7 +376,6 @@ ["new"] = '<function 180>', ["remove"] = '<function 181>', ['<metatable>'] = { - ["__gc"] = '<function 182>', ["__getters"] = { ["__typeof"] = "getter" }, @@ -446,7 +432,6 @@ ["string"] = '<function 192>', ["uint"] = '<function 193>', ['<metatable>'] = { - ["__gc"] = '<function 194>', ["__index"] = '<filtered>', ["__methods"] = '<table 16>', ["__typeof"] = "Pref" @@ -459,7 +444,6 @@ ["stopped"] = '<function 197>', ["update"] = '<function 198>', ['<metatable>'] = { - ["__gc"] = '<function 199>', ["__index"] = '<filtered>', ["__methods"] = '<table 17>', ["__tostring"] = '<function 200>', @@ -471,7 +455,6 @@ ["__typeof"] = "ProtoExpert", ["new"] = '<function 201>', ['<metatable>'] = { - ["__gc"] = '<function 202>', ["__index"] = '<filtered>', ["__methods"] = '<table 18>', ["__tostring"] = '<function 203>', @@ -510,7 +493,6 @@ ["uint64"] = '<function 231>', ["uint8"] = '<function 232>', ['<metatable>'] = { - ["__gc"] = '<function 233>', ["__index"] = '<filtered>', ["__methods"] = '<table 19>', ["__tostring"] = '<function 234>', @@ -524,7 +506,6 @@ ["mtp2"] = '<function 237>', ["none"] = '<function 238>', ['<metatable>'] = { - ["__gc"] = '<function 239>', ["__index"] = '<filtered>', ["__methods"] = '<table 20>', ["__typeof"] = "PseudoHeader" @@ -539,7 +520,6 @@ ["unpack"] = '<function 244>', ["values"] = '<function 245>', ['<metatable>'] = { - ["__gc"] = '<function 246>', ["__index"] = '<filtered>', ["__methods"] = '<table 21>', ["__typeof"] = "Struct" @@ -557,7 +537,6 @@ ["set_atclose"] = '<function 254>', ["set_editable"] = '<function 255>', ['<metatable>'] = { - ["__gc"] = '<function 256>', ["__index"] = '<filtered>', ["__methods"] = '<table 22>', ["__tostring"] = '<function 250>', @@ -579,7 +558,6 @@ ["set_len"] = '<function 267>', ["set_text"] = '<function 268>', ['<metatable>'] = { - ["__gc"] = '<function 269>', ["__index"] = '<filtered>', ["__methods"] = '<table 23>', ["__typeof"] = "TreeItem" @@ -595,7 +573,6 @@ ["reported_length_remaining"] = '<function 275>', ['<metatable>'] = { ["__call"] = '<function 272>', - ["__gc"] = '<function 276>', ["__index"] = '<filtered>', ["__methods"] = '<table 24>', ["__tostring"] = '<function 277>', @@ -637,7 +614,6 @@ ['<metatable>'] = { ["__call"] = '<function 297>', ["__concat"] = '<function 167>', - ["__gc"] = '<function 308>', ["__index"] = '<filtered>', ["__methods"] = '<table 25>', ["__tostring"] = '<function 309>', @@ -672,7 +648,6 @@ ["__concat"] = '<function 167>', ["__div"] = '<function 332>', ["__eq"] = '<function 333>', - ["__gc"] = '<function 334>', ["__index"] = '<filtered>', ["__le"] = '<function 335>', ["__lt"] = '<function 336>', |