degesch: add introspection for refs within str_maps
This required some fixes to the design.
This commit is contained in:
parent
2f758bbdb9
commit
278e2b236b
228
degesch.c
228
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_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_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,7 +1737,7 @@ 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 )
|
||||
|
@ -1760,22 +1746,17 @@ static struct ispect_field g_server_ispect_fields[] =
|
|||
ISPECT( server, irc_user_host, STRING )
|
||||
ISPECT( server, autoaway_active, BOOL )
|
||||
ISPECT( server, cap_echo_message, BOOL )
|
||||
ISPECT_( server, buffer, REF, false, buffer )
|
||||
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,54 +9889,28 @@ 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);
|
||||
// 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);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
static int
|
||||
lua_plugin_property_get (lua_State *L)
|
||||
|
|
Loading…
Reference in New Issue