diff options
Diffstat (limited to 'src/vim9class.c')
-rw-r--r-- | src/vim9class.c | 550 |
1 files changed, 469 insertions, 81 deletions
diff --git a/src/vim9class.c b/src/vim9class.c index 7227c3d..52c2f76 100644 --- a/src/vim9class.c +++ b/src/vim9class.c @@ -168,7 +168,8 @@ struct oc_newmember_S /* * Add a member to an object or a class. * Returns OK when successful, "init_expr" will be consumed then. - * Returns FAIL otherwise, caller might need to free "init_expr". + * Returns OK on success and FAIL on memory allocation failure (caller might + * need to free "init_expr"). */ static int add_member( @@ -323,13 +324,15 @@ validate_extends_class( } if (tv.v_type != VAR_CLASS || tv.vval.v_class == NULL - || (is_class - && (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0) - || (!is_class - && (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)) - // a interface cannot extend a class and a class cannot extend an - // interface. + || (is_class && IS_INTERFACE(tv.vval.v_class)) + || (!is_class && !IS_INTERFACE(tv.vval.v_class)) + || (is_class && IS_ENUM(tv.vval.v_class))) + { + // a class cannot extend an interface + // an interface cannot extend a class + // a class cannot extend an enum. semsg(_(e_cannot_extend_str), extends_name); + } else { class_T *extends_cl = tv.vval.v_class; @@ -793,7 +796,7 @@ validate_implements_classes( if (tv.v_type != VAR_CLASS || tv.vval.v_class == NULL - || (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0) + || !IS_INTERFACE(tv.vval.v_class)) { semsg(_(e_not_valid_interface_str), impl); success = FALSE; @@ -1234,14 +1237,14 @@ add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap) * Add class members to a new class. Allocate a typval for each class member * and initialize it. */ - static void + static int add_class_members(class_T *cl, exarg_T *eap, garray_T *type_list_gap) { // 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; + return FAIL; for (int i = 0; i < cl->class_class_member_count; ++i) { @@ -1250,19 +1253,19 @@ add_class_members(class_T *cl, exarg_T *eap, garray_T *type_list_gap) if (m->ocm_init != NULL) { typval_T *etv = eval_expr(m->ocm_init, eap); - if (etv != NULL) - { - if (m->ocm_type->tt_type == VAR_ANY - && !(m->ocm_flags & OCMFLAG_HAS_TYPE) - && etv->v_type != VAR_SPECIAL) - // If the member variable type is not yet set, then use - // the initialization expression type. - m->ocm_type = typval2type(etv, get_copyID(), - type_list_gap, - TVTT_DO_MEMBER|TVTT_MORE_SPECIFIC); - *tv = *etv; - vim_free(etv); - } + if (etv == NULL) + return FAIL; + + if (m->ocm_type->tt_type == VAR_ANY + && !(m->ocm_flags & OCMFLAG_HAS_TYPE) + && etv->v_type != VAR_SPECIAL) + // If the member variable type is not yet set, then use + // the initialization expression type. + m->ocm_type = typval2type(etv, get_copyID(), + type_list_gap, + TVTT_DO_MEMBER|TVTT_MORE_SPECIFIC); + *tv = *etv; + vim_free(etv); } else { @@ -1273,6 +1276,8 @@ add_class_members(class_T *cl, exarg_T *eap, garray_T *type_list_gap) if (m->ocm_flags & OCMFLAG_CONST) item_lock(tv, DICT_MAXNEST, TRUE, TRUE); } + + return OK; } /* @@ -1284,13 +1289,21 @@ add_default_constructor( garray_T *classfunctions_gap, garray_T *type_list_gap) { - garray_T fga; + garray_T fga; + int is_enum = IS_ENUM(cl); 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) + if (i < 2 && is_enum) + // The first two object variables in an enum are the enum value + // name and ordinal. Don't initialize these object variables in + // the default constructor as they are already initialized right + // after creating the object. + continue; + + if (i > (is_enum ? 2 : 0)) ga_concat(&fga, (char_u *)", "); ga_concat(&fga, (char_u *)"this."); ocmember_T *m = cl->class_obj_members + i; @@ -1336,6 +1349,7 @@ add_default_constructor( * Add the class methods and object methods to the new class "cl". * When extending a class "extends_cl", add the instance methods from the * parent class also. + * Returns OK on success and FAIL on memory allocation failure. */ static int add_classfuncs_objmethods( @@ -1373,7 +1387,7 @@ add_classfuncs_objmethods( if (gap->ga_len != 0) mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len); - vim_free(gap->ga_data); + VIM_CLEAR(gap->ga_data); if (loop == 1) cl->class_class_function_count_child = gap->ga_len; else @@ -1422,6 +1436,8 @@ add_classfuncs_objmethods( if (loop == 2) fp->uf_flags |= FC_OBJECT; } + + ga_clear(gap); } return OK; @@ -1471,6 +1487,246 @@ find_class_name_end(char_u *arg) return end; } +/* + * Returns TRUE if the enum value "varname" is already defined. + */ + static int +is_duplicate_enum( + garray_T *enum_gap, + char_u *varname, + char_u *varname_end) +{ + char_u *name = vim_strnsave(varname, varname_end - varname); + int dup = FALSE; + + for (int i = 0; i < enum_gap->ga_len; ++i) + { + ocmember_T *m = ((ocmember_T *)enum_gap->ga_data) + i; + if (STRCMP(name, m->ocm_name) == 0) + { + semsg(_(e_duplicate_enum_str), name); + dup = TRUE; + break; + } + } + + vim_free(name); + return dup; +} + +/* + * Parse the enum values in "line" separated by comma and add them to "gap". + * If the last enum value is found, then "enum_end" is set to TRUE. + */ + static int +enum_parse_values( + exarg_T *eap, + class_T *en, + char_u *line, + garray_T *gap, + int *num_enum_values, + int *enum_end) +{ + evalarg_T evalarg; + char_u *p = line; + char initexpr_buf[1024]; + char_u last_char = NUL; + int rc = OK; + + fill_evalarg_from_eap(&evalarg, eap, FALSE); + + int did_emsg_before = did_emsg; + while (*p != NUL) + { + // ignore comment + if (*p == '#') + break; + + if (!eval_isnamec1(*p)) + { + semsg(_(e_invalid_enum_value_declaration_str), p); + break; + } + + char_u *eni_name_start = p; + char_u *eni_name_end = to_name_end(p, FALSE); + + if (is_duplicate_enum(gap, eni_name_start, eni_name_end)) + break; + + p = skipwhite(eni_name_end); + + char_u *init_expr = NULL; + if (*p == '(') + { + if (VIM_ISWHITE(p[-1])) + { + semsg(_(e_no_white_space_allowed_before_str_str), "(", line); + break; + } + + char_u *expr_start, *expr_end; + + p = eni_name_start; + (void)skip_expr_concatenate(&p, &expr_start, &expr_end, &evalarg); + + while (*expr_start && *expr_start != '(') + expr_start++; + + if (expr_end > expr_start) + init_expr = vim_strnsave(expr_start, expr_end - expr_start); + } + + if (init_expr == NULL) + vim_snprintf(initexpr_buf, sizeof(initexpr_buf), "%s.new()", + en->class_name); + else + { + vim_snprintf(initexpr_buf, sizeof(initexpr_buf), "%s.new%s", + en->class_name, init_expr); + vim_free(init_expr); + } + if (add_member(gap, eni_name_start, eni_name_end, FALSE, + TRUE, TRUE, TRUE, &en->class_object_type, + vim_strsave((char_u *)initexpr_buf)) == FAIL) + break; + + ++*num_enum_values; + + if (*p != '#') + last_char = *p; + + if (*p != NUL && *p != ',') + break; + + if (*p == ',') + { + if (!IS_WHITE_OR_NUL(p[1])) + { + semsg(_(e_white_space_required_after_str_str), ",", line); + break; + } + if (VIM_ISWHITE(p[-1])) + { + semsg(_(e_no_white_space_allowed_before_str_str), ",", line); + break; + } + p = skipwhite(p + 1); + } + } + + if (*p != NUL && *p != '#') + { + if (did_emsg == did_emsg_before) + semsg(_(e_missing_comma_before_argument_str), p); + rc = FAIL; + } + + if (last_char != ',') + // last enum value should not be terminated by "," + *enum_end = TRUE; + + // Free the memory pointed by expr_start. + clear_evalarg(&evalarg, NULL); + + return rc; +} + +/* + * Add the "values" class variable (List of enum value objects) to the enum + * class "en" + */ + static int +enum_add_values_member( + class_T *en, + garray_T *gap, + int num_enum_values, + garray_T *type_list_gap) +{ + garray_T fga; + int rc = FAIL; + + ga_init2(&fga, 1, 1000); + ga_concat(&fga, (char_u *)"["); + for (int i = 0; i < num_enum_values; ++i) + { + ocmember_T *m = ((ocmember_T *)gap->ga_data) + i; + + if (i > 0) + ga_concat(&fga, (char_u *)", "); + ga_concat(&fga, en->class_name); + ga_concat(&fga, (char_u *)"."); + ga_concat(&fga, (char_u *)m->ocm_name); + } + ga_concat(&fga, (char_u *)"]"); + ga_append(&fga, NUL); + + char_u *varname = (char_u *)"values"; + + type_T *type = get_type_ptr(type_list_gap); + if (type == NULL) + goto done; + + type->tt_type = VAR_LIST; + type->tt_member = get_type_ptr(type_list_gap); + if (type->tt_member != NULL) + { + type->tt_member->tt_type = VAR_OBJECT; + type->tt_member->tt_class = en; + } + + rc = add_member(gap, varname, varname + 6, FALSE, FALSE, TRUE, TRUE, type, + vim_strsave((char_u *)fga.ga_data)); + +done: + vim_free(fga.ga_data); + + return rc; +} + +/* + * Clear the constructor method names in a enum class, so that an enum class + * cannot be instantiated. + */ + static void +enum_clear_constructors(class_T *en) +{ + for (int i = 0; i < en->class_class_function_count; ++i) + { + ufunc_T *fp = en->class_class_functions[i]; + + if (fp->uf_flags & FC_NEW) + *fp->uf_name = NUL; + } +} + +/* + * Initialize the name and ordinal object variable in the enum value "enval" in + * the enum "en". These values are set during the enum value object creation. + */ + void +enum_set_internal_obj_vars(class_T *en, object_T *enval) +{ + int i; + + for (i = 0; i < en->class_class_member_count; ++i) + { + typval_T *en_tv = en->class_members_tv + i; + if (en_tv != NULL && en_tv->v_type == VAR_UNKNOWN) + break; + } + + // First object variable is the name + ocmember_T *value_ocm = en->class_class_members + i; + typval_T *name_tv = (typval_T *)(enval + 1); + name_tv->v_type = VAR_STRING; + name_tv->vval.v_string = vim_strsave(value_ocm->ocm_name); + + // Second object variable is the ordinal + typval_T *ord_tv = (typval_T *)(name_tv + 1); + ord_tv->v_type = VAR_NUMBER; + ord_tv->vval.v_number = i; +} /* * Handle ":class" and ":abstract class" up to ":endclass". @@ -1479,10 +1735,12 @@ find_class_name_end(char_u *arg) void ex_class(exarg_T *eap) { - int is_class = eap->cmdidx == CMD_class; // FALSE for :interface + int is_class = eap->cmdidx == CMD_class; + int is_abstract = eap->cmdidx == CMD_abstract; + int is_enum = eap->cmdidx == CMD_enum; + int is_interface; long start_lnum = SOURCING_LNUM; char_u *arg = eap->arg; - int is_abstract = eap->cmdidx == CMD_abstract; if (is_abstract) { @@ -1495,12 +1753,16 @@ ex_class(exarg_T *eap) is_class = TRUE; } + is_interface = !is_class && !is_enum; + if (!current_script_is_vim9() || (cmdmod.cmod_flags & CMOD_LEGACY) || !getline_equal(eap->ea_getline, eap->cookie, getsourceline)) { if (is_class) emsg(_(e_class_can_only_be_defined_in_vim9_script)); + else if (is_enum) + emsg(_(e_enum_can_only_be_defined_in_vim9_script)); else emsg(_(e_interface_can_only_be_defined_in_vim9_script)); return; @@ -1510,6 +1772,8 @@ ex_class(exarg_T *eap) { if (is_class) semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg); + else if (is_enum) + semsg(_(e_enum_name_must_start_with_uppercase_letter_str), arg); else semsg(_(e_interface_name_must_start_with_uppercase_letter_str), arg); @@ -1523,11 +1787,6 @@ ex_class(exarg_T *eap) } char_u *name_start = arg; - // "export class" gets used when creating the class, don't use "is_export" - // for the items inside the class. - int class_export = is_export; - is_export = FALSE; - // TODO: // generics: <Tkey, Tentry> @@ -1545,6 +1804,11 @@ ex_class(exarg_T *eap) // specifies SomeInterface if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7])) { + if (is_enum) + { + emsg(_(e_enum_cannot_extend_class)); + goto early_ret; + } if (extends != NULL) { emsg(_(e_duplicate_extends)); @@ -1567,7 +1831,7 @@ ex_class(exarg_T *eap) else if (STRNCMP(arg, "implements", 10) == 0 && IS_WHITE_OR_NUL(arg[10])) { - if (!is_class) + if (is_interface) { emsg(_(e_interface_cannot_use_implements)); goto early_ret; @@ -1652,11 +1916,15 @@ early_ret: class_T *cl = NULL; class_T *extends_cl = NULL; // class from "extends" argument class_T **intf_classes = NULL; + int num_enum_values = 0; cl = ALLOC_CLEAR_ONE(class_T); if (cl == NULL) goto cleanup; - if (!is_class) + + if (is_enum) + cl->class_flags = CLASS_ENUM; + else if (is_interface) cl->class_flags = CLASS_INTERFACE; else if (is_abstract) cl->class_flags = CLASS_ABSTRACT; @@ -1666,22 +1934,48 @@ early_ret: if (cl->class_name == NULL) goto cleanup; + cl->class_type.tt_type = VAR_CLASS; + cl->class_type.tt_class = cl; + cl->class_object_type.tt_type = VAR_OBJECT; + cl->class_object_type.tt_class = cl; + // Add the class to the script-local variables. // TODO: handle other context, e.g. in a function // TODO: does uf_hash need to be cleared? typval_T tv; tv.v_type = VAR_CLASS; tv.vval.v_class = cl; - is_export = class_export; SOURCING_LNUM = start_lnum; int rc = set_var_const(cl->class_name, current_sctx.sc_sid, NULL, &tv, FALSE, 0, 0); if (rc == FAIL) goto cleanup; + if (is_enum) + { + // All the enum classes have the name and ordinal object variables. + char_u *varname = (char_u *)"name"; + if (add_member(&objmembers, varname, varname + 4, FALSE, FALSE, TRUE, + TRUE, &t_string, NULL) == FAIL) + goto cleanup; + + varname = (char_u *)"ordinal"; + if (add_member(&objmembers, varname, varname + 7, FALSE, FALSE, TRUE, + TRUE, &t_number, NULL) == FAIL) + goto cleanup; + } + + // "export class" gets used when creating the class, don't use "is_export" + // for the items inside the class. + is_export = FALSE; + + // When parsing an enum definition, this denotes whether all the enumerated + // values are parsed or not. + int enum_end = FALSE; + /* * Go over the body of the class/interface until "endclass" or - * "endinterface" is found. + * "endinterface" or "endenum" is found. */ char_u *theline = NULL; int success = FALSE; @@ -1704,10 +1998,32 @@ early_ret: } char_u *p = line; - char *end_name = is_class ? "endclass" : "endinterface"; - if (checkforcmd(&p, end_name, is_class ? 4 : 5)) + + char *end_name; + int shortlen; + int fullen; + if (is_class) + { + end_name = "endclass"; + shortlen = 4; + fullen = 8; + } + else if (is_enum) + { + end_name = "endenum"; + shortlen = 4; + fullen = 7; + } + else + { + end_name = "endinterface"; + shortlen = 5; + fullen = 12; + } + + if (checkforcmd(&p, end_name, shortlen)) { - if (STRNCMP(line, end_name, is_class ? 8 : 12) != 0) + if (STRNCMP(line, end_name, fullen) != 0) semsg(_(e_command_cannot_be_shortened_str), line); else if (*p == '|' || !ends_excmd2(line, p)) semsg(_(e_trailing_characters_str), p); @@ -1715,13 +2031,39 @@ early_ret: success = TRUE; break; } - char *wrong_name = is_class ? "endinterface" : "endclass"; - if (checkforcmd(&p, wrong_name, is_class ? 5 : 4)) + + int wrong_endname = FALSE; + if (is_class) + wrong_endname = checkforcmd(&p, "endinterface", 5) + || checkforcmd(&p, "endenum", 4); + else if (is_enum) + wrong_endname = checkforcmd(&p, "endclass", 4) + || checkforcmd(&p, "endinterface", 5); + else + wrong_endname = checkforcmd(&p, "endclass", 4) + || checkforcmd(&p, "endenum", 4); + if (wrong_endname) { semsg(_(e_invalid_command_str_expected_str), line, end_name); break; } + if (is_enum && !enum_end) + { + // In an enum, all the enumerated values are at the beginning + // separated by comma. The class and object variables/methods + // follow the values. + if (enum_parse_values(eap, cl, line, &classmembers, + &num_enum_values, &enum_end) == FAIL) + break; + + if (enum_end) + // Add the enum "values" class variable. + enum_add_values_member(cl, &classmembers, num_enum_values, + &type_list); + continue; + } + int has_public = FALSE; if (checkforcmd(&p, "public", 3)) { @@ -1730,7 +2072,7 @@ early_ret: semsg(_(e_command_cannot_be_shortened_str), line); break; } - if (!is_class) + if (is_interface) { emsg(_(e_public_variable_not_supported_in_interface)); break; @@ -1738,6 +2080,12 @@ early_ret: has_public = TRUE; p = skipwhite(line + 6); + if (STRNCMP(p, "def", 3) == 0) + { + emsg(_(e_public_keyword_not_supported_for_method)); + break; + } + if (STRNCMP(p, "var", 3) != 0 && STRNCMP(p, "static", 6) != 0 && STRNCMP(p, "final", 5) != 0 && STRNCMP(p, "const", 5) != 0) { @@ -1756,7 +2104,14 @@ early_ret: break; } - if (!is_class) + if (is_enum) + { + // "abstract" not supported in an enum + emsg(_(e_abstract_cannot_be_used_in_enum)); + break; + } + + if (is_interface) { // "abstract" not supported in an interface emsg(_(e_abstract_cannot_be_used_in_interface)); @@ -1789,7 +2144,7 @@ early_ret: break; } - if (!is_class) + if (is_interface) { emsg(_(e_static_member_not_supported_in_interface)); break; @@ -1812,7 +2167,7 @@ early_ret: has_var = TRUE; else if (checkforcmd(&p, "final", 5)) { - if (!is_class) + if (is_interface) { emsg(_(e_final_variable_not_supported_in_interface)); break; @@ -1821,7 +2176,7 @@ early_ret: } else if (checkforcmd(&p, "const", 5)) { - if (!is_class) + if (is_interface) { emsg(_(e_const_variable_not_supported_in_interface)); break; @@ -1867,7 +2222,7 @@ early_ret: break; } - if (!is_class && *varname == '_') + if (is_interface && *varname == '_') { // private variables are not supported in an interface semsg(_(e_protected_variable_not_supported_in_interface), @@ -1877,7 +2232,7 @@ early_ret: if (parse_member(eap, line, varname, has_public, &varname_end, &has_type, &type_list, &type, - is_class ? &init_expr: NULL) == FAIL) + !is_interface ? &init_expr: NULL) == FAIL) break; if (is_reserved_varname(varname, varname_end) @@ -1930,7 +2285,7 @@ early_ret: break; } - if (!is_class && *p == '_') + if (is_interface && *p == '_') { // private methods are not supported in an interface semsg(_(e_protected_method_not_supported_in_interface), p); @@ -1953,10 +2308,10 @@ early_ret: ga_init2(&lines_to_free, sizeof(char_u *), 50); int class_flags; - if (is_class) - class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS; - else + if (is_interface) class_flags = CF_INTERFACE; + else + class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS; ufunc_T *uf = define_function(&ea, NULL, &lines_to_free, class_flags, objmembers.ga_data, objmembers.ga_len); ga_clear_strings(&lines_to_free); @@ -2011,15 +2366,25 @@ early_ret: { if (is_class) semsg(_(e_not_valid_command_in_class_str), line); + else if (is_enum) + semsg(_(e_not_valid_command_in_enum_str), line); else semsg(_(e_not_valid_command_in_interface_str), line); break; } } + + if (theline == NULL && !success && is_enum) + emsg(_(e_missing_endenum)); + vim_free(theline); + if (success && is_enum && num_enum_values == 0) + // Empty enum statement. Add an empty "values" class variable + enum_add_values_member(cl, &classmembers, 0, &type_list); + /* - * Check a few things before defining the class. + * Check a few things */ // Check the "extends" class is valid. @@ -2067,7 +2432,8 @@ early_ret: if (success) { - // "endclass" encountered without failures: Create the class. + // "endclass" or "endinterface" or "endenum" encountered without any + // failures if (extends_cl != NULL) { @@ -2114,10 +2480,6 @@ early_ret: goto cleanup; } - // Allocate a typval for each class member and initialize it. - if (is_class && cl->class_class_member_count > 0) - add_class_members(cl, eap, &type_list); - int have_new = FALSE; ufunc_T *class_func = NULL; for (int i = 0; i < classfunctions.ga_len; ++i) @@ -2133,7 +2495,7 @@ early_ret: if (have_new) // The return type of new() is an object of class "cl" class_func->uf_ret_type->tt_class = cl; - else if (is_class && !is_abstract && !have_new) + else if ((is_class || is_enum) && !is_abstract && !have_new) // No new() method was defined, add the default constructor. add_default_constructor(cl, &classfunctions, &type_list); @@ -2144,13 +2506,21 @@ early_ret: update_builtin_method_index(cl); - cl->class_type.tt_type = VAR_CLASS; - cl->class_type.tt_class = cl; - cl->class_object_type.tt_type = VAR_OBJECT; - cl->class_object_type.tt_class = cl; + class_created(cl); + + // Allocate a typval for each class member and initialize it. + if ((is_class || is_enum) && cl->class_class_member_count > 0) + if (add_class_members(cl, eap, &type_list) == FAIL) + goto cleanup; + cl->class_type_list = type_list; - class_created(cl); + if (is_enum) + { + // clear the constructor method names, so that an enum class cannot + // be instantiated + enum_clear_constructors(cl); + } // TODO: // - Fill hashtab with object members and methods ? @@ -2265,15 +2635,6 @@ oc_member_type_by_idx( } /* - * Handle ":enum" up to ":endenum". - */ - void -ex_enum(exarg_T *eap UNUSED) -{ - // TODO -} - -/* * Type aliases (:type) */ @@ -2480,6 +2841,14 @@ call_oc_method( return FAIL; } + if (*name == '_') + { + // Protected object or class funcref variable + semsg(_(e_cannot_access_protected_variable_str), ocm->ocm_name, + cl->class_name); + return FAIL; + } + if (rettv->v_type == VAR_OBJECT) { // funcref object variable @@ -3334,8 +3703,14 @@ member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len) semsg(_(e_object_variable_str_accessible_only_using_object_str), varname, cl->class_name); else - semsg(_(e_class_variable_str_not_found_in_class_str), - varname, cl->class_name); + { + if (IS_ENUM(cl)) + semsg(_(e_enum_value_str_not_found_in_enum_str), + varname, cl->class_name); + else + semsg(_(e_class_variable_str_not_found_in_class_str), + varname, cl->class_name); + } } vim_free(varname); } @@ -3354,7 +3729,9 @@ defcompile_class(class_T *cl) { ufunc_T *ufunc = loop == 1 ? cl->class_class_functions[i] : cl->class_obj_methods[i]; - defcompile_function(ufunc, cl); + // Don't compile abstract methods + if (!IS_ABSTRACT_METHOD(ufunc)) + defcompile_function(ufunc, cl); } } } @@ -3426,7 +3803,7 @@ object_call_builtin_method( } /* - * Calls the object "empty()" method and returns the method retun value. In + * Calls the object "empty()" method and returns the method return value. In * case of an error, returns TRUE. */ int @@ -3480,10 +3857,21 @@ object_string( garray_T ga; ga_init2(&ga, 1, 50); - ga_concat(&ga, (char_u *)"object of "); class_T *cl = obj == NULL ? NULL : obj->obj_class; - ga_concat(&ga, cl == NULL ? (char_u *)"[unknown]" - : cl->class_name); + if (cl != NULL && IS_ENUM(cl)) + { + ga_concat(&ga, (char_u *)"enum "); + ga_concat(&ga, cl->class_name); + char_u *enum_name = ((typval_T *)(obj + 1))->vval.v_string; + ga_concat(&ga, (char_u *)"."); + ga_concat(&ga, enum_name); + } + else + { + ga_concat(&ga, (char_u *)"object of "); + ga_concat(&ga, cl == NULL ? (char_u *)"[unknown]" + : cl->class_name); + } if (cl != NULL) { ga_concat(&ga, (char_u *)" {"); |