]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.0020: Vim9: cannot compile all methods in a class v9.1.0020
authorYegappan Lakshmanan <yegappan@yahoo.com>
Fri, 12 Jan 2024 16:36:40 +0000 (17:36 +0100)
committerChristian Brabandt <cb@256bit.org>
Fri, 12 Jan 2024 16:41:09 +0000 (17:41 +0100)
Problem:  Vim9: cannot compile all methods in a class
Solution: Support compiling all the methods in a class using :defcompile
          (Yegappan Lakshmanan)

closes: #13844

Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
runtime/doc/tags
runtime/doc/todo.txt
runtime/doc/vim9.txt
runtime/doc/vim9class.txt
src/proto/userfunc.pro
src/proto/vim9class.pro
src/testdir/test_vim9_class.vim
src/userfunc.c
src/version.c
src/vim9class.c

index bab6b3bf588e0e8b775fd829d80bb2a1f16dfeab..b3691bb93f2d1552b97132ae1232ce64a59e438f 100644 (file)
@@ -6428,6 +6428,7 @@ cino-{    indent.txt      /*cino-{*
 cino-} indent.txt      /*cino-}*
 cinoptions-values      indent.txt      /*cinoptions-values*
 class  vim9class.txt   /*class*
+class-compile  vim9class.txt   /*class-compile*
 class-method   vim9class.txt   /*class-method*
 clear-undo     undo.txt        /*clear-undo*
 clearmatches() builtin.txt     /*clearmatches()*
index 9847ff7d36e02e9f14f17adb72f8d4c396fc578d..746488ecc41954584b4f2903d1257ec0c44d6d3f 100644 (file)
@@ -130,8 +130,6 @@ Further Vim9 improvements:
     Issue #11822: any.Func() can be a dict or an object call, need to handle
     this at runtime.  Also see #12198 for an example.
     Possibly issue #11981 can be fixed at the same time (has two examples).
-  - Make ":defcompile ClassName" compile all functions and methods in the
-    class.
   - Forward declaration of a class?  E.g. for Clone() function.
        Email lifepillar 2023 Mar 26
   - object empty(), len() - can class define a method to be used for them?
index b12b1cc998a312809ebf56ff45d1d841f1ab482d..b246fcbcecdca2bd69ffed3f1f3fd570047b92fc 100644 (file)
@@ -1,4 +1,4 @@
-*vim9.txt*     For Vim version 9.1.  Last change: 2023 Dec 24
+*vim9.txt*     For Vim version 9.1.  Last change: 2024 Jan 12
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -1260,10 +1260,12 @@ Script-local variables in a |Vim9| script must be declared at the script
 level.  They cannot be created in a function, also not in a legacy function.
 
                                                *:defc* *:defcompile*
-:defc[ompile]          Compile functions defined in the current script that
-                       were not compiled yet.
-                       This will report any errors found during compilation.
-                       This excludes functions defined inside a class.
+:defc[ompile]          Compile functions and classes (|class-compile|)
+                       defined in the current script that were not compiled
+                       yet.  This will report any errors found during
+                       compilation.
+
+:defc[ompile] MyClass  Compile all methods in a class |class-compile|.
 
 :defc[ompile] {func}
 :defc[ompile] debug {func}
index 6e94e84832e2fb12e864fee3760df199570bc1b9..ba821c1b29db42a348959e145356a91eb82908df 100644 (file)
@@ -1,4 +1,4 @@
-*vim9class.txt*        For Vim version 9.1.  Last change: 2024 Jan 06
+*vim9class.txt*        For Vim version 9.1.  Last change: 2024 Jan 12
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -830,7 +830,14 @@ Note that the method name must start with "new".  If there is no method called
 "new()" then the default constructor is added, even though there are other
 constructor methods.
 
+Compiling methods in a Class ~
+                                                       *class-compile*
+The |:defcompile| command can be used to compile all the class and object
+methods defined in a class: >
 
+       defcompile MyClass      # Compile class "MyClass"
+       defcompile              # Compile the classes in the current script
+<
 ==============================================================================
 
 7.  Type definition                            *typealias* *Vim9-type* *:type*
index e393c04707398c4217aa417ddde3168c9bf9b6ad..9bb461663e41c064bec5e93b1830f70d3e3ad165 100644 (file)
@@ -50,6 +50,7 @@ void list_functions(regmatch_T *regmatch);
 ufunc_T *define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free, int class_flags, ocmember_T *obj_members, int obj_member_count);
 void ex_function(exarg_T *eap);
 ufunc_T *find_func_by_name(char_u *name, compiletype_T *compile_type);
+void defcompile_function(ufunc_T *ufunc, class_T *cl);
 void ex_defcompile(exarg_T *eap);
 int eval_fname_script(char_u *p);
 int translated_function_exists(char_u *name, int is_global);
index f1b63602a7df66ff38ac4dcf2c06b12d4aa079b7..a746eb772902a0aca6e4eeb610e09c8822105832 100644 (file)
@@ -31,6 +31,9 @@ void object_free_items(int copyID);
 void emsg_var_cl_define(char *msg, char_u *name, size_t len, class_T *cl);
 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 defcompile_class(class_T *cl);
+void defcompile_classes_in_script(void);
+int is_class_name(char_u *name, typval_T *rettv);
 int class_instance_of(class_T *cl, class_T *other_cl);
 void f_instanceof(typval_T *argvars, typval_T *rettv);
 /* vim: set ft=c : */
index b34d2ad12c0e97a600522d6c52eb71b6c2c6ebb5..62a6d043d9d204102826e8cc985d910e10465997 100644 (file)
@@ -9686,4 +9686,87 @@ def Test_method_double_underscore_prefix()
   v9.CheckSourceFailure(lines, 'E1034: Cannot use reserved name __foo()', 3)
 enddef
 
+" Test for compiling class/object methods using :defcompile
+def Test_defcompile_class()
+  # defcompile all the classes in the current script
+  var lines =<< trim END
+    vim9script
+    class A
+      def Foo()
+        var i = 10
+      enddef
+    endclass
+    class B
+      def Bar()
+        var i = 20
+        xxx
+      enddef
+    endclass
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E476: Invalid command: xxx', 2)
+
+  # defcompile a specific class
+  lines =<< trim END
+    vim9script
+    class A
+      def Foo()
+        xxx
+      enddef
+    endclass
+    class B
+      def Bar()
+        yyy
+      enddef
+    endclass
+    defcompile B
+  END
+  v9.CheckSourceFailure(lines, 'E476: Invalid command: yyy', 1)
+
+  # defcompile a non-class
+  lines =<< trim END
+    vim9script
+    class A
+      def Foo()
+      enddef
+    endclass
+    var X: list<number> = []
+    defcompile X
+  END
+  v9.CheckSourceFailure(lines, 'E1061: Cannot find function X', 7)
+
+  # defcompile a class twice
+  lines =<< trim END
+    vim9script
+    class A
+      def new()
+      enddef
+    endclass
+    defcompile A
+    defcompile A
+    assert_equal('Function A.new does not need compiling', v:statusmsg)
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # defcompile should not compile an imported class
+  lines =<< trim END
+    vim9script
+    export class A
+      def Foo()
+        xxx
+      enddef
+    endclass
+  END
+  writefile(lines, 'Xdefcompileimport.vim', 'D')
+  lines =<< trim END
+    vim9script
+
+    import './Xdefcompileimport.vim'
+    class B
+    endclass
+    defcompile
+  END
+  v9.CheckScriptSuccess(lines)
+enddef
+
 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
index 64761ecdb7ba80e40422769d0c5f7b0b22638969..e39ce6e4922caf555464b29adf27e4da4800cb98 100644 (file)
@@ -5545,6 +5545,60 @@ find_func_by_name(char_u *name, compiletype_T *compile_type)
     return ufunc;
 }
 
+/*
+ * Compile the :def function "ufunc".  If "cl" is not NULL, then compile the
+ * class or object method "ufunc" in "cl".
+ */
+    void
+defcompile_function(ufunc_T *ufunc, class_T *cl)
+{
+    compiletype_T compile_type = CT_NONE;
+
+    if (func_needs_compiling(ufunc, compile_type))
+       (void)compile_def_function(ufunc, FALSE, compile_type, NULL);
+    else
+       smsg(_("Function %s%s%s does not need compiling"),
+                               cl != NULL ? cl->class_name : (char_u *)"",
+                               cl != NULL ? (char_u *)"." : (char_u *)"",
+                               ufunc->uf_name);
+}
+
+/*
+ * Compile all the :def functions defined in the current script
+ */
+    static void
+defcompile_funcs_in_script(void)
+{
+    long       todo = (long)func_hashtab.ht_used;
+    int                changed = func_hashtab.ht_changed;
+    hashitem_T *hi;
+
+    for (hi = func_hashtab.ht_array; todo > 0 && !got_int; ++hi)
+    {
+       if (!HASHITEM_EMPTY(hi))
+       {
+           --todo;
+           ufunc_T *ufunc = HI2UF(hi);
+           if (ufunc->uf_script_ctx.sc_sid == current_sctx.sc_sid
+                   && ufunc->uf_def_status == UF_TO_BE_COMPILED
+                   && (ufunc->uf_flags & FC_DEAD) == 0)
+           {
+               (void)compile_def_function(ufunc, FALSE, CT_NONE, NULL);
+
+               if (func_hashtab.ht_changed != changed)
+               {
+                   // a function has been added or removed, need to start
+                   // over
+                   todo = (long)func_hashtab.ht_used;
+                   changed = func_hashtab.ht_changed;
+                   hi = func_hashtab.ht_array;
+                   --hi;
+               }
+           }
+       }
+    }
+}
+
 /*
  * :defcompile - compile all :def functions in the current script that need to
  * be compiled or the one specified by the argument.
@@ -5555,46 +5609,29 @@ ex_defcompile(exarg_T *eap)
 {
     if (*eap->arg != NUL)
     {
-       compiletype_T compile_type = CT_NONE;
-       ufunc_T *ufunc = find_func_by_name(eap->arg, &compile_type);
-       if (ufunc != NULL)
+       typval_T tv;
+
+       if (is_class_name(eap->arg, &tv))
        {
-           if (func_needs_compiling(ufunc, compile_type))
-               (void)compile_def_function(ufunc, FALSE, compile_type, NULL);
-           else
-               smsg(_("Function %s does not need compiling"), eap->arg);
+           class_T *cl = tv.vval.v_class;
+
+           if (cl != NULL)
+               defcompile_class(cl);
+       }
+       else
+       {
+           compiletype_T compile_type = CT_NONE;
+           ufunc_T *ufunc = find_func_by_name(eap->arg, &compile_type);
+           if (ufunc != NULL)
+               defcompile_function(ufunc, NULL);
        }
     }
     else
     {
-       long    todo = (long)func_hashtab.ht_used;
-       int             changed = func_hashtab.ht_changed;
-       hashitem_T      *hi;
-
-       for (hi = func_hashtab.ht_array; todo > 0 && !got_int; ++hi)
-       {
-           if (!HASHITEM_EMPTY(hi))
-           {
-               --todo;
-               ufunc_T *ufunc = HI2UF(hi);
-               if (ufunc->uf_script_ctx.sc_sid == current_sctx.sc_sid
-                       && ufunc->uf_def_status == UF_TO_BE_COMPILED
-                       && (ufunc->uf_flags & FC_DEAD) == 0)
-               {
-                   (void)compile_def_function(ufunc, FALSE, CT_NONE, NULL);
+       defcompile_funcs_in_script();
 
-                   if (func_hashtab.ht_changed != changed)
-                   {
-                       // a function has been added or removed, need to start
-                       // over
-                       todo = (long)func_hashtab.ht_used;
-                       changed = func_hashtab.ht_changed;
-                       hi = func_hashtab.ht_array;
-                       --hi;
-                   }
-               }
-           }
-       }
+       // compile all the class defined in the current script
+       defcompile_classes_in_script();
     }
 }
 
index 627acd24d737628216170b9e0de89d87c31073a9..76a7668b0e7b9c13c3d097254bd458af60a5a742 100644 (file)
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    20,
 /**/
     19,
 /**/
index e5d9aeb4baa0ddc6de089fa7a674632a93d1af6e..525f8d038900eb8c52cae3e648a5bdbebd0d4035 100644 (file)
@@ -3224,6 +3224,54 @@ member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
     vim_free(varname);
 }
 
+/*
+ * Compile all the class and object methods in "cl".
+ */
+    void
+defcompile_class(class_T *cl)
+{
+    for (int loop = 1; loop <= 2; ++loop)
+    {
+       int func_count = loop == 1 ? cl->class_class_function_count
+                                               : cl->class_obj_method_count;
+       for (int i = 0; i < func_count; i++)
+       {
+           ufunc_T *ufunc = loop == 1 ? cl->class_class_functions[i]
+                                               : cl->class_obj_methods[i];
+           defcompile_function(ufunc, cl);
+       }
+    }
+}
+
+/*
+ * Compile all the classes defined in the current script
+ */
+    void
+defcompile_classes_in_script(void)
+{
+    for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
+    {
+       if (eval_variable(cl->class_name, 0, 0, NULL, NULL,
+                       EVAL_VAR_NOAUTOLOAD | EVAL_VAR_NO_FUNC) != FAIL)
+           defcompile_class(cl);
+    }
+}
+
+/*
+ * Returns TRUE if "name" is the name of a class.  The typval for the class is
+ * returned in "rettv".
+ */
+    int
+is_class_name(char_u *name, typval_T *rettv)
+{
+    rettv->v_type = VAR_UNKNOWN;
+
+    if (eval_variable(name, 0, 0, rettv, NULL, EVAL_VAR_NOAUTOLOAD |
+                                               EVAL_VAR_NO_FUNC) != FAIL)
+       return rettv->v_type == VAR_CLASS;
+    return FALSE;
+}
+
 /*
  * Return TRUE when the class "cl", its base class or one of the implemented
  * interfaces matches the class "other_cl".