From 278e2b236b8cf3363ab4c1e098ceedb81e539c48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Janouch?= Date: Fri, 28 Oct 2016 00:07:49 +0200 Subject: [PATCH] degesch: add introspection for refs within str_maps This required some fixes to the design. --- degesch.c | 262 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 137 insertions(+), 125 deletions(-) diff --git a/degesch.c b/degesch.c index b230b98..d407eb8 100644 --- a/degesch.c +++ b/degesch.c @@ -1251,15 +1251,7 @@ enum ispect_type ISPECT_STR, ///< "struct str" ISPECT_STR_MAP, ///< "struct str_map" - ISPECT_REF, ///< Weakly referenced object - - // XXX: maybe a PTR type that doesn't warrant weak_refs but is copied -}; - -// TODO: once this finalizes, turn instatiations into macros -struct ispect -{ - struct ispect_field *fields; ///< Fields + ISPECT_REF ///< Weakly referenced object }; struct ispect_field @@ -1267,15 +1259,23 @@ 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 - bool is_list; ///< This object is a list - struct ispect *subtype; ///< Subtype information + + enum ispect_type subtype; ///< STR_MAP subtype + struct ispect_field *fields; ///< REF target fields + bool is_list; ///< REF target is a list }; #define ISPECT(object, field, type) \ - { #field, offsetof (struct object, field), ISPECT_ ## type, false, NULL }, -#define ISPECT_(object, field, type, is_list, subtype) \ - { #field, offsetof (struct object, field), ISPECT_ ## type, is_list, \ - &g_ ## subtype ## _ispect }, + { #field, offsetof (struct object, field), ISPECT_##type, 0, NULL, false }, +#define ISPECT_REF(object, field, is_list, ref_type) \ + { #field, offsetof (struct object, field), ISPECT_REF, 0, \ + g_##ref_type##_ispect, is_list }, +#define ISPECT_MAP(object, field, subtype) \ + { #field, offsetof (struct object, field), ISPECT_STR_MAP, \ + ISPECT_##subtype, NULL, false }, +#define ISPECT_MAP_REF(object, field, is_list, ref_type) \ + { #field, offsetof (struct object, field), ISPECT_STR_MAP, \ + ISPECT_REF, g_##ref_type##_ispect, is_list }, // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1317,18 +1317,13 @@ struct user struct user_channel *channels; ///< Channels the user is on }; -static struct ispect_field g_user_ispect_fields[] = +static struct ispect_field g_user_ispect[] = { 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) { @@ -1397,21 +1392,17 @@ struct channel bool left_manually; ///< Don't rejoin on reconnect }; -static struct ispect_field g_channel_ispect_fields[] = +static struct ispect_field g_channel_ispect[] = { - ISPECT( channel, name, STRING ) - ISPECT( channel, topic, STRING ) - ISPECT( channel, no_param_modes, STR ) - ISPECT( channel, users_len, SIZE ) - ISPECT( channel, left_manually, BOOL ) + ISPECT( channel, name, STRING ) + ISPECT( channel, topic, STRING ) + ISPECT( channel, no_param_modes, STR ) + ISPECT_MAP( channel, param_modes, 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) { @@ -1584,25 +1575,20 @@ struct buffer struct user *user; ///< Reference to user }; -static struct ispect g_server_ispect; -static struct ispect_field g_buffer_ispect_fields[] = +static struct ispect_field g_server_ispect[]; +static struct ispect_field g_buffer_ispect[] = { - 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 ) - ISPECT_( buffer, server, REF, false, server ) - ISPECT_( buffer, channel, REF, false, channel ) - ISPECT_( buffer, user, REF, false, user ) + 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 ) + ISPECT_REF( buffer, server, false, server ) + ISPECT_REF( buffer, channel, false, channel ) + ISPECT_REF( buffer, user, false, user ) {} }; -static struct ispect g_buffer_ispect = -{ - .fields = g_buffer_ispect_fields, -}; - static struct buffer * buffer_new (struct input *input) { @@ -1751,31 +1737,26 @@ struct server unsigned irc_max_modes; ///< Max parametrized modes per command }; -static struct ispect_field g_server_ispect_fields[] = +static struct ispect_field g_server_ispect[] = { - 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 ) - ISPECT_( server, buffer, REF, false, buffer ) + 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 ) + ISPECT_REF( server, buffer, false, buffer ) // TODO: either rename the underlying field or fix the plugins { "user", offsetof (struct server, irc_user), - ISPECT_REF, false, &g_user_ispect }, + ISPECT_REF, 0, g_user_ispect, false }, { "user_mode", offsetof (struct server, irc_user_mode), - ISPECT_STR, false, NULL }, + ISPECT_STR, 0, NULL, false }, {} }; -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); @@ -2109,19 +2090,15 @@ struct app_context } *g_ctx; -static struct ispect_field g_ctx_ispect_fields[] = +static struct ispect_field g_ctx_ispect[] = { - ISPECT_( app_context, buffers, REF, true, buffer ) - ISPECT_( app_context, global_buffer, REF, false, buffer ) - ISPECT_( app_context, current_buffer, REF, false, buffer ) + ISPECT_MAP_REF( app_context, servers, false, server ) + ISPECT_REF( app_context, buffers, true, buffer ) + ISPECT_REF( app_context, global_buffer, false, buffer ) + ISPECT_REF( app_context, current_buffer, false, buffer ) {} }; -static struct ispect g_ctx_ispect = -{ - .fields = g_ctx_ispect_fields, -}; - static int * filter_color_cube_for_acceptable_nick_colors (size_t *len) { @@ -8540,7 +8517,7 @@ 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 + struct ispect_field *ispect; ///< Introspection data lua_weak_ref_fn ref; ///< Weak link invalidator lua_weak_unref_fn unref; ///< Weak link generator @@ -8616,7 +8593,7 @@ lua_weak_deref (lua_State *L, const struct lua_weak_info *info) static struct lua_weak_info lua_ ## id ## _info = \ { \ .name = metatable_id, \ - .ispect = &g_ ## id ## _ispect, \ + .ispect = g_ ## id ## _ispect, \ .ref = (lua_weak_ref_fn) id ## _weak_ref, \ .unref = (lua_weak_unref_fn) id ## _weak_unref, \ }; @@ -8636,7 +8613,7 @@ LUA_WEAK_DECLARE (server, XLUA_SERVER_METATABLE) static struct lua_weak_info lua_ctx_info = { .name = PROGRAM_NAME, - .ispect = &g_ctx_ispect, + .ispect = g_ctx_ispect, }; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -9783,15 +9760,15 @@ static luaL_Reg lua_plugin_library[] = static struct lua_ispect_mapping { - struct ispect *ispect; ///< Type info + struct ispect_field *ispect; ///< Type info struct lua_weak_info *info; ///< Lua-specific additions } lua_types[] = { - { &g_user_ispect, &lua_user_info }, - { &g_channel_ispect, &lua_channel_info }, - { &g_buffer_ispect, &lua_buffer_info }, - { &g_server_ispect, &lua_server_info }, + { g_user_ispect, &lua_user_info }, + { g_channel_ispect, &lua_channel_info }, + { g_buffer_ispect, &lua_buffer_info }, + { g_server_ispect, &lua_server_info }, }; static void * @@ -9817,6 +9794,7 @@ lua_plugin_panic (lua_State *L) return 0; } +// As a rule in this codebase, these fields are right at the top of structs struct list_header { void *next; ///< Next item @@ -9824,10 +9802,21 @@ struct list_header }; static void -lua_plugin_push_ref (struct lua_plugin *self, void *object, bool is_list, - struct lua_weak_info *info) +lua_plugin_push_ref (struct lua_plugin *self, void *object, + struct ispect_field *field) { - if (!is_list) + // TODO: we can definitely make a resolution table right in Lua, + // lua_plugin_reg_weak() can fill it automatically (lightud->lightud) + struct lua_weak_info *info = NULL; + for (size_t i = 0; i < N_ELEMENTS (lua_types); i++) + if (lua_types[i].ispect == field->fields) + { + info = lua_types[i].info; + break; + } + hard_assert (info != NULL); + + if (!field->is_list) { lua_weak_push (self, object, info); return; @@ -9842,6 +9831,55 @@ lua_plugin_push_ref (struct lua_plugin *self, void *object, bool is_list, } } +static void lua_plugin_push_map_field (struct lua_plugin *self, + const char *key, void *p, struct ispect_field *field); + +static void +lua_plugin_push_struct (struct lua_plugin *self, enum ispect_type type, + void *value, struct ispect_field *field) +{ + if (type == ISPECT_STR) + { + struct str *s = value; + lua_pushlstring (self->L, s->str, s->len); + return; + } + if (type == ISPECT_STR_MAP) + { + struct str_map_iter iter; + str_map_iter_init (&iter, value); + + void *value; + lua_newtable (self->L); + while ((value = str_map_iter_next (&iter))) + lua_plugin_push_map_field (self, iter.link->key, value, field); + return; + } + hard_assert (!"unhandled introspection object type"); +} + +static void +lua_plugin_push_map_field (struct lua_plugin *self, + const char *key, void *p, struct ispect_field *field) +{ + // That would mean maps in maps ad infinitum + hard_assert (field->subtype != ISPECT_STR_MAP); + + intptr_t n = (intptr_t) p; + switch (field->subtype) + { + // Here the types are generally casted to a void pointer + case ISPECT_BOOL: lua_pushboolean (self->L, (bool ) n); break; + case ISPECT_INT: lua_pushinteger (self->L, (int ) n); break; + case ISPECT_UINT: lua_pushinteger (self->L, (unsigned ) n); break; + case ISPECT_SIZE: lua_pushinteger (self->L, (size_t ) n); break; + case ISPECT_STRING: lua_pushstring (self->L, p); break; + case ISPECT_REF: lua_plugin_push_ref (self, p, field); break; + default: lua_plugin_push_struct (self, field->subtype, p, field); + } + lua_setfield (self->L, -2, key); +} + static bool lua_plugin_property_get_ispect (lua_State *L, const char *property_name) { @@ -9851,53 +9889,27 @@ lua_plugin_property_get_ispect (lua_State *L, const char *property_name) 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)) + struct ispect_field *field; + for (field = info->ispect; field->name; field++) + if (!strcmp (property_name, field->name)) break; - if (!iter->name) + if (!field->name) return false; - void *p = (uint8_t *) weak->object + iter->offset; - switch (iter->type) + struct lua_plugin *self = weak->plugin; + void *p = (uint8_t *) weak->object + field->offset; + switch (field->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; - case ISPECT_STR: - { - struct str *s = p; - lua_pushlstring (L, s->str, s->len); - return true; + // Here the types are really what's under the pointer + case ISPECT_BOOL: lua_pushboolean (L, *(bool *) p); break; + case ISPECT_INT: lua_pushinteger (L, *(int *) p); break; + case ISPECT_UINT: lua_pushinteger (L, *(unsigned *) p); break; + case ISPECT_SIZE: lua_pushinteger (L, *(size_t *) p); break; + case ISPECT_STRING: lua_pushstring (L, *(char **) p); break; + case ISPECT_REF: lua_plugin_push_ref (self, *(void **) p, field); break; + default: lua_plugin_push_struct (self, field->type, p, field); } - case ISPECT_STR_MAP: - { - // FIXME: we should be able to support other things than strings - struct str_map_iter iter; - str_map_iter_init (&iter, p); - - char *s; - lua_newtable (L); - while ((s = str_map_iter_next (&iter))) - lua_plugin_kv (L, iter.link->key, s); - return true; - } - case ISPECT_REF: - { - // TODO: we can definitely make a resolution table right in Lua, - // lua_plugin_reg_weak() can fill it automatically (lightud->lightud) - for (size_t i = 0; i < N_ELEMENTS (lua_types); i++) - if (lua_types[i].ispect == iter->subtype) - { - lua_plugin_push_ref (weak->plugin, *(void **) p, - iter->is_list, lua_types[i].info); - return true; - } - } - } - return soft_assert (false); + return true; } static int