summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/README.wslua50
-rw-r--r--epan/wslua/wslua.h81
-rw-r--r--epan/wslua/wslua_internals.c495
-rw-r--r--epan/wslua/wslua_pinfo.c1
-rw-r--r--test/lua/globals_2.2.txt25
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>',