]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.0.2019: Vim9: no support for funcrefs v9.0.2019
authorYegappan Lakshmanan <yegappan@yahoo.com>
Sat, 14 Oct 2023 09:18:50 +0000 (11:18 +0200)
committerChristian Brabandt <cb@256bit.org>
Sat, 14 Oct 2023 09:18:50 +0000 (11:18 +0200)
Problem:  Vim9: no support for funcrefs
Solution: Add support for object/class funcref members

closes: #11981 #12417 #12960 #12324 #13333

Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
15 files changed:
src/eval.c
src/evalfunc.c
src/proto/vim9class.pro
src/proto/vim9instr.pro
src/structs.h
src/testdir/test_vim9_class.vim
src/testdir/test_vim9_func.vim
src/version.c
src/vim9.h
src/vim9class.c
src/vim9compile.c
src/vim9execute.c
src/vim9expr.c
src/vim9instr.c
src/vim9type.c

index 4da6246aced1eed83c3db06eb134c994b1f79583..80ff5a3cbaf7100d7872ea104b2b0f3df1513739 100644 (file)
@@ -5256,6 +5256,7 @@ partial_free(partial_T *pt)
     }
     else
        func_ptr_unref(pt->pt_func);
+    object_unref(pt->pt_obj);
 
     // "out_up" is no longer used, decrement refcount on partial that owns it.
     partial_unref(pt->pt_outer.out_up_partial);
@@ -5578,6 +5579,7 @@ free_unref_items(int copyID)
     /*
      * PASS 2: free the items themselves.
      */
+    object_free_items(copyID);
     dict_free_items(copyID);
     list_free_items(copyID);
 
@@ -5818,6 +5820,15 @@ set_ref_in_item_partial(
        set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
     }
 
+    if (pt->pt_obj != NULL)
+    {
+       typval_T objtv;
+
+       objtv.v_type = VAR_OBJECT;
+       objtv.vval.v_object = pt->pt_obj;
+       set_ref_in_item(&objtv, copyID, ht_stack, list_stack);
+    }
+
     for (int i = 0; i < pt->pt_argc; ++i)
        abort = abort || set_ref_in_item(&pt->pt_argv[i], copyID,
                ht_stack, list_stack);
index 5fccf5270c4a47d37d67245e3f1e66bc3107c375..85c64a23a28ff1b8f486734032163f2600d3a5ed 100644 (file)
@@ -4801,6 +4801,9 @@ common_function(typval_T *argvars, typval_T *rettv, int is_funcref)
                    pt->pt_auto = arg_pt->pt_auto;
                    if (pt->pt_dict != NULL)
                        ++pt->pt_dict->dv_refcount;
+                   pt->pt_obj = arg_pt->pt_obj;
+                   if (pt->pt_obj != NULL)
+                       ++pt->pt_obj->obj_refcount;
                }
 
                pt->pt_refcount = 1;
index 362e2cac5e269dce391d68d092ed922b3f5a071f..62d1b7d0f31e82328f93aa078b34b11bc7dea02d 100644 (file)
@@ -17,13 +17,14 @@ void emsg_var_cl_define(char *msg, char_u *name, size_t len, class_T *cl);
 ufunc_T *method_lookup(class_T *cl, vartype_T v_type, char_u *name, size_t namelen, int *idx);
 int inside_class(cctx_T *cctx_arg, class_T *cl);
 void copy_object(typval_T *from, typval_T *to);
-void object_unref(object_T *obj);
 void copy_class(typval_T *from, typval_T *to);
 void class_unref(class_T *cl);
 int class_free_nonref(int copyID);
 int set_ref_in_classes(int copyID);
 void object_created(object_T *obj);
+void object_unref(object_T *obj);
 int object_free_nonref(int copyID);
+void object_free_items(int copyID);
 void method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len);
 void member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len);
 void f_instanceof(typval_T *argvars, typval_T *rettv);
index 898eb9751c36c91fb5a64bf12c11b49abfaf430c..a236b7561219ba4b0b36155861afa4ede3a67cc1 100644 (file)
@@ -45,7 +45,7 @@ int generate_OLDSCRIPT(cctx_T *cctx, isntype_T isn_type, char_u *name, int sid,
 int generate_VIM9SCRIPT(cctx_T *cctx, isntype_T isn_type, int sid, int idx, type_T *type);
 int generate_NEWLIST(cctx_T *cctx, int count, int use_null);
 int generate_NEWDICT(cctx_T *cctx, int count, int use_null);
-int generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, class_T *cl, int fi, int *isn_idx);
+int generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, class_T *cl, int object_method, int fi, int *isn_idx);
 int generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name);
 int generate_DEF(cctx_T *cctx, char_u *name, size_t len);
 int generate_JUMP(cctx_T *cctx, jumpwhen_T when, int where);
index 51318587419a3028310e289cf5e976b4924b76bd..a221a0406dc7a561e4e67536d8cf515fbfa6a31d 100644 (file)
@@ -2316,6 +2316,7 @@ struct partial_S
 
     int                pt_copyID;      // funcstack may contain pointer to partial
     dict_T     *pt_dict;       // dict for "self"
+    object_T   *pt_obj;        // object method
 };
 
 typedef struct {
index 1aa9de7b99ac6ad2ac06dda14888d8e67ed97003..3af9a1f87a0dadc96754eaa01affe408c08063c9 100644 (file)
@@ -7477,4 +7477,480 @@ def Test_op_and_assignment()
   v9.CheckSourceSuccess(lines)
 enddef
 
+" Test for using an object method as a funcref
+def Test_object_funcref()
+  # Using object method funcref from a def function
+  var lines =<< trim END
+    vim9script
+    class A
+      def Foo(): list<number>
+        return [3, 2, 1]
+      enddef
+    endclass
+    def Bar()
+      var a = A.new()
+      var Fn = a.Foo
+      assert_equal([3, 2, 1], Fn())
+    enddef
+    Bar()
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Using object method funcref at the script level
+  lines =<< trim END
+    vim9script
+    class A
+      def Foo(): dict<number>
+        return {a: 1, b: 2}
+      enddef
+    endclass
+    var a = A.new()
+    var Fn = a.Foo
+    assert_equal({a: 1, b: 2}, Fn())
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Using object method funcref from another object method
+  lines =<< trim END
+    vim9script
+    class A
+      def Foo(): list<number>
+        return [3, 2, 1]
+      enddef
+      def Bar()
+        var Fn = this.Foo
+        assert_equal([3, 2, 1], Fn())
+      enddef
+    endclass
+    var a = A.new()
+    a.Bar()
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Using function() to get a object method funcref
+  lines =<< trim END
+    vim9script
+    class A
+      def Foo(l: list<any>): list<any>
+        return l
+      enddef
+    endclass
+    var a = A.new()
+    var Fn = function(a.Foo, [[{a: 1, b: 2}, [3, 4]]])
+    assert_equal([{a: 1, b: 2}, [3, 4]], Fn())
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Use an object method with a function returning a funcref and then call the
+  # funcref.
+  lines =<< trim END
+    vim9script
+
+    def Map(F: func(number): number): func(number): number
+      return (n: number) => F(n)
+    enddef
+
+    class Math
+      def Double(n: number): number
+        return 2 * n
+      enddef
+    endclass
+
+    const math = Math.new()
+    assert_equal(48, Map(math.Double)(24))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Try using a private object method funcref from a def function
+  lines =<< trim END
+    vim9script
+    class A
+      def _Foo()
+      enddef
+    endclass
+    def Bar()
+      var a = A.new()
+      var Fn = a._Foo
+    enddef
+    Bar()
+  END
+  v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo', 2)
+
+  # Try using a private object method funcref at the script level
+  lines =<< trim END
+    vim9script
+    class A
+      def _Foo()
+      enddef
+    endclass
+    var a = A.new()
+    var Fn = a._Foo
+  END
+  v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo', 7)
+
+  # Using a private object method funcref from another object method
+  lines =<< trim END
+    vim9script
+    class A
+      def _Foo(): list<number>
+        return [3, 2, 1]
+      enddef
+      def Bar()
+        var Fn = this._Foo
+        assert_equal([3, 2, 1], Fn())
+      enddef
+    endclass
+    var a = A.new()
+    a.Bar()
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using a class method as a funcref
+def Test_class_funcref()
+  # Using class method funcref in a def function
+  var lines =<< trim END
+    vim9script
+    class A
+      static def Foo(): list<number>
+        return [3, 2, 1]
+      enddef
+    endclass
+    def Bar()
+      var Fn = A.Foo
+      assert_equal([3, 2, 1], Fn())
+    enddef
+    Bar()
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Using class method funcref at script level
+  lines =<< trim END
+    vim9script
+    class A
+      static def Foo(): dict<number>
+        return {a: 1, b: 2}
+      enddef
+    endclass
+    var Fn = A.Foo
+    assert_equal({a: 1, b: 2}, Fn())
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Using function() to get a class method funcref
+  lines =<< trim END
+    vim9script
+    class A
+      static def Foo(l: list<any>): list<any>
+        return l
+      enddef
+    endclass
+    var Fn = function(A.Foo, [[{a: 1, b: 2}, [3, 4]]])
+    assert_equal([{a: 1, b: 2}, [3, 4]], Fn())
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Using a class method funcref from another class method
+  lines =<< trim END
+    vim9script
+    class A
+      static def Foo(): list<number>
+        return [3, 2, 1]
+      enddef
+      static def Bar()
+        var Fn = Foo
+        assert_equal([3, 2, 1], Fn())
+      enddef
+    endclass
+    A.Bar()
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Use a class method with a function returning a funcref and then call the
+  # funcref.
+  lines =<< trim END
+    vim9script
+
+    def Map(F: func(number): number): func(number): number
+      return (n: number) => F(n)
+    enddef
+
+    class Math
+      static def StaticDouble(n: number): number
+        return 2 * n
+      enddef
+    endclass
+
+    assert_equal(48, Map(Math.StaticDouble)(24))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Try using a private class method funcref in a def function
+  lines =<< trim END
+    vim9script
+    class A
+      static def _Foo()
+      enddef
+    endclass
+    def Bar()
+      var Fn = A._Foo
+    enddef
+    Bar()
+  END
+  v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo', 1)
+
+  # Try using a private class method funcref at script level
+  lines =<< trim END
+    vim9script
+    class A
+      static def _Foo()
+      enddef
+    endclass
+    var Fn = A._Foo
+  END
+  v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo', 6)
+
+  # Using a private class method funcref from another class method
+  lines =<< trim END
+    vim9script
+    class A
+      static def _Foo(): list<number>
+        return [3, 2, 1]
+      enddef
+      static def Bar()
+        var Fn = _Foo
+        assert_equal([3, 2, 1], Fn())
+      enddef
+    endclass
+    A.Bar()
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using an object member as a funcref
+def Test_object_member_funcref()
+  # Using a funcref object variable in an object method
+  var lines =<< trim END
+    vim9script
+    def Foo(n: number): number
+      return n * 10
+    enddef
+
+    class A
+      this.Cb: func(number): number = Foo
+      def Bar()
+        assert_equal(200, this.Cb(20))
+      enddef
+    endclass
+
+    var a = A.new()
+    a.Bar()
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Using a funcref object variable in a def method
+  lines =<< trim END
+    vim9script
+    def Foo(n: number): number
+      return n * 10
+    enddef
+
+    class A
+      this.Cb: func(number): number = Foo
+    endclass
+
+    def Bar()
+      var a = A.new()
+      assert_equal(200, a.Cb(20))
+    enddef
+    Bar()
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Using a funcref object variable at script level
+  lines =<< trim END
+    vim9script
+    def Foo(n: number): number
+      return n * 10
+    enddef
+
+    class A
+      this.Cb: func(number): number = Foo
+    endclass
+
+    var a = A.new()
+    assert_equal(200, a.Cb(20))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Using a funcref object variable pointing to an object method in an object
+  # method.
+  lines =<< trim END
+    vim9script
+    class A
+      this.Cb: func(number): number = this.Foo
+      def Foo(n: number): number
+        return n * 10
+      enddef
+      def Bar()
+        assert_equal(200, this.Cb(20))
+      enddef
+    endclass
+
+    var a = A.new()
+    a.Bar()
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Using a funcref object variable pointing to an object method in a def
+  # method.
+  lines =<< trim END
+    vim9script
+    class A
+      this.Cb: func(number): number = this.Foo
+      def Foo(n: number): number
+        return n * 10
+      enddef
+    endclass
+
+    def Bar()
+      var a = A.new()
+      assert_equal(200, a.Cb(20))
+    enddef
+    Bar()
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Using a funcref object variable pointing to an object method at script
+  # level.
+  lines =<< trim END
+    vim9script
+    class A
+      this.Cb = this.Foo
+      def Foo(n: number): number
+        return n * 10
+      enddef
+    endclass
+
+    var a = A.new()
+    assert_equal(200, a.Cb(20))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using a class member as a funcref
+def Test_class_member_funcref()
+  # Using a funcref class variable in a class method
+  var lines =<< trim END
+    vim9script
+    def Foo(n: number): number
+      return n * 10
+    enddef
+
+    class A
+      static Cb = Foo
+      static def Bar()
+        assert_equal(200, Cb(20))
+      enddef
+    endclass
+
+    A.Bar()
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Using a funcref class variable in a def method
+  lines =<< trim END
+    vim9script
+    def Foo(n: number): number
+      return n * 10
+    enddef
+
+    class A
+      public static Cb = Foo
+    endclass
+
+    def Bar()
+      assert_equal(200, A.Cb(20))
+    enddef
+    Bar()
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Using a funcref class variable at script level
+  lines =<< trim END
+    vim9script
+    def Foo(n: number): number
+      return n * 10
+    enddef
+
+    class A
+      public static Cb = Foo
+    endclass
+
+    assert_equal(200, A.Cb(20))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Using a funcref class variable pointing to a class method in a class
+  # method.
+  lines =<< trim END
+    vim9script
+    class A
+      static Cb: func(number): number
+      static def Foo(n: number): number
+        return n * 10
+      enddef
+      static def Init()
+        Cb = Foo
+      enddef
+      static def Bar()
+        assert_equal(200, Cb(20))
+      enddef
+    endclass
+
+    A.Init()
+    A.Bar()
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Using a funcref class variable pointing to a class method in a def method.
+  lines =<< trim END
+    vim9script
+    class A
+      static Cb: func(number): number
+      static def Foo(n: number): number
+        return n * 10
+      enddef
+      static def Init()
+        Cb = Foo
+      enddef
+    endclass
+
+    def Bar()
+      A.Init()
+      assert_equal(200, A.Cb(20))
+    enddef
+    Bar()
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Using a funcref class variable pointing to a class method at script level.
+  lines =<< trim END
+    vim9script
+    class A
+      static Cb: func(number): number
+      static def Foo(n: number): number
+        return n * 10
+      enddef
+      static def Init()
+        Cb = Foo
+      enddef
+    endclass
+
+    A.Init()
+    assert_equal(200, A.Cb(20))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
index 852f3a7edec49b7277bb5141ef7481cfcbeb7875..597a10c581882c180a1f96a5ae1000680c2edeb0 100644 (file)
@@ -3663,6 +3663,17 @@ def Test_partial_call()
       const Call = Foo(Expr)
   END
   v9.CheckScriptFailure(lines, 'E1031:')
+
+  # Test for calling a partial that takes a single argument.
+  # This used to produce a "E340: Internal error" message.
+  lines =<< trim END
+      def Foo(n: number): number
+        return n * 2
+      enddef
+      var Fn = function(Foo, [10])
+      assert_equal(20, Fn())
+  END
+  v9.CheckDefAndScriptSuccess(lines)
 enddef
 
 def Test_partial_double_nested()
index d05016389ad7426e551fb494ee29dcb766796299..9147441a40fccd4b698b5e9fd9f9311a431a30c0 100644 (file)
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2019,
 /**/
     2018,
 /**/
index 785486963a6d89989187d169c86b294a8857df83..6bfbd9ee8f69ae64831b64867be3bf6857225649 100644 (file)
@@ -383,6 +383,7 @@ typedef struct {
     char_u       *fre_func_name;       // function name for legacy function
     loopvarinfo_T fre_loopvar_info;    // info about variables inside loops
     class_T      *fre_class;           // class for a method
+    int                  fre_object_method;    // class or object method
     int                  fre_method_idx;       // method index on "fre_class"
 } funcref_extra_T;
 
index 5dda700fa3e40837ceb47ef28cec5ed267ffec13..bfa6149602d11d3faac5730c22db5fc74c383c1d 100644 (file)
@@ -2167,15 +2167,35 @@ call_oc_method(
     ufunc_T    *fp;
     typval_T   argvars[MAX_FUNC_ARGS + 1];
     int                argcount = 0;
+    ocmember_T *ocm = NULL;
+    int                m_idx;
 
     fp = method_lookup(cl, rettv->v_type, name, len, NULL);
     if (fp == NULL)
     {
-       method_not_found_msg(cl, rettv->v_type, name, len);
-       return FAIL;
+       // could be an object or class funcref variable
+       ocm = member_lookup(cl, rettv->v_type, name, len, &m_idx);
+       if (ocm == NULL || ocm->ocm_type->tt_type != VAR_FUNC)
+       {
+           method_not_found_msg(cl, rettv->v_type, name, len);
+           return FAIL;
+       }
+
+       if (rettv->v_type == VAR_OBJECT)
+       {
+           // funcref object variable
+           object_T    *obj = rettv->vval.v_object;
+           typval_T    *tv = (typval_T *)(obj + 1) + m_idx;
+           copy_tv(tv, rettv);
+       }
+       else
+           // funcref class variable
+           copy_tv(&cl->class_members_tv[m_idx], rettv);
+       *arg = name_end;
+       return OK;
     }
 
-    if (*fp->uf_name == '_')
+    if (ocm == NULL && *fp->uf_name == '_')
     {
        // Cannot access a private method outside of a class
        semsg(_(e_cannot_access_private_method_str), fp->uf_name);
@@ -2288,6 +2308,37 @@ class_object_index(
            return OK;
        }
 
+       // could be a class method or an object method
+       int     fidx;
+       ufunc_T *fp = method_lookup(cl, rettv->v_type, name, len, &fidx);
+       if (fp != NULL)
+       {
+           // Private methods are not accessible outside the class
+           if (*name == '_')
+           {
+               semsg(_(e_cannot_access_private_method_str), fp->uf_name);
+               return FAIL;
+           }
+
+           partial_T   *pt = ALLOC_CLEAR_ONE(partial_T);
+           if (pt == NULL)
+               return FAIL;
+
+           pt->pt_refcount = 1;
+           if (is_object)
+           {
+               pt->pt_obj = rettv->vval.v_object;
+               ++pt->pt_obj->obj_refcount;
+           }
+           pt->pt_auto = TRUE;
+           pt->pt_func = fp;
+           func_ptr_ref(pt->pt_func);
+           rettv->v_type = VAR_PARTIAL;
+           rettv->vval.v_partial = pt;
+           *arg = name_end;
+           return OK;
+       }
+
        if (did_emsg == did_emsg_save)
            member_not_found_msg(cl, is_object, name, len);
     }
@@ -2774,8 +2825,6 @@ object_created(object_T *obj)
     first_object = obj;
 }
 
-static object_T        *next_nonref_obj = NULL;
-
 /*
  * Call this function when an object has been cleared and is about to be freed.
  * It is removed from the list headed by "first_object".
@@ -2789,30 +2838,35 @@ object_cleared(object_T *obj)
        obj->obj_prev_used->obj_next_used = obj->obj_next_used;
     else if (first_object == obj)
        first_object = obj->obj_next_used;
-
-    // update the next object to check if needed
-    if (obj == next_nonref_obj)
-       next_nonref_obj = obj->obj_next_used;
 }
 
 /*
- * Free an object.
+ * Free the contents of an object ignoring the reference count.
  */
     static void
-object_clear(object_T *obj)
+object_free_contents(object_T *obj)
 {
-    // Avoid a recursive call, it can happen if "obj" has a circular reference.
-    obj->obj_refcount = INT_MAX;
-
     class_T *cl = obj->obj_class;
 
     if (!cl)
        return;
 
+    // Avoid a recursive call, it can happen if "obj" has a circular reference.
+    obj->obj_refcount = INT_MAX;
+
     // the member values are just after the object structure
     typval_T *tv = (typval_T *)(obj + 1);
     for (int i = 0; i < cl->class_obj_member_count; ++i)
        clear_tv(tv + i);
+}
+
+    static void
+object_free_object(object_T *obj)
+{
+    class_T *cl = obj->obj_class;
+
+    if (!cl)
+       return;
 
     // Remove from the list headed by "first_object".
     object_cleared(obj);
@@ -2821,6 +2875,16 @@ object_clear(object_T *obj)
     class_unref(cl);
 }
 
+    static void
+object_free(object_T *obj)
+{
+    if (in_free_unref_items)
+       return;
+
+    object_free_contents(obj);
+    object_free_object(obj);
+}
+
 /*
  * Unreference an object.
  */
@@ -2828,7 +2892,7 @@ object_clear(object_T *obj)
 object_unref(object_T *obj)
 {
     if (obj != NULL && --obj->obj_refcount <= 0)
-       object_clear(obj);
+       object_free(obj);
 }
 
 /*
@@ -2839,21 +2903,32 @@ object_free_nonref(int copyID)
 {
     int                did_free = FALSE;
 
-    for (object_T *obj = first_object; obj != NULL; obj = next_nonref_obj)
+    for (object_T *obj = first_object; obj != NULL; obj = obj->obj_next_used)
     {
-       next_nonref_obj = obj->obj_next_used;
        if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
        {
-           // Free the object and items it contains.
-           object_clear(obj);
+           // Free the object contents.  Object itself will be freed later.
+           object_free_contents(obj);
            did_free = TRUE;
        }
     }
 
-    next_nonref_obj = NULL;
     return did_free;
 }
 
+    void
+object_free_items(int copyID)
+{
+    object_T   *obj_next;
+
+    for (object_T *obj = first_object; obj != NULL; obj = obj_next)
+    {
+       obj_next = obj->obj_next_used;
+       if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
+           object_free_object(obj);
+    }
+}
+
 /*
  * Output message which takes a variable name and the class that defines it.
  * "cl" is that class where the name was found. Search "cl"'s hierarchy to
index 544ad17e101f29dc9655bb85c5430d328fc997dc..03e79f5655df65346c4e057b7169f90014f192e4 100644 (file)
@@ -1148,7 +1148,7 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free)
                                            ASSIGN_CONST, ufunc->uf_func_type);
        if (lvar == NULL)
            goto theend;
-       if (generate_FUNCREF(cctx, ufunc, NULL, 0, &funcref_isn_idx) == FAIL)
+       if (generate_FUNCREF(cctx, ufunc, NULL, FALSE, 0, &funcref_isn_idx) == FAIL)
            goto theend;
        r = generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL);
     }
index a89bcd19b4a516c3f5b0eef0aeff217eb486fa86..d8087bf08dc877150208918f8594c9659549804e 100644 (file)
@@ -616,6 +616,12 @@ call_dfunc(
     // the first local variable.
     if (IS_OBJECT_METHOD(ufunc))
     {
+       if (obj->v_type != VAR_OBJECT)
+       {
+           semsg(_(e_internal_error_str), "type in stack is not an object");
+           return FAIL;
+       }
+
        *STACK_TV_VAR(0) = *obj;
        obj->v_type = VAR_UNKNOWN;
     }
@@ -1497,6 +1503,23 @@ call_partial(
        partial_T   *pt = tv->vval.v_partial;
        int         i;
 
+       if (pt->pt_obj != NULL)
+       {
+           // partial with an object method.  Push the object before the
+           // function arguments.
+           if (GA_GROW_FAILS(&ectx->ec_stack, 1))
+               return FAIL;
+           for (i = 1; i <= argcount; ++i)
+               *STACK_TV_BOT(-i + 1) = *STACK_TV_BOT(-i);
+
+           typval_T *obj_tv = STACK_TV_BOT(-argcount);
+           obj_tv->v_type = VAR_OBJECT;
+           obj_tv->v_lock = 0;
+           obj_tv->vval.v_object = pt->pt_obj;
+           ++pt->pt_obj->obj_refcount;
+           ++ectx->ec_stack.ga_len;
+       }
+
        if (pt->pt_argc > 0)
        {
            // Make space for arguments from the partial, shift the "argcount"
@@ -4447,20 +4470,44 @@ exec_instructions(ectx_T *ectx)
                    }
                    if (extra != NULL && extra->fre_class != NULL)
                    {
-                       tv = STACK_TV_BOT(-1);
-                       if (tv->v_type != VAR_OBJECT)
+                       class_T *cl;
+                       if (extra->fre_object_method)
                        {
-                           object_required_error(tv);
-                           vim_free(pt);
-                           goto on_error;
+                           tv = STACK_TV_BOT(-1);
+                           if (tv->v_type != VAR_OBJECT)
+                           {
+                               object_required_error(tv);
+                               vim_free(pt);
+                               goto on_error;
+                           }
+
+                           object_T *obj = tv->vval.v_object;
+                           cl = obj->obj_class;
+                           // drop the value from the stack
+                           clear_tv(tv);
+                           --ectx->ec_stack.ga_len;
+
+                           pt->pt_obj = obj;
+                           ++obj->obj_refcount;
                        }
-                       object_T *obj = tv->vval.v_object;
-                       class_T *cl = obj->obj_class;
+                       else
+                           cl = extra->fre_class;
 
-                       // convert the interface index to the object index
-                       int idx = object_index_from_itf_index(extra->fre_class,
-                                             TRUE, extra->fre_method_idx, cl);
-                       ufunc = cl->class_obj_methods[idx];
+                       if (extra->fre_object_method)
+                       {
+                           // object method
+                           // convert the interface index to the object index
+                           int idx =
+                               object_index_from_itf_index(extra->fre_class,
+                                       TRUE, extra->fre_method_idx, cl);
+                           ufunc = cl->class_obj_methods[idx];
+                       }
+                       else
+                       {
+                           // class method
+                           ufunc =
+                               cl->class_class_functions[extra->fre_method_idx];
+                       }
                    }
                    else if (extra == NULL || extra->fre_func_name == NULL)
                    {
index c15021e82ac78039601ce31bef5c4cf376ff847e..c91ca9325bcde736b3f8c1e66fe10edcafe7d9a4 100644 (file)
@@ -281,6 +281,8 @@ inside_class_hierarchy(cctx_T *cctx_arg, class_T *cl)
     static int
 compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
 {
+    int m_idx;
+
     if (VIM_ISWHITE((*arg)[1]))
     {
        semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
@@ -365,17 +367,34 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
                break;
            }
        }
+       ocmember_T  *ocm = NULL;
        if (ufunc == NULL)
        {
-           method_not_found_msg(cl, type->tt_type, name, len);
-           return FAIL;
+           // could be a funcref in a member variable
+           ocm = member_lookup(cl, type->tt_type, name, len, &m_idx);
+           if (ocm == NULL || ocm->ocm_type->tt_type != VAR_FUNC)
+           {
+               method_not_found_msg(cl, type->tt_type, name, len);
+               return FAIL;
+           }
+           if (type->tt_type == VAR_CLASS)
+           {
+               if (generate_CLASSMEMBER(cctx, TRUE, cl, m_idx) == FAIL)
+                   return FAIL;
+           }
+           else
+           {
+               if (generate_GET_OBJ_MEMBER(cctx, m_idx, ocm->ocm_type) ==
+                                                                       FAIL)
+                   return FAIL;
+           }
        }
 
        // A private object method can be used only inside the class where it
        // is defined or in one of the child classes.
        // A private class method can be used only in the class where it is
        // defined.
-       if (*ufunc->uf_name == '_' &&
+       if (ocm == NULL && *ufunc->uf_name == '_' &&
                ((type->tt_type == VAR_OBJECT
                  && !inside_class_hierarchy(cctx, cl))
                 || (type->tt_type == VAR_CLASS
@@ -393,6 +412,8 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
        if (compile_arguments(arg, cctx, &argcount, CA_NOT_SPECIAL) == FAIL)
            return FAIL;
 
+       if (ocm != NULL)
+           return generate_PCALL(cctx, argcount, name, ocm->ocm_type, TRUE);
        if (type->tt_type == VAR_OBJECT
                     && (cl->class_flags & (CLASS_INTERFACE | CLASS_EXTENDED)))
            return generate_CALL(cctx, ufunc, cl, fi, argcount);
@@ -401,7 +422,6 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
 
     if (type->tt_type == VAR_OBJECT)
     {
-        int m_idx;
         ocmember_T *m = object_member_lookup(cl, name, len, &m_idx);
        if (m_idx >= 0)
        {
@@ -418,15 +438,21 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
            return generate_GET_OBJ_MEMBER(cctx, m_idx, m->ocm_type);
        }
 
-       // Could be a function reference: "obj.Func".
+       // Could be an object method reference: "obj.Func".
        m_idx = object_method_idx(cl, name, len);
        if (m_idx >= 0)
        {
            ufunc_T *fp = cl->class_obj_methods[m_idx];
-           if (type->tt_type == VAR_OBJECT
-                   && (cl->class_flags & (CLASS_INTERFACE | CLASS_EXTENDED)))
-               return generate_FUNCREF(cctx, fp, cl, m_idx, NULL);
-           return generate_FUNCREF(cctx, fp, NULL, 0, NULL);
+           // Private methods are not accessible outside the class
+           if (*name == '_' && !inside_class(cctx, cl))
+           {
+               semsg(_(e_cannot_access_private_method_str), fp->uf_name);
+               return FAIL;
+           }
+           *arg = name_end;
+           if (type->tt_type == VAR_OBJECT)
+               return generate_FUNCREF(cctx, fp, cl, TRUE, m_idx, NULL);
+           return generate_FUNCREF(cctx, fp, NULL, FALSE, 0, NULL);
        }
 
        member_not_found_msg(cl, VAR_OBJECT, name, len);
@@ -451,6 +477,24 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
            *arg = name_end;
            return generate_CLASSMEMBER(cctx, TRUE, cl, idx);
        }
+
+       // Could be a class method reference: "class.Func".
+       m_idx = class_method_idx(cl, name, len);
+       if (m_idx >= 0)
+       {
+           ufunc_T *fp = cl->class_class_functions[m_idx];
+           // Private methods are not accessible outside the class
+           if (*name == '_' && !inside_class(cctx, cl))
+           {
+               semsg(_(e_cannot_access_private_method_str), fp->uf_name);
+               return FAIL;
+           }
+           *arg = name_end;
+           if (type->tt_type == VAR_CLASS)
+               return generate_FUNCREF(cctx, fp, cl, FALSE, m_idx, NULL);
+           return generate_FUNCREF(cctx, fp, NULL, FALSE, 0, NULL);
+       }
+
        member_not_found_msg(cl, VAR_CLASS, name, len);
     }
 
@@ -716,6 +760,7 @@ compile_load(
     {
        size_t      len = end - *arg;
        int         idx;
+       int         method_idx;
        int         gen_load = FALSE;
        int         gen_load_outer = 0;
        int         outer_loop_depth = -1;
@@ -764,13 +809,27 @@ compile_load(
                else
                    gen_load = TRUE;
            }
-           else if ((idx = cctx_class_member_idx(cctx, *arg, len, &cl)) >= 0)
+           else if (cctx->ctx_ufunc->uf_defclass != NULL &&
+                   (((idx =
+                      cctx_class_member_idx(cctx, *arg, len, &cl)) >= 0)
+                    || ((method_idx =
+                            cctx_class_method_idx(cctx, *arg, len, &cl)) >= 0)))
            {
-               // Referencing a class variable without the class name.
-               // A class variable can be referenced without the class name
-               // only in the class where the function is defined.
+               // Referencing a class variable or method without the class
+               // name.  A class variable or method can be referenced without
+               // the class name only in the class where the function is
+               // defined.
                if (cctx->ctx_ufunc->uf_defclass == cl)
-                   res = generate_CLASSMEMBER(cctx, TRUE, cl, idx);
+               {
+                   if (idx >= 0)
+                       res = generate_CLASSMEMBER(cctx, TRUE, cl, idx);
+                   else
+                   {
+                       ufunc_T *fp = cl->class_class_functions[method_idx];
+                       res = generate_FUNCREF(cctx, fp, cl, FALSE, method_idx,
+                                                                       NULL);
+                   }
+               }
                else
                {
                    semsg(_(e_class_variable_str_accessible_only_inside_class_str),
@@ -1387,7 +1446,7 @@ compile_lambda(char_u **arg, cctx_T *cctx)
        // The function reference count will be 1.  When the ISN_FUNCREF
        // instruction is deleted the reference count is decremented and the
        // function is freed.
-       return generate_FUNCREF(cctx, ufunc, NULL, 0, NULL);
+       return generate_FUNCREF(cctx, ufunc, NULL, FALSE, 0, NULL);
     }
 
     func_ptr_unref(ufunc);
index 6de29b617f0c27a6df7799f3844ad179841eae84..f7b074c79abfecf87be40e25e55d0c52a3cd379d 100644 (file)
@@ -1384,6 +1384,7 @@ generate_FUNCREF(
        cctx_T      *cctx,
        ufunc_T     *ufunc,
        class_T     *cl,
+       int         object_method,
        int         fi,
        int         *isn_idx)
 {
@@ -1412,6 +1413,7 @@ generate_FUNCREF(
        {
            extra->fre_class = cl;
            ++cl->class_refcount;
+           extra->fre_object_method = object_method;
            extra->fre_method_idx = fi;
        }
     }
index 338aee14a731c028dc03b423b2e2ee66d27445cd..00ee76b487c6d47ad0eaaa521091c99001fefd5c 100644 (file)
@@ -144,7 +144,7 @@ alloc_type(type_T *type)
     if (ret->tt_member != NULL)
        ret->tt_member = alloc_type(ret->tt_member);
 
-    if (type->tt_args != NULL)
+    if (type->tt_argcount > 0 && type->tt_args != NULL)
     {
        int i;
 
@@ -153,6 +153,8 @@ alloc_type(type_T *type)
            for (i = 0; i < type->tt_argcount; ++i)
                ret->tt_args[i] = alloc_type(type->tt_args[i]);
     }
+    else
+       ret->tt_args = NULL;
 
     return ret;
 }