forked from aniani/vim
patch 9.1.0148: Vim9: can't call internal methods with objects
Problem: Vim9: can't call internal methods with objects Solution: Add support for empty(), len() and string() function calls for objects (Yegappan Lakshmanan) closes: #14129 Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
2157035637
commit
d3eae7bc11
@@ -1,4 +1,4 @@
|
|||||||
*builtin.txt* For Vim version 9.1. Last change: 2024 Mar 01
|
*builtin.txt* For Vim version 9.1. Last change: 2024 Mar 03
|
||||||
|
|
||||||
|
|
||||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||||
@@ -2265,6 +2265,8 @@ empty({expr}) *empty()*
|
|||||||
- A |Job| is empty when it failed to start.
|
- A |Job| is empty when it failed to start.
|
||||||
- A |Channel| is empty when it is closed.
|
- A |Channel| is empty when it is closed.
|
||||||
- A |Blob| is empty when its length is zero.
|
- A |Blob| is empty when its length is zero.
|
||||||
|
- An |Object| is empty, when the |empty()| builtin method in
|
||||||
|
the object (if present) returns true.
|
||||||
|
|
||||||
For a long |List| this is much faster than comparing the
|
For a long |List| this is much faster than comparing the
|
||||||
length with zero.
|
length with zero.
|
||||||
@@ -5476,7 +5478,9 @@ len({expr}) The result is a Number, which is the length of the argument.
|
|||||||
When {expr} is a |Blob| the number of bytes is returned.
|
When {expr} is a |Blob| the number of bytes is returned.
|
||||||
When {expr} is a |Dictionary| the number of entries in the
|
When {expr} is a |Dictionary| the number of entries in the
|
||||||
|Dictionary| is returned.
|
|Dictionary| is returned.
|
||||||
Otherwise an error is given and returns zero.
|
When {expr} is an |Object|, invokes the |len()| method in the
|
||||||
|
object (if present) to get the length. Otherwise returns
|
||||||
|
zero.
|
||||||
|
|
||||||
Can also be used as a |method|: >
|
Can also be used as a |method|: >
|
||||||
mylist->len()
|
mylist->len()
|
||||||
@@ -9587,6 +9591,10 @@ string({expr}) Return {expr} converted to a String. If {expr} is a Number,
|
|||||||
replaced by "[...]" or "{...}". Using eval() on the result
|
replaced by "[...]" or "{...}". Using eval() on the result
|
||||||
will then fail.
|
will then fail.
|
||||||
|
|
||||||
|
For an object, invokes the |string()| method to get a textual
|
||||||
|
representation of the object. If the method is not present,
|
||||||
|
then the default representation is used.
|
||||||
|
|
||||||
Can also be used as a |method|: >
|
Can also be used as a |method|: >
|
||||||
mylist->string()
|
mylist->string()
|
||||||
|
|
||||||
|
@@ -4518,6 +4518,8 @@ E1409 vim9class.txt /*E1409*
|
|||||||
E141 message.txt /*E141*
|
E141 message.txt /*E141*
|
||||||
E1410 vim9class.txt /*E1410*
|
E1410 vim9class.txt /*E1410*
|
||||||
E1411 vim9class.txt /*E1411*
|
E1411 vim9class.txt /*E1411*
|
||||||
|
E1412 vim9class.txt /*E1412*
|
||||||
|
E1413 vim9class.txt /*E1413*
|
||||||
E142 message.txt /*E142*
|
E142 message.txt /*E142*
|
||||||
E143 autocmd.txt /*E143*
|
E143 autocmd.txt /*E143*
|
||||||
E144 various.txt /*E144*
|
E144 various.txt /*E144*
|
||||||
@@ -6183,6 +6185,7 @@ bugs intro.txt /*bugs*
|
|||||||
builtin-function-details builtin.txt /*builtin-function-details*
|
builtin-function-details builtin.txt /*builtin-function-details*
|
||||||
builtin-function-list builtin.txt /*builtin-function-list*
|
builtin-function-list builtin.txt /*builtin-function-list*
|
||||||
builtin-functions builtin.txt /*builtin-functions*
|
builtin-functions builtin.txt /*builtin-functions*
|
||||||
|
builtin-object-methods vim9class.txt /*builtin-object-methods*
|
||||||
builtin-terms term.txt /*builtin-terms*
|
builtin-terms term.txt /*builtin-terms*
|
||||||
builtin-tools gui.txt /*builtin-tools*
|
builtin-tools gui.txt /*builtin-tools*
|
||||||
builtin.txt builtin.txt /*builtin.txt*
|
builtin.txt builtin.txt /*builtin.txt*
|
||||||
@@ -9153,9 +9156,12 @@ o_object-select motion.txt /*o_object-select*
|
|||||||
o_v motion.txt /*o_v*
|
o_v motion.txt /*o_v*
|
||||||
object vim9class.txt /*object*
|
object vim9class.txt /*object*
|
||||||
object-const-variable vim9class.txt /*object-const-variable*
|
object-const-variable vim9class.txt /*object-const-variable*
|
||||||
|
object-empty() vim9class.txt /*object-empty()*
|
||||||
object-final-variable vim9class.txt /*object-final-variable*
|
object-final-variable vim9class.txt /*object-final-variable*
|
||||||
|
object-len() vim9class.txt /*object-len()*
|
||||||
object-motions motion.txt /*object-motions*
|
object-motions motion.txt /*object-motions*
|
||||||
object-select motion.txt /*object-select*
|
object-select motion.txt /*object-select*
|
||||||
|
object-string() vim9class.txt /*object-string()*
|
||||||
objects index.txt /*objects*
|
objects index.txt /*objects*
|
||||||
obtaining-exted netbeans.txt /*obtaining-exted*
|
obtaining-exted netbeans.txt /*obtaining-exted*
|
||||||
ocaml.vim syntax.txt /*ocaml.vim*
|
ocaml.vim syntax.txt /*ocaml.vim*
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
*todo.txt* For Vim version 9.1. Last change: 2024 Feb 01
|
*todo.txt* For Vim version 9.1. Last change: 2024 Mar 03
|
||||||
|
|
||||||
|
|
||||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||||
@@ -132,7 +132,6 @@ Further Vim9 improvements:
|
|||||||
Possibly issue #11981 can be fixed at the same time (has two examples).
|
Possibly issue #11981 can be fixed at the same time (has two examples).
|
||||||
- Forward declaration of a class? E.g. for Clone() function.
|
- Forward declaration of a class? E.g. for Clone() function.
|
||||||
Email lifepillar 2023 Mar 26
|
Email lifepillar 2023 Mar 26
|
||||||
- object empty(), len() - can class define a method to be used for them?
|
|
||||||
- When "Meta" is a class, is "const MetaAlias = Meta" allowed? It should
|
- When "Meta" is a class, is "const MetaAlias = Meta" allowed? It should
|
||||||
either work or given an error. Possibly give an error now and implement it
|
either work or given an error. Possibly give an error now and implement it
|
||||||
later (using a typedef). #12006
|
later (using a typedef). #12006
|
||||||
|
@@ -41543,6 +41543,11 @@ and is a work in progress.
|
|||||||
|
|
||||||
Support for Wayland UI.
|
Support for Wayland UI.
|
||||||
|
|
||||||
|
Vim9 script
|
||||||
|
-----------
|
||||||
|
Add support for internal builtin functions with vim9 objects, see
|
||||||
|
|builtin-object-methods|
|
||||||
|
|
||||||
Other improvements *new-other-9.2*
|
Other improvements *new-other-9.2*
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
*vim9class.txt* For Vim version 9.1. Last change: 2024 Jan 12
|
*vim9class.txt* For Vim version 9.1. Last change: 2024 Mar 03
|
||||||
|
|
||||||
|
|
||||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||||
@@ -710,6 +710,32 @@ The initialization isn't needed, the list is empty by default.
|
|||||||
*E1330*
|
*E1330*
|
||||||
Some types cannot be used, such as "void", "null" and "v:none".
|
Some types cannot be used, such as "void", "null" and "v:none".
|
||||||
|
|
||||||
|
Builtin Object Methods ~
|
||||||
|
*builtin-object-methods*
|
||||||
|
Some of the builtin functions like |empty()|, |len()| and |string()| can be
|
||||||
|
used with an object. An object can implement a method with the same name as
|
||||||
|
these builtin functions to return an object-specific value.
|
||||||
|
|
||||||
|
*E1412*
|
||||||
|
The following builtin methods are supported:
|
||||||
|
*object-empty()*
|
||||||
|
empty() Invoked by the |empty()| function to check whether an object is
|
||||||
|
empty. If this method is missing, then true is returned. This
|
||||||
|
method should not accept any arguments and must return a boolean.
|
||||||
|
*object-len()*
|
||||||
|
len() Invoked by the |len()| function to return the length of an
|
||||||
|
object. If this method is missing in the class, then an error is
|
||||||
|
given and zero is returned. This method should not accept any
|
||||||
|
arguments and must return a number.
|
||||||
|
*object-string()*
|
||||||
|
string() Invoked by the |string()| function to get a textual
|
||||||
|
representation of an object. Also used by the |:echo| command
|
||||||
|
for an object. If this method is missing in the class, then a
|
||||||
|
built-in default textual representation is used. This method
|
||||||
|
should not accept any arguments and must return a string.
|
||||||
|
|
||||||
|
*E1413*
|
||||||
|
A class method cannot be used as a builtin method.
|
||||||
|
|
||||||
Defining an interface ~
|
Defining an interface ~
|
||||||
*Interface* *:interface* *:endinterface*
|
*Interface* *:interface* *:endinterface*
|
||||||
|
@@ -3579,8 +3579,12 @@ EXTERN char e_const_variable_not_supported_in_interface[]
|
|||||||
INIT(= N_("E1410: Const variable not supported in an interface"));
|
INIT(= N_("E1410: Const variable not supported in an interface"));
|
||||||
EXTERN char e_missing_dot_after_object_str[]
|
EXTERN char e_missing_dot_after_object_str[]
|
||||||
INIT(= N_("E1411: Missing dot after object \"%s\""));
|
INIT(= N_("E1411: Missing dot after object \"%s\""));
|
||||||
|
EXTERN char e_builtin_object_method_str_not_supported[]
|
||||||
|
INIT(= N_("E1412: Builtin object method \"%s\" not supported"));
|
||||||
|
EXTERN char e_builtin_class_method_not_supported[]
|
||||||
|
INIT(= N_("E1413: Builtin class method not supported"));
|
||||||
#endif
|
#endif
|
||||||
// E1412 - E1499 unused (reserved for Vim9 class support)
|
// E1415 - E1499 unused (reserved for Vim9 class support)
|
||||||
EXTERN char e_cannot_mix_positional_and_non_positional_str[]
|
EXTERN char e_cannot_mix_positional_and_non_positional_str[]
|
||||||
INIT(= N_("E1500: Cannot mix positional and non-positional arguments: %s"));
|
INIT(= N_("E1500: Cannot mix positional and non-positional arguments: %s"));
|
||||||
EXTERN char e_fmt_arg_nr_unused_str[]
|
EXTERN char e_fmt_arg_nr_unused_str[]
|
||||||
|
33
src/eval.c
33
src/eval.c
@@ -6318,36 +6318,9 @@ echo_string_core(
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case VAR_OBJECT:
|
case VAR_OBJECT:
|
||||||
{
|
*tofree = r = object_string(tv->vval.v_object, numbuf, copyID,
|
||||||
garray_T ga;
|
echo_style, restore_copyID,
|
||||||
ga_init2(&ga, 1, 50);
|
composite_val);
|
||||||
ga_concat(&ga, (char_u *)"object of ");
|
|
||||||
object_T *obj = tv->vval.v_object;
|
|
||||||
class_T *cl = obj == NULL ? NULL : obj->obj_class;
|
|
||||||
ga_concat(&ga, cl == NULL ? (char_u *)"[unknown]"
|
|
||||||
: cl->class_name);
|
|
||||||
if (cl != NULL)
|
|
||||||
{
|
|
||||||
ga_concat(&ga, (char_u *)" {");
|
|
||||||
for (int i = 0; i < cl->class_obj_member_count; ++i)
|
|
||||||
{
|
|
||||||
if (i > 0)
|
|
||||||
ga_concat(&ga, (char_u *)", ");
|
|
||||||
ocmember_T *m = &cl->class_obj_members[i];
|
|
||||||
ga_concat(&ga, m->ocm_name);
|
|
||||||
ga_concat(&ga, (char_u *)": ");
|
|
||||||
char_u *tf = NULL;
|
|
||||||
ga_concat(&ga, echo_string_core(
|
|
||||||
(typval_T *)(obj + 1) + i,
|
|
||||||
&tf, numbuf, copyID, echo_style,
|
|
||||||
restore_copyID, composite_val));
|
|
||||||
vim_free(tf);
|
|
||||||
}
|
|
||||||
ga_concat(&ga, (char_u *)"}");
|
|
||||||
}
|
|
||||||
|
|
||||||
*tofree = r = ga.ga_data;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VAR_FLOAT:
|
case VAR_FLOAT:
|
||||||
|
@@ -986,6 +986,7 @@ arg_len1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
|||||||
|| type->tt_type == VAR_BLOB
|
|| type->tt_type == VAR_BLOB
|
||||||
|| type->tt_type == VAR_LIST
|
|| type->tt_type == VAR_LIST
|
||||||
|| type->tt_type == VAR_DICT
|
|| type->tt_type == VAR_DICT
|
||||||
|
|| type->tt_type == VAR_OBJECT
|
||||||
|| type_any_or_unknown(type))
|
|| type_any_or_unknown(type))
|
||||||
return OK;
|
return OK;
|
||||||
|
|
||||||
@@ -3981,7 +3982,7 @@ f_empty(typval_T *argvars, typval_T *rettv)
|
|||||||
n = argvars[0].vval.v_class != NULL;
|
n = argvars[0].vval.v_class != NULL;
|
||||||
break;
|
break;
|
||||||
case VAR_OBJECT:
|
case VAR_OBJECT:
|
||||||
n = argvars[0].vval.v_object != NULL;
|
n = object_empty(argvars[0].vval.v_object);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VAR_BLOB:
|
case VAR_BLOB:
|
||||||
@@ -7831,6 +7832,9 @@ f_len(typval_T *argvars, typval_T *rettv)
|
|||||||
case VAR_DICT:
|
case VAR_DICT:
|
||||||
rettv->vval.v_number = dict_len(argvars[0].vval.v_dict);
|
rettv->vval.v_number = dict_len(argvars[0].vval.v_dict);
|
||||||
break;
|
break;
|
||||||
|
case VAR_OBJECT:
|
||||||
|
rettv->vval.v_number = object_len(argvars[0].vval.v_object);
|
||||||
|
break;
|
||||||
case VAR_UNKNOWN:
|
case VAR_UNKNOWN:
|
||||||
case VAR_ANY:
|
case VAR_ANY:
|
||||||
case VAR_VOID:
|
case VAR_VOID:
|
||||||
@@ -7843,7 +7847,6 @@ f_len(typval_T *argvars, typval_T *rettv)
|
|||||||
case VAR_CHANNEL:
|
case VAR_CHANNEL:
|
||||||
case VAR_INSTR:
|
case VAR_INSTR:
|
||||||
case VAR_CLASS:
|
case VAR_CLASS:
|
||||||
case VAR_OBJECT:
|
|
||||||
case VAR_TYPEALIAS:
|
case VAR_TYPEALIAS:
|
||||||
emsg(_(e_invalid_type_for_len));
|
emsg(_(e_invalid_type_for_len));
|
||||||
break;
|
break;
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
/* vim9class.c */
|
/* vim9class.c */
|
||||||
int object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl);
|
int object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl);
|
||||||
|
int is_valid_builtin_obj_methodname(char_u *funcname);
|
||||||
|
ufunc_T *class_get_builtin_method(class_T *cl, class_builtin_T builtin_method, int *method_idx);
|
||||||
void ex_class(exarg_T *eap);
|
void ex_class(exarg_T *eap);
|
||||||
type_T *oc_member_type(class_T *cl, int is_object, char_u *name, char_u *name_end, int *member_idx);
|
type_T *oc_member_type(class_T *cl, int is_object, char_u *name, char_u *name_end, int *member_idx);
|
||||||
type_T *oc_member_type_by_idx(class_T *cl, int is_object, int member_idx);
|
type_T *oc_member_type_by_idx(class_T *cl, int is_object, int member_idx);
|
||||||
@@ -34,6 +36,10 @@ void member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t le
|
|||||||
void defcompile_class(class_T *cl);
|
void defcompile_class(class_T *cl);
|
||||||
void defcompile_classes_in_script(void);
|
void defcompile_classes_in_script(void);
|
||||||
int is_class_name(char_u *name, typval_T *rettv);
|
int is_class_name(char_u *name, typval_T *rettv);
|
||||||
|
void protected_method_access_errmsg(char_u *method_name);
|
||||||
|
int object_empty(object_T *obj);
|
||||||
|
int object_len(object_T *obj);
|
||||||
|
char_u *object_string(object_T *obj, char_u *numbuf, int copyID, int echo_style, int restore_copyID, int composite_val);
|
||||||
int class_instance_of(class_T *cl, class_T *other_cl);
|
int class_instance_of(class_T *cl, class_T *other_cl);
|
||||||
void f_instanceof(typval_T *argvars, typval_T *rettv);
|
void f_instanceof(typval_T *argvars, typval_T *rettv);
|
||||||
/* vim: set ft=c : */
|
/* vim: set ft=c : */
|
||||||
|
@@ -1530,6 +1530,17 @@ typedef enum {
|
|||||||
#define OCMFLAG_FINAL 0x02 // "final" object/class member
|
#define OCMFLAG_FINAL 0x02 // "final" object/class member
|
||||||
#define OCMFLAG_CONST 0x04 // "const" object/class member
|
#define OCMFLAG_CONST 0x04 // "const" object/class member
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Object methods called by builtin functions (e.g. string(), empty(), etc.)
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
CLASS_BUILTIN_INVALID,
|
||||||
|
CLASS_BUILTIN_STRING,
|
||||||
|
CLASS_BUILTIN_EMPTY,
|
||||||
|
CLASS_BUILTIN_LEN,
|
||||||
|
CLASS_BUILTIN_MAX
|
||||||
|
} class_builtin_T;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Entry for an object or class member variable.
|
* Entry for an object or class member variable.
|
||||||
*/
|
*/
|
||||||
@@ -1593,6 +1604,9 @@ struct class_S
|
|||||||
int class_obj_method_count_child; // count without "extends"
|
int class_obj_method_count_child; // count without "extends"
|
||||||
ufunc_T **class_obj_methods; // allocated
|
ufunc_T **class_obj_methods; // allocated
|
||||||
|
|
||||||
|
// index of builtin methods
|
||||||
|
int class_builtin_methods[CLASS_BUILTIN_MAX];
|
||||||
|
|
||||||
garray_T class_type_list; // used for type pointers
|
garray_T class_type_list; // used for type pointers
|
||||||
type_T class_type; // type used for the class
|
type_T class_type; // type used for the class
|
||||||
type_T class_object_type; // same as class_type but VAR_OBJECT
|
type_T class_object_type; // same as class_type but VAR_OBJECT
|
||||||
|
@@ -9659,33 +9659,6 @@ def Test_const_class_object_variable()
|
|||||||
v9.CheckSourceFailure(lines, 'E1022: Type or initialization required', 3)
|
v9.CheckSourceFailure(lines, 'E1022: Type or initialization required', 3)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
" Test for using double underscore prefix in a class/object method name.
|
|
||||||
def Test_method_double_underscore_prefix()
|
|
||||||
# class method
|
|
||||||
var lines =<< trim END
|
|
||||||
vim9script
|
|
||||||
class A
|
|
||||||
static def __foo()
|
|
||||||
echo "foo"
|
|
||||||
enddef
|
|
||||||
endclass
|
|
||||||
defcompile
|
|
||||||
END
|
|
||||||
v9.CheckSourceFailure(lines, 'E1034: Cannot use reserved name __foo()', 3)
|
|
||||||
|
|
||||||
# object method
|
|
||||||
lines =<< trim END
|
|
||||||
vim9script
|
|
||||||
class A
|
|
||||||
def __foo()
|
|
||||||
echo "foo"
|
|
||||||
enddef
|
|
||||||
endclass
|
|
||||||
defcompile
|
|
||||||
END
|
|
||||||
v9.CheckSourceFailure(lines, 'E1034: Cannot use reserved name __foo()', 3)
|
|
||||||
enddef
|
|
||||||
|
|
||||||
" Test for compiling class/object methods using :defcompile
|
" Test for compiling class/object methods using :defcompile
|
||||||
def Test_defcompile_class()
|
def Test_defcompile_class()
|
||||||
# defcompile all the classes in the current script
|
# defcompile all the classes in the current script
|
||||||
@@ -9769,4 +9742,534 @@ def Test_defcompile_class()
|
|||||||
v9.CheckScriptSuccess(lines)
|
v9.CheckScriptSuccess(lines)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
" Test for cases common to all the object builtin methods
|
||||||
|
def Test_object_builtin_method()
|
||||||
|
var lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
def abc()
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
END
|
||||||
|
v9.CheckSourceFailure(lines, 'E1267: Function name must start with a capital: abc()', 3)
|
||||||
|
|
||||||
|
for funcname in ["len", "string", "empty"]
|
||||||
|
lines =<< trim eval END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
static def {funcname}(): number
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
END
|
||||||
|
v9.CheckSourceFailure(lines, 'E1413: Builtin class method not supported', 3)
|
||||||
|
endfor
|
||||||
|
enddef
|
||||||
|
|
||||||
|
" Test for using the empty() builtin method with an object
|
||||||
|
" This is a legacy function to use the test_garbagecollect_now() function.
|
||||||
|
func Test_object_empty()
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
def empty(): bool
|
||||||
|
return true
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
def Foo()
|
||||||
|
var afoo = A.new()
|
||||||
|
assert_equal(true, empty(afoo))
|
||||||
|
assert_equal(true, afoo->empty())
|
||||||
|
enddef
|
||||||
|
|
||||||
|
var a = A.new()
|
||||||
|
assert_equal(1, empty(a))
|
||||||
|
assert_equal(1, a->empty())
|
||||||
|
test_garbagecollect_now()
|
||||||
|
assert_equal(1, empty(a))
|
||||||
|
Foo()
|
||||||
|
test_garbagecollect_now()
|
||||||
|
Foo()
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
" empty() should return 1 without a builtin method
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
endclass
|
||||||
|
|
||||||
|
def Foo()
|
||||||
|
var afoo = A.new()
|
||||||
|
assert_equal(1, empty(afoo))
|
||||||
|
enddef
|
||||||
|
|
||||||
|
var a = A.new()
|
||||||
|
assert_equal(1, empty(a))
|
||||||
|
Foo()
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
" Unsupported signature for the empty() method
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
def empty()
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
END
|
||||||
|
call v9.CheckSourceFailure(lines, 'E1383: Method "empty": type mismatch, expected func(): bool but got func()', 4)
|
||||||
|
|
||||||
|
" Error when calling the empty() method
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
def empty(): bool
|
||||||
|
throw "Failed to check emptiness"
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
def Foo()
|
||||||
|
var afoo = A.new()
|
||||||
|
var i = empty(afoo)
|
||||||
|
enddef
|
||||||
|
|
||||||
|
var a = A.new()
|
||||||
|
assert_fails('empty(a)', 'Failed to check emptiness')
|
||||||
|
assert_fails('Foo()', 'Failed to check emptiness')
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
" call empty() using an object from a script
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
def empty(): bool
|
||||||
|
return true
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
var afoo = A.new()
|
||||||
|
assert_equal(true, afoo.empty())
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
" call empty() using an object from a method
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
def empty(): bool
|
||||||
|
return true
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
def Foo()
|
||||||
|
var afoo = A.new()
|
||||||
|
assert_equal(true, afoo.empty())
|
||||||
|
enddef
|
||||||
|
Foo()
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
" call empty() using "this" from an object method
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
def empty(): bool
|
||||||
|
return true
|
||||||
|
enddef
|
||||||
|
def Foo(): bool
|
||||||
|
return this.empty()
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
def Bar()
|
||||||
|
var abar = A.new()
|
||||||
|
assert_equal(true, abar.Foo())
|
||||||
|
enddef
|
||||||
|
Bar()
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
" Call empty() from a derived object
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
def empty(): bool
|
||||||
|
return false
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
class B extends A
|
||||||
|
def empty(): bool
|
||||||
|
return true
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
def Foo(afoo: A)
|
||||||
|
assert_equal(true, empty(afoo))
|
||||||
|
var bfoo = B.new()
|
||||||
|
assert_equal(true, empty(bfoo))
|
||||||
|
enddef
|
||||||
|
var b = B.new()
|
||||||
|
assert_equal(1, empty(b))
|
||||||
|
Foo(b)
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
" Invoking empty method using an interface
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
interface A
|
||||||
|
def empty(): bool
|
||||||
|
endinterface
|
||||||
|
class B implements A
|
||||||
|
def empty(): bool
|
||||||
|
return false
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
def Foo(a: A)
|
||||||
|
assert_equal(false, empty(a))
|
||||||
|
enddef
|
||||||
|
var b = B.new()
|
||||||
|
Foo(b)
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" Test for using the len() builtin method with an object
|
||||||
|
" This is a legacy function to use the test_garbagecollect_now() function.
|
||||||
|
func Test_object_length()
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
var mylen: number = 0
|
||||||
|
def new(n: number)
|
||||||
|
this.mylen = n
|
||||||
|
enddef
|
||||||
|
def len(): number
|
||||||
|
return this.mylen
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
def Foo()
|
||||||
|
var afoo = A.new(12)
|
||||||
|
assert_equal(12, len(afoo))
|
||||||
|
assert_equal(12, afoo->len())
|
||||||
|
enddef
|
||||||
|
|
||||||
|
var a = A.new(22)
|
||||||
|
assert_equal(22, len(a))
|
||||||
|
assert_equal(22, a->len())
|
||||||
|
test_garbagecollect_now()
|
||||||
|
assert_equal(22, len(a))
|
||||||
|
Foo()
|
||||||
|
test_garbagecollect_now()
|
||||||
|
Foo()
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
" len() should return 0 without a builtin method
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
endclass
|
||||||
|
|
||||||
|
def Foo()
|
||||||
|
var afoo = A.new()
|
||||||
|
assert_equal(0, len(afoo))
|
||||||
|
enddef
|
||||||
|
|
||||||
|
var a = A.new()
|
||||||
|
assert_equal(0, len(a))
|
||||||
|
Foo()
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
" Unsupported signature for the len() method
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
def len()
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
END
|
||||||
|
call v9.CheckSourceFailure(lines, 'E1383: Method "len": type mismatch, expected func(): number but got func()', 4)
|
||||||
|
|
||||||
|
" Error when calling the len() method
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
def len(): number
|
||||||
|
throw "Failed to compute length"
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
def Foo()
|
||||||
|
var afoo = A.new()
|
||||||
|
var i = len(afoo)
|
||||||
|
enddef
|
||||||
|
|
||||||
|
var a = A.new()
|
||||||
|
assert_fails('len(a)', 'Failed to compute length')
|
||||||
|
assert_fails('Foo()', 'Failed to compute length')
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
" call len() using an object from a script
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
def len(): number
|
||||||
|
return 5
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
var afoo = A.new()
|
||||||
|
assert_equal(5, afoo.len())
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
" call len() using an object from a method
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
def len(): number
|
||||||
|
return 5
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
def Foo()
|
||||||
|
var afoo = A.new()
|
||||||
|
assert_equal(5, afoo.len())
|
||||||
|
enddef
|
||||||
|
Foo()
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
" call len() using "this" from an object method
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
def len(): number
|
||||||
|
return 8
|
||||||
|
enddef
|
||||||
|
def Foo(): number
|
||||||
|
return this.len()
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
def Bar()
|
||||||
|
var abar = A.new()
|
||||||
|
assert_equal(8, abar.Foo())
|
||||||
|
enddef
|
||||||
|
Bar()
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
" Call len() from a derived object
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
def len(): number
|
||||||
|
return 10
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
class B extends A
|
||||||
|
def len(): number
|
||||||
|
return 20
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
def Foo(afoo: A)
|
||||||
|
assert_equal(20, len(afoo))
|
||||||
|
var bfoo = B.new()
|
||||||
|
assert_equal(20, len(bfoo))
|
||||||
|
enddef
|
||||||
|
var b = B.new()
|
||||||
|
assert_equal(20, len(b))
|
||||||
|
Foo(b)
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
" Invoking len method using an interface
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
interface A
|
||||||
|
def len(): number
|
||||||
|
endinterface
|
||||||
|
class B implements A
|
||||||
|
def len(): number
|
||||||
|
return 123
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
def Foo(a: A)
|
||||||
|
assert_equal(123, len(a))
|
||||||
|
enddef
|
||||||
|
var b = B.new()
|
||||||
|
Foo(b)
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" Test for using the string() builtin method with an object
|
||||||
|
" This is a legacy function to use the test_garbagecollect_now() function.
|
||||||
|
func Test_object_string()
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
var name: string
|
||||||
|
def string(): string
|
||||||
|
return this.name
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
def Foo()
|
||||||
|
var afoo = A.new("foo-A")
|
||||||
|
assert_equal('foo-A', string(afoo))
|
||||||
|
assert_equal('foo-A', afoo->string())
|
||||||
|
enddef
|
||||||
|
|
||||||
|
var a = A.new("script-A")
|
||||||
|
assert_equal('script-A', string(a))
|
||||||
|
assert_equal('script-A', a->string())
|
||||||
|
assert_equal(['script-A'], execute('echo a')->split("\n"))
|
||||||
|
test_garbagecollect_now()
|
||||||
|
assert_equal('script-A', string(a))
|
||||||
|
Foo()
|
||||||
|
test_garbagecollect_now()
|
||||||
|
Foo()
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
" string() should return "object of A {}" without a builtin method
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
endclass
|
||||||
|
|
||||||
|
def Foo()
|
||||||
|
var afoo = A.new()
|
||||||
|
assert_equal('object of A {}', string(afoo))
|
||||||
|
enddef
|
||||||
|
|
||||||
|
var a = A.new()
|
||||||
|
assert_equal('object of A {}', string(a))
|
||||||
|
Foo()
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
" Unsupported signature for the string() method
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
def string()
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
END
|
||||||
|
call v9.CheckSourceFailure(lines, 'E1383: Method "string": type mismatch, expected func(): string but got func()', 4)
|
||||||
|
|
||||||
|
" Error when calling the string() method
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
def string(): string
|
||||||
|
throw "Failed to get text"
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
def Foo()
|
||||||
|
var afoo = A.new()
|
||||||
|
var i = string(afoo)
|
||||||
|
enddef
|
||||||
|
|
||||||
|
var a = A.new()
|
||||||
|
assert_fails('string(a)', 'Failed to get text')
|
||||||
|
assert_fails('Foo()', 'Failed to get text')
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
" call string() using an object from a script
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
def string(): string
|
||||||
|
return 'A'
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
var afoo = A.new()
|
||||||
|
assert_equal('A', afoo.string())
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
" call string() using an object from a method
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
def string(): string
|
||||||
|
return 'A'
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
def Foo()
|
||||||
|
var afoo = A.new()
|
||||||
|
assert_equal('A', afoo.string())
|
||||||
|
enddef
|
||||||
|
Foo()
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
" call string() using "this" from an object method
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
def string(): string
|
||||||
|
return 'A'
|
||||||
|
enddef
|
||||||
|
def Foo(): string
|
||||||
|
return this.string()
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
def Bar()
|
||||||
|
var abar = A.new()
|
||||||
|
assert_equal('A', abar.string())
|
||||||
|
enddef
|
||||||
|
Bar()
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
" Call string() from a derived object
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
def string(): string
|
||||||
|
return 'A'
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
class B extends A
|
||||||
|
def string(): string
|
||||||
|
return 'B'
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
def Foo(afoo: A)
|
||||||
|
assert_equal('B', string(afoo))
|
||||||
|
var bfoo = B.new()
|
||||||
|
assert_equal('B', string(bfoo))
|
||||||
|
enddef
|
||||||
|
var b = B.new()
|
||||||
|
assert_equal('B', string(b))
|
||||||
|
Foo(b)
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
" Invoking string method using an interface
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
interface A
|
||||||
|
def string(): string
|
||||||
|
endinterface
|
||||||
|
class B implements A
|
||||||
|
def string(): string
|
||||||
|
return 'B'
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
def Foo(a: A)
|
||||||
|
assert_equal('B', string(a))
|
||||||
|
enddef
|
||||||
|
var b = B.new()
|
||||||
|
Foo(b)
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
endfunc
|
||||||
|
|
||||||
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
||||||
|
@@ -3273,4 +3273,167 @@ def Test_funcref_with_class()
|
|||||||
unlet g:instr
|
unlet g:instr
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
" Disassemble instructions for calls to a string() function in an object
|
||||||
|
def Test_disassemble_object_string()
|
||||||
|
var lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
def string(): string
|
||||||
|
return 'A'
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
def Bar()
|
||||||
|
var a = A.new()
|
||||||
|
var s = string(a)
|
||||||
|
s = string(A)
|
||||||
|
enddef
|
||||||
|
g:instr = execute('disassemble Bar')
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
|
assert_match('<SNR>\d*_Bar\_s*' ..
|
||||||
|
'var a = A.new()\_s*' ..
|
||||||
|
'0 DCALL new(argc 0)\_s*' ..
|
||||||
|
'1 STORE $0\_s*' ..
|
||||||
|
'var s = string(a)\_s*' ..
|
||||||
|
'2 LOAD $0\_s*' ..
|
||||||
|
'3 METHODCALL A.string(argc 0)\_s*' ..
|
||||||
|
'4 STORE $1\_s*' ..
|
||||||
|
's = string(A)\_s*' ..
|
||||||
|
'5 LOADSCRIPT A-0 from .*\_s*' ..
|
||||||
|
'6 BCALL string(argc 1)\_s*' ..
|
||||||
|
'7 STORE $1\_s*' ..
|
||||||
|
'8 RETURN void', g:instr)
|
||||||
|
unlet g:instr
|
||||||
|
|
||||||
|
# Use the default string() function for a class
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
endclass
|
||||||
|
def Bar()
|
||||||
|
var a = A.new()
|
||||||
|
var s = string(a)
|
||||||
|
s = string(A)
|
||||||
|
enddef
|
||||||
|
g:instr = execute('disassemble Bar')
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
|
assert_match('<SNR>\d*_Bar\_s*' ..
|
||||||
|
'var a = A.new()\_s*' ..
|
||||||
|
'0 DCALL new(argc 0)\_s*' ..
|
||||||
|
'1 STORE $0\_s*' ..
|
||||||
|
'var s = string(a)\_s*' ..
|
||||||
|
'2 LOAD $0\_s*' ..
|
||||||
|
'3 BCALL string(argc 1)\_s*' ..
|
||||||
|
'4 STORE $1\_s*' ..
|
||||||
|
's = string(A)\_s*' ..
|
||||||
|
'5 LOADSCRIPT A-0 from .*\_s*' ..
|
||||||
|
'6 BCALL string(argc 1)\_s*' ..
|
||||||
|
'7 STORE $1\_s*' ..
|
||||||
|
'8 RETURN void', g:instr)
|
||||||
|
unlet g:instr
|
||||||
|
enddef
|
||||||
|
|
||||||
|
" Disassemble instructions for calls to a empty() function in an object
|
||||||
|
def Test_disassemble_object_empty()
|
||||||
|
var lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
def empty(): bool
|
||||||
|
return true
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
def Bar()
|
||||||
|
var a = A.new()
|
||||||
|
var s = empty(a)
|
||||||
|
enddef
|
||||||
|
g:instr = execute('disassemble Bar')
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
|
assert_match('<SNR>\d*_Bar\_s*' ..
|
||||||
|
'var a = A.new()\_s*' ..
|
||||||
|
'0 DCALL new(argc 0)\_s*' ..
|
||||||
|
'1 STORE $0\_s*' ..
|
||||||
|
'var s = empty(a)\_s*' ..
|
||||||
|
'2 LOAD $0\_s*' ..
|
||||||
|
'3 METHODCALL A.empty(argc 0)\_s*' ..
|
||||||
|
'4 STORE $1\_s*' ..
|
||||||
|
'5 RETURN void', g:instr)
|
||||||
|
unlet g:instr
|
||||||
|
|
||||||
|
# Use the default empty() function for a class
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
endclass
|
||||||
|
def Bar()
|
||||||
|
var a = A.new()
|
||||||
|
var s = empty(a)
|
||||||
|
enddef
|
||||||
|
g:instr = execute('disassemble Bar')
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
|
assert_match('<SNR>\d*_Bar\_s*' ..
|
||||||
|
'var a = A.new()\_s*' ..
|
||||||
|
'0 DCALL new(argc 0)\_s*' ..
|
||||||
|
'1 STORE $0\_s*' ..
|
||||||
|
'var s = empty(a)\_s*' ..
|
||||||
|
'2 LOAD $0\_s*' ..
|
||||||
|
'3 BCALL empty(argc 1)\_s*' ..
|
||||||
|
'4 STORE $1\_s*' ..
|
||||||
|
'5 RETURN void', g:instr)
|
||||||
|
unlet g:instr
|
||||||
|
enddef
|
||||||
|
|
||||||
|
" Disassemble instructions for calls to a len() function in an object
|
||||||
|
def Test_disassemble_object_len()
|
||||||
|
var lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
def len(): number
|
||||||
|
return 10
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
def Bar()
|
||||||
|
var a = A.new()
|
||||||
|
var s = len(a)
|
||||||
|
enddef
|
||||||
|
g:instr = execute('disassemble Bar')
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
|
assert_match('<SNR>\d*_Bar\_s*' ..
|
||||||
|
'var a = A.new()\_s*' ..
|
||||||
|
'0 DCALL new(argc 0)\_s*' ..
|
||||||
|
'1 STORE $0\_s*' ..
|
||||||
|
'var s = len(a)\_s*' ..
|
||||||
|
'2 LOAD $0\_s*' ..
|
||||||
|
'3 METHODCALL A.len(argc 0)\_s*' ..
|
||||||
|
'4 STORE $1\_s*' ..
|
||||||
|
'5 RETURN void', g:instr)
|
||||||
|
unlet g:instr
|
||||||
|
|
||||||
|
# Use the default len() function for a class
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
endclass
|
||||||
|
def Bar()
|
||||||
|
var a = A.new()
|
||||||
|
var s = len(a)
|
||||||
|
enddef
|
||||||
|
g:instr = execute('disassemble Bar')
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
|
assert_match('<SNR>\d*_Bar\_s*' ..
|
||||||
|
'var a = A.new()\_s*' ..
|
||||||
|
'0 DCALL new(argc 0)\_s*' ..
|
||||||
|
'1 STORE $0\_s*' ..
|
||||||
|
'var s = len(a)\_s*' ..
|
||||||
|
'2 LOAD $0\_s*' ..
|
||||||
|
'3 BCALL len(argc 1)\_s*' ..
|
||||||
|
'4 STORE $1\_s*' ..
|
||||||
|
'5 RETURN void', g:instr)
|
||||||
|
unlet g:instr
|
||||||
|
enddef
|
||||||
|
|
||||||
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
||||||
|
@@ -4459,12 +4459,13 @@ trans_function_name_ext(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The function name must start with an upper case letter (unless it is a
|
// The function name must start with an upper case letter (unless it is a
|
||||||
// Vim9 class new() function or a Vim9 class private method)
|
// Vim9 class new() function or a Vim9 class private method or one of the
|
||||||
|
// supported Vim9 object builtin functions)
|
||||||
else if (!(flags & TFN_INT)
|
else if (!(flags & TFN_INT)
|
||||||
&& (builtin_function(lv.ll_name, len)
|
&& (builtin_function(lv.ll_name, len)
|
||||||
|| (vim9script && *lv.ll_name == '_'))
|
|| (vim9script && *lv.ll_name == '_'))
|
||||||
&& !((flags & TFN_IN_CLASS)
|
&& !((flags & TFN_IN_CLASS)
|
||||||
&& (STRNCMP(lv.ll_name, "new", 3) == 0
|
&& (is_valid_builtin_obj_methodname(lv.ll_name)
|
||||||
|| (*lv.ll_name == '_'))))
|
|| (*lv.ll_name == '_'))))
|
||||||
{
|
{
|
||||||
semsg(_(vim9script ? e_function_name_must_start_with_capital_str
|
semsg(_(vim9script ? e_function_name_must_start_with_capital_str
|
||||||
|
@@ -704,6 +704,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
148,
|
||||||
/**/
|
/**/
|
||||||
147,
|
147,
|
||||||
/**/
|
/**/
|
||||||
|
279
src/vim9class.c
279
src/vim9class.c
@@ -973,6 +973,100 @@ is_valid_constructor(ufunc_T *uf, int is_abstract, int has_static)
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns TRUE if 'uf' is a supported builtin method and has the correct
|
||||||
|
* method signature.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
object_check_builtin_method_sig(ufunc_T *uf)
|
||||||
|
{
|
||||||
|
char_u *name = uf->uf_name;
|
||||||
|
int valid = FALSE;
|
||||||
|
type_T method_sig;
|
||||||
|
type_T method_rt;
|
||||||
|
where_T where = WHERE_INIT;
|
||||||
|
|
||||||
|
// validate the method signature
|
||||||
|
CLEAR_FIELD(method_sig);
|
||||||
|
CLEAR_FIELD(method_rt);
|
||||||
|
method_sig.tt_type = VAR_FUNC;
|
||||||
|
|
||||||
|
if (STRCMP(name, "len") == 0)
|
||||||
|
{
|
||||||
|
// def __len(): number
|
||||||
|
method_rt.tt_type = VAR_NUMBER;
|
||||||
|
method_sig.tt_member = &method_rt;
|
||||||
|
valid = TRUE;
|
||||||
|
}
|
||||||
|
else if (STRCMP(name, "empty") == 0)
|
||||||
|
{
|
||||||
|
// def __empty(): bool
|
||||||
|
method_rt.tt_type = VAR_BOOL;
|
||||||
|
method_sig.tt_member = &method_rt;
|
||||||
|
valid = TRUE;
|
||||||
|
}
|
||||||
|
else if (STRCMP(name, "string") == 0)
|
||||||
|
{
|
||||||
|
// def __string(): string
|
||||||
|
method_rt.tt_type = VAR_STRING;
|
||||||
|
method_sig.tt_member = &method_rt;
|
||||||
|
valid = TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
semsg(_(e_builtin_object_method_str_not_supported), uf->uf_name);
|
||||||
|
|
||||||
|
where.wt_func_name = (char *)uf->uf_name;
|
||||||
|
where.wt_kind = WT_METHOD;
|
||||||
|
if (valid && !check_type(&method_sig, uf->uf_func_type, TRUE, where))
|
||||||
|
valid = FALSE;
|
||||||
|
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns TRUE if "funcname" is a supported builtin object method name
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
is_valid_builtin_obj_methodname(char_u *funcname)
|
||||||
|
{
|
||||||
|
switch (funcname[0])
|
||||||
|
{
|
||||||
|
case 'e':
|
||||||
|
return STRNCMP(funcname, "empty", 5) == 0;
|
||||||
|
|
||||||
|
case 'l':
|
||||||
|
return STRNCMP(funcname, "len", 3) == 0;
|
||||||
|
|
||||||
|
case 'n':
|
||||||
|
return STRNCMP(funcname, "new", 3) == 0;
|
||||||
|
|
||||||
|
case 's':
|
||||||
|
return STRNCMP(funcname, "string", 6) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the builtin method "name" in object "obj". Returns NULL if the
|
||||||
|
* method is not found.
|
||||||
|
*/
|
||||||
|
ufunc_T *
|
||||||
|
class_get_builtin_method(
|
||||||
|
class_T *cl,
|
||||||
|
class_builtin_T builtin_method,
|
||||||
|
int *method_idx)
|
||||||
|
{
|
||||||
|
*method_idx = -1;
|
||||||
|
|
||||||
|
if (cl == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
*method_idx = cl->class_builtin_methods[builtin_method];
|
||||||
|
return *method_idx != -1 ? cl->class_obj_methods[*method_idx] : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Update the interface class lookup table for the member index on the
|
* Update the interface class lookup table for the member index on the
|
||||||
* interface to the member index in the class implementing the interface.
|
* interface to the member index in the class implementing the interface.
|
||||||
@@ -1326,6 +1420,33 @@ add_classfuncs_objmethods(
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the index of object methods called by builtin functions.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
update_builtin_method_index(class_T *cl)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < CLASS_BUILTIN_MAX; i++)
|
||||||
|
cl->class_builtin_methods[i] = -1;
|
||||||
|
|
||||||
|
for (i = 0; i < cl->class_obj_method_count; i++)
|
||||||
|
{
|
||||||
|
ufunc_T *uf = cl->class_obj_methods[i];
|
||||||
|
|
||||||
|
if (cl->class_builtin_methods[CLASS_BUILTIN_STRING] == -1
|
||||||
|
&& STRCMP(uf->uf_name, "string") == 0)
|
||||||
|
cl->class_builtin_methods[CLASS_BUILTIN_STRING] = i;
|
||||||
|
else if (cl->class_builtin_methods[CLASS_BUILTIN_EMPTY] == -1 &&
|
||||||
|
STRCMP(uf->uf_name, "empty") == 0)
|
||||||
|
cl->class_builtin_methods[CLASS_BUILTIN_EMPTY] = i;
|
||||||
|
else if (cl->class_builtin_methods[CLASS_BUILTIN_LEN] == -1 &&
|
||||||
|
STRCMP(uf->uf_name, "len") == 0)
|
||||||
|
cl->class_builtin_methods[CLASS_BUILTIN_LEN] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return the end of the class name starting at "arg". Valid characters in a
|
* Return the end of the class name starting at "arg". Valid characters in a
|
||||||
* class name are alphanumeric characters and "_". Also handles imported class
|
* class name are alphanumeric characters and "_". Also handles imported class
|
||||||
@@ -1721,13 +1842,10 @@ early_ret:
|
|||||||
&varname_end, &has_type, &type_list, &type,
|
&varname_end, &has_type, &type_list, &type,
|
||||||
is_class ? &init_expr: NULL) == FAIL)
|
is_class ? &init_expr: NULL) == FAIL)
|
||||||
break;
|
break;
|
||||||
if (is_reserved_varname(varname, varname_end))
|
|
||||||
{
|
if (is_reserved_varname(varname, varname_end)
|
||||||
vim_free(init_expr);
|
|| is_duplicate_variable(&classmembers, &objmembers,
|
||||||
break;
|
varname, varname_end))
|
||||||
}
|
|
||||||
if (is_duplicate_variable(&classmembers, &objmembers, varname,
|
|
||||||
varname_end))
|
|
||||||
{
|
{
|
||||||
vim_free(init_expr);
|
vim_free(init_expr);
|
||||||
break;
|
break;
|
||||||
@@ -1758,6 +1876,7 @@ early_ret:
|
|||||||
{
|
{
|
||||||
exarg_T ea;
|
exarg_T ea;
|
||||||
garray_T lines_to_free;
|
garray_T lines_to_free;
|
||||||
|
int is_new = STRNCMP(p, "new", 3) == 0;
|
||||||
|
|
||||||
if (has_public)
|
if (has_public)
|
||||||
{
|
{
|
||||||
@@ -1774,12 +1893,17 @@ early_ret:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*p == '_' && *(p + 1) == '_')
|
if (!is_class && *p == '_')
|
||||||
{
|
{
|
||||||
// double underscore prefix for a method name is currently
|
// private methods are not supported in an interface
|
||||||
// reserved. This could be used in the future to support
|
semsg(_(e_protected_method_not_supported_in_interface), p);
|
||||||
// object methods called by Vim builtin functions.
|
break;
|
||||||
semsg(_(e_cannot_use_reserved_name_str), p);
|
}
|
||||||
|
|
||||||
|
if (has_static && !is_new && SAFE_islower(*p) &&
|
||||||
|
is_valid_builtin_obj_methodname(p))
|
||||||
|
{
|
||||||
|
semsg(_(e_builtin_class_method_not_supported), p);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1803,9 +1927,9 @@ early_ret:
|
|||||||
if (uf != NULL)
|
if (uf != NULL)
|
||||||
{
|
{
|
||||||
char_u *name = uf->uf_name;
|
char_u *name = uf->uf_name;
|
||||||
int is_new = STRNCMP(name, "new", 3) == 0;
|
|
||||||
|
|
||||||
if (!is_class && *name == '_')
|
if (is_new && !is_valid_constructor(uf, is_abstract,
|
||||||
|
has_static))
|
||||||
{
|
{
|
||||||
// private variables are not supported in an interface
|
// private variables are not supported in an interface
|
||||||
semsg(_(e_protected_method_not_supported_in_interface),
|
semsg(_(e_protected_method_not_supported_in_interface),
|
||||||
@@ -1813,8 +1937,10 @@ early_ret:
|
|||||||
func_clear_free(uf, FALSE);
|
func_clear_free(uf, FALSE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (is_new && !is_valid_constructor(uf, is_abstract,
|
|
||||||
has_static))
|
// check for builtin method
|
||||||
|
if (!is_new && SAFE_islower(*name) &&
|
||||||
|
!object_check_builtin_method_sig(uf))
|
||||||
{
|
{
|
||||||
func_clear_free(uf, FALSE);
|
func_clear_free(uf, FALSE);
|
||||||
break;
|
break;
|
||||||
@@ -1997,6 +2123,8 @@ early_ret:
|
|||||||
&objmethods) == FAIL)
|
&objmethods) == FAIL)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
|
update_builtin_method_index(cl);
|
||||||
|
|
||||||
cl->class_type.tt_type = VAR_CLASS;
|
cl->class_type.tt_type = VAR_CLASS;
|
||||||
cl->class_type.tt_class = cl;
|
cl->class_type.tt_class = cl;
|
||||||
cl->class_object_type.tt_type = VAR_OBJECT;
|
cl->class_object_type.tt_type = VAR_OBJECT;
|
||||||
@@ -3272,6 +3400,125 @@ is_class_name(char_u *name, typval_T *rettv)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calls the object builtin method "name" with arguments "argv". The value
|
||||||
|
* returned by the builtin method is in "rettv". Returns OK or FAIL.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
object_call_builtin_method(
|
||||||
|
object_T *obj,
|
||||||
|
class_builtin_T builtin_method,
|
||||||
|
int argc,
|
||||||
|
typval_T *argv,
|
||||||
|
typval_T *rettv)
|
||||||
|
{
|
||||||
|
ufunc_T *uf;
|
||||||
|
int midx;
|
||||||
|
|
||||||
|
if (obj == NULL)
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
uf = class_get_builtin_method(obj->obj_class, builtin_method, &midx);
|
||||||
|
if (uf == NULL)
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
funccall_T *fc = create_funccal(uf, rettv);
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (fc == NULL)
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
++obj->obj_refcount;
|
||||||
|
|
||||||
|
r = call_def_function(uf, argc, argv, 0, NULL, obj, fc, rettv);
|
||||||
|
|
||||||
|
remove_funccal();
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calls the object "empty()" method and returns the method retun value. In
|
||||||
|
* case of an error, returns TRUE.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
object_empty(object_T *obj)
|
||||||
|
{
|
||||||
|
typval_T rettv;
|
||||||
|
|
||||||
|
if (object_call_builtin_method(obj, CLASS_BUILTIN_EMPTY, 0, NULL, &rettv)
|
||||||
|
== FAIL)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
return tv_get_bool(&rettv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use the object "len()" method to get an object length. Returns 0 if the
|
||||||
|
* method is not found or there is an error.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
object_len(object_T *obj)
|
||||||
|
{
|
||||||
|
typval_T rettv;
|
||||||
|
|
||||||
|
if (object_call_builtin_method(obj, CLASS_BUILTIN_LEN, 0, NULL, &rettv)
|
||||||
|
== FAIL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return tv_to_number(&rettv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return a textual representation of object "obj"
|
||||||
|
*/
|
||||||
|
char_u *
|
||||||
|
object_string(
|
||||||
|
object_T *obj,
|
||||||
|
char_u *numbuf,
|
||||||
|
int copyID,
|
||||||
|
int echo_style,
|
||||||
|
int restore_copyID,
|
||||||
|
int composite_val)
|
||||||
|
{
|
||||||
|
typval_T rettv;
|
||||||
|
|
||||||
|
if (object_call_builtin_method(obj, CLASS_BUILTIN_STRING, 0, NULL, &rettv)
|
||||||
|
== OK
|
||||||
|
&& rettv.vval.v_string != NULL)
|
||||||
|
return rettv.vval.v_string;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
garray_T ga;
|
||||||
|
ga_init2(&ga, 1, 50);
|
||||||
|
|
||||||
|
ga_concat(&ga, (char_u *)"object of ");
|
||||||
|
class_T *cl = obj == NULL ? NULL : obj->obj_class;
|
||||||
|
ga_concat(&ga, cl == NULL ? (char_u *)"[unknown]"
|
||||||
|
: cl->class_name);
|
||||||
|
if (cl != NULL)
|
||||||
|
{
|
||||||
|
ga_concat(&ga, (char_u *)" {");
|
||||||
|
for (int i = 0; i < cl->class_obj_member_count; ++i)
|
||||||
|
{
|
||||||
|
if (i > 0)
|
||||||
|
ga_concat(&ga, (char_u *)", ");
|
||||||
|
ocmember_T *m = &cl->class_obj_members[i];
|
||||||
|
ga_concat(&ga, m->ocm_name);
|
||||||
|
ga_concat(&ga, (char_u *)": ");
|
||||||
|
char_u *tf = NULL;
|
||||||
|
ga_concat(&ga, echo_string_core(
|
||||||
|
(typval_T *)(obj + 1) + i,
|
||||||
|
&tf, numbuf, copyID, echo_style,
|
||||||
|
restore_copyID, composite_val));
|
||||||
|
vim_free(tf);
|
||||||
|
}
|
||||||
|
ga_concat(&ga, (char_u *)"}");
|
||||||
|
}
|
||||||
|
return ga.ga_data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return TRUE when the class "cl", its base class or one of the implemented
|
* Return TRUE when the class "cl", its base class or one of the implemented
|
||||||
* interfaces matches the class "other_cl".
|
* interfaces matches the class "other_cl".
|
||||||
|
@@ -1013,6 +1013,32 @@ failret:
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compile a builtin method call of an object (e.g. string(), len(), empty(),
|
||||||
|
* etc.) if the class implements it.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
compile_builtin_method_call(cctx_T *cctx, class_builtin_T builtin_method)
|
||||||
|
{
|
||||||
|
type_T *type = get_decl_type_on_stack(cctx, 0);
|
||||||
|
int res = FAIL;
|
||||||
|
|
||||||
|
// If the built in function is invoked on an object and the class
|
||||||
|
// implements the corresponding built in method, then invoke the object
|
||||||
|
// method.
|
||||||
|
if (type->tt_type == VAR_OBJECT)
|
||||||
|
{
|
||||||
|
int method_idx;
|
||||||
|
ufunc_T *uf = class_get_builtin_method(type->tt_class, builtin_method,
|
||||||
|
&method_idx);
|
||||||
|
if (uf != NULL)
|
||||||
|
res = generate_CALL(cctx, uf, type->tt_class, method_idx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compile a function call: name(arg1, arg2)
|
* Compile a function call: name(arg1, arg2)
|
||||||
* "arg" points to "name", "arg + varlen" to the "(".
|
* "arg" points to "name", "arg + varlen" to the "(".
|
||||||
@@ -1170,6 +1196,20 @@ compile_call(
|
|||||||
idx = -1;
|
idx = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class_builtin_T builtin_method = CLASS_BUILTIN_INVALID;
|
||||||
|
if (STRCMP(name, "string") == 0)
|
||||||
|
builtin_method = CLASS_BUILTIN_STRING;
|
||||||
|
else if (STRCMP(name, "empty") == 0)
|
||||||
|
builtin_method = CLASS_BUILTIN_EMPTY;
|
||||||
|
else if (STRCMP(name, "len") == 0)
|
||||||
|
builtin_method = CLASS_BUILTIN_LEN;
|
||||||
|
if (builtin_method != CLASS_BUILTIN_INVALID)
|
||||||
|
{
|
||||||
|
res = compile_builtin_method_call(cctx, builtin_method);
|
||||||
|
if (res == OK)
|
||||||
|
idx = -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (idx >= 0)
|
if (idx >= 0)
|
||||||
res = generate_BCALL(cctx, idx, argcount, argcount_init == 1);
|
res = generate_BCALL(cctx, idx, argcount, argcount_init == 1);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user