1
0
forked from aniani/vim

patch 9.0.2019: Vim9: no support for funcrefs

Problem:  Vim9: no support for funcrefs
Solution: Add support for object/class funcref members

closes: #11981 #12417 #12960 #12324 #13333

Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
This commit is contained in:
Yegappan Lakshmanan
2023-10-14 11:18:50 +02:00
committed by Christian Brabandt
parent 69fb5afb3b
commit 29bb67f1be
15 changed files with 742 additions and 51 deletions

View File

@@ -281,6 +281,8 @@ inside_class_hierarchy(cctx_T *cctx_arg, class_T *cl)
static int
compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
{
int m_idx;
if (VIM_ISWHITE((*arg)[1]))
{
semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
@@ -365,17 +367,34 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
break;
}
}
ocmember_T *ocm = NULL;
if (ufunc == NULL)
{
method_not_found_msg(cl, type->tt_type, name, len);
return FAIL;
// could be a funcref in a member variable
ocm = member_lookup(cl, type->tt_type, name, len, &m_idx);
if (ocm == NULL || ocm->ocm_type->tt_type != VAR_FUNC)
{
method_not_found_msg(cl, type->tt_type, name, len);
return FAIL;
}
if (type->tt_type == VAR_CLASS)
{
if (generate_CLASSMEMBER(cctx, TRUE, cl, m_idx) == FAIL)
return FAIL;
}
else
{
if (generate_GET_OBJ_MEMBER(cctx, m_idx, ocm->ocm_type) ==
FAIL)
return FAIL;
}
}
// A private object method can be used only inside the class where it
// is defined or in one of the child classes.
// A private class method can be used only in the class where it is
// defined.
if (*ufunc->uf_name == '_' &&
if (ocm == NULL && *ufunc->uf_name == '_' &&
((type->tt_type == VAR_OBJECT
&& !inside_class_hierarchy(cctx, cl))
|| (type->tt_type == VAR_CLASS
@@ -393,6 +412,8 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
if (compile_arguments(arg, cctx, &argcount, CA_NOT_SPECIAL) == FAIL)
return FAIL;
if (ocm != NULL)
return generate_PCALL(cctx, argcount, name, ocm->ocm_type, TRUE);
if (type->tt_type == VAR_OBJECT
&& (cl->class_flags & (CLASS_INTERFACE | CLASS_EXTENDED)))
return generate_CALL(cctx, ufunc, cl, fi, argcount);
@@ -401,7 +422,6 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
if (type->tt_type == VAR_OBJECT)
{
int m_idx;
ocmember_T *m = object_member_lookup(cl, name, len, &m_idx);
if (m_idx >= 0)
{
@@ -418,15 +438,21 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
return generate_GET_OBJ_MEMBER(cctx, m_idx, m->ocm_type);
}
// Could be a function reference: "obj.Func".
// Could be an object method reference: "obj.Func".
m_idx = object_method_idx(cl, name, len);
if (m_idx >= 0)
{
ufunc_T *fp = cl->class_obj_methods[m_idx];
if (type->tt_type == VAR_OBJECT
&& (cl->class_flags & (CLASS_INTERFACE | CLASS_EXTENDED)))
return generate_FUNCREF(cctx, fp, cl, m_idx, NULL);
return generate_FUNCREF(cctx, fp, NULL, 0, NULL);
// Private methods are not accessible outside the class
if (*name == '_' && !inside_class(cctx, cl))
{
semsg(_(e_cannot_access_private_method_str), fp->uf_name);
return FAIL;
}
*arg = name_end;
if (type->tt_type == VAR_OBJECT)
return generate_FUNCREF(cctx, fp, cl, TRUE, m_idx, NULL);
return generate_FUNCREF(cctx, fp, NULL, FALSE, 0, NULL);
}
member_not_found_msg(cl, VAR_OBJECT, name, len);
@@ -451,6 +477,24 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
*arg = name_end;
return generate_CLASSMEMBER(cctx, TRUE, cl, idx);
}
// Could be a class method reference: "class.Func".
m_idx = class_method_idx(cl, name, len);
if (m_idx >= 0)
{
ufunc_T *fp = cl->class_class_functions[m_idx];
// Private methods are not accessible outside the class
if (*name == '_' && !inside_class(cctx, cl))
{
semsg(_(e_cannot_access_private_method_str), fp->uf_name);
return FAIL;
}
*arg = name_end;
if (type->tt_type == VAR_CLASS)
return generate_FUNCREF(cctx, fp, cl, FALSE, m_idx, NULL);
return generate_FUNCREF(cctx, fp, NULL, FALSE, 0, NULL);
}
member_not_found_msg(cl, VAR_CLASS, name, len);
}
@@ -716,6 +760,7 @@ compile_load(
{
size_t len = end - *arg;
int idx;
int method_idx;
int gen_load = FALSE;
int gen_load_outer = 0;
int outer_loop_depth = -1;
@@ -764,13 +809,27 @@ compile_load(
else
gen_load = TRUE;
}
else if ((idx = cctx_class_member_idx(cctx, *arg, len, &cl)) >= 0)
else if (cctx->ctx_ufunc->uf_defclass != NULL &&
(((idx =
cctx_class_member_idx(cctx, *arg, len, &cl)) >= 0)
|| ((method_idx =
cctx_class_method_idx(cctx, *arg, len, &cl)) >= 0)))
{
// Referencing a class variable without the class name.
// A class variable can be referenced without the class name
// only in the class where the function is defined.
// Referencing a class variable or method without the class
// name. A class variable or method can be referenced without
// the class name only in the class where the function is
// defined.
if (cctx->ctx_ufunc->uf_defclass == cl)
res = generate_CLASSMEMBER(cctx, TRUE, cl, idx);
{
if (idx >= 0)
res = generate_CLASSMEMBER(cctx, TRUE, cl, idx);
else
{
ufunc_T *fp = cl->class_class_functions[method_idx];
res = generate_FUNCREF(cctx, fp, cl, FALSE, method_idx,
NULL);
}
}
else
{
semsg(_(e_class_variable_str_accessible_only_inside_class_str),
@@ -1387,7 +1446,7 @@ compile_lambda(char_u **arg, cctx_T *cctx)
// The function reference count will be 1. When the ISN_FUNCREF
// instruction is deleted the reference count is decremented and the
// function is freed.
return generate_FUNCREF(cctx, ufunc, NULL, 0, NULL);
return generate_FUNCREF(cctx, ufunc, NULL, FALSE, 0, NULL);
}
func_ptr_unref(ufunc);