Patch 8.1.0134
Problem:    Lua interface does not support funcref.
Solution:   Add funcref support. (Luis Carvalho)
Files:      src/if_lua.c, src/testdir/test_lua.vim


*** ../vim-8.1.0133/src/if_lua.c        2017-12-16 18:17:27.000000000 +0100
--- src/if_lua.c        2018-07-01 14:49:42.013143173 +0200
***************
*** 28,37 ****
--- 28,43 ----
  typedef win_T *luaV_Window;
  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 *);
  
  static const char LUAVIM_DICT[] = "dict";
  static const char LUAVIM_LIST[] = "list";
+ static const char LUAVIM_FUNCREF[] = "funcref";
  static const char LUAVIM_BUFFER[] = "buffer";
  static const char LUAVIM_WINDOW[] = "window";
  static const char LUAVIM_FREE[] = "luaV_free";
***************
*** 55,63 ****
      if (sandbox) luaL_error((L), "not allowed in sandbox")
  #define luaV_msg(L) luaV_msgfunc((L), (msgfunc_T) msg)
  #define luaV_emsg(L) luaV_msgfunc((L), (msgfunc_T) emsg)
! 
! static luaV_List *luaV_pushlist (lua_State *L, list_T *lis);
! static luaV_Dict *luaV_pushdict (lua_State *L, dict_T *dic);
  
  #if LUA_VERSION_NUM <= 501
  #define luaV_openlib(L, l, n) luaL_openlib(L, NULL, l, n)
--- 61,75 ----
      if (sandbox) luaL_error((L), "not allowed in sandbox")
  #define luaV_msg(L) luaV_msgfunc((L), (msgfunc_T) msg)
  #define luaV_emsg(L) luaV_msgfunc((L), (msgfunc_T) emsg)
! #define luaV_checktypval(L, a, v, msg) \
!     do { \
!         if (luaV_totypval(L, a, v) == FAIL) \
!           luaL_error(L, msg ": cannot convert value"); \
!     } while (0)
! 
! 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)
***************
*** 506,521 ****
            else
                lua_pushnil(L);
            break;
        default:
            lua_pushnil(L);
      }
  }
  
! /* converts lua value at 'pos' to typval 'tv' */
!     static void
! luaV_totypval (lua_State *L, int pos, typval_T *tv)
  {
!     switch(lua_type(L, pos)) {
        case LUA_TBOOLEAN:
            tv->v_type = VAR_SPECIAL;
            tv->vval.v_number = (varnumber_T) lua_toboolean(L, pos);
--- 518,542 ----
            else
                lua_pushnil(L);
            break;
+       case VAR_FUNC:
+           luaV_pushfuncref(L, tv);
+           break;
        default:
            lua_pushnil(L);
      }
  }
  
! /*
!  * Converts lua value at 'pos' to typval 'tv'.
!  * Returns OK or FAIL.
!  */
!     static int
! luaV_totypval(lua_State *L, int pos, typval_T *tv)
  {
!     int status = OK;
! 
!     switch (lua_type(L, pos))
!     {
        case LUA_TBOOLEAN:
            tv->v_type = VAR_SPECIAL;
            tv->vval.v_number = (varnumber_T) lua_toboolean(L, pos);
***************
*** 533,540 ****
            tv->vval.v_number = (varnumber_T) lua_tointeger(L, pos);
  #endif
            break;
!       case LUA_TUSERDATA: {
            void *p = lua_touserdata(L, pos);
            if (lua_getmetatable(L, pos)) /* has metatable? */
            {
                /* check list */
--- 554,563 ----
            tv->vval.v_number = (varnumber_T) lua_tointeger(L, pos);
  #endif
            break;
!       case LUA_TUSERDATA:
!       {
            void *p = lua_touserdata(L, pos);
+ 
            if (lua_getmetatable(L, pos)) /* has metatable? */
            {
                /* check list */
***************
*** 545,551 ****
                    tv->vval.v_list = *((luaV_List *) p);
                    ++tv->vval.v_list->lv_refcount;
                    lua_pop(L, 2); /* MTs */
!                   return;
                }
                /* check dict */
                luaV_getfield(L, LUAVIM_DICT);
--- 568,574 ----
                    tv->vval.v_list = *((luaV_List *) p);
                    ++tv->vval.v_list->lv_refcount;
                    lua_pop(L, 2); /* MTs */
!                   break;
                }
                /* check dict */
                luaV_getfield(L, LUAVIM_DICT);
***************
*** 555,570 ****
                    tv->vval.v_dict = *((luaV_Dict *) p);
                    ++tv->vval.v_dict->dv_refcount;
                    lua_pop(L, 3); /* MTs */
!                   return;
                }
!               lua_pop(L, 3); /* MTs */
            }
-           break;
        }
        default:
            tv->v_type = VAR_NUMBER;
            tv->vval.v_number = 0;
      }
  }
  
  /* similar to luaL_addlstring, but replaces \0 with \n if toline and
--- 578,604 ----
                    tv->vval.v_dict = *((luaV_Dict *) p);
                    ++tv->vval.v_dict->dv_refcount;
                    lua_pop(L, 3); /* MTs */
!                   break;
                }
!               /* check funcref */
!               luaV_getfield(L, LUAVIM_FUNCREF);
!               if (lua_rawequal(L, -1, -4))
!               {
!                   luaV_Funcref *f = (luaV_Funcref *) p;
!                   copy_tv(&f->tv, tv);
!                   lua_pop(L, 4); /* MTs */
!                   break;
!               }
!               lua_pop(L, 4); /* MTs */
            }
        }
+       /* FALLTHROUGH */
        default:
            tv->v_type = VAR_NUMBER;
            tv->vval.v_number = 0;
+           status = FAIL;
      }
+     return status;
  }
  
  /* similar to luaL_addlstring, but replaces \0 with \n if toline and
***************
*** 646,652 ****
  
  #define luaV_pushtype(typ,tname,luatyp) \
        static luatyp * \
!     luaV_push##tname (lua_State *L, typ *obj) \
      { \
        luatyp *o = NULL; \
        if (obj == NULL) \
--- 680,686 ----
  
  #define luaV_pushtype(typ,tname,luatyp) \
        static luatyp * \
!     luaV_push##tname(lua_State *L, typ *obj) \
      { \
        luatyp *o = NULL; \
        if (obj == NULL) \
***************
*** 766,772 ****
      else
      {
        typval_T v;
!       luaV_totypval(L, 3, &v);
        clear_tv(&li->li_tv);
        copy_tv(&v, &li->li_tv);
        clear_tv(&v);
--- 800,806 ----
      else
      {
        typval_T v;
!       luaV_checktypval(L, 3, &v, "setting list item");
        clear_tv(&li->li_tv);
        copy_tv(&v, &li->li_tv);
        clear_tv(&v);
***************
*** 783,793 ****
      if (l->lv_lock)
        luaL_error(L, "list is locked");
      lua_settop(L, 2);
!     luaV_totypval(L, 2, &v);
      if (list_append_tv(l, &v) == FAIL)
      {
        clear_tv(&v);
!       luaL_error(L, "Failed to add item to list");
      }
      clear_tv(&v);
      lua_settop(L, 1);
--- 817,827 ----
      if (l->lv_lock)
        luaL_error(L, "list is locked");
      lua_settop(L, 2);
!     luaV_checktypval(L, 2, &v, "adding list item");
      if (list_append_tv(l, &v) == FAIL)
      {
        clear_tv(&v);
!       luaL_error(L, "failed to add item to list");
      }
      clear_tv(&v);
      lua_settop(L, 1);
***************
*** 811,821 ****
            luaL_error(L, "invalid position");
      }
      lua_settop(L, 2);
!     luaV_totypval(L, 2, &v);
      if (list_insert_tv(l, &v, li) == FAIL)
      {
        clear_tv(&v);
!       luaL_error(L, "Failed to add item to list");
      }
      clear_tv(&v);
      lua_settop(L, 1);
--- 845,855 ----
            luaL_error(L, "invalid position");
      }
      lua_settop(L, 2);
!     luaV_checktypval(L, 2, &v, "inserting list item");
      if (list_insert_tv(l, &v, li) == FAIL)
      {
        clear_tv(&v);
!       luaL_error(L, "failed to add item to list");
      }
      clear_tv(&v);
      lua_settop(L, 1);
***************
*** 894,919 ****
  }
  
      static int
! luaV_dict_index (lua_State *L)
  {
      dict_T *d = luaV_unbox(L, luaV_Dict, 1);
      char_u *key = (char_u *) luaL_checkstring(L, 2);
      dictitem_T *di = dict_find(d, key, -1);
      if (di == NULL)
        lua_pushnil(L);
      else
        luaV_pushtypval(L, &di->di_tv);
      return 1;
  }
  
      static int
! luaV_dict_newindex (lua_State *L)
  {
      dict_T *d = luaV_unbox(L, luaV_Dict, 1);
      char_u *key = (char_u *) luaL_checkstring(L, 2);
      dictitem_T *di;
      if (d->dv_lock)
        luaL_error(L, "dict is locked");
      di = dict_find(d, key, -1);
      if (di == NULL) /* non-existing key? */
      {
--- 928,970 ----
  }
  
      static int
! luaV_dict_index(lua_State *L)
  {
      dict_T *d = luaV_unbox(L, luaV_Dict, 1);
      char_u *key = (char_u *) luaL_checkstring(L, 2);
      dictitem_T *di = dict_find(d, key, -1);
+ 
      if (di == NULL)
        lua_pushnil(L);
      else
+     {
        luaV_pushtypval(L, &di->di_tv);
+       if (di->di_tv.v_type == VAR_FUNC) /* funcref? */
+       {
+           luaV_Funcref *f = (luaV_Funcref *) lua_touserdata(L, -1);
+           f->self = d; /* keep "self" reference */
+           d->dv_refcount++;
+       }
+     }
      return 1;
  }
  
      static int
! luaV_dict_newindex(lua_State *L)
  {
      dict_T *d = luaV_unbox(L, luaV_Dict, 1);
      char_u *key = (char_u *) luaL_checkstring(L, 2);
      dictitem_T *di;
+     typval_T v;
      if (d->dv_lock)
        luaL_error(L, "dict is locked");
+     if (key != NULL && *key == NUL)
+       luaL_error(L, "empty key");
+     if (!lua_isnil(L, 3)) { /* read value? */
+       luaV_checktypval(L, 3, &v, "setting dict item");
+       if (d->dv_scope == VAR_DEF_SCOPE && v.v_type == VAR_FUNC)
+           luaL_error(L, "cannot assign funcref to builtin scope");
+     }
      di = dict_find(d, key, -1);
      if (di == NULL) /* non-existing key? */
      {
***************
*** 934,942 ****
        hash_remove(&d->dv_hashtab, hi);
        dictitem_free(di);
      }
!     else {
!       typval_T v;
!       luaV_totypval(L, 3, &v);
        copy_tv(&v, &di->di_tv);
        clear_tv(&v);
      }
--- 985,992 ----
        hash_remove(&d->dv_hashtab, hi);
        dictitem_free(di);
      }
!     else
!     {
        copy_tv(&v, &di->di_tv);
        clear_tv(&v);
      }
***************
*** 953,958 ****
--- 1003,1094 ----
  };
  
  
+ /* =======   Funcref type   ======= */
+ 
+     static luaV_Funcref *
+ luaV_newfuncref(lua_State *L, char_u *name)
+ {
+     luaV_Funcref *f = (luaV_Funcref *)lua_newuserdata(L, 
sizeof(luaV_Funcref));
+ 
+     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);
+     return f;
+ }
+ 
+     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;
+ }
+ 
+ 
+ luaV_type_tostring(funcref, LUAVIM_FUNCREF)
+ 
+     static int
+ luaV_funcref_gc(lua_State *L)
+ {
+     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;
+ }
+ 
+ /* equivalent to string(funcref) */
+     static int
+ luaV_funcref_len(lua_State *L)
+ {
+     luaV_Funcref *f = (luaV_Funcref *) lua_touserdata(L, 1);
+ 
+     lua_pushstring(L, (const char *) f->tv.vval.v_string);
+     return 1;
+ }
+ 
+     static int
+ 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 */
+     for (i = 0; i < n; i++) {
+       luaV_checktypval(L, i + 2, &v, "calling funcref");
+       list_append_tv(f->args.vval.v_list, &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)
+       luaL_error(L, "cannot call funcref");
+     return 1;
+ }
+ 
+ static const luaL_Reg luaV_Funcref_mt[] = {
+     {"__tostring", luaV_funcref_tostring},
+     {"__gc", luaV_funcref_gc},
+     {"__len", luaV_funcref_len},
+     {"__call", luaV_funcref_call},
+     {NULL, NULL}
+ };
+ 
+ 
  /* =======   Buffer type   ======= */
  
  luaV_newtype(buf_T, buffer, luaV_Buffer, LUAVIM_BUFFER)
***************
*** 1033,1039 ****
            curbuf = buf;
            luaL_error(L, "cannot delete line");
        }
!       else {
            deleted_lines_mark(n, 1L);
            if (b == curwin->w_buffer) /* fix cursor in current window? */
            {
--- 1169,1176 ----
            curbuf = buf;
            luaL_error(L, "cannot delete line");
        }
!       else
!       {
            deleted_lines_mark(n, 1L);
            if (b == curwin->w_buffer) /* fix cursor in current window? */
            {
***************
*** 1371,1392 ****
      static int
  luaV_list(lua_State *L)
  {
!     list_T *l = list_alloc();
      if (l == NULL)
        lua_pushnil(L);
      else
        luaV_newlist(L, l);
      return 1;
  }
  
      static int
  luaV_dict(lua_State *L)
  {
!     dict_T *d = dict_alloc();
      if (d == NULL)
        lua_pushnil(L);
      else
        luaV_newdict(L, d);
      return 1;
  }
  
--- 1508,1591 ----
      static int
  luaV_list(lua_State *L)
  {
!     list_T *l;
!     int initarg = !lua_isnoneornil(L, 1);
! 
!     if (initarg && lua_type(L, 1) != LUA_TTABLE)
!       luaL_error(L, "table expected, got %s", luaL_typename(L, 1));
!     l = list_alloc();
      if (l == NULL)
        lua_pushnil(L);
      else
+     {
        luaV_newlist(L, l);
+       if (initarg) { /* traverse table to init dict */
+           int notnil, i = 0;
+           typval_T v;
+           do {
+               lua_rawgeti(L, 1, ++i);
+               notnil = !lua_isnil(L, -1);
+               if (notnil) {
+                   luaV_checktypval(L, -1, &v, "vim.list");
+                   list_append_tv(l, &v);
+               }
+               lua_pop(L, 1); /* value */
+           } while (notnil);
+       }
+     }
      return 1;
  }
  
      static int
  luaV_dict(lua_State *L)
  {
!     dict_T *d;
!     int initarg = !lua_isnoneornil(L, 1);
! 
!     if (initarg && lua_type(L, 1) != LUA_TTABLE)
!       luaL_error(L, "table expected, got %s", luaL_typename(L, 1));
!     d = dict_alloc();
      if (d == NULL)
        lua_pushnil(L);
      else
+     {
        luaV_newdict(L, d);
+       if (initarg) /* traverse table to init dict */
+       {
+           lua_pushnil(L);
+           while (lua_next(L, 1))
+           {
+               char_u *key;
+               dictitem_T *di;
+               typval_T v;
+               lua_pushvalue(L, -2); /* dup key in case it's a number */
+               key = (char_u *) lua_tostring(L, -1);
+               if (key != NULL && *key == NUL)
+                   luaL_error(L, "table has empty key");
+               luaV_checktypval(L, -2, &v, "vim.dict"); /* value */
+               di = dictitem_alloc(key);
+               if (di == NULL || dict_add(d, di) == FAIL) {
+                   vim_free(di);
+                   lua_pushnil(L);
+                   return 1;
+               }
+               copy_tv(&v, &di->di_tv);
+               clear_tv(&v);
+               lua_pop(L, 2); /* key copy and value */
+           }
+       }
+     }
+     return 1;
+ }
+ 
+     static int
+ luaV_funcref(lua_State *L)
+ {
+     const char *name = luaL_checkstring(L, 1);
+     /* note: not checking if function exists (needs function_exists) */
+     if (name == NULL || *name == NUL || VIM_ISDIGIT(*name))
+       luaL_error(L, "invalid function name: %s", name);
+     luaV_newfuncref(L, (char_u *) name);
      return 1;
  }
  
***************
*** 1402,1408 ****
            FOR_ALL_BUFFERS(buf)
                if (buf->b_fnum == n) break;
        }
!       else { /* by name */
            size_t l;
            const char *s = lua_tolstring(L, 1, &l);
            FOR_ALL_BUFFERS(buf)
--- 1601,1608 ----
            FOR_ALL_BUFFERS(buf)
                if (buf->b_fnum == n) break;
        }
!       else // by name
!       {
            size_t l;
            const char *s = lua_tolstring(L, 1, &l);
            FOR_ALL_BUFFERS(buf)
***************
*** 1472,1477 ****
--- 1672,1683 ----
                lua_pushstring(L, "dict");
                return 1;
            }
+           luaV_getfield(L, LUAVIM_FUNCREF);
+           if (lua_rawequal(L, -1, 2))
+           {
+               lua_pushstring(L, "funcref");
+               return 1;
+           }
            luaV_getfield(L, LUAVIM_BUFFER);
            if (lua_rawequal(L, -1, 2))
            {
***************
*** 1497,1502 ****
--- 1703,1709 ----
      {"line", luaV_line},
      {"list", luaV_list},
      {"dict", luaV_dict},
+     {"funcref", luaV_funcref},
      {"buffer", luaV_buffer},
      {"window", luaV_window},
      {"open", luaV_open},
***************
*** 1537,1543 ****
        luaV_emsg(L);
        return 0;
      }
!     luaV_totypval(L, -1, rettv);
      return 0;
  }
  
--- 1744,1751 ----
        luaV_emsg(L);
        return 0;
      }
!     if (luaV_totypval(L, -1, rettv) == FAIL)
!       EMSG("luaeval: cannot convert value");
      return 0;
  }
  
***************
*** 1612,1617 ****
--- 1820,1828 ----
      luaV_newmetatable(L, LUAVIM_DICT);
      lua_pushvalue(L, 1);
      luaV_openlib(L, luaV_Dict_mt, 1);
+     luaV_newmetatable(L, LUAVIM_FUNCREF);
+     lua_pushvalue(L, 1);
+     luaV_openlib(L, luaV_Funcref_mt, 1);
      luaV_newmetatable(L, LUAVIM_BUFFER);
      lua_pushvalue(L, 1); /* cache table */
      luaV_openlib(L, luaV_Buffer_mt, 1);
*** ../vim-8.1.0133/src/testdir/test_lua.vim    2018-06-30 21:50:16.856674912 
+0200
--- src/testdir/test_lua.vim    2018-07-01 15:00:55.884371772 +0200
***************
*** 397,402 ****
--- 397,425 ----
    lua str, k, v, d = nil, nil, nil, nil
  endfunc
  
+ func Test_funcref()
+   function I(x)
+     return a:x
+   endfunction
+   let R = function('I')
+   lua i1 = vim.funcref"I"
+   lua i2 = vim.eval"R"
+   lua msg = "funcref|test|" .. (#i2(i1) == #i1(i2) and "OK" or "FAIL")
+   lua msg = vim.funcref"tr"(msg, "|", " ")
+   call assert_equal("funcref test OK", luaeval('msg'))
+ 
+   " dict funcref
+   function Mylen() dict
+     return len(self.data)
+   endfunction
+   let l = [0, 1, 2, 3]
+   let mydict = {'data': l}
+   lua d = vim.eval"mydict"
+   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'))
+ endfunc
+ 
  " Test vim.type()
  func Test_type()
    " The following values are identical to Lua's type function.
***************
*** 414,419 ****
--- 437,443 ----
    call assert_equal('buffer',   luaeval('vim.type(vim.buffer())'))
    call assert_equal('list',     luaeval('vim.type(vim.list())'))
    call assert_equal('dict',     luaeval('vim.type(vim.dict())'))
+   call assert_equal('funcref',  luaeval('vim.type(vim.funcref("Test_type"))'))
  endfunc
  
  " Test vim.open()
*** ../vim-8.1.0133/src/version.c       2018-06-30 22:40:39.097551835 +0200
--- src/version.c       2018-07-01 15:01:56.939951330 +0200
***************
*** 791,792 ****
--- 791,794 ----
  {   /* Add new patch number below this line */
+ /**/
+     134,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
162. You go outside and look for a brightness knob to turn down the sun.

 /// 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