]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.0.1123: class function not implemented yet v9.0.1123
authorBram Moolenaar <Bram@vim.org>
Sun, 1 Jan 2023 12:58:33 +0000 (12:58 +0000)
committerBram Moolenaar <Bram@vim.org>
Sun, 1 Jan 2023 12:58:33 +0000 (12:58 +0000)
Problem:    Class function not implemented yet.
Solution:   Implement defining and calling a class function.

src/proto/vim9class.pro
src/structs.h
src/testdir/test_vim9_class.vim
src/version.c
src/vim9class.c
src/vim9expr.c

index 8ae20eba81d81d3c99fa6d2c4172bcd36a44b0ce..edbddd267bbba2a4094808871ce3dbfef716b45e 100644 (file)
@@ -6,6 +6,7 @@ void ex_enum(exarg_T *eap);
 void ex_type(exarg_T *eap);
 int class_object_index(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose);
 ufunc_T *find_class_func(char_u **arg);
+int class_member_exists(char_u *name, class_T **cl_ret, int *idx_ret, cctx_T *cctx);
 void copy_object(typval_T *from, typval_T *to);
 void object_unref(object_T *obj);
 void copy_class(typval_T *from, typval_T *to);
index 45c821199dff3aca57cade8b9ef1830ef159a5e0..3bc92788b7934f2ba94dc17695d558a8422538ea 100644 (file)
@@ -1493,9 +1493,9 @@ struct class_S
     ocmember_T *class_class_members;   // allocated
     typval_T   *class_members_tv;      // allocated array of class member vals
 
-    // class methods: "static def SomeMethod()"
-    int                class_class_method_count;
-    ufunc_T    **class_class_methods;  // allocated
+    // class functions: "static def SomeMethod()"
+    int                class_class_function_count;
+    ufunc_T    **class_class_functions;        // allocated
 
     // object members: "this.varname"
     int                class_obj_member_count;
index 964f78e42aa83716022316e25d75aee351af5eef..9dd6f83942fb22dbf0a61a4ac4dff832070a0361 100644 (file)
@@ -377,7 +377,7 @@ def Test_class_member_access()
          static _secret = 7
          public static  anybody = 42
 
-         def AddToCounter(nr: number)
+         static def AddToCounter(nr: number)
            counter += nr
          enddef
       endclass
@@ -403,6 +403,32 @@ def Test_class_member_access()
   v9.CheckScriptSuccess(lines)
 enddef
 
+def Test_class_function()
+  var lines =<< trim END
+      vim9script
+      class Value
+        this.value = 0
+        static objects = 0
+
+        def new(v: number)
+          this.value = v
+          ++objects
+        enddef
+
+        static def GetCount(): number
+          return objects
+        enddef
+      endclass
+
+      assert_equal(0, Value.GetCount())
+      var v1 = Value.new(2)
+      assert_equal(1, Value.GetCount())
+      var v2 = Value.new(7)
+      assert_equal(2, Value.GetCount())
+  END
+  v9.CheckScriptSuccess(lines)
+enddef
+
 def Test_class_object_to_string()
   var lines =<< trim END
       vim9script
index 84fe1ba38eebc59490093a56821ba11cbfee4da3..531d7bc3f0e86f8d65e0ffc04f2902c59b2d05fa 100644 (file)
@@ -695,6 +695,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1123,
 /**/
     1122,
 /**/
index 2e1cac0c441eecdcffbb65eb74852c3ad66eca45..b1de90b8b7a3e3c81c9270c9db4e9382a716b3ac 100644 (file)
@@ -223,9 +223,9 @@ ex_class(exarg_T *eap)
     garray_T classmembers;
     ga_init2(&classmembers, sizeof(ocmember_T), 10);
 
-    // Growarray with object methods declared in the class.
-    garray_T classmethods;
-    ga_init2(&classmethods, sizeof(ufunc_T *), 10);
+    // Growarray with functions declared in the class.
+    garray_T classfunctions;
+    ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
 
     // Growarray with object members declared in the class.
     garray_T objmembers;
@@ -288,6 +288,19 @@ ex_class(exarg_T *eap)
            }
        }
 
+       int has_static = FALSE;
+       char_u *ps = p;
+       if (checkforcmd(&p, "static", 4))
+       {
+           if (STRNCMP(ps, "static", 6) != 0)
+           {
+               semsg(_(e_command_cannot_be_shortened_str), ps);
+               break;
+           }
+           has_static = TRUE;
+           p = skipwhite(ps + 6);
+       }
+
        // object members (public, read access, private):
        //      "this._varname"
        //      "this.varname"
@@ -314,47 +327,15 @@ ex_class(exarg_T *eap)
            }
        }
 
-       // class members and methods
-       else if (checkforcmd(&p, "static", 6))
-       {
-           p = skipwhite(p);
-           if (checkforcmd(&p, "def", 3))
-           {
-               // TODO: class method
-               //        static def someMethod()
-               //        enddef
-               //        static def <Tval> someMethod()
-               //        enddef
-           }
-           else
-           {
-               // class members (public, read access, private):
-               //      "static _varname"
-               //      "static varname"
-               //      "public static varname"
-               char_u *varname = p;
-               char_u *varname_end = NULL;
-               type_T *type = NULL;
-               char_u *init_expr = NULL;
-               if (parse_member(eap, line, varname, has_public,
-                         &varname_end, &type_list, &type, &init_expr) == FAIL)
-                   break;
-               if (add_member(&classmembers, varname, varname_end,
-                                         has_public, type, init_expr) == FAIL)
-               {
-                   vim_free(init_expr);
-                   break;
-               }
-           }
-       }
-
        // constructors:
        //        def new()
        //        enddef
        //        def newOther()
        //        enddef
-       // methods:
-       //        def someMethod()
+       // object methods and class functions:
+       //        def SomeMethod()
+       //        enddef
+       //        static def ClassFunction()
        //        enddef
        // TODO:
        //        def <Tval> someMethod()
@@ -364,6 +345,8 @@ ex_class(exarg_T *eap)
            exarg_T     ea;
            garray_T    lines_to_free;
 
+           // TODO: error for "public static def Func()"?
+
            CLEAR_FIELD(ea);
            ea.cmd = line;
            ea.arg = p;
@@ -376,13 +359,38 @@ ex_class(exarg_T *eap)
            ga_clear_strings(&lines_to_free);
 
            // TODO: how about errors?
-           if (uf != NULL && ga_grow(&objmethods, 1) == OK)
+           int is_new = STRNCMP(uf->uf_name, "new", 3) == 0;
+           garray_T *fgap = has_static || is_new
+                                              ? &classfunctions : &objmethods;
+           if (uf != NULL && ga_grow(fgap, 1) == OK)
            {
-               if (STRNCMP(uf->uf_name, "new", 3) == 0)
+               if (is_new)
                    uf->uf_flags |= FC_NEW;
 
-               ((ufunc_T **)objmethods.ga_data)[objmethods.ga_len] = uf;
-               ++objmethods.ga_len;
+               ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
+               ++fgap->ga_len;
+           }
+       }
+
+       // class members
+       else if (has_static)
+       {
+           // class members (public, read access, private):
+           //  "static _varname"
+           //  "static varname"
+           //  "public static varname"
+           char_u *varname = p;
+           char_u *varname_end = NULL;
+           type_T *type = NULL;
+           char_u *init_expr = NULL;
+           if (parse_member(eap, line, varname, has_public,
+                     &varname_end, &type_list, &type, &init_expr) == FAIL)
+               break;
+           if (add_member(&classmembers, varname, varname_end,
+                                     has_public, type, init_expr) == FAIL)
+           {
+               vim_free(init_expr);
+               break;
            }
        }
 
@@ -445,8 +453,8 @@ ex_class(exarg_T *eap)
        }
 
        int have_new = FALSE;
-       for (int i = 0; i < objmethods.ga_len; ++i)
-           if (STRCMP(((ufunc_T **)objmethods.ga_data)[i]->uf_name,
+       for (int i = 0; i < classfunctions.ga_len; ++i)
+           if (STRCMP(((ufunc_T **)classfunctions.ga_data)[i]->uf_name,
                                                                   "new") == 0)
            {
                have_new = TRUE;
@@ -483,10 +491,10 @@ ex_class(exarg_T *eap)
            ga_clear_strings(&lines_to_free);
            vim_free(fga.ga_data);
 
-           if (nf != NULL && ga_grow(&objmethods, 1) == OK)
+           if (nf != NULL && ga_grow(&classfunctions, 1) == OK)
            {
-               ((ufunc_T **)objmethods.ga_data)[objmethods.ga_len] = nf;
-               ++objmethods.ga_len;
+               ((ufunc_T **)classfunctions.ga_data)[classfunctions.ga_len] = nf;
+               ++classfunctions.ga_len;
 
                nf->uf_flags |= FC_NEW;
                nf->uf_ret_type = get_type_ptr(&type_list);
@@ -500,21 +508,35 @@ ex_class(exarg_T *eap)
            }
        }
 
-       // TODO: class methods
-       cl->class_obj_method_count = objmethods.ga_len;
-       cl->class_obj_methods = ALLOC_MULT(ufunc_T *, objmethods.ga_len);
-       if (cl->class_obj_methods == NULL)
-           goto cleanup;
-       mch_memmove(cl->class_obj_methods, objmethods.ga_data,
-                                       sizeof(ufunc_T *) * objmethods.ga_len);
-       vim_free(objmethods.ga_data);
-
-       // Set the class pointer on all the object methods.
-       for (int i = 0; i < objmethods.ga_len; ++i)
+       // loop 1: class functions, loop 2: object methods
+       for (int loop = 1; loop <= 2; ++loop)
        {
-           ufunc_T *fp = cl->class_obj_methods[i];
-           fp->uf_class = cl;
-           fp->uf_flags |= FC_OBJECT;  // TODO: not for class method
+           garray_T *gap = loop == 1 ? &classfunctions : &objmethods;
+           int      *fcount = loop == 1 ? &cl->class_class_function_count
+                                        : &cl->class_obj_method_count;
+           ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
+                                      : &cl->class_obj_methods;
+
+           *fcount = gap->ga_len;
+           if (gap->ga_len == 0)
+           {
+               *fup = NULL;
+               continue;
+           }
+           *fup = ALLOC_MULT(ufunc_T *, gap->ga_len);
+           if (*fup == NULL)
+               goto cleanup;
+           mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
+           vim_free(gap->ga_data);
+
+           // Set the class pointer on all the object methods.
+           for (int i = 0; i < gap->ga_len; ++i)
+           {
+               ufunc_T *fp = (*fup)[i];
+               fp->uf_class = cl;
+               if (loop == 2)
+                   fp->uf_flags |= FC_OBJECT;
+           }
        }
 
        cl->class_type.tt_type = VAR_CLASS;
@@ -539,6 +561,7 @@ cleanup:
     if (cl != NULL)
     {
        vim_free(cl->class_name);
+       vim_free(cl->class_class_functions);
        vim_free(cl->class_obj_members);
        vim_free(cl->class_obj_methods);
        vim_free(cl);
@@ -565,6 +588,14 @@ cleanup:
        func_clear_free(uf, FALSE);
     }
     ga_clear(&objmethods);
+
+    for (int i = 0; i < classfunctions.ga_len; ++i)
+    {
+       ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
+       func_clear_free(uf, FALSE);
+    }
+    ga_clear(&classfunctions);
+
     clear_type_list(&type_list);
 }
 
@@ -627,7 +658,7 @@ ex_type(exarg_T *eap UNUSED)
 /*
  * Evaluate what comes after a class:
  * - class member: SomeClass.varname
- * - class method: SomeClass.SomeMethod()
+ * - class function: SomeClass.SomeMethod()
  * - class constructor: SomeClass.new()
  * - object member: someObject.varname
  * - object method: someObject.SomeMethod()
@@ -664,9 +695,13 @@ class_object_index(
                                             : rettv->vval.v_object->obj_class;
     if (*name_end == '(')
     {
-       for (int i = 0; i < cl->class_obj_method_count; ++i)
+       int on_class = rettv->v_type == VAR_CLASS;
+       int count = on_class ? cl->class_class_function_count
+                            : cl->class_obj_method_count;
+       for (int i = 0; i < count; ++i)
        {
-           ufunc_T *fp = cl->class_obj_methods[i];
+           ufunc_T *fp = on_class ? cl->class_class_functions[i]
+                                  : cl->class_obj_methods[i];
            // Use a separate pointer to avoid that ASAN complains about
            // uf_name[] only being 4 characters.
            char_u *ufname = (char_u *)fp->uf_name;
@@ -805,9 +840,13 @@ find_class_func(char_u **arg)
        goto fail_after_eval;
     len = fname_end - fname;
 
-    for (int i = 0; i < cl->class_obj_method_count; ++i)
+    int count = tv.v_type == VAR_CLASS ? cl->class_class_function_count
+                                      : cl->class_obj_method_count;
+    ufunc_T **funcs = tv.v_type == VAR_CLASS ? cl->class_class_functions
+                                            : cl->class_obj_methods;
+    for (int i = 0; i < count; ++i)
     {
-       ufunc_T *fp = cl->class_obj_methods[i];
+       ufunc_T *fp = funcs[i];
        // Use a separate pointer to avoid that ASAN complains about
        // uf_name[] only being 4 characters.
        char_u *ufname = (char_u *)fp->uf_name;
@@ -823,6 +862,35 @@ fail_after_eval:
     return NULL;
 }
 
+/*
+ * If "cctx->ctx_ufunc" indicates we are in a class, check if "name" is a class
+ * member.  If it is then return TRUE and set "cl_ret" and "idx_ret".
+ */
+    int
+class_member_exists(
+       char_u  *name,
+       class_T **cl_ret,
+       int     *idx_ret,
+       cctx_T  *cctx)
+{
+    if (cctx->ctx_ufunc == NULL || cctx->ctx_ufunc->uf_class == NULL)
+       return FALSE;
+    class_T *cl = cctx->ctx_ufunc->uf_class;
+
+    for (int idx = 0; idx < cl->class_class_member_count; ++idx)
+    {
+       ocmember_T *m = &cl->class_class_members[idx];
+       if (STRCMP(m->ocm_name, name) == 0)
+       {
+           *cl_ret = cl;
+           *idx_ret = idx;
+           return TRUE;
+       }
+    }
+
+    return FALSE;
+}
+
 /*
  * Make a copy of an object.
  */
index ba2c826cff03489035ea3a4bde6fbd0f00f786d6..463e79cbe4dffac85df352e1416e7539f3aa5b4e 100644 (file)
@@ -587,7 +587,8 @@ compile_load(
        }
        else
        {
-           lvar_T lvar;
+           lvar_T  lvar;
+           class_T *cl = NULL;
 
            if (lookup_local(*arg, len, &lvar, cctx) == OK)
            {
@@ -602,6 +603,10 @@ compile_load(
                else
                    gen_load = TRUE;
            }
+           else if (class_member_exists(name, &cl, &idx, cctx))
+           {
+               res = generate_CLASSMEMBER(cctx, TRUE, cl, idx);
+           }
            else
            {
                // "var" can be script-local even without using "s:" if it