wslua: prepare for split class/instance (meta)methods

Previously the metatables for classes were the same for the class and
its instances. This results in issues like calling __gc on the class
table on exit.

Make it possible to declare separate class methods (functions) and
instance methods. Observe that all attributes apply to the instances
only, so make these just available on the instance.

The attribute/methods lookup method (via __index/__newindex) have been
rewritten to use upvalues, removing the technical need for the
properties __getters/__setters/__methods. The "lua globals" test still
checks for these, but it could be removed in the future.

To fix bug 12968, the __gc method is removed from the class method.
Future patches should remove the WSLUA_REGISTER_CLASS,
WSLUA_REGISTER_META and WSLUA_REGISTER_ATTRIBUTES macros completely and
create split class functions/methods (such that __call for an instance
cannot accidentally be invoked on the class).

Removed duplicate "fragmented" property from Pinfo (which triggered an
error) and replaced exit() by g_error() for debugger friendliness.
Remove lua_shiftstring since checkstring always returns non-NULL.

Bug: 12968
Change-Id: I57f8a93d08bb84c79b0e94cf2c82d8402fc16646
Reviewed-on: https://code.wireshark.org/review/18026
Petri-Dish: Peter Wu <peter@lekensteyn.nl>
Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org>
Reviewed-by: Balint Reczey <balint@balintreczey.hu>
This commit is contained in:
Peter Wu 2016-10-02 18:02:22 +02:00
parent 41c6b944b4
commit 537705a8b2
5 changed files with 366 additions and 294 deletions

View File

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

View File

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

View File

@ -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);
g_error("%s expected in metatable, found none!\n", metafield);
}
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);
lua_pop(L, 1);
/* 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);
}
/* make our new getter/setter table - we don't need to pop it later */
lua_newtable(L);
idx = lua_gettop(L);
/* 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);
}
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.
/**
* The __index metamethod for classes. Expected upvalues: class name.
*/
int wslua_set__index(lua_State *L) {
static int wslua_classmeta_index(lua_State *L) {
const char *fieldname = luaL_checkstring(L, 2);
const char *classname = luaL_checkstring(L, lua_upvalueindex(1));
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);
return luaL_error(L, "No such '%s' function/property 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);
}
}
/* 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");
/* 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. */
}
/* set the wslua__index to be the __index metamethod */
lua_pushcfunction(L, wslua__index);
lua_rawsetfield(L, 2, "__index");
/* 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);
}
/* we should now be back to real metatable being on top */
return 0;
const char *classname = luaL_checkstring(L, lua_upvalueindex(1));
return luaL_error(L, "No such '%s' method/field for object type '%s'", fieldname, classname);
}
static int wslua_instancemeta_index(lua_State *L)
{
return wslua_instancemeta_index_impl(L, TRUE);
}
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
*

View File

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

View File

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