summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYegappan Lakshmanan <yegappan@yahoo.com>2024-01-12 17:36:40 +0100
committerChristian Brabandt <cb@256bit.org>2024-01-12 17:41:09 +0100
commit4f32c83a775a195ae7e1545b2840fb773f93414f (patch)
treed9519e67920220913c08fee59ccc63f7c37572c0
parent8610f74382039c9c54d6c4aeb978d252e762360a (diff)
patch 9.1.0020: Vim9: cannot compile all methods in a classv9.1.0020
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>
-rw-r--r--runtime/doc/tags1
-rw-r--r--runtime/doc/todo.txt2
-rw-r--r--runtime/doc/vim9.txt12
-rw-r--r--runtime/doc/vim9class.txt9
-rw-r--r--src/proto/userfunc.pro1
-rw-r--r--src/proto/vim9class.pro3
-rw-r--r--src/testdir/test_vim9_class.vim83
-rw-r--r--src/userfunc.c105
-rw-r--r--src/version.c2
-rw-r--r--src/vim9class.c48
10 files changed, 224 insertions, 42 deletions
diff --git a/runtime/doc/tags b/runtime/doc/tags
index bab6b3bf58..b3691bb93f 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -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()*
diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt
index 9847ff7d36..746488ecc4 100644
--- a/runtime/doc/todo.txt
+++ b/runtime/doc/todo.txt
@@ -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?
diff --git a/runtime/doc/vim9.txt b/runtime/doc/vim9.txt
index b12b1cc998..b246fcbcec 100644
--- a/runtime/doc/vim9.txt
+++ b/runtime/doc/vim9.txt
@@ -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}
diff --git a/runtime/doc/vim9class.txt b/runtime/doc/vim9class.txt
index 6e94e84832..ba821c1b29 100644
--- a/runtime/doc/vim9class.txt
+++ b/runtime/doc/vim9class.txt
@@ -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*
diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro
index e393c04707..9bb461663e 100644
--- a/src/proto/userfunc.pro
+++ b/src/proto/userfunc.pro
@@ -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);
diff --git a/src/proto/vim9class.pro b/src/proto/vim9class.pro
index f1b63602a7..a746eb7729 100644
--- a/src/proto/vim9class.pro
+++ b/src/proto/vim9class.pro
@@ -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 : */
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
index b34d2ad12c..62a6d043d9 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -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
diff --git a/src/userfunc.c b/src/userfunc.c
index 64761ecdb7..e39ce6e492 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -5546,6 +5546,60 @@ find_func_by_name(char_u *name, compiletype_T *compile_type)
}
/*
+ * 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.
* Skips dead functions. Doesn't do profiling.
@@ -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();
}
}
diff --git a/src/version.c b/src/version.c
index 627acd24d7..76a7668b0e 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 20,
+/**/
19,
/**/
18,
diff --git a/src/vim9class.c b/src/vim9class.c
index e5d9aeb4ba..525f8d0389 100644
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -3225,6 +3225,54 @@ member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
}
/*
+ * 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".
*/