diff options
Diffstat (limited to 'src/vim9class.c')
-rw-r--r-- | src/vim9class.c | 153 |
1 files changed, 133 insertions, 20 deletions
diff --git a/src/vim9class.c b/src/vim9class.c index 4a8dd7c0e..9b1f91334 100644 --- a/src/vim9class.c +++ b/src/vim9class.c @@ -160,6 +160,8 @@ add_member( /* * Move the class or object members found while parsing a class into the class. * "gap" contains the found members. + * "parent_members" points to the members in the parent class (if any) + * "parent_count" is the number of members in the parent class * "members" will be set to the newly allocated array of members and * "member_count" set to the number of members. * Returns OK or FAIL. @@ -167,15 +169,28 @@ add_member( static int add_members_to_class( garray_T *gap, + ocmember_T *parent_members, + int parent_count, ocmember_T **members, int *member_count) { - *member_count = gap->ga_len; - *members = gap->ga_len == 0 ? NULL : ALLOC_MULT(ocmember_T, gap->ga_len); - if (gap->ga_len > 0 && *members == NULL) + *member_count = parent_count + gap->ga_len; + *members = *member_count == 0 ? NULL + : ALLOC_MULT(ocmember_T, *member_count); + if (*member_count > 0 && *members == NULL) return FAIL; + for (int i = 0; i < parent_count; ++i) + { + // parent members need to be copied + *members[i] = parent_members[i]; + members[i]->ocm_name = vim_strsave(members[i]->ocm_name); + if (members[i]->ocm_init != NULL) + members[i]->ocm_init = vim_strsave(members[i]->ocm_init); + } if (gap->ga_len > 0) - mch_memmove(*members, gap->ga_data, sizeof(ocmember_T) * gap->ga_len); + // new members are moved + mch_memmove(*members + parent_count, + gap->ga_data, sizeof(ocmember_T) * gap->ga_len); VIM_CLEAR(gap->ga_data); return OK; } @@ -233,6 +248,9 @@ ex_class(exarg_T *eap) // generics: <Tkey, Tentry> // handle "is_export" if it is set + // Name for "extends BaseClass" + char_u *extends = NULL; + // Names for "implements SomeInterface" garray_T ga_impl; ga_init2(&ga_impl, sizeof(char_u *), 5); @@ -241,9 +259,29 @@ ex_class(exarg_T *eap) while (*arg != NUL && *arg != '#' && *arg != '\n') { // TODO: - // extends SomeClass // specifies SomeInterface - if (STRNCMP(arg, "implements", 10) == 0 && IS_WHITE_OR_NUL(arg[10])) + if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7])) + { + if (extends != NULL) + { + emsg(_(e_duplicate_extends)); + goto early_ret; + } + arg = skipwhite(arg + 7); + char_u *end = find_name_end(arg, NULL, NULL, FNE_CHECK_START); + if (!IS_WHITE_OR_NUL(*end)) + { + semsg(_(e_white_space_required_after_name_str), arg); + goto early_ret; + } + extends = vim_strnsave(arg, end - arg); + if (extends == NULL) + goto early_ret; + + arg = skipwhite(end + 1); + } + else if (STRNCMP(arg, "implements", 10) == 0 + && IS_WHITE_OR_NUL(arg[10])) { if (ga_impl.ga_len > 0) { @@ -289,6 +327,7 @@ ex_class(exarg_T *eap) { semsg(_(e_trailing_characters_str), arg); early_ret: + vim_free(extends); ga_clear_strings(&ga_impl); return; } @@ -496,17 +535,50 @@ early_ret: } vim_free(theline); - // Check a few things before defining the class. + class_T *extends_cl = NULL; // class from "extends" argument + + /* + * Check a few things before defining the class. + */ + + // Check the "extends" class is valid. + if (success && extends != NULL) + { + typval_T tv; + tv.v_type = VAR_UNKNOWN; + if (eval_variable(extends, 0, 0, &tv, NULL, EVAL_VAR_IMPORT) == 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); + } + } + VIM_CLEAR(extends); + + // Check all "implements" entries are valid. if (success && ga_impl.ga_len > 0) { - // Check all "implements" entries are valid and correct. 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(impl, 0, 0, &tv, NULL, - EVAL_VAR_VERBOSE + EVAL_VAR_IMPORT) == FAIL) + if (eval_variable(impl, 0, 0, &tv, NULL, EVAL_VAR_IMPORT) == FAIL) { semsg(_(e_interface_name_not_found_str), impl); success = FALSE; @@ -620,6 +692,8 @@ early_ret: if (cl->class_name == NULL) goto cleanup; + cl->class_extends = extends_cl; + if (ga_impl.ga_len > 0) { // Move the "implements" names into the class. @@ -635,11 +709,19 @@ early_ret: // Add class and object members to "cl". if (add_members_to_class(&classmembers, - &cl->class_class_members, - &cl->class_class_member_count) == FAIL + extends_cl == NULL ? NULL + : extends_cl->class_class_members, + extends_cl == NULL ? 0 + : extends_cl->class_class_member_count, + &cl->class_class_members, + &cl->class_class_member_count) == FAIL || add_members_to_class(&objmembers, - &cl->class_obj_members, - &cl->class_obj_member_count) == FAIL) + extends_cl == NULL ? NULL + : extends_cl->class_obj_members, + extends_cl == NULL ? 0 + : extends_cl->class_obj_member_count, + &cl->class_obj_members, + &cl->class_obj_member_count) == FAIL) goto cleanup; if (is_class && cl->class_class_member_count > 0) @@ -735,20 +817,47 @@ early_ret: ufunc_T ***fup = loop == 1 ? &cl->class_class_functions : &cl->class_obj_methods; - *fcount = gap->ga_len; - if (gap->ga_len == 0) + 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 *, gap->ga_len); + *fup = ALLOC_MULT(ufunc_T *, *fcount); if (*fup == NULL) goto cleanup; - mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * 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. + // 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 + *fup[i - skipped] = copy_function((loop == 1 + ? extends_cl->class_class_functions + : extends_cl->class_obj_methods)[i]); + } + + mch_memmove(*fup + parent_count - skipped, gap->ga_data, + sizeof(ufunc_T *) * gap->ga_len); vim_free(gap->ga_data); + *fcount -= skipped; - // Set the class pointer on all the object methods. - for (int i = 0; i < gap->ga_len; ++i) + // 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; @@ -786,6 +895,8 @@ cleanup: vim_free(cl); } + vim_free(extends); + class_unref(extends_cl); ga_clear_strings(&ga_impl); for (int round = 1; round <= 2; ++round) @@ -1167,6 +1278,8 @@ class_unref(class_T *cl) // be freed. VIM_CLEAR(cl->class_name); + class_unref(cl->class_extends); + for (int i = 0; i < cl->class_interface_count; ++i) vim_free(((char_u **)cl->class_interfaces)[i]); vim_free(cl->class_interfaces); |