0
0
mirror of https://github.com/vim/vim.git synced 2025-09-24 03:44:06 -04:00

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:
Yegappan Lakshmanan
2024-03-03 16:26:58 +01:00
committed by Christian Brabandt
parent 2157035637
commit d3eae7bc11
16 changed files with 1083 additions and 83 deletions

View File

@@ -973,6 +973,100 @@ is_valid_constructor(ufunc_T *uf, int is_abstract, int has_static)
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
* interface to the member index in the class implementing the interface.
@@ -1326,6 +1420,33 @@ add_classfuncs_objmethods(
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
* class name are alphanumeric characters and "_". Also handles imported class
@@ -1721,13 +1842,10 @@ early_ret:
&varname_end, &has_type, &type_list, &type,
is_class ? &init_expr: NULL) == FAIL)
break;
if (is_reserved_varname(varname, varname_end))
{
vim_free(init_expr);
break;
}
if (is_duplicate_variable(&classmembers, &objmembers, varname,
varname_end))
if (is_reserved_varname(varname, varname_end)
|| is_duplicate_variable(&classmembers, &objmembers,
varname, varname_end))
{
vim_free(init_expr);
break;
@@ -1758,6 +1876,7 @@ early_ret:
{
exarg_T ea;
garray_T lines_to_free;
int is_new = STRNCMP(p, "new", 3) == 0;
if (has_public)
{
@@ -1774,12 +1893,17 @@ early_ret:
break;
}
if (*p == '_' && *(p + 1) == '_')
if (!is_class && *p == '_')
{
// double underscore prefix for a method name is currently
// reserved. This could be used in the future to support
// object methods called by Vim builtin functions.
semsg(_(e_cannot_use_reserved_name_str), p);
// private methods are not supported in an interface
semsg(_(e_protected_method_not_supported_in_interface), p);
break;
}
if (has_static && !is_new && SAFE_islower(*p) &&
is_valid_builtin_obj_methodname(p))
{
semsg(_(e_builtin_class_method_not_supported), p);
break;
}
@@ -1803,9 +1927,9 @@ early_ret:
if (uf != NULL)
{
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
semsg(_(e_protected_method_not_supported_in_interface),
@@ -1813,8 +1937,10 @@ early_ret:
func_clear_free(uf, FALSE);
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);
break;
@@ -1997,6 +2123,8 @@ early_ret:
&objmethods) == FAIL)
goto cleanup;
update_builtin_method_index(cl);
cl->class_type.tt_type = VAR_CLASS;
cl->class_type.tt_class = cl;
cl->class_object_type.tt_type = VAR_OBJECT;
@@ -3272,6 +3400,125 @@ is_class_name(char_u *name, typval_T *rettv)
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
* interfaces matches the class "other_cl".