From fed8b06afff540c1af3a83fb5f5115e3336ee9ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Janouch?= Date: Thu, 27 Oct 2016 17:03:53 +0200 Subject: [PATCH] degesch: begin work on direct introspection --- degesch.c | 206 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 176 insertions(+), 30 deletions(-) diff --git a/degesch.c b/degesch.c index bbc9b0f..255bcff 100644 --- a/degesch.c +++ b/degesch.c @@ -1241,6 +1241,49 @@ weak_unref (struct weak_ref_link **list, struct weak_ref_link **link) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Simple introspection framework to simplify exporting stuff to Lua, since +// there is a lot of it. While not fully automated, at least we have control +// over which fields are exported. + +enum ispect_type +{ + ISPECT_BOOL, + ISPECT_INT, + ISPECT_UINT, + ISPECT_SIZE, + ISPECT_STRING, +#if 0 + // TODO: also str_map, str_vector + ISPECT_REF, ///< Weakly referenced + ISPECT_LIST, ///< Typically copied, depending on type +#endif +}; + +// TODO: once this finalizes, turn instatiations into macros +struct ispect +{ + // TODO: + // - "list" flag? + // - weak_ref/weak_unref methods? + struct ispect_field *fields; ///< Fields +}; + +struct ispect_field +{ + const char *name; ///< Name of the field + size_t offset; ///< Offset in the structure + enum ispect_type type; ///< Type of the field + struct ispect *subtype; ///< Subtype information +}; + +#define ISPECT(object, field, type) \ + { #field, offsetof (struct object, field), ISPECT_ ## type, NULL }, +#define ISPECT_(object, field, type, subtype) \ + { #field, offsetof (struct object, field), ISPECT_ ## type, \ + &g_ ## subtype ## _type }, + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + struct user_channel { LIST_HEADER (struct user_channel) @@ -1279,6 +1322,18 @@ struct user struct user_channel *channels; ///< Channels the user is on }; +static struct ispect_field g_user_ispect_fields[] = +{ + ISPECT( user, nickname, STRING ) + ISPECT( user, away, BOOL ) + {} +}; + +static struct ispect g_user_ispect = +{ + .fields = g_user_ispect_fields, +}; + static struct user * user_new (void) { @@ -1347,6 +1402,20 @@ struct channel bool left_manually; ///< Don't rejoin on reconnect }; +static struct ispect_field g_channel_ispect_fields[] = +{ + ISPECT( channel, name, STRING ) + ISPECT( channel, topic, STRING ) + ISPECT( channel, users_len, SIZE ) + ISPECT( channel, left_manually, BOOL ) + {} +}; + +static struct ispect g_channel_ispect = +{ + .fields = g_channel_ispect_fields, +}; + static struct channel * channel_new (void) { @@ -1519,6 +1588,21 @@ struct buffer struct user *user; ///< Reference to user }; +static struct ispect_field g_buffer_ispect_fields[] = +{ + ISPECT( buffer, name, STRING ) + ISPECT( buffer, new_messages_count, UINT ) + ISPECT( buffer, new_unimportant_count, UINT ) + ISPECT( buffer, highlighted, BOOL ) + ISPECT( buffer, hide_unimportant, BOOL ) + {} +}; + +static struct ispect g_buffer_ispect = +{ + .fields = g_buffer_ispect_fields, +}; + static struct buffer * buffer_new (struct input *input) { @@ -1667,6 +1751,23 @@ struct server unsigned irc_max_modes; ///< Max parametrized modes per command }; +static struct ispect_field g_server_ispect_fields[] = +{ + ISPECT( server, name, STRING ) + ISPECT( server, state, INT ) + ISPECT( server, reconnect_attempt, UINT ) + ISPECT( server, manual_disconnect, BOOL ) + ISPECT( server, irc_user_host, STRING ) + ISPECT( server, autoaway_active, BOOL ) + ISPECT( server, cap_echo_message, BOOL ) + {} +}; + +static struct ispect g_server_ispect = +{ + .fields = g_server_ispect_fields, +}; + static void on_irc_timeout (void *user_data); static void on_irc_ping_timeout (void *user_data); static void on_irc_autojoin_timeout (void *user_data); @@ -8418,6 +8519,10 @@ typedef void (*lua_weak_unref_fn) (void *object, struct weak_ref_link **link); struct lua_weak_info { const char *name; ///< Metatable name + struct ispect *ispect; ///< Introspection data + + // XXX: not sure if these should be _here_ and not in "struct ispect" + lua_weak_ref_fn ref; ///< Weak link invalidator lua_weak_unref_fn unref; ///< Weak link generator }; @@ -8425,6 +8530,7 @@ struct lua_weak_info struct lua_weak { struct lua_plugin *plugin; ///< The plugin we belong to + struct lua_weak_info *info; ///< Introspection data void *object; ///< The object struct weak_ref_link *weak_ref; ///< A weak reference link }; @@ -8455,6 +8561,7 @@ lua_weak_push (struct lua_plugin *plugin, void *object, struct lua_weak *wrapper = lua_newuserdata (L, sizeof *wrapper); luaL_setmetatable (L, info->name); wrapper->plugin = plugin; + wrapper->info = info; wrapper->object = object; wrapper->weak_ref = info->ref (object, lua_weak_invalidate, wrapper); lua_cache_store (L, object, -1); @@ -8486,9 +8593,10 @@ lua_weak_deref (lua_State *L, const struct lua_weak_info *info) #define LUA_WEAK_DECLARE(id, metatable_id) \ static struct lua_weak_info lua_ ## id ## _info = \ { \ - .name = metatable_id, \ - .ref = (lua_weak_ref_fn) id ## _weak_ref, \ - .unref = (lua_weak_unref_fn) id ## _weak_unref, \ + .name = metatable_id, \ + .ispect = &g_ ## id ## _ispect, \ + .ref = (lua_weak_ref_fn) id ## _weak_ref, \ + .unref = (lua_weak_unref_fn) id ## _weak_unref, \ }; #define XLUA_USER_METATABLE "user" ///< Identifier for Lua metatable @@ -8509,15 +8617,6 @@ lua_user_gc (lua_State *L) return lua_weak_gc (L, &lua_user_info); } -static int -lua_user_get_nickname (lua_State *L) -{ - struct lua_weak *wrapper = lua_weak_deref (L, &lua_user_info); - struct user *user = wrapper->object; - lua_pushstring (L, user->nickname); - return 1; -} - static int lua_user_get_channels (lua_State *L) { @@ -8537,7 +8636,6 @@ lua_user_get_channels (lua_State *L) static luaL_Reg lua_user_table[] = { { "__gc", lua_user_gc }, - { "get_nickname", lua_user_get_nickname }, { "get_channels", lua_user_get_channels }, { NULL, NULL } }; @@ -8550,15 +8648,6 @@ lua_channel_gc (lua_State *L) return lua_weak_gc (L, &lua_channel_info); } -static int -lua_channel_get_name (lua_State *L) -{ - struct lua_weak *wrapper = lua_weak_deref (L, &lua_channel_info); - struct channel *channel = wrapper->object; - lua_pushstring (L, channel->name); - return 1; -} - static int lua_channel_get_users (lua_State *L) { @@ -8582,7 +8671,6 @@ lua_channel_get_users (lua_State *L) static luaL_Reg lua_channel_table[] = { { "__gc", lua_channel_gc }, - { "get_name", lua_channel_get_name }, { "get_users", lua_channel_get_users }, { NULL, NULL } }; @@ -8645,7 +8733,6 @@ lua_buffer_execute (lua_State *L) static luaL_Reg lua_buffer_table[] = { - // TODO: some useful methods or values { "__gc", lua_buffer_gc }, { "get_user", lua_buffer_get_user }, { "get_channel", lua_buffer_get_channel }, @@ -8692,7 +8779,6 @@ lua_server_send (lua_State *L) static luaL_Reg lua_server_table[] = { - // TODO: some useful methods or values { "__gc", lua_server_gc }, { "get_user", lua_server_get_user }, { "get_buffer", lua_server_get_buffer }, @@ -9701,6 +9787,16 @@ static luaL_Reg lua_plugin_library[] = // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +static struct lua_ispect_mapping +{ + struct ispect *ispect; ///< Type info + struct lua_weak_info *info; ///< Lua-specific additions +} +lua_types[] = +{ + { &g_buffer_ispect, &lua_buffer_info }, +}; + static void * lua_plugin_alloc (void *ud, void *ptr, size_t o_size, size_t n_size) { @@ -9724,6 +9820,35 @@ lua_plugin_panic (lua_State *L) return 0; } +static bool +lua_plugin_property_get_ispect (lua_State *L, const char *property_name) +{ + struct lua_weak_info *info = lua_touserdata (L, lua_upvalueindex (1)); + if (!info || !info->ispect) + return false; + + struct lua_weak *weak = luaL_checkudata (L, 1, info->name); + // TODO: I think we can do better than this, maybe binary search at least? + struct ispect_field *iter; + for (iter = info->ispect->fields; iter->name; iter++) + if (!strcmp (property_name, iter->name)) + break; + + if (iter->name) + { + void *p = (uint8_t *) weak->object + iter->offset; + switch (iter->type) + { + case ISPECT_BOOL: lua_pushboolean (L, *(bool *) p); return true; + case ISPECT_INT: lua_pushinteger (L, *(int *) p); return true; + case ISPECT_UINT: lua_pushinteger (L, *(unsigned *) p); return true; + case ISPECT_SIZE: lua_pushinteger (L, *(size_t *) p); return true; + case ISPECT_STRING: lua_pushstring (L, *(char **) p); return true; + } + } + return false; +} + static int lua_plugin_property_get (lua_State *L) { @@ -9746,6 +9871,10 @@ lua_plugin_property_get (lua_State *L) return 1; } + // Maybe we can find it via introspection + if (lua_plugin_property_get_ispect (L, property_name)) + return 1; + // Or we look for a property set by the user (__gc cannot be overriden) if (lua_getuservalue (L, 1) != LUA_TTABLE) lua_pushnil (L); @@ -9787,7 +9916,24 @@ lua_plugin_create_meta (lua_State *L, const char *name, luaL_Reg *fns) luaL_setfuncs (L, fns, 0); // Emulate properties for convenience - lua_pushcfunction (L, lua_plugin_property_get); + lua_pushlightuserdata (L, NULL); + lua_pushcclosure (L, lua_plugin_property_get, 1); + lua_setfield (L, -2, "__index"); + lua_pushcfunction (L, lua_plugin_property_set); + lua_setfield (L, -2, "__newindex"); + + lua_pop (L, 1); +} + +static void +lua_plugin_create_weak (lua_State *L, struct lua_weak_info *info, luaL_Reg *fns) +{ + luaL_newmetatable (L, info->name); + luaL_setfuncs (L, fns, 0); + + // Emulate properties for convenience + lua_pushlightuserdata (L, info); + lua_pushcclosure (L, lua_plugin_property_get, 1); lua_setfield (L, -2, "__index"); lua_pushcfunction (L, lua_plugin_property_set); lua_setfield (L, -2, "__newindex"); @@ -9824,10 +9970,10 @@ lua_plugin_load (struct app_context *ctx, const char *filename, // Create metatables for our objects lua_plugin_create_meta (L, XLUA_HOOK_METATABLE, lua_hook_table); - lua_plugin_create_meta (L, XLUA_USER_METATABLE, lua_user_table); - lua_plugin_create_meta (L, XLUA_CHANNEL_METATABLE, lua_channel_table); - lua_plugin_create_meta (L, XLUA_BUFFER_METATABLE, lua_buffer_table); - lua_plugin_create_meta (L, XLUA_SERVER_METATABLE, lua_server_table); + lua_plugin_create_weak (L, &lua_user_info, lua_user_table); + lua_plugin_create_weak (L, &lua_channel_info, lua_channel_table); + lua_plugin_create_weak (L, &lua_buffer_info, lua_buffer_table); + lua_plugin_create_weak (L, &lua_server_info, lua_server_table); lua_plugin_create_meta (L, XLUA_SCHEMA_METATABLE, lua_schema_table); lua_plugin_create_meta (L, XLUA_CONNECTION_METATABLE, lua_connection_table); lua_plugin_create_meta (L, XLUA_CONNECTOR_METATABLE, lua_connector_table);