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>
diff --git a/src/vim9class.c b/src/vim9class.c
index 90cdb8e..15d2b09 100644
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -269,14 +269,10 @@
     {
 	// 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 @@
     int		*member_idx,
     ocmember_T	**p_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;
+    ocmember_T	*m;
 
-    for (int i = 0; i < member_count; ++i)
+    *member_idx = -1;  // not found (yet)
+
+    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 @@
     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 @@
 
     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;
+	int	m_idx;
+
+	fp = method_lookup(cl, rettv->v_type, name, len, &m_idx);
+	if (fp == NULL)
 	{
-	    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;
-
-		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;
-	    }
+	    semsg(_(e_method_not_found_on_class_str_str), cl->class_name,
+		    name);
+	    return FAIL;
 	}
 
-	semsg(_(e_method_not_found_on_class_str_str), cl->class_name, name);
+	typval_T	argvars[MAX_FUNC_ARGS + 1];
+	int		argcount = 0;
+
+	if (*fp->uf_name == '_')
+	{
+	    // 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;
+
+	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 @@
 
     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 @@
     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 @@
 	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;
-	    return i;
+	    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;
 }
 
 /*
- * 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 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_index(char_u *name, size_t len, class_T **cl_ret, cctx_T *cctx)
+class_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)
     {
 	ufunc_T *fp = cl->class_class_functions[i];
-	if (STRNCMP(name, fp->uf_name, len) == 0 && fp->uf_name[len] == NUL)
+	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 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 (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 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
+object_method_idx(class_T *cl, char_u *name, size_t namelen)
+{
+    for (int i = 0; i < cl->class_obj_method_count; ++i)
+    {
+	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.
  */