diff src/vim9expr.c @ 33540:86dbcbb94fdb v9.0.2019

patch 9.0.2019: Vim9: no support for funcrefs Commit: https://github.com/vim/vim/commit/29bb67f1beefc7fd393dbfd9ee77d92f1db3a3c0 Author: Yegappan Lakshmanan <yegappan@yahoo.com> Date: Sat Oct 14 11:18:50 2023 +0200 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>
author Christian Brabandt <cb@256bit.org>
date Sat, 14 Oct 2023 11:30:07 +0200
parents bff8ac203a22
children c470d4fd5eba
line wrap: on
line diff
--- a/src/vim9expr.c
+++ b/src/vim9expr.c
@@ -281,6 +281,8 @@ inside_class_hierarchy(cctx_T *cctx_arg,
     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,
 		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,
 	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,
 
     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,
 	    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,
 	    *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 *cct
 	// 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);