mirror of
https://github.com/vim/vim.git
synced 2025-07-26 11:04:33 -04:00
patch 8.1.1019: Lua: may garbage collect function reference in use
Problem: Lua: may garbage collect function reference in use. Solution: Keep the function name instead of the typeval. Make luaV_setref() handle funcref objects. (Ozaki Kiichi, closes #4127)
This commit is contained in:
parent
8376c3d321
commit
4eefe47ea4
100
src/if_lua.c
100
src/if_lua.c
@ -29,8 +29,7 @@ typedef win_T *luaV_Window;
|
|||||||
typedef dict_T *luaV_Dict;
|
typedef dict_T *luaV_Dict;
|
||||||
typedef list_T *luaV_List;
|
typedef list_T *luaV_List;
|
||||||
typedef struct {
|
typedef struct {
|
||||||
typval_T tv; // funcref
|
char_u *name; // funcref
|
||||||
typval_T args;
|
|
||||||
dict_T *self; // selfdict
|
dict_T *self; // selfdict
|
||||||
} luaV_Funcref;
|
} luaV_Funcref;
|
||||||
typedef void (*msgfunc_T)(char_u *);
|
typedef void (*msgfunc_T)(char_u *);
|
||||||
@ -69,7 +68,7 @@ static const char LUAVIM_SETREF[] = "luaV_setref";
|
|||||||
|
|
||||||
static luaV_List *luaV_pushlist(lua_State *L, list_T *lis);
|
static luaV_List *luaV_pushlist(lua_State *L, list_T *lis);
|
||||||
static luaV_Dict *luaV_pushdict(lua_State *L, dict_T *dic);
|
static luaV_Dict *luaV_pushdict(lua_State *L, dict_T *dic);
|
||||||
static luaV_Funcref *luaV_pushfuncref(lua_State *L, typval_T *tv);
|
static luaV_Funcref *luaV_pushfuncref(lua_State *L, char_u *name);
|
||||||
|
|
||||||
#if LUA_VERSION_NUM <= 501
|
#if LUA_VERSION_NUM <= 501
|
||||||
#define luaV_openlib(L, l, n) luaL_openlib(L, NULL, l, n)
|
#define luaV_openlib(L, l, n) luaL_openlib(L, NULL, l, n)
|
||||||
@ -540,7 +539,7 @@ luaV_pushtypval(lua_State *L, typval_T *tv)
|
|||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
break;
|
break;
|
||||||
case VAR_FUNC:
|
case VAR_FUNC:
|
||||||
luaV_pushfuncref(L, tv);
|
luaV_pushfuncref(L, tv->vval.v_string);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
@ -610,7 +609,9 @@ luaV_totypval(lua_State *L, int pos, typval_T *tv)
|
|||||||
if (lua_rawequal(L, -1, -4))
|
if (lua_rawequal(L, -1, -4))
|
||||||
{
|
{
|
||||||
luaV_Funcref *f = (luaV_Funcref *) p;
|
luaV_Funcref *f = (luaV_Funcref *) p;
|
||||||
copy_tv(&f->tv, tv);
|
func_ref(f->name);
|
||||||
|
tv->v_type = VAR_FUNC;
|
||||||
|
tv->vval.v_string = vim_strsave(f->name);
|
||||||
lua_pop(L, 4); /* MTs */
|
lua_pop(L, 4); /* MTs */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1037,11 +1038,9 @@ luaV_newfuncref(lua_State *L, char_u *name)
|
|||||||
|
|
||||||
if (name != NULL)
|
if (name != NULL)
|
||||||
{
|
{
|
||||||
func_ref(name); /* as in copy_tv */
|
func_ref(name);
|
||||||
f->tv.vval.v_string = vim_strsave(name);
|
f->name = vim_strsave(name);
|
||||||
}
|
}
|
||||||
f->tv.v_type = VAR_FUNC;
|
|
||||||
f->args.v_type = VAR_LIST;
|
|
||||||
f->self = NULL;
|
f->self = NULL;
|
||||||
luaV_getfield(L, LUAVIM_FUNCREF);
|
luaV_getfield(L, LUAVIM_FUNCREF);
|
||||||
lua_setmetatable(L, -2);
|
lua_setmetatable(L, -2);
|
||||||
@ -1049,12 +1048,9 @@ luaV_newfuncref(lua_State *L, char_u *name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static luaV_Funcref *
|
static luaV_Funcref *
|
||||||
luaV_pushfuncref(lua_State *L, typval_T *tv)
|
luaV_pushfuncref(lua_State *L, char_u *name)
|
||||||
{
|
{
|
||||||
luaV_Funcref *f = luaV_newfuncref(L, NULL);
|
return luaV_newfuncref(L, name);
|
||||||
copy_tv(tv, &f->tv);
|
|
||||||
clear_tv(tv);
|
|
||||||
return f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1065,9 +1061,10 @@ luaV_funcref_gc(lua_State *L)
|
|||||||
{
|
{
|
||||||
luaV_Funcref *f = (luaV_Funcref *) lua_touserdata(L, 1);
|
luaV_Funcref *f = (luaV_Funcref *) lua_touserdata(L, 1);
|
||||||
|
|
||||||
func_unref(f->tv.vval.v_string);
|
func_unref(f->name);
|
||||||
vim_free(f->tv.vval.v_string);
|
vim_free(f->name);
|
||||||
dict_unref(f->self);
|
// NOTE: Don't call "dict_unref(f->self)", because the dict of "f->self"
|
||||||
|
// will be (or has been already) freed by Vim's garbage collection.
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1077,7 +1074,7 @@ luaV_funcref_len(lua_State *L)
|
|||||||
{
|
{
|
||||||
luaV_Funcref *f = (luaV_Funcref *) lua_touserdata(L, 1);
|
luaV_Funcref *f = (luaV_Funcref *) lua_touserdata(L, 1);
|
||||||
|
|
||||||
lua_pushstring(L, (const char *) f->tv.vval.v_string);
|
lua_pushstring(L, (const char *) f->name);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1085,27 +1082,28 @@ luaV_funcref_len(lua_State *L)
|
|||||||
luaV_funcref_call(lua_State *L)
|
luaV_funcref_call(lua_State *L)
|
||||||
{
|
{
|
||||||
luaV_Funcref *f = (luaV_Funcref *) lua_touserdata(L, 1);
|
luaV_Funcref *f = (luaV_Funcref *) lua_touserdata(L, 1);
|
||||||
int i, n = lua_gettop(L) - 1; /* #args */
|
int i, n = lua_gettop(L) - 1; // #args
|
||||||
int status;
|
int status = FAIL;
|
||||||
typval_T v, rettv;
|
typval_T args;
|
||||||
|
typval_T rettv;
|
||||||
|
|
||||||
f->args.vval.v_list = list_alloc();
|
args.v_type = VAR_LIST;
|
||||||
rettv.v_type = VAR_UNKNOWN; /* as in clear_tv */
|
args.vval.v_list = list_alloc();
|
||||||
if (f->args.vval.v_list == NULL)
|
rettv.v_type = VAR_UNKNOWN; // as in clear_tv
|
||||||
status = FAIL;
|
if (args.vval.v_list != NULL)
|
||||||
else
|
|
||||||
{
|
{
|
||||||
|
typval_T v;
|
||||||
|
|
||||||
for (i = 0; i < n; i++)
|
for (i = 0; i < n; i++)
|
||||||
{
|
{
|
||||||
luaV_checktypval(L, i + 2, &v, "calling funcref");
|
luaV_checktypval(L, i + 2, &v, "calling funcref");
|
||||||
list_append_tv(f->args.vval.v_list, &v);
|
list_append_tv(args.vval.v_list, &v);
|
||||||
clear_tv(&v);
|
clear_tv(&v);
|
||||||
}
|
}
|
||||||
status = func_call(f->tv.vval.v_string, &f->args,
|
status = func_call(f->name, &args, NULL, f->self, &rettv);
|
||||||
NULL, f->self, &rettv);
|
|
||||||
if (status == OK)
|
if (status == OK)
|
||||||
luaV_pushtypval(L, &rettv);
|
luaV_pushtypval(L, &rettv);
|
||||||
clear_tv(&f->args);
|
clear_tv(&args);
|
||||||
clear_tv(&rettv);
|
clear_tv(&rettv);
|
||||||
}
|
}
|
||||||
if (status != OK)
|
if (status != OK)
|
||||||
@ -1801,28 +1799,46 @@ luaV_setref (lua_State *L)
|
|||||||
{
|
{
|
||||||
int copyID = lua_tointeger(L, 1);
|
int copyID = lua_tointeger(L, 1);
|
||||||
int abort = FALSE;
|
int abort = FALSE;
|
||||||
typval_T tv;
|
|
||||||
|
|
||||||
luaV_getfield(L, LUAVIM_LIST);
|
luaV_getfield(L, LUAVIM_LIST);
|
||||||
luaV_getfield(L, LUAVIM_DICT);
|
luaV_getfield(L, LUAVIM_DICT);
|
||||||
|
luaV_getfield(L, LUAVIM_FUNCREF);
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
/* traverse cache table */
|
// traverse cache table
|
||||||
while (!abort && lua_next(L, lua_upvalueindex(1)) != 0)
|
while (!abort && lua_next(L, lua_upvalueindex(1)) != 0)
|
||||||
{
|
{
|
||||||
lua_getmetatable(L, -1);
|
lua_getmetatable(L, -1);
|
||||||
if (lua_rawequal(L, -1, 2)) /* list? */
|
if (lua_rawequal(L, -1, 2)) // list?
|
||||||
{
|
{
|
||||||
tv.v_type = VAR_LIST;
|
list_T *l = (list_T *)lua_touserdata(L, 5); // key
|
||||||
tv.vval.v_list = (list_T *) lua_touserdata(L, 4); /* key */
|
|
||||||
abort = set_ref_in_item(&tv, copyID, NULL, NULL);
|
if (l->lv_copyID != copyID)
|
||||||
}
|
|
||||||
else if (lua_rawequal(L, -1, 3)) /* dict? */
|
|
||||||
{
|
{
|
||||||
tv.v_type = VAR_DICT;
|
l->lv_copyID = copyID;
|
||||||
tv.vval.v_dict = (dict_T *) lua_touserdata(L, 4); /* key */
|
abort = set_ref_in_list(l, copyID, NULL);
|
||||||
abort = set_ref_in_item(&tv, copyID, NULL, NULL);
|
|
||||||
}
|
}
|
||||||
lua_pop(L, 2); /* metatable and value */
|
}
|
||||||
|
else if (lua_rawequal(L, -1, 3)) // dict?
|
||||||
|
{
|
||||||
|
dict_T *d = (dict_T *)lua_touserdata(L, 5); // key
|
||||||
|
|
||||||
|
if (d->dv_copyID != copyID)
|
||||||
|
{
|
||||||
|
d->dv_copyID = copyID;
|
||||||
|
abort = set_ref_in_ht(&d->dv_hashtab, copyID, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (lua_rawequal(L, -1, 4)) // funcref?
|
||||||
|
{
|
||||||
|
luaV_Funcref *f = (luaV_Funcref *)lua_touserdata(L, 5); // key
|
||||||
|
|
||||||
|
if (f->self != NULL && f->self->dv_copyID != copyID)
|
||||||
|
{
|
||||||
|
f->self->dv_copyID = copyID;
|
||||||
|
abort = set_ref_in_ht(&f->self->dv_hashtab, copyID, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lua_pop(L, 2); // metatable and value
|
||||||
}
|
}
|
||||||
lua_pushinteger(L, abort);
|
lua_pushinteger(L, abort);
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -449,6 +449,7 @@ func Test_funcref()
|
|||||||
lua d.len = vim.funcref"Mylen" -- assign d as 'self'
|
lua d.len = vim.funcref"Mylen" -- assign d as 'self'
|
||||||
lua res = (d.len() == vim.funcref"len"(vim.eval"l")) and "OK" or "FAIL"
|
lua res = (d.len() == vim.funcref"len"(vim.eval"l")) and "OK" or "FAIL"
|
||||||
call assert_equal("OK", luaeval('res'))
|
call assert_equal("OK", luaeval('res'))
|
||||||
|
call assert_equal(function('Mylen', {'data': l, 'len': function('Mylen')}), mydict.len)
|
||||||
|
|
||||||
lua i1, i2, msg, d, res = nil
|
lua i1, i2, msg, d, res = nil
|
||||||
endfunc
|
endfunc
|
||||||
|
@ -779,6 +779,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
1019,
|
||||||
/**/
|
/**/
|
||||||
1018,
|
1018,
|
||||||
/**/
|
/**/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user