Skip to content

Commit

Permalink
patch 9.1.0148: Vim9: can't call internal methods with objects
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
yegappan authored and chrisbra committed Mar 3, 2024
1 parent 2157035 commit d3eae7b
Show file tree
Hide file tree
Showing 16 changed files with 1,083 additions and 83 deletions.
12 changes: 10 additions & 2 deletions runtime/doc/builtin.txt
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -2265,6 +2265,8 @@ empty({expr}) *empty()*
- A |Job| is empty when it failed to start.
- A |Channel| is empty when it is closed.
- 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
length with zero.
Expand Down Expand Up @@ -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 |Dictionary| the number of entries in the
|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|: >
mylist->len()
Expand Down Expand Up @@ -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
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|: >
mylist->string()
Expand Down
6 changes: 6 additions & 0 deletions runtime/doc/tags
Original file line number Diff line number Diff line change
Expand Up @@ -4518,6 +4518,8 @@ E1409 vim9class.txt /*E1409*
E141 message.txt /*E141*
E1410 vim9class.txt /*E1410*
E1411 vim9class.txt /*E1411*
E1412 vim9class.txt /*E1412*
E1413 vim9class.txt /*E1413*
E142 message.txt /*E142*
E143 autocmd.txt /*E143*
E144 various.txt /*E144*
Expand Down Expand Up @@ -6183,6 +6185,7 @@ bugs intro.txt /*bugs*
builtin-function-details builtin.txt /*builtin-function-details*
builtin-function-list builtin.txt /*builtin-function-list*
builtin-functions builtin.txt /*builtin-functions*
builtin-object-methods vim9class.txt /*builtin-object-methods*
builtin-terms term.txt /*builtin-terms*
builtin-tools gui.txt /*builtin-tools*
builtin.txt builtin.txt /*builtin.txt*
Expand Down Expand Up @@ -9153,9 +9156,12 @@ o_object-select motion.txt /*o_object-select*
o_v motion.txt /*o_v*
object vim9class.txt /*object*
object-const-variable vim9class.txt /*object-const-variable*
object-empty() vim9class.txt /*object-empty()*
object-final-variable vim9class.txt /*object-final-variable*
object-len() vim9class.txt /*object-len()*
object-motions motion.txt /*object-motions*
object-select motion.txt /*object-select*
object-string() vim9class.txt /*object-string()*
objects index.txt /*objects*
obtaining-exted netbeans.txt /*obtaining-exted*
ocaml.vim syntax.txt /*ocaml.vim*
Expand Down
3 changes: 1 addition & 2 deletions runtime/doc/todo.txt
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -132,7 +132,6 @@ Further Vim9 improvements:
Possibly issue #11981 can be fixed at the same time (has two examples).
- 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?
- 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
later (using a typedef). #12006
Expand Down
5 changes: 5 additions & 0 deletions runtime/doc/version9.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41543,6 +41543,11 @@ and is a work in progress.

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

Expand Down
28 changes: 27 additions & 1 deletion runtime/doc/vim9class.txt
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -710,6 +710,32 @@ The initialization isn't needed, the list is empty by default.
*E1330*
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 ~
*Interface* *:interface* *:endinterface*
Expand Down
6 changes: 5 additions & 1 deletion src/errors.h
Original file line number Diff line number Diff line change
Expand Up @@ -3579,8 +3579,12 @@ EXTERN char e_const_variable_not_supported_in_interface[]
INIT(= N_("E1410: Const variable not supported in an interface"));
EXTERN char e_missing_dot_after_object_str[]
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
// 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[]
INIT(= N_("E1500: Cannot mix positional and non-positional arguments: %s"));
EXTERN char e_fmt_arg_nr_unused_str[]
Expand Down
33 changes: 3 additions & 30 deletions src/eval.c
Original file line number Diff line number Diff line change
Expand Up @@ -6318,36 +6318,9 @@ echo_string_core(
break;

case VAR_OBJECT:
{
garray_T ga;
ga_init2(&ga, 1, 50);
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;
}
*tofree = r = object_string(tv->vval.v_object, numbuf, copyID,
echo_style, restore_copyID,
composite_val);
break;

case VAR_FLOAT:
Expand Down
7 changes: 5 additions & 2 deletions src/evalfunc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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_LIST
|| type->tt_type == VAR_DICT
|| type->tt_type == VAR_OBJECT
|| type_any_or_unknown(type))
return OK;

Expand Down Expand Up @@ -3981,7 +3982,7 @@ f_empty(typval_T *argvars, typval_T *rettv)
n = argvars[0].vval.v_class != NULL;
break;
case VAR_OBJECT:
n = argvars[0].vval.v_object != NULL;
n = object_empty(argvars[0].vval.v_object);
break;

case VAR_BLOB:
Expand Down Expand Up @@ -7831,6 +7832,9 @@ f_len(typval_T *argvars, typval_T *rettv)
case VAR_DICT:
rettv->vval.v_number = dict_len(argvars[0].vval.v_dict);
break;
case VAR_OBJECT:
rettv->vval.v_number = object_len(argvars[0].vval.v_object);
break;
case VAR_UNKNOWN:
case VAR_ANY:
case VAR_VOID:
Expand All @@ -7843,7 +7847,6 @@ f_len(typval_T *argvars, typval_T *rettv)
case VAR_CHANNEL:
case VAR_INSTR:
case VAR_CLASS:
case VAR_OBJECT:
case VAR_TYPEALIAS:
emsg(_(e_invalid_type_for_len));
break;
Expand Down
6 changes: 6 additions & 0 deletions src/proto/vim9class.pro
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/* vim9class.c */
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);
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);
Expand Down Expand Up @@ -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_classes_in_script(void);
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);
void f_instanceof(typval_T *argvars, typval_T *rettv);
/* vim: set ft=c : */
14 changes: 14 additions & 0 deletions src/structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1530,6 +1530,17 @@ typedef enum {
#define OCMFLAG_FINAL 0x02 // "final" 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.
*/
Expand Down Expand Up @@ -1593,6 +1604,9 @@ struct class_S
int class_obj_method_count_child; // count without "extends"
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
type_T class_type; // type used for the class
type_T class_object_type; // same as class_type but VAR_OBJECT
Expand Down

0 comments on commit d3eae7b

Please sign in to comment.