diff options
author | Yegappan Lakshmanan <yegappan@yahoo.com> | 2024-01-12 17:36:40 +0100 |
---|---|---|
committer | Christian Brabandt <cb@256bit.org> | 2024-01-12 17:41:09 +0100 |
commit | 4f32c83a775a195ae7e1545b2840fb773f93414f (patch) | |
tree | d9519e67920220913c08fee59ccc63f7c37572c0 | |
parent | 8610f74382039c9c54d6c4aeb978d252e762360a (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/tags | 1 | ||||
-rw-r--r-- | runtime/doc/todo.txt | 2 | ||||
-rw-r--r-- | runtime/doc/vim9.txt | 12 | ||||
-rw-r--r-- | runtime/doc/vim9class.txt | 9 | ||||
-rw-r--r-- | src/proto/userfunc.pro | 1 | ||||
-rw-r--r-- | src/proto/vim9class.pro | 3 | ||||
-rw-r--r-- | src/testdir/test_vim9_class.vim | 83 | ||||
-rw-r--r-- | src/userfunc.c | 105 | ||||
-rw-r--r-- | src/version.c | 2 | ||||
-rw-r--r-- | src/vim9class.c | 48 |
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". */ |