changeset 32896:223765c87ea9 v9.0.1757

patch 9.0.1757: ex_class() function is too long Commit: https://github.com/vim/vim/commit/4b1cc7906f2fa9aa5a41c16467e5d3ac4b64e323 Author: Yegappan Lakshmanan <yegappan@yahoo.com> Date: Sat Aug 19 22:39:33 2023 +0200 patch 9.0.1757: ex_class() function is too long Problem: ex_class() function is too long Solution: refactor it closes: #12858 Signed-off-by: Christian Brabandt <cb@256bit.org> Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
author Christian Brabandt <cb@256bit.org>
date Sat, 19 Aug 2023 22:45:04 +0200
parents bb3b59d99484
children 44195816b052
files src/version.c src/vim9class.c
diffstat 2 files changed, 521 insertions(+), 373 deletions(-) [+]
line wrap: on
line diff
--- a/src/version.c
+++ b/src/version.c
@@ -696,6 +696,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1757,
+/**/
     1756,
 /**/
     1755,
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -231,6 +231,276 @@ object_index_from_itf_index(class_T *itf
 }
 
 /*
+ * Check whether a class named "extends_name" is present.  If the class is
+ * valid, then "extends_clp" is set with the class pointer.
+ * Returns TRUE if the class name "extends_names" is a valid class.
+ */
+    static int
+validate_extends_class(char_u *extends_name, class_T **extends_clp)
+{
+    typval_T	tv;
+    int		success = FALSE;
+
+    tv.v_type = VAR_UNKNOWN;
+    if (eval_variable_import(extends_name, &tv) == FAIL)
+    {
+	semsg(_(e_class_name_not_found_str), extends_name);
+	return success;
+    }
+    else
+    {
+	if (tv.v_type != VAR_CLASS
+		|| tv.vval.v_class == NULL
+		|| (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0)
+	    semsg(_(e_cannot_extend_str), extends_name);
+	else
+	{
+	    class_T *extends_cl = tv.vval.v_class;
+	    ++extends_cl->class_refcount;
+	    *extends_clp = extends_cl;
+	    success = TRUE;
+	}
+	clear_tv(&tv);
+    }
+
+    return success;
+}
+
+/*
+ * Check the members of the interface class "ifcl" match the class members
+ * ("classmembers_gap") and object members ("objmembers_gap") of a class.
+ * Returns TRUE if the class and object member names are valid.
+ */
+    static int
+validate_interface_members(
+    char_u	*intf_class_name,
+    class_T	*ifcl,
+    garray_T	*classmembers_gap,
+    garray_T	*objmembers_gap)
+{
+    int success = TRUE;
+
+    for (int loop = 1; loop <= 2 && success; ++loop)
+    {
+	// loop == 1: check class members
+	// loop == 2: check object members
+	int if_count = loop == 1 ? ifcl->class_class_member_count
+						: ifcl->class_obj_member_count;
+	if (if_count == 0)
+	    continue;
+	ocmember_T *if_ms = loop == 1 ? ifcl->class_class_members
+						: ifcl->class_obj_members;
+	ocmember_T *cl_ms = (ocmember_T *)(loop == 1
+						? classmembers_gap->ga_data
+						: objmembers_gap->ga_data);
+	int cl_count = loop == 1 ? classmembers_gap->ga_len
+						: objmembers_gap->ga_len;
+	for (int if_i = 0; if_i < if_count; ++if_i)
+	{
+	    int cl_i;
+	    for (cl_i = 0; cl_i < cl_count; ++cl_i)
+	    {
+		ocmember_T	*m = &cl_ms[cl_i];
+		where_T		where = WHERE_INIT;
+
+		if (STRCMP(if_ms[if_i].ocm_name, m->ocm_name) != 0)
+		    continue;
+
+		// Ensure the type is matching.
+		where.wt_func_name = (char *)m->ocm_name;
+		where.wt_kind = WT_MEMBER;
+		if (check_type_maybe(if_ms[if_i].ocm_type, m->ocm_type, TRUE,
+								where) != OK)
+		    success = FALSE;
+
+		break;
+	    }
+	    if (cl_i == cl_count)
+	    {
+		semsg(_(e_member_str_of_interface_str_not_implemented),
+			if_ms[if_i].ocm_name, intf_class_name);
+		success = FALSE;
+		break;
+	    }
+	}
+    }
+
+    return success;
+}
+
+/*
+ * Check the functions/methods of the interface class "ifcl" match the class
+ * methods ("classfunctions_gap") and object functions ("objmemthods_gap") of a
+ * class.
+ * Returns TRUE if the class and object member names are valid.
+ */
+    static int
+validate_interface_methods(
+    char_u	*intf_class_name,
+    class_T	*ifcl,
+    garray_T	*classfunctions_gap,
+    garray_T	*objmethods_gap)
+{
+    int success = TRUE;
+
+    for (int loop = 1; loop <= 2 && success; ++loop)
+    {
+	// loop == 1: check class functions
+	// loop == 2: check object methods
+	int if_count = loop == 1 ? ifcl->class_class_function_count
+					: ifcl->class_obj_method_count;
+	if (if_count == 0)
+	    continue;
+	ufunc_T **if_fp = loop == 1 ? ifcl->class_class_functions
+						: ifcl->class_obj_methods;
+	ufunc_T **cl_fp = (ufunc_T **)(loop == 1
+						? classfunctions_gap->ga_data
+						: objmethods_gap->ga_data);
+	int cl_count = loop == 1 ? classfunctions_gap->ga_len
+						: objmethods_gap->ga_len;
+	for (int if_i = 0; if_i < if_count; ++if_i)
+	{
+	    char_u *if_name = if_fp[if_i]->uf_name;
+	    int cl_i;
+	    for (cl_i = 0; cl_i < cl_count; ++cl_i)
+	    {
+		char_u *cl_name = cl_fp[cl_i]->uf_name;
+		if (STRCMP(if_name, cl_name) == 0)
+		{
+		    where_T where = WHERE_INIT;
+
+		    // Ensure the type is matching.
+		    where.wt_func_name = (char *)if_name;
+		    where.wt_kind = WT_METHOD;
+		    if (check_type_maybe(if_fp[if_i]->uf_func_type,
+				cl_fp[cl_i]->uf_func_type, TRUE, where) != OK)
+			success = FALSE;
+		    break;
+		}
+	    }
+	    if (cl_i == cl_count)
+	    {
+		semsg(_(e_function_str_of_interface_str_not_implemented),
+			if_name, intf_class_name);
+		success = FALSE;
+		break;
+	    }
+	}
+    }
+
+    return success;
+}
+
+/*
+ * Validate all the "implements" classes when creating a new class.  The
+ * classes are returned in "intf_classes".  The class functions, class methods,
+ * object methods and object members in the new class are in
+ * "classfunctions_gap", "classmembers_gap", "objmethods_gap", and
+ * "objmembers_gap" respectively.
+ */
+    static int
+validate_implements_classes(
+    garray_T	*impl_gap,
+    class_T	**intf_classes,
+    garray_T	*classfunctions_gap,
+    garray_T	*classmembers_gap,
+    garray_T	*objmethods_gap,
+    garray_T	*objmembers_gap)
+{
+    int		success = TRUE;
+
+    for (int i = 0; i < impl_gap->ga_len && success; ++i)
+    {
+	char_u *impl = ((char_u **)impl_gap->ga_data)[i];
+	typval_T tv;
+	tv.v_type = VAR_UNKNOWN;
+	if (eval_variable_import(impl, &tv) == FAIL)
+	{
+	    semsg(_(e_interface_name_not_found_str), impl);
+	    success = FALSE;
+	    break;
+	}
+
+	if (tv.v_type != VAR_CLASS
+		|| tv.vval.v_class == NULL
+		|| (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
+	{
+	    semsg(_(e_not_valid_interface_str), impl);
+	    success = FALSE;
+	    clear_tv(&tv);
+	    break;
+	}
+
+	class_T *ifcl = tv.vval.v_class;
+	intf_classes[i] = ifcl;
+	++ifcl->class_refcount;
+
+	// check the members of the interface match the members of the class
+	success = validate_interface_members(impl, ifcl, classmembers_gap,
+							objmembers_gap);
+
+	// check the functions/methods of the interface match the
+	// functions/methods of the class
+	success = validate_interface_methods(impl, ifcl, classfunctions_gap,
+							objmethods_gap);
+	clear_tv(&tv);
+    }
+
+    return success;
+}
+
+/*
+ * Check no function argument name is used as a class member.
+ * (Object members are always accessed with "this." prefix, so no need
+ * to check them.)
+ */
+    static int
+check_func_arg_names(
+    garray_T	*classfunctions_gap,
+    garray_T	*objmethods_gap,
+    garray_T	*classmembers_gap)
+{
+    int success = TRUE;
+
+    // loop 1: class functions, loop 2: object methods
+    for (int loop = 1; loop <= 2 && success; ++loop)
+    {
+	garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
+
+	for (int fi = 0; fi < gap->ga_len && success; ++fi)
+	{
+	    ufunc_T *uf = ((ufunc_T **)gap->ga_data)[fi];
+
+	    for (int i = 0; i < uf->uf_args.ga_len && success; ++i)
+	    {
+		char_u *aname = ((char_u **)uf->uf_args.ga_data)[i];
+		garray_T *mgap = classmembers_gap;
+
+		// Check all the class member names
+		for (int mi = 0; mi < mgap->ga_len; ++mi)
+		{
+		    char_u *mname = ((ocmember_T *)mgap->ga_data + mi)
+			->ocm_name;
+		    if (STRCMP(aname, mname) == 0)
+		    {
+			success = FALSE;
+
+			if (uf->uf_script_ctx.sc_sid > 0)
+			    SOURCING_LNUM = uf->uf_script_ctx.sc_lnum;
+
+			semsg(_(e_argument_already_declared_in_class_str),
+				aname);
+			break;
+		    }
+		}
+	    }
+	}
+    }
+
+    return success;
+}
+
+/*
  * Update the interface class lookup table for the member index on the
  * interface to the member index in the class implementing the interface.
  * And a lookup table for the object method index on the interface
@@ -342,6 +612,238 @@ update_member_method_lookup_table(
 }
 
 /*
+ * Update the member and object method lookup tables for a new class in the
+ * interface class.
+ * For each interface add a lookup table for the member index on the interface
+ * to the member index in the new class.  And a lookup table for the object
+ * method index on the interface to the object method index in the new class.
+ */
+    static int
+add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap)
+{
+    for (int i = 0; i < cl->class_interface_count; ++i)
+    {
+	class_T *ifcl = cl->class_interfaces_cl[i];
+
+	if (update_member_method_lookup_table(ifcl, cl, objmethods_gap,
+							0, TRUE) == FAIL)
+	    return FAIL;
+    }
+
+    // Update the lookup table for the extended class, if nay
+    if (extends_cl != NULL)
+    {
+	class_T		*pclass = extends_cl;
+	int		pobj_method_offset = objmethods_gap->ga_len;
+
+	// Update the entire lineage of extended classes.
+	while (pclass != NULL)
+	{
+	    if (update_member_method_lookup_table(pclass, cl,
+			objmethods_gap, pobj_method_offset, FALSE) == FAIL)
+		return FAIL;
+
+	    pobj_method_offset += pclass->class_obj_method_count_child;
+	    pclass = pclass->class_extends;
+	}
+    }
+
+    return OK;
+}
+
+/*
+ * Add class members to a new class.  Allocate a typval for each class member
+ * and initialize it.
+ */
+    static void
+add_class_members(class_T *cl, exarg_T *eap)
+{
+    // Allocate a typval for each class member and initialize it.
+    cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
+					    cl->class_class_member_count);
+    if (cl->class_members_tv == NULL)
+	return;
+
+    for (int i = 0; i < cl->class_class_member_count; ++i)
+    {
+	ocmember_T *m = &cl->class_class_members[i];
+	typval_T *tv = &cl->class_members_tv[i];
+	if (m->ocm_init != NULL)
+	{
+	    typval_T *etv = eval_expr(m->ocm_init, eap);
+	    if (etv != NULL)
+	    {
+		*tv = *etv;
+		vim_free(etv);
+	    }
+	}
+	else
+	{
+	    // TODO: proper default value
+	    tv->v_type = m->ocm_type->tt_type;
+	    tv->vval.v_string = NULL;
+	}
+    }
+}
+
+/*
+ * Add a default constructor to the class "cl".
+ */
+    static void
+add_default_constructor(
+    class_T	*cl,
+    garray_T	*classfunctions_gap,
+    garray_T	*type_list_gap)
+{
+    garray_T fga;
+
+    ga_init2(&fga, 1, 1000);
+    ga_concat(&fga, (char_u *)"new(");
+    for (int i = 0; i < cl->class_obj_member_count; ++i)
+    {
+	if (i > 0)
+	    ga_concat(&fga, (char_u *)", ");
+	ga_concat(&fga, (char_u *)"this.");
+	ocmember_T *m = cl->class_obj_members + i;
+	ga_concat(&fga, (char_u *)m->ocm_name);
+	ga_concat(&fga, (char_u *)" = v:none");
+    }
+    ga_concat(&fga, (char_u *)")\nenddef\n");
+    ga_append(&fga, NUL);
+
+    exarg_T fea;
+    CLEAR_FIELD(fea);
+    fea.cmdidx = CMD_def;
+    fea.cmd = fea.arg = fga.ga_data;
+
+    garray_T lines_to_free;
+    ga_init2(&lines_to_free, sizeof(char_u *), 50);
+
+    ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS);
+
+    ga_clear_strings(&lines_to_free);
+    vim_free(fga.ga_data);
+
+    if (nf != NULL && ga_grow(classfunctions_gap, 1) == OK)
+    {
+	((ufunc_T **)classfunctions_gap->ga_data)[classfunctions_gap->ga_len]
+	    = nf;
+	++classfunctions_gap->ga_len;
+
+	nf->uf_flags |= FC_NEW;
+	nf->uf_ret_type = get_type_ptr(type_list_gap);
+	if (nf->uf_ret_type != NULL)
+	{
+	    nf->uf_ret_type->tt_type = VAR_OBJECT;
+	    nf->uf_ret_type->tt_class = cl;
+	    nf->uf_ret_type->tt_argcount = 0;
+	    nf->uf_ret_type->tt_args = NULL;
+	}
+    }
+}
+
+/*
+ * Add the class functions and object methods to the new class "cl".
+ * When extending a class, add the functions and methods from the parent class
+ * also.
+ */
+    static int
+add_classfuncs_objmethods(
+    class_T	*cl,
+    class_T	*extends_cl,
+    garray_T	*classfunctions_gap,
+    garray_T	*objmethods_gap)
+{
+    // loop 1: class functions, loop 2: object methods
+    for (int loop = 1; loop <= 2; ++loop)
+    {
+	garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
+	int	     *fcount = loop == 1 ? &cl->class_class_function_count
+						: &cl->class_obj_method_count;
+	ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
+						: &cl->class_obj_methods;
+
+	int parent_count = 0;
+	if (extends_cl != NULL)
+	    // Include functions from the parent.
+	    parent_count = loop == 1
+				? extends_cl->class_class_function_count
+				: extends_cl->class_obj_method_count;
+
+	*fcount = parent_count + gap->ga_len;
+	if (*fcount == 0)
+	{
+	    *fup = NULL;
+	    continue;
+	}
+	*fup = ALLOC_MULT(ufunc_T *, *fcount);
+	if (*fup == NULL)
+	    return FAIL;
+
+	if (gap->ga_len != 0)
+	    mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
+	vim_free(gap->ga_data);
+	if (loop == 1)
+	    cl->class_class_function_count_child = gap->ga_len;
+	else
+	    cl->class_obj_method_count_child = gap->ga_len;
+
+	int skipped = 0;
+	for (int i = 0; i < parent_count; ++i)
+	{
+	    // Copy functions from the parent.  Can't use the same
+	    // function, because "uf_class" is different and compilation
+	    // will have a different result.
+	    // Put them after the functions in the current class, object
+	    // methods may be overruled, then "super.Method()" is used to
+	    // find a method from the parent.
+	    // Skip "new" functions. TODO: not all of them.
+	    if (loop == 1 && STRNCMP(
+				extends_cl->class_class_functions[i]->uf_name,
+				"new", 3) == 0)
+		++skipped;
+	    else
+	    {
+		ufunc_T *pf = (loop == 1
+					? extends_cl->class_class_functions
+					: extends_cl->class_obj_methods)[i];
+		(*fup)[gap->ga_len + i - skipped] = copy_function(pf);
+
+		// If the child class overrides a function from the parent
+		// the signature must be equal.
+		char_u *pname = pf->uf_name;
+		for (int ci = 0; ci < gap->ga_len; ++ci)
+		{
+		    ufunc_T *cf = (*fup)[ci];
+		    char_u *cname = cf->uf_name;
+		    if (STRCMP(pname, cname) == 0)
+		    {
+			where_T where = WHERE_INIT;
+			where.wt_func_name = (char *)pname;
+			where.wt_kind = WT_METHOD;
+			(void)check_type(pf->uf_func_type, cf->uf_func_type,
+								TRUE, where);
+		    }
+		}
+	    }
+	}
+
+	*fcount -= skipped;
+
+	// Set the class pointer on all the functions and object methods.
+	for (int i = 0; i < *fcount; ++i)
+	{
+	    ufunc_T *fp = (*fup)[i];
+	    fp->uf_class = cl;
+	    if (loop == 2)
+		fp->uf_flags |= FC_OBJECT;
+	}
+    }
+
+    return OK;
+}
+
+/*
  * Handle ":class" and ":abstract class" up to ":endclass".
  * Handle ":interface" up to ":endinterface".
  */
@@ -714,31 +1216,7 @@ early_ret:
 
     // Check the "extends" class is valid.
     if (success && extends != NULL)
-    {
-	typval_T tv;
-	tv.v_type = VAR_UNKNOWN;
-	if (eval_variable_import(extends, &tv) == FAIL)
-	{
-	    semsg(_(e_class_name_not_found_str), extends);
-	    success = FALSE;
-	}
-	else
-	{
-	    if (tv.v_type != VAR_CLASS
-		    || tv.vval.v_class == NULL
-		    || (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0)
-	    {
-		semsg(_(e_cannot_extend_str), extends);
-		success = FALSE;
-	    }
-	    else
-	    {
-		extends_cl = tv.vval.v_class;
-		++extends_cl->class_refcount;
-	    }
-	    clear_tv(&tv);
-	}
-    }
+	success = validate_extends_class(extends, &extends_cl);
     VIM_CLEAR(extends);
 
     class_T **intf_classes = NULL;
@@ -748,168 +1226,15 @@ early_ret:
     {
 	intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
 
-	for (int i = 0; i < ga_impl.ga_len && success; ++i)
-	{
-	    char_u *impl = ((char_u **)ga_impl.ga_data)[i];
-	    typval_T tv;
-	    tv.v_type = VAR_UNKNOWN;
-	    if (eval_variable_import(impl, &tv) == FAIL)
-	    {
-		semsg(_(e_interface_name_not_found_str), impl);
-		success = FALSE;
-		break;
-	    }
-
-	    if (tv.v_type != VAR_CLASS
-		    || tv.vval.v_class == NULL
-		    || (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
-	    {
-		semsg(_(e_not_valid_interface_str), impl);
-		success = FALSE;
-		clear_tv(&tv);
-		break;
-	    }
-
-	    class_T *ifcl = tv.vval.v_class;
-	    intf_classes[i] = ifcl;
-	    ++ifcl->class_refcount;
-
-	    // check the members of the interface match the members of the class
-	    for (int loop = 1; loop <= 2 && success; ++loop)
-	    {
-		// loop == 1: check class members
-		// loop == 2: check object members
-		int if_count = loop == 1 ? ifcl->class_class_member_count
-					 : ifcl->class_obj_member_count;
-		if (if_count == 0)
-		    continue;
-		ocmember_T *if_ms = loop == 1 ? ifcl->class_class_members
-					       : ifcl->class_obj_members;
-		ocmember_T *cl_ms = (ocmember_T *)(loop == 1
-						    ? classmembers.ga_data
-						    : objmembers.ga_data);
-		int cl_count = loop == 1 ? classmembers.ga_len
-							   : objmembers.ga_len;
-		for (int if_i = 0; if_i < if_count; ++if_i)
-		{
-		    int cl_i;
-		    for (cl_i = 0; cl_i < cl_count; ++cl_i)
-		    {
-			ocmember_T	*m = &cl_ms[cl_i];
-			where_T		where = WHERE_INIT;
-
-			if (STRCMP(if_ms[if_i].ocm_name, m->ocm_name) != 0)
-			    continue;
-
-			// Ensure the type is matching.
-			where.wt_func_name = (char *)m->ocm_name;
-			where.wt_kind = WT_MEMBER;
-			if (check_type_maybe(if_ms[if_i].ocm_type, m->ocm_type, TRUE,
-				    where) != OK)
-			    success = FALSE;
-
-			break;
-		    }
-		    if (cl_i == cl_count)
-		    {
-			semsg(_(e_member_str_of_interface_str_not_implemented),
-						   if_ms[if_i].ocm_name, impl);
-			success = FALSE;
-			break;
-		    }
-		}
-	    }
-
-	    // check the functions/methods of the interface match the
-	    // functions/methods of the class
-	    for (int loop = 1; loop <= 2 && success; ++loop)
-	    {
-		// loop == 1: check class functions
-		// loop == 2: check object methods
-		int if_count = loop == 1 ? ifcl->class_class_function_count
-					 : ifcl->class_obj_method_count;
-		if (if_count == 0)
-		    continue;
-		ufunc_T **if_fp = loop == 1 ? ifcl->class_class_functions
-					    : ifcl->class_obj_methods;
-		ufunc_T **cl_fp = (ufunc_T **)(loop == 1
-						? classfunctions.ga_data
-						: objmethods.ga_data);
-		int cl_count = loop == 1 ? classfunctions.ga_len
-							   : objmethods.ga_len;
-		for (int if_i = 0; if_i < if_count; ++if_i)
-		{
-		    char_u *if_name = if_fp[if_i]->uf_name;
-		    int cl_i;
-		    for (cl_i = 0; cl_i < cl_count; ++cl_i)
-		    {
-			char_u *cl_name = cl_fp[cl_i]->uf_name;
-			if (STRCMP(if_name, cl_name) == 0)
-			{
-			    where_T where = WHERE_INIT;
-
-			    // Ensure the type is matching.
-			    where.wt_func_name = (char *)if_name;
-			    where.wt_kind = WT_METHOD;
-			    if (check_type_maybe(if_fp[if_i]->uf_func_type,
-					cl_fp[cl_i]->uf_func_type, TRUE, where) != OK)
-				success = FALSE;
-			    break;
-			}
-		    }
-		    if (cl_i == cl_count)
-		    {
-			semsg(_(e_function_str_of_interface_str_not_implemented),
-								if_name, impl);
-			success = FALSE;
-			break;
-		    }
-		}
-	    }
-
-	    clear_tv(&tv);
-	}
+	success = validate_implements_classes(&ga_impl, intf_classes,
+					&classfunctions, &classmembers,
+					&objmethods, &objmembers);
     }
 
+    // Check no function argument name is used as a class member.
     if (success)
-    {
-	// Check no function argument name is used as a class member.
-	// (Object members are always accessed with "this." prefix, so no need
-	// to check them.)
-	for (int loop = 1; loop <= 2 && success; ++loop)
-	{
-	    garray_T *gap = loop == 1 ? &classfunctions : &objmethods;
-
-	    for (int fi = 0; fi < gap->ga_len && success; ++fi)
-	    {
-		ufunc_T *uf = ((ufunc_T **)gap->ga_data)[fi];
-
-		for (int i = 0; i < uf->uf_args.ga_len && success; ++i)
-		{
-		    char_u *aname = ((char_u **)uf->uf_args.ga_data)[i];
-		    garray_T *mgap = &classmembers;
-
-		    for (int mi = 0; mi < mgap->ga_len; ++mi)
-		    {
-			char_u *mname = ((ocmember_T *)mgap->ga_data + mi)
-								    ->ocm_name;
-			if (STRCMP(aname, mname) == 0)
-			{
-			    success = FALSE;
-
-			    if (uf->uf_script_ctx.sc_sid > 0)
-				SOURCING_LNUM = uf->uf_script_ctx.sc_lnum;
-
-			    semsg(_(e_argument_already_declared_in_class_str),
-									aname);
-			    break;
-			}
-		    }
-		}
-	    }
-	}
-    }
-
+	success = check_func_arg_names(&classfunctions, &objmethods,
+							&classmembers);
 
     class_T *cl = NULL;
     if (success)
@@ -968,66 +1293,15 @@ early_ret:
 
 	if (cl->class_interface_count > 0 || extends_cl != NULL)
 	{
-	    // For each interface add a lookup table for the member index on
-	    // the interface to the member index in this class.
-	    // And a lookup table for the object method index on the interface
-	    // to the object method index in this class.
-	    for (int i = 0; i < cl->class_interface_count; ++i)
-	    {
-		class_T *ifcl = cl->class_interfaces_cl[i];
-
-		if (update_member_method_lookup_table(ifcl, cl, &objmethods,
-			    0, TRUE) == FAIL)
-		    goto cleanup;
-	    }
-
-	    // Update the lookup table for the extended class, if nay
-	    if (extends_cl != NULL)
-	    {
-		class_T		*pclass = extends_cl;
-		int		pobj_method_offset = objmethods.ga_len;
-
-		// Update the entire lineage of extended classes.
-		while (pclass != NULL)
-		{
-		    if (update_member_method_lookup_table(pclass, cl,
-				&objmethods, pobj_method_offset, FALSE) ==
-			    FAIL)
-			goto cleanup;
-
-		    pobj_method_offset += pclass->class_obj_method_count_child;
-		    pclass = pclass->class_extends;
-		}
-	    }
+	    // Add a method and member lookup table to each of the interface
+	    // classes.
+	    if (add_lookup_tables(cl, extends_cl, &objmethods) == FAIL)
+		goto cleanup;
 	}
 
+	// Allocate a typval for each class member and initialize it.
 	if (is_class && cl->class_class_member_count > 0)
-	{
-	    // Allocate a typval for each class member and initialize it.
-	    cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
-						 cl->class_class_member_count);
-	    if (cl->class_members_tv != NULL)
-		for (int i = 0; i < cl->class_class_member_count; ++i)
-		{
-		    ocmember_T *m = &cl->class_class_members[i];
-		    typval_T *tv = &cl->class_members_tv[i];
-		    if (m->ocm_init != NULL)
-		    {
-			typval_T *etv = eval_expr(m->ocm_init, eap);
-			if (etv != NULL)
-			{
-			    *tv = *etv;
-			    vim_free(etv);
-			}
-		    }
-		    else
-		    {
-			// TODO: proper default value
-			tv->v_type = m->ocm_type->tt_type;
-			tv->vval.v_string = NULL;
-		    }
-		}
-	}
+	    add_class_members(cl, eap);
 
 	int have_new = FALSE;
 	for (int i = 0; i < classfunctions.ga_len; ++i)
@@ -1038,141 +1312,13 @@ early_ret:
 		break;
 	    }
 	if (is_class && !is_abstract && !have_new)
-	{
 	    // No new() method was defined, add the default constructor.
-	    garray_T fga;
-	    ga_init2(&fga, 1, 1000);
-	    ga_concat(&fga, (char_u *)"new(");
-	    for (int i = 0; i < cl->class_obj_member_count; ++i)
-	    {
-		if (i > 0)
-		    ga_concat(&fga, (char_u *)", ");
-		ga_concat(&fga, (char_u *)"this.");
-		ocmember_T *m = cl->class_obj_members + i;
-		ga_concat(&fga, (char_u *)m->ocm_name);
-		ga_concat(&fga, (char_u *)" = v:none");
-	    }
-	    ga_concat(&fga, (char_u *)")\nenddef\n");
-	    ga_append(&fga, NUL);
-
-	    exarg_T fea;
-	    CLEAR_FIELD(fea);
-	    fea.cmdidx = CMD_def;
-	    fea.cmd = fea.arg = fga.ga_data;
-
-	    garray_T lines_to_free;
-	    ga_init2(&lines_to_free, sizeof(char_u *), 50);
-
-	    ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS);
-
-	    ga_clear_strings(&lines_to_free);
-	    vim_free(fga.ga_data);
-
-	    if (nf != NULL && ga_grow(&classfunctions, 1) == OK)
-	    {
-		((ufunc_T **)classfunctions.ga_data)[classfunctions.ga_len]
-									  = nf;
-		++classfunctions.ga_len;
-
-		nf->uf_flags |= FC_NEW;
-		nf->uf_ret_type = get_type_ptr(&type_list);
-		if (nf->uf_ret_type != NULL)
-		{
-		    nf->uf_ret_type->tt_type = VAR_OBJECT;
-		    nf->uf_ret_type->tt_class = cl;
-		    nf->uf_ret_type->tt_argcount = 0;
-		    nf->uf_ret_type->tt_args = NULL;
-		}
-	    }
-	}
+	    add_default_constructor(cl, &classfunctions, &type_list);
 
 	// Move all the functions into the created class.
-	// loop 1: class functions, loop 2: object methods
-	for (int loop = 1; loop <= 2; ++loop)
-	{
-	    garray_T *gap = loop == 1 ? &classfunctions : &objmethods;
-	    int	     *fcount = loop == 1 ? &cl->class_class_function_count
-					 : &cl->class_obj_method_count;
-	    ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
-				       : &cl->class_obj_methods;
-
-	    int parent_count = 0;
-	    if (extends_cl != NULL)
-		// Include functions from the parent.
-		parent_count = loop == 1
-				    ? extends_cl->class_class_function_count
-				    : extends_cl->class_obj_method_count;
-
-	    *fcount = parent_count + gap->ga_len;
-	    if (*fcount == 0)
-	    {
-		*fup = NULL;
-		continue;
-	    }
-	    *fup = ALLOC_MULT(ufunc_T *, *fcount);
-	    if (*fup == NULL)
-		goto cleanup;
-
-	    if (gap->ga_len != 0)
-		mch_memmove(*fup, gap->ga_data,
-					      sizeof(ufunc_T *) * gap->ga_len);
-	    vim_free(gap->ga_data);
-	    if (loop == 1)
-		cl->class_class_function_count_child = gap->ga_len;
-	    else
-		cl->class_obj_method_count_child = gap->ga_len;
-
-	    int skipped = 0;
-	    for (int i = 0; i < parent_count; ++i)
-	    {
-		// Copy functions from the parent.  Can't use the same
-		// function, because "uf_class" is different and compilation
-		// will have a different result.
-		// Put them after the functions in the current class, object
-		// methods may be overruled, then "super.Method()" is used to
-		// find a method from the parent.
-		// Skip "new" functions. TODO: not all of them.
-		if (loop == 1 && STRNCMP(
-			    extends_cl->class_class_functions[i]->uf_name,
-								"new", 3) == 0)
-		    ++skipped;
-		else
-		{
-		    ufunc_T *pf = (loop == 1
-					? extends_cl->class_class_functions
-					: extends_cl->class_obj_methods)[i];
-		    (*fup)[gap->ga_len + i - skipped] = copy_function(pf);
-
-		    // If the child class overrides a function from the parent
-		    // the signature must be equal.
-		    char_u *pname = pf->uf_name;
-		    for (int ci = 0; ci < gap->ga_len; ++ci)
-		    {
-			ufunc_T *cf = (*fup)[ci];
-			char_u *cname = cf->uf_name;
-			if (STRCMP(pname, cname) == 0)
-			{
-			    where_T where = WHERE_INIT;
-			    where.wt_func_name = (char *)pname;
-			    where.wt_kind = WT_METHOD;
-			    (void)check_type(pf->uf_func_type, cf->uf_func_type,
-								  TRUE, where);
-			}
-		    }
-		}
-	    }
-
-	    *fcount -= skipped;
-
-	    // Set the class pointer on all the functions and object methods.
-	    for (int i = 0; i < *fcount; ++i)
-	    {
-		ufunc_T *fp = (*fup)[i];
-		fp->uf_class = cl;
-		if (loop == 2)
-		    fp->uf_flags |= FC_OBJECT;
-	    }
-	}
+	if (add_classfuncs_objmethods(cl, extends_cl, &classfunctions,
+							&objmethods) == FAIL)
+	    goto cleanup;
 
 	cl->class_type.tt_type = VAR_CLASS;
 	cl->class_type.tt_class = cl;