changeset 33233:108d890d887f v9.0.1890

patch 9.0.1890: Vim9: lookup code for class/object repaeated Commit: https://github.com/vim/vim/commit/f36bbcd402c6ee5a27bcab3b20b6362ab93b8898 Author: Yegappan Lakshmanan <yegappan@yahoo.com> Date: Sun Sep 10 18:19:06 2023 +0200 patch 9.0.1890: Vim9: lookup code for class/object repaeated Problem: Vim9: lookup code for class/object repaeated Solution: Refactor and make use of lookup functions closes: #13067 Signed-off-by: Christian Brabandt <cb@256bit.org> Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
author Christian Brabandt <cb@256bit.org>
date Sun, 10 Sep 2023 18:30:04 +0200
parents d72f150af9f1
children e156c26a2a88
files src/eval.c src/proto/vim9class.pro src/proto/vim9compile.pro src/version.c src/vim9class.c src/vim9compile.c src/vim9execute.c src/vim9expr.c src/vim9instr.c
diffstat 9 files changed, 439 insertions(+), 319 deletions(-) [+]
line wrap: on
line diff
--- a/src/eval.c
+++ b/src/eval.c
@@ -1540,71 +1540,57 @@ get_lval(
 		    // round 1: class functions (skipped for an object)
 		    // round 2: object methods
 		    for (int round = v_type == VAR_OBJECT ? 2 : 1;
-							   round <= 2; ++round)
+							round <= 2; ++round)
 		    {
-			int count = round == 1
-					    ? cl->class_class_function_count
-					    : cl->class_obj_method_count;
-			ufunc_T **funcs = round == 1
-					    ? cl->class_class_functions
-					    : cl->class_obj_methods;
-			for (int i = 0; i < count; ++i)
+			int	m_idx;
+			ufunc_T	*fp;
+
+			fp = method_lookup(cl,
+				round == 1 ? VAR_CLASS : VAR_OBJECT,
+				key, p - key, &m_idx);
+			if (fp != NULL)
 			{
-			    ufunc_T *fp = funcs[i];
-			    char_u *ufname = (char_u *)fp->uf_name;
-			    if (STRNCMP(ufname, key, p - key) == 0
-						     && ufname[p - key] == NUL)
-			    {
-				lp->ll_ufunc = fp;
-				lp->ll_valtype = fp->uf_func_type;
-				round = 3;
-				break;
-			    }
+			    lp->ll_ufunc = fp;
+			    lp->ll_valtype = fp->uf_func_type;
+			    break;
 			}
 		    }
 		}
 
 		if (lp->ll_valtype == NULL)
 		{
-		    int count = v_type == VAR_OBJECT
-					    ? cl->class_obj_member_count
-					    : cl->class_class_member_count;
-		    ocmember_T *members = v_type == VAR_OBJECT
-					    ? cl->class_obj_members
-					    : cl->class_class_members;
-		    for (int i = 0; i < count; ++i)
+		    int		m_idx;
+		    ocmember_T	*om;
+
+		    om = member_lookup(cl, v_type, key, p - key, &m_idx);
+		    if (om != NULL)
 		    {
-			ocmember_T *om = members + i;
-			if (STRNCMP(om->ocm_name, key, p - key) == 0
-					       && om->ocm_name[p - key] == NUL)
+			switch (om->ocm_access)
 			{
-			    switch (om->ocm_access)
-			    {
-				case VIM_ACCESS_PRIVATE:
-					semsg(_(e_cannot_access_private_member_str),
-								 om->ocm_name);
-					return NULL;
-				case VIM_ACCESS_READ:
-					if ((flags & GLV_READ_ONLY) == 0)
-					{
-					    semsg(_(e_member_is_not_writable_str),
-								 om->ocm_name);
-					    return NULL;
-					}
-					break;
-				case VIM_ACCESS_ALL:
-					break;
-			    }
-
-			    lp->ll_valtype = om->ocm_type;
-
-			    if (v_type == VAR_OBJECT)
-				lp->ll_tv = ((typval_T *)(
-					    lp->ll_tv->vval.v_object + 1)) + i;
-			    else
-				lp->ll_tv = &cl->class_members_tv[i];
-			    break;
+			    case VIM_ACCESS_PRIVATE:
+				semsg(_(e_cannot_access_private_member_str),
+					om->ocm_name);
+				return NULL;
+			    case VIM_ACCESS_READ:
+				if ((flags & GLV_READ_ONLY) == 0)
+				{
+				    semsg(_(e_member_is_not_writable_str),
+					    om->ocm_name);
+				    return NULL;
+				}
+				break;
+			    case VIM_ACCESS_ALL:
+				break;
 			}
+
+			lp->ll_valtype = om->ocm_type;
+
+			if (v_type == VAR_OBJECT)
+			    lp->ll_tv = ((typval_T *)(
+					lp->ll_tv->vval.v_object + 1)) + m_idx;
+			else
+			    lp->ll_tv = &cl->class_members_tv[m_idx];
+			break;
 		    }
 		}
 
--- a/src/proto/vim9class.pro
+++ b/src/proto/vim9class.pro
@@ -6,8 +6,16 @@ void ex_enum(exarg_T *eap);
 void ex_type(exarg_T *eap);
 int class_object_index(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose);
 ufunc_T *find_class_func(char_u **arg);
-int class_member_index(char_u *name, size_t len, class_T **cl_ret, cctx_T *cctx);
-int class_method_index(char_u *name, size_t len, class_T **cl_ret, cctx_T *cctx);
+int class_member_idx(class_T *cl, char_u *name, size_t namelen);
+ocmember_T *class_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx);
+int class_method_idx(class_T *cl, char_u *name, size_t namelen);
+ufunc_T *class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx);
+int object_member_idx(class_T *cl, char_u *name, size_t namelen);
+ocmember_T *object_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx);
+int object_method_idx(class_T *cl, char_u *name, size_t namelen);
+ufunc_T *object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx);
+ocmember_T *member_lookup(class_T *cl, vartype_T v_type, char_u *name, size_t namelen, int *idx);
+ufunc_T *method_lookup(class_T *cl, vartype_T v_type, char_u *name, size_t namelen, int *idx);
 int inside_class(cctx_T *cctx_arg, class_T *cl);
 void copy_object(typval_T *from, typval_T *to);
 void object_unref(object_T *obj);
--- a/src/proto/vim9compile.pro
+++ b/src/proto/vim9compile.pro
@@ -4,6 +4,8 @@ int arg_exists(char_u *name, size_t len,
 void update_script_var_block_id(char_u *name, int block_id);
 int script_is_vim9(void);
 int script_var_exists(char_u *name, size_t len, cctx_T *cctx, cstack_T *cstack);
+int cctx_class_method_idx(cctx_T *cctx, char_u *name, size_t len, class_T **cl_ret);
+int cctx_class_member_idx(cctx_T *cctx, char_u *name, size_t len, class_T **cl_ret);
 int check_defined(char_u *p, size_t len, cctx_T *cctx, cstack_T *cstack, int is_arg);
 int need_type_where(type_T *actual, type_T *expected, int number_ok, int offset, where_T where, cctx_T *cctx, int silent, int actual_is_const);
 int need_type(type_T *actual, type_T *expected, int number_ok, int offset, int arg_idx, cctx_T *cctx, int silent, int actual_is_const);
--- a/src/version.c
+++ b/src/version.c
@@ -700,6 +700,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1890,
+/**/
     1889,
 /**/
     1888,
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -269,14 +269,10 @@ object_index_from_itf_index(class_T *itf
     {
 	// TODO: Need a table for fast lookup?
 	char_u *name = itf->class_class_members[idx].ocm_name;
-	for (int i = 0; i < i2c->i2c_class->class_class_member_count; ++i)
-	{
-	    ocmember_T *m = &i2c->i2c_class->class_class_members[i];
-	    if (STRCMP(name, m->ocm_name) == 0)
-	    {
-		return i;
-	    }
-	}
+	int	m_idx = class_member_idx(i2c->i2c_class, name, 0);
+	if (m_idx >= 0)
+	    return m_idx;
+
 	siemsg("class %s, interface %s, static %s not found",
 				      cl->class_name, itf->class_name, name);
 	return 0;
@@ -1751,27 +1747,23 @@ class_member_type(
     int		*member_idx,
     ocmember_T	**p_m)
 {
+    size_t	len = name_end - name;
+    ocmember_T	*m;
+
     *member_idx = -1;  // not found (yet)
-    size_t	len = name_end - name;
-    int		member_count = is_object ? cl->class_obj_member_count
-						: cl->class_class_member_count;
-    ocmember_T	*members = is_object ? cl->class_obj_members
-						: cl->class_class_members;
 
-    for (int i = 0; i < member_count; ++i)
+    m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, len,
+								member_idx);
+    if (m == NULL)
     {
-	ocmember_T *m = members + i;
-	if (STRNCMP(m->ocm_name, name, len) == 0 && m->ocm_name[len] == NUL)
-	{
-	    *member_idx = i;
-	    if (p_m != NULL)
-		*p_m = m;
-	    return m->ocm_type;
-	}
+	semsg(_(e_unknown_variable_str), name);
+	return &t_any;
     }
 
-    semsg(_(e_unknown_variable_str), name);
-    return &t_any;
+    if (p_m != NULL)
+	*p_m = m;
+
+    return m->ocm_type;
 }
 
 /*
@@ -1806,41 +1798,34 @@ get_member_tv(
     size_t	namelen,
     typval_T	*rettv)
 {
-    int member_count = is_object ? cl->class_obj_member_count
-						: cl->class_class_member_count;
-    ocmember_T *members = is_object ? cl->class_obj_members
-						: cl->class_class_members;
+    ocmember_T *m;
+    int		m_idx;
 
-    for (int i = 0; i < member_count; ++i)
+    m = member_lookup(cl, is_object ? VAR_OBJECT : VAR_CLASS, name, namelen,
+								&m_idx);
+    if (m == NULL)
+	return FAIL;
+
+    if (*name == '_')
     {
-	ocmember_T *m = &members[i];
-	if (STRNCMP(name, m->ocm_name, namelen) == 0
-						&& m->ocm_name[namelen] == NUL)
-	{
-	    if (*name == '_')
-	    {
-		semsg(_(e_cannot_access_private_member_str), m->ocm_name);
-		return FAIL;
-	    }
-
-	    // The object only contains a pointer to the class, the member
-	    // values array follows right after that.
-	    object_T *obj = rettv->vval.v_object;
-	    if (is_object)
-	    {
-		typval_T *tv = (typval_T *)(obj + 1) + i;
-		copy_tv(tv, rettv);
-	    }
-	    else
-		copy_tv(&cl->class_members_tv[i], rettv);
-
-	    object_unref(obj);
-
-	    return OK;
-	}
+	semsg(_(e_cannot_access_private_member_str), m->ocm_name);
+	return FAIL;
     }
 
-    return FAIL;
+    // The object only contains a pointer to the class, the member
+    // values array follows right after that.
+    object_T *obj = rettv->vval.v_object;
+    if (is_object)
+    {
+	typval_T *tv = (typval_T *)(obj + 1) + m_idx;
+	copy_tv(tv, rettv);
+    }
+    else
+	copy_tv(&cl->class_members_tv[m_idx], rettv);
+
+    object_unref(obj);
+
+    return OK;
 }
 
 /*
@@ -1897,69 +1882,64 @@ class_object_index(
 
     if (*name_end == '(')
     {
-	int on_class = rettv->v_type == VAR_CLASS;
-	int count = on_class ? cl->class_class_function_count
-			     : cl->class_obj_method_count;
-	for (int i = 0; i < count; ++i)
-	{
-	    ufunc_T *fp = on_class ? cl->class_class_functions[i]
-				   : cl->class_obj_methods[i];
-	    // Use a separate pointer to avoid that ASAN complains about
-	    // uf_name[] only being 4 characters.
-	    char_u *ufname = (char_u *)fp->uf_name;
-	    if (STRNCMP(name, ufname, len) == 0 && ufname[len] == NUL)
-	    {
-		typval_T	argvars[MAX_FUNC_ARGS + 1];
-		int		argcount = 0;
-
-		if (*ufname == '_')
-		{
-		    // Cannot access a private method outside of a class
-		    semsg(_(e_cannot_access_private_method_str), name);
-		    return FAIL;
-		}
-
-		char_u *argp = name_end;
-		int ret = get_func_arguments(&argp, evalarg, 0,
-							   argvars, &argcount);
-		if (ret == FAIL)
-		    return FAIL;
+	ufunc_T *fp;
+	int	m_idx;
 
-		funcexe_T funcexe;
-		CLEAR_FIELD(funcexe);
-		funcexe.fe_evaluate = TRUE;
-		if (rettv->v_type == VAR_OBJECT)
-		{
-		    funcexe.fe_object = rettv->vval.v_object;
-		    ++funcexe.fe_object->obj_refcount;
-		}
-
-		// Clear the class or object after calling the function, in
-		// case the refcount is one.
-		typval_T tv_tofree = *rettv;
-		rettv->v_type = VAR_UNKNOWN;
+	fp = method_lookup(cl, rettv->v_type, name, len, &m_idx);
+	if (fp == NULL)
+	{
+	    semsg(_(e_method_not_found_on_class_str_str), cl->class_name,
+		    name);
+	    return FAIL;
+	}
 
-		// Call the user function.  Result goes into rettv;
-		int error = call_user_func_check(fp, argcount, argvars,
-							rettv, &funcexe, NULL);
-
-		// Clear the previous rettv and the arguments.
-		clear_tv(&tv_tofree);
-		for (int idx = 0; idx < argcount; ++idx)
-		    clear_tv(&argvars[idx]);
+	typval_T	argvars[MAX_FUNC_ARGS + 1];
+	int		argcount = 0;
 
-		if (error != FCERR_NONE)
-		{
-		    user_func_error(error, printable_func_name(fp),
-							 funcexe.fe_found_var);
-		    return FAIL;
-		}
-		*arg = argp;
-		return OK;
-	    }
+	if (*fp->uf_name == '_')
+	{
+	    // Cannot access a private method outside of a class
+	    semsg(_(e_cannot_access_private_method_str), name);
+	    return FAIL;
 	}
 
-	semsg(_(e_method_not_found_on_class_str_str), cl->class_name, name);
+	char_u *argp = name_end;
+	int ret = get_func_arguments(&argp, evalarg, 0,
+		argvars, &argcount);
+	if (ret == FAIL)
+	    return FAIL;
+
+	funcexe_T funcexe;
+	CLEAR_FIELD(funcexe);
+	funcexe.fe_evaluate = TRUE;
+	if (rettv->v_type == VAR_OBJECT)
+	{
+	    funcexe.fe_object = rettv->vval.v_object;
+	    ++funcexe.fe_object->obj_refcount;
+	}
+
+	// Clear the class or object after calling the function, in
+	// case the refcount is one.
+	typval_T tv_tofree = *rettv;
+	rettv->v_type = VAR_UNKNOWN;
+
+	// Call the user function.  Result goes into rettv;
+	int error = call_user_func_check(fp, argcount, argvars,
+		rettv, &funcexe, NULL);
+
+	// Clear the previous rettv and the arguments.
+	clear_tv(&tv_tofree);
+	for (int idx = 0; idx < argcount; ++idx)
+	    clear_tv(&argvars[idx]);
+
+	if (error != FCERR_NONE)
+	{
+	    user_func_error(error, printable_func_name(fp),
+		    funcexe.fe_found_var);
+	    return FAIL;
+	}
+	*arg = argp;
+	return OK;
     }
 
     else if (rettv->v_type == VAR_OBJECT)
@@ -1977,34 +1957,34 @@ class_object_index(
 
     else if (rettv->v_type == VAR_CLASS)
     {
+	int	    m_idx;
+
 	// class member
-	for (int i = 0; i < cl->class_class_member_count; ++i)
+	ocmember_T *m = class_member_lookup(cl, name, len, &m_idx);
+	if (m == NULL)
 	{
-	    ocmember_T *m = &cl->class_class_members[i];
-	    if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
-	    {
-		if (*name == '_')
-		{
-		    semsg(_(e_cannot_access_private_member_str), m->ocm_name);
-		    return FAIL;
-		}
-		if ((cl->class_flags & CLASS_INTERFACE) != 0)
-		{
-		    semsg(_(e_interface_static_direct_access_str),
-						cl->class_name, m->ocm_name);
-		    return FAIL;
-		}
-
-		typval_T *tv = &cl->class_members_tv[i];
-		copy_tv(tv, rettv);
-		class_unref(cl);
-
-		*arg = name_end;
-		return OK;
-	    }
+	    semsg(_(e_member_not_found_on_class_str_str), cl->class_name, name);
+	    return FAIL;
 	}
 
-	semsg(_(e_member_not_found_on_class_str_str), cl->class_name, name);
+	if (*name == '_')
+	{
+	    semsg(_(e_cannot_access_private_member_str), m->ocm_name);
+	    return FAIL;
+	}
+	if ((cl->class_flags & CLASS_INTERFACE) != 0)
+	{
+	    semsg(_(e_interface_static_direct_access_str),
+		    cl->class_name, m->ocm_name);
+	    return FAIL;
+	}
+
+	typval_T *tv = &cl->class_members_tv[m_idx];
+	copy_tv(tv, rettv);
+	class_unref(cl);
+
+	*arg = name_end;
+	return OK;
     }
 
     return FAIL;
@@ -2022,6 +2002,7 @@ find_class_func(char_u **arg)
     if (name_end == name || *name_end != '.')
 	return NULL;
 
+    ufunc_T	*fp = NULL;
     size_t	len = name_end - name;
     typval_T	tv;
     tv.v_type = VAR_UNKNOWN;
@@ -2041,83 +2022,192 @@ find_class_func(char_u **arg)
 	goto fail_after_eval;
     len = fname_end - fname;
 
-    int count = tv.v_type == VAR_CLASS ? cl->class_class_function_count
-				       : cl->class_obj_method_count;
-    ufunc_T **funcs = tv.v_type == VAR_CLASS ? cl->class_class_functions
-					     : cl->class_obj_methods;
-    for (int i = 0; i < count; ++i)
-    {
-	ufunc_T *fp = funcs[i];
-	// Use a separate pointer to avoid that ASAN complains about
-	// uf_name[] only being 4 characters.
-	char_u *ufname = (char_u *)fp->uf_name;
-	if (STRNCMP(fname, ufname, len) == 0 && ufname[len] == NUL)
-	{
-	    clear_tv(&tv);
-	    return fp;
-	}
-    }
+    int m_idx;
+    fp = method_lookup(cl, tv.v_type, fname, len, &m_idx);
 
 fail_after_eval:
     clear_tv(&tv);
-    return NULL;
+    return fp;
 }
 
 /*
- * If "name[len]" is a class member in cctx->ctx_ufunc->uf_class return the
- * index in class.class_class_members[].
- * If "cl_ret" is not NULL set it to the class.
- * Otherwise return -1;
+ * Returns the index of class member variable "name" in the class "cl".
+ * Returns -1, if the variable is not found.
+ * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
  */
     int
-class_member_index(char_u *name, size_t len, class_T **cl_ret, cctx_T *cctx)
+class_member_idx(class_T *cl, char_u *name, size_t namelen)
 {
-    if (cctx == NULL || cctx->ctx_ufunc == NULL
-					  || cctx->ctx_ufunc->uf_class == NULL)
-	return -1;
-    class_T *cl = cctx->ctx_ufunc->uf_class;
-
     for (int i = 0; i < cl->class_class_member_count; ++i)
     {
 	ocmember_T *m = &cl->class_class_members[i];
-	if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
+	if (namelen)
 	{
-	    if (cl_ret != NULL)
-		*cl_ret = cl;
+	    if (STRNCMP(name, m->ocm_name, namelen) == 0
+		    && m->ocm_name[namelen] == NUL)
+		return i;
+	}
+	else if (STRCMP(name, m->ocm_name) == 0)
 	    return i;
-	}
     }
+
+    return -1;
+}
+
+/*
+ * Returns a pointer to the class member variable "name" in the class "cl".
+ * Returns NULL if the variable is not found.
+ * The member variable index is set in "idx".
+ */
+    ocmember_T *
+class_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
+{
+    *idx = class_member_idx(cl, name, namelen);
+    return *idx >= 0 ? &cl->class_class_members[*idx] : NULL;
+}
+
+/*
+ * Returns the index of class method "name" in the class "cl".
+ * Returns -1, if the method is not found.
+ */
+    int
+class_method_idx(class_T *cl, char_u *name, size_t namelen)
+{
+    for (int i = 0; i < cl->class_class_function_count; ++i)
+    {
+	ufunc_T *fp = cl->class_class_functions[i];
+	char_u *ufname = (char_u *)fp->uf_name;
+	if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
+	    return i;
+    }
+
     return -1;
 }
 
 /*
- * If "name[len]" is a class method in cctx->ctx_ufunc->uf_class return the
- * index in class.class_class_functions[].
- * If "cl_ret" is not NULL set it to the class.
- * Otherwise return -1.
+ * Returns a pointer to the class method "name" in class "cl".
+ * Returns NULL if the method is not found.
+ * The method index is set in "idx".
+ */
+    ufunc_T *
+class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
+{
+    *idx = class_method_idx(cl, name, namelen);
+    return *idx >= 0 ? cl->class_class_functions[*idx] : NULL;
+}
+
+/*
+ * Returns the index of object member variable "name" in the class "cl".
+ * Returns -1, if the variable is not found.
+ * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
+ */
+    int
+object_member_idx(class_T *cl, char_u *name, size_t namelen)
+{
+    for (int i = 0; i < cl->class_obj_member_count; ++i)
+    {
+	ocmember_T *m = &cl->class_obj_members[i];
+	if (namelen)
+	{
+	    if (STRNCMP(name, m->ocm_name, namelen) == 0
+		    && m->ocm_name[namelen] == NUL)
+	    return i;
+	}
+	else if (STRCMP(name, m->ocm_name) == 0)
+	    return i;
+    }
+
+    return -1;
+}
+
+/*
+ * Returns a pointer to the object member variable "name" in the class "cl".
+ * Returns NULL if the variable is not found.
+ * The object member variable index is set in "idx".
+ */
+    ocmember_T *
+object_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
+{
+    *idx = object_member_idx(cl, name, namelen);
+    return *idx >= 0 ? &cl->class_obj_members[*idx] : NULL;
+}
+
+/*
+ * Returns the index of object method "name" in the class "cl".
+ * Returns -1, if the method is not found.
  */
     int
-class_method_index(char_u *name, size_t len, class_T **cl_ret, cctx_T *cctx)
+object_method_idx(class_T *cl, char_u *name, size_t namelen)
 {
-    if (cctx == NULL || cctx->ctx_ufunc == NULL
-					  || cctx->ctx_ufunc->uf_class == NULL)
-	return -1;
-    class_T *cl = cctx->ctx_ufunc->uf_class;
-
-    for (int i = 0; i < cl->class_class_function_count; ++i)
+    for (int i = 0; i < cl->class_obj_method_count; ++i)
     {
-	ufunc_T *fp = cl->class_class_functions[i];
-	if (STRNCMP(name, fp->uf_name, len) == 0 && fp->uf_name[len] == NUL)
-	{
-	    if (cl_ret != NULL)
-		*cl_ret = cl;
+	ufunc_T *fp = cl->class_obj_methods[i];
+	// Use a separate pointer to avoid that ASAN complains about
+	// uf_name[] only being 4 characters.
+	char_u *ufname = (char_u *)fp->uf_name;
+	if (STRNCMP(name, ufname, namelen) == 0 && ufname[namelen] == NUL)
 	    return i;
-	}
     }
+
     return -1;
 }
 
 /*
+ * Returns a pointer to the object method "name" in class "cl".
+ * Returns NULL if the method is not found.
+ * The object method index is set in "idx".
+ */
+    ufunc_T *
+object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
+{
+    *idx = object_method_idx(cl, name, namelen);
+    return *idx >= 0 ? cl->class_obj_methods[*idx] : NULL;
+}
+
+/*
+ * Lookup a class or object member variable by name.  If v_type is VAR_CLASS,
+ * then lookup a class member variable and if it is VAR_OBJECT, then lookup a
+ * object member variable.
+ *
+ * Returns a pointer to the member variable structure if variable is found.
+ * Otherwise returns NULL.  The member variable index is set in "*idx".
+ */
+    ocmember_T *
+member_lookup(
+    class_T	*cl,
+    vartype_T	v_type,
+    char_u	*name,
+    size_t	namelen,
+    int		*idx)
+{
+    if (v_type == VAR_CLASS)
+	return class_member_lookup(cl, name, namelen, idx);
+    else
+	return object_member_lookup(cl, name, namelen, idx);
+}
+
+/*
+ * Lookup a class or object method by name.  If v_type is VAR_CLASS, then
+ * lookup a class method and if it is VAR_OBJECT, then lookup a object method.
+ *
+ * Returns a pointer to the method structure if variable is found.
+ * Otherwise returns NULL.  The method variable index is set in "*idx".
+ */
+    ufunc_T *
+method_lookup(
+    class_T	*cl,
+    vartype_T	v_type,
+    char_u	*name,
+    size_t	namelen,
+    int		*idx)
+{
+    if (v_type == VAR_CLASS)
+	return class_method_lookup(cl, name, namelen, idx);
+    else
+	return object_method_lookup(cl, name, namelen, idx);
+}
+
+/*
  * Return TRUE if current context "cctx_arg" is inside class "cl".
  * Return FALSE if not.
  */
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -332,6 +332,62 @@ script_var_exists(char_u *name, size_t l
 }
 
 /*
+ * If "name[len]" is a class method in cctx->ctx_ufunc->uf_class return the
+ * class method index.
+ * If "cl_ret" is not NULL set it to the class.
+ * Otherwise return -1.
+ */
+    int
+cctx_class_method_idx(
+    cctx_T  *cctx,
+    char_u  *name,
+    size_t  len,
+    class_T **cl_ret)
+{
+    if (cctx == NULL || cctx->ctx_ufunc == NULL
+	    || cctx->ctx_ufunc->uf_class == NULL)
+	return -1;
+
+    class_T *cl = cctx->ctx_ufunc->uf_class;
+    int m_idx = class_method_idx(cl, name, len);
+    if (m_idx >= 0)
+    {
+	if (cl_ret != NULL)
+	    *cl_ret = cl;
+    }
+
+    return m_idx;
+}
+
+/*
+ * If "name[len]" is a class member in cctx->ctx_ufunc->uf_class return the
+ * class member variable index.
+ * If "cl_ret" is not NULL set it to the class.
+ * Otherwise return -1;
+ */
+    int
+cctx_class_member_idx(
+    cctx_T  *cctx,
+    char_u  *name,
+    size_t  len,
+    class_T **cl_ret)
+{
+    if (cctx == NULL || cctx->ctx_ufunc == NULL
+	    || cctx->ctx_ufunc->uf_class == NULL)
+	return -1;
+
+    class_T *cl = cctx->ctx_ufunc->uf_class;
+    int m_idx = class_member_idx(cl, name, len);
+    if (m_idx >= 0)
+    {
+	if (cl_ret != NULL)
+	    *cl_ret = cl;
+    }
+
+    return m_idx;
+}
+
+/*
  * Return TRUE if "name" is a local variable, argument, script variable or
  * imported.  Also if "name" is "this" and in a class method.
  */
@@ -346,7 +402,7 @@ variable_exists(char_u *name, size_t len
 			&& (cctx->ctx_ufunc->uf_flags & (FC_OBJECT|FC_NEW))
 			&& STRNCMP(name, "this", 4) == 0)))
 	    || script_var_exists(name, len, cctx, NULL) == OK
-	    || class_member_index(name, len, NULL, cctx) >= 0
+	    || cctx_class_member_idx(cctx, name, len, NULL) >= 0
 	    || find_imported(name, len, FALSE) != NULL;
 }
 
@@ -393,7 +449,7 @@ check_defined(
 	return FAIL;
     }
 
-    if (class_member_index(p, len, NULL, cctx) >= 0)
+    if (cctx_class_member_idx(cctx, p, len, NULL) >= 0)
     {
 	if (is_arg)
 	    semsg(_(e_argument_already_declared_in_class_str), p);
@@ -1617,8 +1673,8 @@ compile_lhs(
 		    return FAIL;
 		}
 	    }
-	    else if ((lhs->lhs_classmember_idx = class_member_index(
-				 var_start, lhs->lhs_varlen, NULL, cctx)) >= 0)
+	    else if ((lhs->lhs_classmember_idx = cctx_class_member_idx(
+			    cctx, var_start, lhs->lhs_varlen, NULL)) >= 0)
 	    {
 		if (is_decl)
 		{
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -2166,26 +2166,20 @@ execute_storeindex(isn_T *iptr, ectx_T *
 	    class_T *cl = obj->obj_class;
 	    char_u  *member = tv_idx->vval.v_string;
 
-	    ocmember_T *m = NULL;
-	    for (int i = 0; i < cl->class_obj_member_count; ++i)
+	    int		m_idx;
+	    ocmember_T *m = object_member_lookup(cl, member, 0, &m_idx);
+	    if (m != NULL)
 	    {
-		m = &cl->class_obj_members[i];
-		if (STRCMP(member, m->ocm_name) == 0)
+		if (*member == '_')
 		{
-		    if (*member == '_')
-		    {
-			semsg(_(e_cannot_access_private_member_str),
-								  m->ocm_name);
-			status = FAIL;
-		    }
-
-		    lidx = i;
-		    break;
+		    semsg(_(e_cannot_access_private_member_str),
+			    m->ocm_name);
+		    status = FAIL;
 		}
-		m = NULL;
+
+		lidx = m_idx;
 	    }
-
-	    if (m == NULL)
+	    else
 	    {
 		semsg(_(e_member_not_found_on_object_str_str),
 						       cl->class_name, member);
--- a/src/vim9expr.c
+++ b/src/vim9expr.c
@@ -394,39 +394,32 @@ compile_class_object_index(cctx_T *cctx,
 
     if (type->tt_type == VAR_OBJECT)
     {
-	for (int i = 0; i < cl->class_obj_member_count; ++i)
+	int m_idx = object_member_idx(cl, name, len);
+	if (m_idx >= 0)
 	{
-	    ocmember_T *m = &cl->class_obj_members[i];
-	    if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
+	    ocmember_T *m = &cl->class_obj_members[m_idx];
+	    if (*name == '_' && !inside_class(cctx, cl))
 	    {
-		if (*name == '_' && !inside_class(cctx, cl))
-		{
-		    semsg(_(e_cannot_access_private_member_str), m->ocm_name);
-		    return FAIL;
-		}
-
-		*arg = name_end;
-		if (cl->class_flags & (CLASS_INTERFACE | CLASS_EXTENDED))
-		    return generate_GET_ITF_MEMBER(cctx, cl, i, m->ocm_type,
+		semsg(_(e_cannot_access_private_member_str), m->ocm_name);
+		return FAIL;
+	    }
+
+	    *arg = name_end;
+	    if (cl->class_flags & (CLASS_INTERFACE | CLASS_EXTENDED))
+		return generate_GET_ITF_MEMBER(cctx, cl, m_idx, m->ocm_type,
 									FALSE);
-		return generate_GET_OBJ_MEMBER(cctx, i, m->ocm_type, FALSE);
-	    }
+	    return generate_GET_OBJ_MEMBER(cctx, m_idx, m->ocm_type, FALSE);
 	}
 
 	// Could be a function reference: "obj.Func".
-	for (int i = 0; i < cl->class_obj_method_count; ++i)
+	m_idx = object_method_idx(cl, name, len);
+	if (m_idx >= 0)
 	{
-	    ufunc_T *fp = cl->class_obj_methods[i];
-	    // Use a separate pointer to avoid that ASAN complains about
-	    // uf_name[] only being 4 characters.
-	    char_u *ufname = (char_u *)fp->uf_name;
-	    if (STRNCMP(name, ufname, len) == 0 && ufname[len] == NUL)
-	    {
-		if (type->tt_type == VAR_OBJECT
-		     && (cl->class_flags & (CLASS_INTERFACE | CLASS_EXTENDED)))
-		    return generate_FUNCREF(cctx, fp, cl, i, NULL);
-		return generate_FUNCREF(cctx, fp, NULL, 0, NULL);
-	    }
+	    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);
 	}
 
 	semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name);
@@ -435,28 +428,22 @@ compile_class_object_index(cctx_T *cctx,
     {
 	// load class member
 	int idx;
-	for (idx = 0; idx < cl->class_class_member_count; ++idx)
+	ocmember_T *m = class_member_lookup(cl, name, len, &idx);
+	if (m != NULL)
 	{
-	    ocmember_T *m = &cl->class_class_members[idx];
-	    if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
+	    // Note: type->tt_type = VAR_CLASS
+	    if ((cl->class_flags & CLASS_INTERFACE) != 0)
 	    {
-		// Note: type->tt_type = VAR_CLASS
-		if ((cl->class_flags & CLASS_INTERFACE) != 0)
-		{
-		    semsg(_(e_interface_static_direct_access_str),
-						cl->class_name, m->ocm_name);
-		    return FAIL;
-		}
-		if (*name == '_' && !inside_class(cctx, cl))
-		{
-		    semsg(_(e_cannot_access_private_member_str), m->ocm_name);
-		    return FAIL;
-		}
-		break;
+		semsg(_(e_interface_static_direct_access_str),
+			cl->class_name, m->ocm_name);
+		return FAIL;
 	    }
-	}
-	if (idx < cl->class_class_member_count)
-	{
+	    if (*name == '_' && !inside_class(cctx, cl))
+	    {
+		semsg(_(e_cannot_access_private_member_str), m->ocm_name);
+		return FAIL;
+	    }
+
 	    *arg = name_end;
 	    return generate_CLASSMEMBER(cctx, TRUE, cl, idx);
 	}
@@ -773,7 +760,7 @@ compile_load(
 		else
 		    gen_load = TRUE;
 	    }
-	    else if ((idx = class_member_index(*arg, len, &cl, cctx)) >= 0)
+	    else if ((idx = cctx_class_member_idx(cctx, *arg, len, &cl)) >= 0)
 	    {
 		// Referencing a class member without the class name.  Infer
 		// the class from the def function context.
@@ -1141,7 +1128,7 @@ compile_call(
 		goto theend;
 	    }
 	}
-	else if ((mi = class_method_index(name, varlen, &cl, cctx)) >= 0)
+	else if ((mi = cctx_class_method_idx(cctx, name, varlen, &cl)) >= 0)
 	{
 	    // Class method invocation without the class name.  The
 	    // generate_CALL() function expects the class type at the top of
--- a/src/vim9instr.c
+++ b/src/vim9instr.c
@@ -1837,17 +1837,12 @@ generate_CALL(
 		if (class_constructor && expected->tt_type == VAR_ANY)
 		{
 		    class_T *clp = mtype->tt_class;
-		    char_u *aname = ((char_u **)ufunc->uf_args.ga_data)[i];
-		    for (int om = 0; om < clp->class_obj_member_count; ++om)
-		    {
-			if (STRCMP(aname, clp->class_obj_members[om].ocm_name)
-									== 0)
-			{
-			    expected = clp->class_obj_members[om].ocm_type;
-			    break;
-			}
-		    }
-
+		    char_u  *aname = ((char_u **)ufunc->uf_args.ga_data)[i];
+		    int	    m_idx;
+		    ocmember_T *m = object_member_lookup(clp, aname, 0,
+									&m_idx);
+		    if (m != NULL)
+			expected = m->ocm_type;
 		}
 	    }
 	    else if (ufunc->uf_va_type == NULL