diff --git a/degesch.c b/degesch.c index f1eceb8..61355ef 100644 --- a/degesch.c +++ b/degesch.c @@ -8322,113 +8322,126 @@ lua_plugin_parse (lua_State *L) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#define XLUA_USER_METATABLE "user" ///< Identifier for the Lua metatable +// Lua code can use weakly referenced wrappers for internal objects. -struct lua_user +typedef struct weak_ref_link * + (*lua_weak_ref_fn) (void *object, destroy_cb_fn cb, void *user_data); +typedef void (*lua_weak_unref_fn) (void *object, struct weak_ref_link **link); + +struct lua_weak_info +{ + const char *name; ///< Metatable name + lua_weak_ref_fn ref; ///< Weak link invalidator + lua_weak_unref_fn unref; ///< Weak link generator +}; + +struct lua_weak { struct lua_plugin *plugin; ///< The plugin we belong to - struct user *user; ///< The user + void *object; ///< The object struct weak_ref_link *weak_ref; ///< A weak reference link }; static void -lua_user_invalidate (void *object, void *user_data) +lua_weak_invalidate (void *object, void *user_data) { - struct lua_user *wrapper = user_data; - wrapper->user = NULL; + struct lua_weak *wrapper = user_data; + wrapper->object = NULL; wrapper->weak_ref = NULL; // This can in theory call the GC, order isn't arbitrary here lua_cache_invalidate (wrapper->plugin->L, object); } static void -lua_plugin_push_user (struct lua_plugin *plugin, struct user *user) +lua_weak_push (struct lua_plugin *plugin, void *object, + struct lua_weak_info *info) { lua_State *L = plugin->L; - if (lua_cache_get (L, user)) + if (!object) + { + lua_pushnil (L); + return; + } + if (lua_cache_get (L, object)) return; - struct lua_user *wrapper = lua_newuserdata (L, sizeof *wrapper); - luaL_setmetatable (L, XLUA_USER_METATABLE); + struct lua_weak *wrapper = lua_newuserdata (L, sizeof *wrapper); + luaL_setmetatable (L, info->name); wrapper->plugin = plugin; - wrapper->user = user; - wrapper->weak_ref = user_weak_ref (user, lua_user_invalidate, wrapper); - lua_cache_store (L, user, -1); + wrapper->object = object; + wrapper->weak_ref = info->ref (object, lua_weak_invalidate, wrapper); + lua_cache_store (L, object, -1); +} + +static int +lua_weak_gc (lua_State *L, const struct lua_weak_info *info) +{ + struct lua_weak *wrapper = luaL_checkudata (L, 1, info->name); + if (wrapper->object) + { + lua_cache_invalidate (L, wrapper->object); + info->unref (wrapper->object, &wrapper->weak_ref); + wrapper->object = NULL; + } + return 0; +} + +static struct lua_weak * +lua_weak_deref (lua_State *L, const struct lua_weak_info *info) +{ + struct lua_weak *weak = luaL_checkudata (L, 1, info->name); + luaL_argcheck (L, weak->object, 1, "dead reference used"); + return weak; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -/// Identifier for the Lua metatable -#define XLUA_CHANNEL_METATABLE "channel" +#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, \ + }; -struct lua_channel -{ - struct lua_plugin *plugin; ///< The plugin we belong to - struct channel *channel; ///< The channel - struct weak_ref_link *weak_ref; ///< A weak reference link -}; +#define XLUA_USER_METATABLE "user" ///< Identifier for Lua metatable +#define XLUA_CHANNEL_METATABLE "channel" ///< Identifier for Lua metatable +#define XLUA_BUFFER_METATABLE "buffer" ///< Identifier for Lua metatable +#define XLUA_SERVER_METATABLE "server" ///< Identifier for Lua metatable -static void -lua_channel_invalidate (void *object, void *user_data) -{ - struct lua_channel *wrapper = user_data; - wrapper->channel = NULL; - wrapper->weak_ref = NULL; - // This can in theory call the GC, order isn't arbitrary here - lua_cache_invalidate (wrapper->plugin->L, object); -} - -static void -lua_plugin_push_channel (struct lua_plugin *plugin, struct channel *channel) -{ - lua_State *L = plugin->L; - if (lua_cache_get (L, channel)) - return; - - struct lua_channel *wrapper = lua_newuserdata (L, sizeof *wrapper); - luaL_setmetatable (L, XLUA_CHANNEL_METATABLE); - wrapper->plugin = plugin; - wrapper->channel = channel; - wrapper->weak_ref = channel_weak_ref - (channel, lua_channel_invalidate, wrapper); - lua_cache_store (L, channel, -1); -} +LUA_WEAK_DECLARE (user, XLUA_USER_METATABLE) +LUA_WEAK_DECLARE (channel, XLUA_CHANNEL_METATABLE) +LUA_WEAK_DECLARE (buffer, XLUA_BUFFER_METATABLE) +LUA_WEAK_DECLARE (server, XLUA_SERVER_METATABLE) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - static int lua_user_gc (lua_State *L) { - struct lua_user *wrapper = luaL_checkudata (L, 1, XLUA_USER_METATABLE); - if (wrapper->user) - { - lua_cache_invalidate (L, wrapper->user); - user_weak_unref (wrapper->user, &wrapper->weak_ref); - wrapper->user = NULL; - } - return 0; + return lua_weak_gc (L, &lua_user_info); } static int lua_user_get_nickname (lua_State *L) { - struct lua_user *wrapper = luaL_checkudata (L, 1, XLUA_USER_METATABLE); - luaL_argcheck (L, wrapper->user, 1, "dead reference used"); - lua_pushstring (L, wrapper->user->nickname); + 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) { - struct lua_user *wrapper = luaL_checkudata (L, 1, XLUA_USER_METATABLE); - luaL_argcheck (L, wrapper->user, 1, "dead reference used"); + struct lua_weak *wrapper = lua_weak_deref (L, &lua_user_info); + struct user *user = wrapper->object; int i = 1; lua_newtable (L); - LIST_FOR_EACH (struct user_channel, iter, wrapper->user->channels) + LIST_FOR_EACH (struct user_channel, iter, user->channels) { - lua_plugin_push_channel (wrapper->plugin, iter->channel); + lua_weak_push (wrapper->plugin, iter->channel, &lua_channel_info); lua_rawseti (L, -2, i++); } return 1; @@ -8447,40 +8460,30 @@ static luaL_Reg lua_user_table[] = static int lua_channel_gc (lua_State *L) { - struct lua_channel *wrapper = - luaL_checkudata (L, 1, XLUA_CHANNEL_METATABLE); - if (wrapper->channel) - { - lua_cache_invalidate (L, wrapper->channel); - channel_weak_unref (wrapper->channel, &wrapper->weak_ref); - wrapper->channel = NULL; - } - return 0; + return lua_weak_gc (L, &lua_channel_info); } static int lua_channel_get_name (lua_State *L) { - struct lua_channel *wrapper = - luaL_checkudata (L, 1, XLUA_CHANNEL_METATABLE); - luaL_argcheck (L, wrapper->channel, 1, "dead reference used"); - lua_pushstring (L, wrapper->channel->name); + 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) { - struct lua_channel *wrapper = - luaL_checkudata (L, 1, XLUA_CHANNEL_METATABLE); - luaL_argcheck (L, wrapper->channel, 1, "dead reference used"); + struct lua_weak *wrapper = lua_weak_deref (L, &lua_channel_info); + struct channel *channel = wrapper->object; int i = 1; lua_newtable (L); - LIST_FOR_EACH (struct channel_user, iter, wrapper->channel->users) + LIST_FOR_EACH (struct channel_user, iter, channel->users) { lua_createtable (L, 0, 2); - lua_plugin_push_user (wrapper->plugin, iter->user); + lua_weak_push (wrapper->plugin, iter->user, &lua_user_info); lua_setfield (L, -2, "user"); lua_plugin_kv (L, "prefixes", iter->prefixes.str); @@ -8499,140 +8502,45 @@ static luaL_Reg lua_channel_table[] = // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#define XLUA_BUFFER_METATABLE "buffer" ///< Identifier for the Lua metatable - -struct lua_buffer -{ - struct lua_plugin *plugin; ///< The plugin we belong to - struct buffer *buffer; ///< The buffer - struct weak_ref_link *weak_ref; ///< A weak reference link -}; - -static void -lua_buffer_invalidate (void *object, void *user_data) -{ - struct lua_buffer *wrapper = user_data; - wrapper->buffer = NULL; - wrapper->weak_ref = NULL; - // This can in theory call the GC, order isn't arbitrary here - lua_cache_invalidate (wrapper->plugin->L, object); -} - -static void -lua_plugin_push_buffer (struct lua_plugin *plugin, struct buffer *buffer) -{ - lua_State *L = plugin->L; - if (lua_cache_get (L, buffer)) - return; - - struct lua_buffer *wrapper = lua_newuserdata (L, sizeof *wrapper); - luaL_setmetatable (L, XLUA_BUFFER_METATABLE); - wrapper->plugin = plugin; - wrapper->buffer = buffer; - wrapper->weak_ref = buffer_weak_ref - (buffer, lua_buffer_invalidate, wrapper); - lua_cache_store (L, buffer, -1); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#define XLUA_SERVER_METATABLE "server" ///< Identifier for the Lua metatable - -struct lua_server -{ - struct lua_plugin *plugin; ///< The plugin we belong to - struct server *server; ///< The server - struct weak_ref_link *weak_ref; ///< A weak reference link -}; - -static void -lua_server_invalidate (void *object, void *user_data) -{ - struct lua_server *wrapper = user_data; - wrapper->server = NULL; - wrapper->weak_ref = NULL; - // This can in theory call the GC, order isn't arbitrary here - lua_cache_invalidate (wrapper->plugin->L, object); -} - -static void -lua_plugin_push_server (struct lua_plugin *plugin, struct server *server) -{ - lua_State *L = plugin->L; - if (lua_cache_get (L, server)) - return; - - struct lua_server *wrapper = lua_newuserdata (L, sizeof *wrapper); - luaL_setmetatable (L, XLUA_SERVER_METATABLE); - wrapper->plugin = plugin; - wrapper->server = server; - wrapper->weak_ref = server_weak_ref - (server, lua_server_invalidate, wrapper); - lua_cache_store (L, server, -1); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - static int lua_buffer_gc (lua_State *L) { - struct lua_buffer *wrapper = luaL_checkudata (L, 1, XLUA_BUFFER_METATABLE); - if (wrapper->buffer) - { - lua_cache_invalidate (L, wrapper->buffer); - buffer_weak_unref (wrapper->buffer, &wrapper->weak_ref); - wrapper->buffer = NULL; - } - return 0; + return lua_weak_gc (L, &lua_buffer_info); } static int lua_buffer_get_user (lua_State *L) { - struct lua_buffer *wrapper = luaL_checkudata (L, 1, XLUA_BUFFER_METATABLE); - luaL_argcheck (L, wrapper->buffer, 1, "dead reference used"); - - if (wrapper->buffer->user) - lua_plugin_push_user (wrapper->plugin, wrapper->buffer->user); - else - lua_pushnil (L); + struct lua_weak *wrapper = lua_weak_deref (L, &lua_buffer_info); + struct buffer *buffer = wrapper->object; + lua_weak_push (wrapper->plugin, buffer->user, &lua_user_info); return 1; } static int lua_buffer_get_channel (lua_State *L) { - struct lua_buffer *wrapper = luaL_checkudata (L, 1, XLUA_BUFFER_METATABLE); - luaL_argcheck (L, wrapper->buffer, 1, "dead reference used"); - - if (wrapper->buffer->channel) - lua_plugin_push_channel (wrapper->plugin, wrapper->buffer->channel); - else - lua_pushnil (L); + struct lua_weak *wrapper = lua_weak_deref (L, &lua_buffer_info); + struct buffer *buffer = wrapper->object; + lua_weak_push (wrapper->plugin, buffer->channel, &lua_channel_info); return 1; } static int lua_buffer_get_server (lua_State *L) { - struct lua_buffer *wrapper = luaL_checkudata (L, 1, XLUA_BUFFER_METATABLE); - luaL_argcheck (L, wrapper->buffer, 1, "dead reference used"); - - if (wrapper->buffer->server) - lua_plugin_push_server (wrapper->plugin, wrapper->buffer->server); - else - lua_pushnil (L); + struct lua_weak *wrapper = lua_weak_deref (L, &lua_buffer_info); + struct buffer *buffer = wrapper->object; + lua_weak_push (wrapper->plugin, buffer->server, &lua_server_info); return 1; } static int lua_buffer_log (lua_State *L) { - struct lua_buffer *wrapper = luaL_checkudata (L, 1, XLUA_BUFFER_METATABLE); - luaL_argcheck (L, wrapper->buffer, 1, "dead reference used"); + struct lua_weak *wrapper = lua_weak_deref (L, &lua_buffer_info); + struct buffer *buffer = wrapper->object; const char *message = lua_plugin_check_utf8 (L, 2); - - struct buffer *buffer = wrapper->buffer; log_full (wrapper->plugin->ctx, buffer->server, buffer, BUFFER_LINE_STATUS, "#s", message); return 0; @@ -8641,11 +8549,9 @@ lua_buffer_log (lua_State *L) static int lua_buffer_execute (lua_State *L) { - struct lua_buffer *wrapper = luaL_checkudata (L, 1, XLUA_BUFFER_METATABLE); - luaL_argcheck (L, wrapper->buffer, 1, "dead reference used"); + struct lua_weak *wrapper = lua_weak_deref (L, &lua_buffer_info); + struct buffer *buffer = wrapper->object; const char *line = lua_plugin_check_utf8 (L, 2); - - struct buffer *buffer = wrapper->buffer; process_input_utf8 (wrapper->plugin->ctx, buffer, line, 0); return 0; } @@ -8667,50 +8573,33 @@ static luaL_Reg lua_buffer_table[] = static int lua_server_gc (lua_State *L) { - struct lua_server *wrapper = luaL_checkudata (L, 1, XLUA_SERVER_METATABLE); - if (wrapper->server) - { - lua_cache_invalidate (L, wrapper->server); - server_weak_unref (wrapper->server, &wrapper->weak_ref); - wrapper->server = NULL; - } - return 0; + return lua_weak_gc (L, &lua_server_info); } static int lua_server_get_user (lua_State *L) { - struct lua_server *wrapper = luaL_checkudata (L, 1, XLUA_SERVER_METATABLE); - luaL_argcheck (L, wrapper->server, 1, "dead reference used"); - - if (wrapper->server->irc_user) - lua_plugin_push_user (wrapper->plugin, wrapper->server->irc_user); - else - lua_pushnil (L); + struct lua_weak *wrapper = lua_weak_deref (L, &lua_server_info); + struct server *server = wrapper->object; + lua_weak_push (wrapper->plugin, server->irc_user, &lua_user_info); return 1; } static int lua_server_get_buffer (lua_State *L) { - struct lua_server *wrapper = luaL_checkudata (L, 1, XLUA_SERVER_METATABLE); - luaL_argcheck (L, wrapper->server, 1, "dead reference used"); - - if (wrapper->server->buffer) - lua_plugin_push_buffer (wrapper->plugin, wrapper->server->buffer); - else - lua_pushnil (L); + struct lua_weak *wrapper = lua_weak_deref (L, &lua_server_info); + struct server *server = wrapper->object; + lua_weak_push (wrapper->plugin, server->buffer, &lua_buffer_info); return 1; } static int lua_server_send (lua_State *L) { - struct lua_server *wrapper = luaL_checkudata (L, 1, XLUA_SERVER_METATABLE); - luaL_argcheck (L, wrapper->server, 1, "dead reference used"); - const char *line = luaL_checkstring (L, 2); - - irc_send (wrapper->server, "%s", line); + struct lua_weak *wrapper = lua_weak_deref (L, &lua_server_info); + struct server *server = wrapper->object; + irc_send (server, "%s", luaL_checkstring (L, 2)); return 0; } @@ -8807,9 +8696,9 @@ lua_input_hook_filter (struct input_hook *self, struct buffer *buffer, lua_State *L = plugin->L; lua_rawgeti (L, LUA_REGISTRYINDEX, hook->ref_callback); - lua_rawgetp (L, LUA_REGISTRYINDEX, hook); // 1: hook - lua_plugin_push_buffer (plugin, buffer); // 2: buffer - lua_pushstring (L, input); // 3: input + lua_rawgetp (L, LUA_REGISTRYINDEX, hook); // 1: hook + lua_weak_push (plugin, buffer, &lua_buffer_info); // 2: buffer + lua_pushstring (L, input); // 3: input struct error *e = NULL; if (lua_plugin_call (plugin, 3, 1, &e)) @@ -8838,9 +8727,9 @@ lua_irc_hook_filter (struct irc_hook *self, struct server *s, char *message) lua_State *L = plugin->L; lua_rawgeti (L, LUA_REGISTRYINDEX, hook->ref_callback); - lua_rawgetp (L, LUA_REGISTRYINDEX, hook); // 1: hook - lua_plugin_push_server (plugin, s); // 2: server - lua_pushstring (L, message); // 3: message + lua_rawgetp (L, LUA_REGISTRYINDEX, hook); // 1: hook + lua_weak_push (plugin, s, &lua_server_info); // 2: server + lua_pushstring (L, message); // 3: message struct error *e = NULL; if (lua_plugin_call (plugin, 3, 1, &e)) @@ -8927,7 +8816,7 @@ lua_completion_hook_complete (struct completion_hook *self, lua_rawgetp (L, LUA_REGISTRYINDEX, hook); // 1: hook lua_plugin_push_completion (L, data); // 2: data - lua_plugin_push_buffer (plugin, plugin->ctx->current_buffer); + lua_weak_push (plugin, plugin->ctx->current_buffer, &lua_buffer_info); lua_setfield (L, -2, "buffer"); lua_pushstring (L, word); // 3: word