[Lua] Intern and invalidate contexts as needed.

This commit is contained in:
SoniEx2 2018-10-15 19:16:08 -03:00
parent aaa319735b
commit eaad08d462
1 changed files with 108 additions and 26 deletions

View File

@ -21,6 +21,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include <limits.h>
#include <lua.h> #include <lua.h>
#include <lauxlib.h> #include <lauxlib.h>
@ -51,6 +52,7 @@ static char command_help[] =
" console"; " console";
static char registry_field[] = "plugin"; static char registry_field[] = "plugin";
static char registry_field_context_intern[] = "context_intern";
static hexchat_plugin *ph; static hexchat_plugin *ph;
@ -86,6 +88,38 @@ script_info;
#define STATUS_DEFERRED_UNLOAD 2 #define STATUS_DEFERRED_UNLOAD 2
#define STATUS_DEFERRED_RELOAD 4 #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 void check_deferred(script_info *info);
static inline script_info *get_info(lua_State *L) 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); hexchat_context *context = hexchat_find_context(ph, server, channel);
if(context) if(context)
{ {
hexchat_context **u = lua_newuserdata(L, sizeof(hexchat_context *)); if (get_interned_context(L, context))
*u = context; {
luaL_newmetatable(L, "context");
lua_setmetatable(L, -2);
return 1; return 1;
} }
else }
{
lua_pushnil(L); lua_pushnil(L);
return 1; return 1;
}
} }
static int api_hexchat_get_context(lua_State *L) static int api_hexchat_get_context(lua_State *L)
{ {
hexchat_context *context = hexchat_get_context(ph); hexchat_context *context = hexchat_get_context(ph);
hexchat_context **u = lua_newuserdata(L, sizeof(hexchat_context *)); if (get_interned_context(L, context))
*u = context; {
luaL_newmetatable(L, "context");
lua_setmetatable(L, -2);
return 1; return 1;
}
return luaL_error(L, "Unexpected error getting context");
} }
static int api_hexchat_set_context(lua_State *L) 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) 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_pushvalue(L, lua_upvalueindex(1));
lua_replace(L, 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)) if(!hexchat_set_context(ph, context))
return luaL_error(L, "could not switch into context"); return luaL_error(L, "could not switch into context");
lua_call(L, lua_gettop(L) - 1, LUA_MULTRET); lua_call(L, lua_gettop(L) - 2, LUA_MULTRET);
hexchat_set_context(ph, old); hexchat_set_context(ph, *old);
return lua_gettop(L); return lua_gettop(L) - 1;
} }
static inline void wrap_context(lua_State *L, char const *field, lua_CFunction func) 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 *this = *(hexchat_context **)luaL_checkudata(L, 1, "context");
hexchat_context *that = *(hexchat_context **)luaL_checkudata(L, 2, "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; return 1;
} }
@ -812,12 +845,12 @@ static inline int list_marshal(lua_State *L, const char *key, hexchat_list *list
int number; int number;
if(str) if(str)
{ {
if(!strcmp(key, "context")) if(strcmp(key, "context") == 0)
{ {
hexchat_context **u = lua_newuserdata(L, sizeof(hexchat_context *)); if (!get_interned_context(L, (hexchat_context *)str))
*u = (hexchat_context *)str; {
luaL_newmetatable(L, "context"); lua_pushnil(L);
lua_setmetatable(L, -2); }
return 1; return 1;
} }
lua_pushstring(L, str); lua_pushstring(L, str);
@ -1262,6 +1295,13 @@ static void prepare_state(lua_State *L, script_info *info)
lua_pop(L, 1); lua_pop(L, 1);
lua_pushlightuserdata(L, info); lua_pushlightuserdata(L, info);
lua_setfield(L, LUA_REGISTRYINDEX, registry_field); 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); luaopen_hexchat(L);
lua_setglobal(L, "hexchat"); lua_setglobal(L, "hexchat");
lua_getglobal(L, "hexchat"); lua_getglobal(L, "hexchat");
@ -1698,6 +1738,46 @@ static int command_lua(char *word[], char *word_eol[], void *userdata)
/* Reinitialization safegaurd */ /* Reinitialization safegaurd */
static int initialized = 0; 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) G_MODULE_EXPORT int hexchat_plugin_init(hexchat_plugin *plugin_handle, char **name, char **description, char **version, char *arg)
{ {
if(initialized != 0) 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, "UNLOAD", HEXCHAT_PRI_NORM, command_unload, NULL, NULL);
hexchat_hook_command(ph, "RELOAD", HEXCHAT_PRI_NORM, command_reload, 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); 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); hexchat_printf(ph, "%s version %s loaded.\n", plugin_name, plugin_version);