From eaad08d462cc916a211456f44d363e26a119c94a Mon Sep 17 00:00:00 2001 From: SoniEx2 Date: Mon, 15 Oct 2018 19:16:08 -0300 Subject: [PATCH] [Lua] Intern and invalidate contexts as needed. --- plugins/lua/lua.c | 134 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 108 insertions(+), 26 deletions(-) diff --git a/plugins/lua/lua.c b/plugins/lua/lua.c index 941342bb..0903ec07 100644 --- a/plugins/lua/lua.c +++ b/plugins/lua/lua.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -51,6 +52,7 @@ static char command_help[] = " console"; static char registry_field[] = "plugin"; +static char registry_field_context_intern[] = "context_intern"; static hexchat_plugin *ph; @@ -86,6 +88,38 @@ script_info; #define STATUS_DEFERRED_UNLOAD 2 #define STATUS_DEFERRED_RELOAD 4 +/* context interning funtions */ +static int get_interned_context(lua_State *L, hexchat_context *context) +{ + if (!lua_checkstack(L, 4)) + { + return 0; + } + lua_getfield(L, LUA_REGISTRYINDEX, registry_field_context_intern); + if (!lua_istable(L, -1)) + { + lua_pop(L, 1); + return 0; + } + /* find the context */ + lua_pushlightuserdata(L, context); + lua_rawget(L, -2); + if(lua_type(L, -1) == LUA_TNIL) + { + lua_pop(L, 1); + /* couldn't find the context, create it */ + hexchat_context **u = lua_newuserdata(L, sizeof(hexchat_context *)); + *u = context; + luaL_newmetatable(L, "context"); + lua_setmetatable(L, -2); + lua_pushlightuserdata(L, context); + lua_pushvalue(L, -2); + lua_rawset(L, -4); + } + lua_remove(L, -2); + return 1; +} + static void check_deferred(script_info *info); static inline script_info *get_info(lua_State *L) @@ -662,27 +696,23 @@ static int api_hexchat_find_context(lua_State *L) hexchat_context *context = hexchat_find_context(ph, server, channel); if(context) { - hexchat_context **u = lua_newuserdata(L, sizeof(hexchat_context *)); - *u = context; - luaL_newmetatable(L, "context"); - lua_setmetatable(L, -2); - return 1; - } - else - { - lua_pushnil(L); - return 1; + if (get_interned_context(L, context)) + { + return 1; + } } + lua_pushnil(L); + return 1; } static int api_hexchat_get_context(lua_State *L) { hexchat_context *context = hexchat_get_context(ph); - hexchat_context **u = lua_newuserdata(L, sizeof(hexchat_context *)); - *u = context; - luaL_newmetatable(L, "context"); - lua_setmetatable(L, -2); - return 1; + if (get_interned_context(L, context)) + { + return 1; + } + return luaL_error(L, "Unexpected error getting context"); } static int api_hexchat_set_context(lua_State *L) @@ -695,15 +725,18 @@ static int api_hexchat_set_context(lua_State *L) static int wrap_context_closure(lua_State *L) { - hexchat_context *old, *context = *(hexchat_context **)luaL_checkudata(L, 1, "context"); + hexchat_context **old, *context = *(hexchat_context **)luaL_checkudata(L, 1, "context"); lua_pushvalue(L, lua_upvalueindex(1)); lua_replace(L, 1); - old = hexchat_get_context(ph); + api_hexchat_get_context(L); + old = lua_touserdata(L, -1); + /* need to keep it on the stack so it doesn't get free'd */ + lua_insert(L, 1); if(!hexchat_set_context(ph, context)) return luaL_error(L, "could not switch into context"); - lua_call(L, lua_gettop(L) - 1, LUA_MULTRET); - hexchat_set_context(ph, old); - return lua_gettop(L); + lua_call(L, lua_gettop(L) - 2, LUA_MULTRET); + hexchat_set_context(ph, *old); + return lua_gettop(L) - 1; } static inline void wrap_context(lua_State *L, char const *field, lua_CFunction func) @@ -717,7 +750,7 @@ static int api_hexchat_context_meta_eq(lua_State *L) { hexchat_context *this = *(hexchat_context **)luaL_checkudata(L, 1, "context"); hexchat_context *that = *(hexchat_context **)luaL_checkudata(L, 2, "context"); - lua_pushboolean(L, this == that); + lua_pushboolean(L, this == that && this != NULL); return 1; } @@ -812,12 +845,12 @@ static inline int list_marshal(lua_State *L, const char *key, hexchat_list *list int number; if(str) { - if(!strcmp(key, "context")) + if(strcmp(key, "context") == 0) { - hexchat_context **u = lua_newuserdata(L, sizeof(hexchat_context *)); - *u = (hexchat_context *)str; - luaL_newmetatable(L, "context"); - lua_setmetatable(L, -2); + if (!get_interned_context(L, (hexchat_context *)str)) + { + lua_pushnil(L); + } return 1; } lua_pushstring(L, str); @@ -1262,6 +1295,13 @@ static void prepare_state(lua_State *L, script_info *info) lua_pop(L, 1); lua_pushlightuserdata(L, info); lua_setfield(L, LUA_REGISTRYINDEX, registry_field); + lua_newtable(L); + /* do we need __mode=v? */ + lua_newtable(L); + lua_pushstring(L, "v"); + lua_setfield(L, -2, "__mode"); + lua_setmetatable(L, -2); + lua_setfield(L, LUA_REGISTRYINDEX, registry_field_context_intern); luaopen_hexchat(L); lua_setglobal(L, "hexchat"); lua_getglobal(L, "hexchat"); @@ -1698,6 +1738,46 @@ static int command_lua(char *word[], char *word_eol[], void *userdata) /* Reinitialization safegaurd */ static int initialized = 0; +static int ctx_close_cb(char *word[], void *user_data) +{ + int i; + hexchat_context *context = hexchat_get_context(ph); + for(i = 0; i < scripts->len; i++) + { + script_info *info = (script_info*)scripts->pdata[i]; + lua_State *L = info->state; + if (!lua_checkstack(L, 4)) + { + continue; + } + lua_getfield(L, LUA_REGISTRYINDEX, registry_field_context_intern); + if (!lua_istable(L, -1)) + { + lua_pop(L, 1); + continue; + } + /* find the context */ + lua_pushlightuserdata(L, context); + lua_rawget(L, -2); + if(lua_type(L, -1) != LUA_TNIL) + { + /* found the context, NULLify and detach it */ + hexchat_context **ud = lua_touserdata(L, -1); + if (ud) { + *ud = NULL; + } + lua_pop(L, 1); + lua_pushlightuserdata(L, context); + lua_pushnil(L); + lua_rawset(L, -4); + } else { + lua_pop(L, 1); + } + lua_pop(L, 1); + } + return HEXCHAT_EAT_NONE; +} + G_MODULE_EXPORT int hexchat_plugin_init(hexchat_plugin *plugin_handle, char **name, char **description, char **version, char *arg) { if(initialized != 0) @@ -1724,6 +1804,8 @@ G_MODULE_EXPORT int hexchat_plugin_init(hexchat_plugin *plugin_handle, char **na hexchat_hook_command(ph, "UNLOAD", HEXCHAT_PRI_NORM, command_unload, NULL, NULL); hexchat_hook_command(ph, "RELOAD", HEXCHAT_PRI_NORM, command_reload, NULL, NULL); hexchat_hook_command(ph, "lua", HEXCHAT_PRI_NORM, command_lua, command_help, NULL); + /* use INT_MIN (< -32767) so as to avoid breaking "Close Context" with PRI_LOWEST (-128) */ + hexchat_hook_print(ph, "Close Context", INT_MIN, ctx_close_cb, NULL); hexchat_printf(ph, "%s version %s loaded.\n", plugin_name, plugin_version);