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:
committed by
Christian Brabandt
parent
2157035637
commit
d3eae7bc11
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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".
|
||||
|
Reference in New Issue
Block a user