diff options
author | Yegappan Lakshmanan <yegappan@yahoo.com> | 2023-08-19 22:39:33 +0200 |
---|---|---|
committer | Christian Brabandt <cb@256bit.org> | 2023-08-19 22:39:33 +0200 |
commit | 4b1cc7906f2fa9aa5a41c16467e5d3ac4b64e323 (patch) | |
tree | 631f81394457c15a17fe3859fe3034787c97c3df | |
parent | e1eaae27f4f2d2522e45397756f3bca42be50988 (diff) |
patch 9.0.1757: ex_class() function is too longv9.0.1757
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>
-rw-r--r-- | src/version.c | 2 | ||||
-rw-r--r-- | src/vim9class.c | 892 |
2 files changed, 521 insertions, 373 deletions
diff --git a/src/version.c b/src/version.c index bc3ddc1beb..781d38915b 100644 --- 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, diff --git a/src/vim9class.c b/src/vim9class.c index 10b178ef1f..9e5bb56bf1 100644 --- a/src/vim9class.c +++ b/src/vim9class.c @@ -231,6 +231,276 @@ object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl) } /* + * 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; |