0
0
mirror of https://github.com/vim/vim.git synced 2025-08-26 20:03:41 -04:00

patch 9.1.0020: Vim9: cannot compile all methods in a class

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>
This commit is contained in:
Yegappan Lakshmanan 2024-01-12 17:36:40 +01:00 committed by Christian Brabandt
parent 8610f74382
commit 4f32c83a77
No known key found for this signature in database
GPG Key ID: F3F92DA383FDDE09
10 changed files with 224 additions and 42 deletions

View 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()*

View 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?

View 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}

View 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*

View 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);

View 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 : */

View 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

View 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;
defcompile_funcs_in_script();
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;
}
}
}
}
// compile all the class defined in the current script
defcompile_classes_in_script();
}
}

View File

@ -704,6 +704,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
20,
/**/
19,
/**/

View 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".