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 /src | |
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>
Diffstat (limited to 'src')
-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 |
6 files changed, 208 insertions, 34 deletions
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". */ |