To: vim_dev@googlegroups.com Subject: Patch 8.1.1019 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.1.1019 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) Files: src/if_lua.c, src/testdir/test_lua.vim *** ../vim-8.1.1018/src/if_lua.c 2019-03-16 16:38:37.560654505 +0100 --- src/if_lua.c 2019-03-19 21:57:33.226164566 +0100 *************** *** 29,36 **** typedef dict_T *luaV_Dict; typedef list_T *luaV_List; typedef struct { ! typval_T tv; // funcref ! typval_T args; dict_T *self; // selfdict } luaV_Funcref; typedef void (*msgfunc_T)(char_u *); --- 29,35 ---- typedef dict_T *luaV_Dict; typedef list_T *luaV_List; typedef struct { ! char_u *name; // funcref dict_T *self; // selfdict } luaV_Funcref; typedef void (*msgfunc_T)(char_u *); *************** *** 69,75 **** static luaV_List *luaV_pushlist(lua_State *L, list_T *lis); static luaV_Dict *luaV_pushdict(lua_State *L, dict_T *dic); ! static luaV_Funcref *luaV_pushfuncref(lua_State *L, typval_T *tv); #if LUA_VERSION_NUM <= 501 #define luaV_openlib(L, l, n) luaL_openlib(L, NULL, l, n) --- 68,74 ---- static luaV_List *luaV_pushlist(lua_State *L, list_T *lis); static luaV_Dict *luaV_pushdict(lua_State *L, dict_T *dic); ! static luaV_Funcref *luaV_pushfuncref(lua_State *L, char_u *name); #if LUA_VERSION_NUM <= 501 #define luaV_openlib(L, l, n) luaL_openlib(L, NULL, l, n) *************** *** 443,449 **** #if LUA_VERSION_NUM > 501 static int ! luaL_typeerror (lua_State *L, int narg, const char *tname) { const char *msg = lua_pushfstring(L, "%s expected, got %s", tname, luaL_typename(L, narg)); --- 442,448 ---- #if LUA_VERSION_NUM > 501 static int ! luaL_typeerror(lua_State *L, int narg, const char *tname) { const char *msg = lua_pushfstring(L, "%s expected, got %s", tname, luaL_typename(L, narg)); *************** *** 540,546 **** lua_pushnil(L); break; case VAR_FUNC: ! luaV_pushfuncref(L, tv); break; default: lua_pushnil(L); --- 539,545 ---- lua_pushnil(L); break; case VAR_FUNC: ! luaV_pushfuncref(L, tv->vval.v_string); break; default: lua_pushnil(L); *************** *** 610,616 **** if (lua_rawequal(L, -1, -4)) { luaV_Funcref *f = (luaV_Funcref *) p; ! copy_tv(&f->tv, tv); lua_pop(L, 4); /* MTs */ break; } --- 609,617 ---- if (lua_rawequal(L, -1, -4)) { luaV_Funcref *f = (luaV_Funcref *) p; ! func_ref(f->name); ! tv->v_type = VAR_FUNC; ! tv->vval.v_string = vim_strsave(f->name); lua_pop(L, 4); /* MTs */ break; } *************** *** 693,699 **** #define luaV_newtype(typ,tname,luatyp,luatname) \ static luatyp * \ ! luaV_new##tname (lua_State *L, typ *obj) \ { \ luatyp *o = (luatyp *) lua_newuserdata(L, sizeof(luatyp)); \ *o = obj; \ --- 694,700 ---- #define luaV_newtype(typ,tname,luatyp,luatname) \ static luatyp * \ ! luaV_new##tname(lua_State *L, typ *obj) \ { \ luatyp *o = (luatyp *) lua_newuserdata(L, sizeof(luatyp)); \ *o = obj; \ *************** *** 725,731 **** #define luaV_type_tostring(tname,luatname) \ static int \ ! luaV_##tname##_tostring (lua_State *L) \ { \ lua_pushfstring(L, "%s: %p", luatname, lua_touserdata(L, 1)); \ return 1; \ --- 726,732 ---- #define luaV_type_tostring(tname,luatname) \ static int \ ! luaV_##tname##_tostring(lua_State *L) \ { \ lua_pushfstring(L, "%s: %p", luatname, lua_touserdata(L, 1)); \ return 1; \ *************** *** 734,740 **** /* ======= List type ======= */ static luaV_List * ! luaV_newlist (lua_State *L, list_T *lis) { luaV_List *l = (luaV_List *) lua_newuserdata(L, sizeof(luaV_List)); *l = lis; --- 735,741 ---- /* ======= List type ======= */ static luaV_List * ! luaV_newlist(lua_State *L, list_T *lis) { luaV_List *l = (luaV_List *) lua_newuserdata(L, sizeof(luaV_List)); *l = lis; *************** *** 749,755 **** luaV_type_tostring(list, LUAVIM_LIST) static int ! luaV_list_len (lua_State *L) { list_T *l = luaV_unbox(L, luaV_List, 1); lua_pushinteger(L, (l == NULL) ? 0 : (int) l->lv_len); --- 750,756 ---- luaV_type_tostring(list, LUAVIM_LIST) static int ! luaV_list_len(lua_State *L) { list_T *l = luaV_unbox(L, luaV_List, 1); lua_pushinteger(L, (l == NULL) ? 0 : (int) l->lv_len); *************** *** 757,763 **** } static int ! luaV_list_iter (lua_State *L) { listitem_T *li = (listitem_T *) lua_touserdata(L, lua_upvalueindex(2)); if (li == NULL) return 0; --- 758,764 ---- } static int ! luaV_list_iter(lua_State *L) { listitem_T *li = (listitem_T *) lua_touserdata(L, lua_upvalueindex(2)); if (li == NULL) return 0; *************** *** 768,774 **** } static int ! luaV_list_call (lua_State *L) { list_T *l = luaV_unbox(L, luaV_List, 1); lua_pushvalue(L, lua_upvalueindex(1)); /* pass cache table along */ --- 769,775 ---- } static int ! luaV_list_call(lua_State *L) { list_T *l = luaV_unbox(L, luaV_List, 1); lua_pushvalue(L, lua_upvalueindex(1)); /* pass cache table along */ *************** *** 778,784 **** } static int ! luaV_list_index (lua_State *L) { list_T *l = luaV_unbox(L, luaV_List, 1); if (lua_isnumber(L, 2)) /* list item? */ --- 779,785 ---- } static int ! luaV_list_index(lua_State *L) { list_T *l = luaV_unbox(L, luaV_List, 1); if (lua_isnumber(L, 2)) /* list item? */ *************** *** 807,813 **** } static int ! luaV_list_newindex (lua_State *L) { list_T *l = luaV_unbox(L, luaV_List, 1); long n = (long) luaL_checkinteger(L, 2); --- 808,814 ---- } static int ! luaV_list_newindex(lua_State *L) { list_T *l = luaV_unbox(L, luaV_List, 1); long n = (long) luaL_checkinteger(L, 2); *************** *** 834,840 **** } static int ! luaV_list_add (lua_State *L) { luaV_List *lis = luaV_checkudata(L, 1, LUAVIM_LIST); list_T *l = (list_T *) luaV_checkcache(L, (void *) *lis); --- 835,841 ---- } static int ! luaV_list_add(lua_State *L) { luaV_List *lis = luaV_checkudata(L, 1, LUAVIM_LIST); list_T *l = (list_T *) luaV_checkcache(L, (void *) *lis); *************** *** 851,857 **** } static int ! luaV_list_insert (lua_State *L) { luaV_List *lis = luaV_checkudata(L, 1, LUAVIM_LIST); list_T *l = (list_T *) luaV_checkcache(L, (void *) *lis); --- 852,858 ---- } static int ! luaV_list_insert(lua_State *L) { luaV_List *lis = luaV_checkudata(L, 1, LUAVIM_LIST); list_T *l = (list_T *) luaV_checkcache(L, (void *) *lis); *************** *** 890,896 **** /* ======= Dict type ======= */ static luaV_Dict * ! luaV_newdict (lua_State *L, dict_T *dic) { luaV_Dict *d = (luaV_Dict *) lua_newuserdata(L, sizeof(luaV_Dict)); *d = dic; --- 891,897 ---- /* ======= Dict type ======= */ static luaV_Dict * ! luaV_newdict(lua_State *L, dict_T *dic) { luaV_Dict *d = (luaV_Dict *) lua_newuserdata(L, sizeof(luaV_Dict)); *d = dic; *************** *** 905,911 **** luaV_type_tostring(dict, LUAVIM_DICT) static int ! luaV_dict_len (lua_State *L) { dict_T *d = luaV_unbox(L, luaV_Dict, 1); lua_pushinteger(L, (d == NULL) ? 0 : (int) d->dv_hashtab.ht_used); --- 906,912 ---- luaV_type_tostring(dict, LUAVIM_DICT) static int ! luaV_dict_len(lua_State *L) { dict_T *d = luaV_unbox(L, luaV_Dict, 1); lua_pushinteger(L, (d == NULL) ? 0 : (int) d->dv_hashtab.ht_used); *************** *** 913,919 **** } static int ! luaV_dict_iter (lua_State *L UNUSED) { #ifdef FEAT_EVAL hashitem_T *hi = (hashitem_T *) lua_touserdata(L, lua_upvalueindex(2)); --- 914,920 ---- } static int ! luaV_dict_iter(lua_State *L UNUSED) { #ifdef FEAT_EVAL hashitem_T *hi = (hashitem_T *) lua_touserdata(L, lua_upvalueindex(2)); *************** *** 935,941 **** } static int ! luaV_dict_call (lua_State *L) { dict_T *d = luaV_unbox(L, luaV_Dict, 1); hashtab_T *ht = &d->dv_hashtab; --- 936,942 ---- } static int ! luaV_dict_call(lua_State *L) { dict_T *d = luaV_unbox(L, luaV_Dict, 1); hashtab_T *ht = &d->dv_hashtab; *************** *** 1037,1047 **** if (name != NULL) { ! func_ref(name); /* as in copy_tv */ ! f->tv.vval.v_string = vim_strsave(name); } - f->tv.v_type = VAR_FUNC; - f->args.v_type = VAR_LIST; f->self = NULL; luaV_getfield(L, LUAVIM_FUNCREF); lua_setmetatable(L, -2); --- 1038,1046 ---- if (name != NULL) { ! func_ref(name); ! f->name = vim_strsave(name); } f->self = NULL; luaV_getfield(L, LUAVIM_FUNCREF); lua_setmetatable(L, -2); *************** *** 1049,1060 **** } static luaV_Funcref * ! luaV_pushfuncref(lua_State *L, typval_T *tv) { ! luaV_Funcref *f = luaV_newfuncref(L, NULL); ! copy_tv(tv, &f->tv); ! clear_tv(tv); ! return f; } --- 1048,1056 ---- } static luaV_Funcref * ! luaV_pushfuncref(lua_State *L, char_u *name) { ! return luaV_newfuncref(L, name); } *************** *** 1065,1073 **** { luaV_Funcref *f = (luaV_Funcref *) lua_touserdata(L, 1); ! func_unref(f->tv.vval.v_string); ! vim_free(f->tv.vval.v_string); ! dict_unref(f->self); return 0; } --- 1061,1070 ---- { luaV_Funcref *f = (luaV_Funcref *) lua_touserdata(L, 1); ! func_unref(f->name); ! vim_free(f->name); ! // 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; } *************** *** 1077,1083 **** { luaV_Funcref *f = (luaV_Funcref *) lua_touserdata(L, 1); ! lua_pushstring(L, (const char *) f->tv.vval.v_string); return 1; } --- 1074,1080 ---- { luaV_Funcref *f = (luaV_Funcref *) lua_touserdata(L, 1); ! lua_pushstring(L, (const char *) f->name); return 1; } *************** *** 1085,1111 **** luaV_funcref_call(lua_State *L) { luaV_Funcref *f = (luaV_Funcref *) lua_touserdata(L, 1); ! int i, n = lua_gettop(L) - 1; /* #args */ ! int status; ! typval_T v, rettv; ! ! f->args.vval.v_list = list_alloc(); ! rettv.v_type = VAR_UNKNOWN; /* as in clear_tv */ ! if (f->args.vval.v_list == NULL) ! status = FAIL; ! else { for (i = 0; i < n; i++) { luaV_checktypval(L, i + 2, &v, "calling funcref"); ! list_append_tv(f->args.vval.v_list, &v); clear_tv(&v); } ! status = func_call(f->tv.vval.v_string, &f->args, ! NULL, f->self, &rettv); if (status == OK) luaV_pushtypval(L, &rettv); ! clear_tv(&f->args); clear_tv(&rettv); } if (status != OK) --- 1082,1109 ---- luaV_funcref_call(lua_State *L) { luaV_Funcref *f = (luaV_Funcref *) lua_touserdata(L, 1); ! int i, n = lua_gettop(L) - 1; // #args ! int status = FAIL; ! typval_T args; ! typval_T rettv; ! ! args.v_type = VAR_LIST; ! args.vval.v_list = list_alloc(); ! rettv.v_type = VAR_UNKNOWN; // as in clear_tv ! if (args.vval.v_list != NULL) { + typval_T v; + for (i = 0; i < n; i++) { luaV_checktypval(L, i + 2, &v, "calling funcref"); ! list_append_tv(args.vval.v_list, &v); clear_tv(&v); } ! status = func_call(f->name, &args, NULL, f->self, &rettv); if (status == OK) luaV_pushtypval(L, &rettv); ! clear_tv(&args); clear_tv(&rettv); } if (status != OK) *************** *** 1368,1374 **** } static int ! luaV_window_newindex (lua_State *L) { win_T *w = (win_T *) luaV_checkvalid(L, luaV_Window, 1); const char *s = luaL_checkstring(L, 2); --- 1366,1372 ---- } static int ! luaV_window_newindex(lua_State *L) { win_T *w = (win_T *) luaV_checkvalid(L, luaV_Window, 1); const char *s = luaL_checkstring(L, 2); *************** *** 1768,1774 **** } static int ! luaV_luaeval (lua_State *L) { luaL_Buffer b; size_t l; --- 1766,1772 ---- } static int ! luaV_luaeval(lua_State *L) { luaL_Buffer b; size_t l; *************** *** 1797,1828 **** } static int ! luaV_setref (lua_State *L) { ! int copyID = lua_tointeger(L, 1); ! int abort = FALSE; ! typval_T tv; luaV_getfield(L, LUAVIM_LIST); luaV_getfield(L, LUAVIM_DICT); lua_pushnil(L); ! /* traverse cache table */ while (!abort && lua_next(L, lua_upvalueindex(1)) != 0) { lua_getmetatable(L, -1); ! if (lua_rawequal(L, -1, 2)) /* list? */ { ! tv.v_type = VAR_LIST; ! tv.vval.v_list = (list_T *) lua_touserdata(L, 4); /* key */ ! abort = set_ref_in_item(&tv, copyID, NULL, NULL); } ! else if (lua_rawequal(L, -1, 3)) /* dict? */ { ! tv.v_type = VAR_DICT; ! tv.vval.v_dict = (dict_T *) lua_touserdata(L, 4); /* key */ ! abort = set_ref_in_item(&tv, copyID, NULL, NULL); } ! lua_pop(L, 2); /* metatable and value */ } lua_pushinteger(L, abort); return 1; --- 1795,1844 ---- } static int ! luaV_setref(lua_State *L) { ! int copyID = lua_tointeger(L, 1); ! int abort = FALSE; luaV_getfield(L, LUAVIM_LIST); luaV_getfield(L, LUAVIM_DICT); + luaV_getfield(L, LUAVIM_FUNCREF); lua_pushnil(L); ! // traverse cache table while (!abort && lua_next(L, lua_upvalueindex(1)) != 0) { lua_getmetatable(L, -1); ! if (lua_rawequal(L, -1, 2)) // list? ! { ! list_T *l = (list_T *)lua_touserdata(L, 5); // key ! ! if (l->lv_copyID != copyID) ! { ! l->lv_copyID = copyID; ! abort = set_ref_in_list(l, copyID, NULL); ! } ! } ! 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); return 1; *************** *** 2053,2059 **** luaV_freetype(win_T, window) void ! do_luaeval (char_u *str, typval_T *arg, typval_T *rettv) { lua_init(); luaV_getfield(L, LUAVIM_LUAEVAL); --- 2069,2075 ---- luaV_freetype(win_T, window) void ! do_luaeval(char_u *str, typval_T *arg, typval_T *rettv) { lua_init(); luaV_getfield(L, LUAVIM_LUAEVAL); *************** *** 2064,2070 **** } int ! set_ref_in_lua (int copyID) { int aborted = 0; --- 2080,2086 ---- } int ! set_ref_in_lua(int copyID) { int aborted = 0; *** ../vim-8.1.1018/src/testdir/test_lua.vim 2019-03-10 09:48:55.711808501 +0100 --- src/testdir/test_lua.vim 2019-03-19 21:50:17.437605532 +0100 *************** *** 449,454 **** --- 449,455 ---- lua d.len = vim.funcref"Mylen" -- assign d as 'self' lua res = (d.len() == vim.funcref"len"(vim.eval"l")) and "OK" or "FAIL" 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 endfunc *** ../vim-8.1.1018/src/version.c 2019-03-19 20:50:40.290035255 +0100 --- src/version.c 2019-03-19 21:53:59.151973880 +0100 *************** *** 781,782 **** --- 781,784 ---- { /* Add new patch number below this line */ + /**/ + 1019, /**/ -- From "know your smileys": :-O>-o Smiley American tourist (note big mouth and camera) /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///