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 -- [email protected] -- 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    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui