To: vim_dev@googlegroups.com Subject: Patch 9.0.1074 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.1074 Problem: Class members are not supported yet. Solution: Add initial support for class members. Files: src/errors.h, src/eval.c, src/structs.h, src/vim9.h, src/vim9class.c, src/vim9compile.c, src/vim9expr.c, src/vim9instr.c, src/proto/vim9instr.pro, src/vim9cmds.c, src/vim9execute.c, src/testdir/test_vim9_class.vim *** ../vim-9.0.1073/src/errors.h 2022-12-16 16:41:19.204714805 +0000 --- src/errors.h 2022-12-18 18:30:04.387224250 +0000 *************** *** 3378,3393 **** INIT(= N_("E1329: Cannot get object member type from initializer: %s")); EXTERN char e_invalid_type_for_object_member_str[] INIT(= N_("E1330: Invalid type for object member: %s")); ! EXTERN char e_public_must_be_followed_by_this[] ! INIT(= N_("E1331: Public must be followed by \"this\"")); ! EXTERN char e_public_object_member_name_cannot_start_with_underscore_str[] ! INIT(= N_("E1332: Public object member name cannot start with underscore: %s")); ! EXTERN char e_cannot_access_private_object_member_str[] ! INIT(= N_("E1333: Cannot access private object member: %s")); EXTERN char e_object_member_not_found_str[] INIT(= N_("E1334: Object member not found: %s")); ! EXTERN char e_object_member_is_not_writable_str[] ! INIT(= N_("E1335: Object member is not writable: %s")); #endif EXTERN char e_internal_error_shortmess_too_long[] INIT(= N_("E1336: Internal error: shortmess too long")); --- 3378,3399 ---- INIT(= N_("E1329: Cannot get object member type from initializer: %s")); EXTERN char e_invalid_type_for_object_member_str[] INIT(= N_("E1330: Invalid type for object member: %s")); ! EXTERN char e_public_must_be_followed_by_this_or_static[] ! INIT(= N_("E1331: Public must be followed by \"this\" or \"static\"")); ! EXTERN char e_public_member_name_cannot_start_with_underscore_str[] ! INIT(= N_("E1332: Public member name cannot start with underscore: %s")); ! EXTERN char e_cannot_access_private_member_str[] ! INIT(= N_("E1333: Cannot access private member: %s")); EXTERN char e_object_member_not_found_str[] INIT(= N_("E1334: Object member not found: %s")); ! EXTERN char e_member_is_not_writable_str[] ! INIT(= N_("E1335: Member is not writable: %s")); #endif EXTERN char e_internal_error_shortmess_too_long[] INIT(= N_("E1336: Internal error: shortmess too long")); + #ifdef FEAT_EVAL + EXTERN char e_class_member_not_found_str[] + INIT(= N_("E1337: Class member not found: %s")); + EXTERN char e_member_not_found_on_class_str_str[] + INIT(= N_("E1338: Member not found on class \"%s\": %s")); + #endif *** ../vim-9.0.1073/src/eval.c 2022-12-14 20:54:52.411699476 +0000 --- src/eval.c 2022-12-18 18:23:43.039785841 +0000 *************** *** 1193,1211 **** var2.v_type = VAR_UNKNOWN; while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.')) { ! if (*p == '.' && lp->ll_tv->v_type != VAR_DICT ! && lp->ll_tv->v_type != VAR_OBJECT ! && lp->ll_tv->v_type != VAR_CLASS) { if (!quiet) semsg(_(e_dot_can_only_be_used_on_dictionary_str), name); return NULL; } ! if (lp->ll_tv->v_type != VAR_LIST ! && lp->ll_tv->v_type != VAR_DICT ! && lp->ll_tv->v_type != VAR_BLOB ! && lp->ll_tv->v_type != VAR_OBJECT ! && lp->ll_tv->v_type != VAR_CLASS) { if (!quiet) emsg(_(e_can_only_index_list_dictionary_or_blob)); --- 1193,1213 ---- var2.v_type = VAR_UNKNOWN; while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.')) { ! vartype_T v_type = lp->ll_tv->v_type; ! ! if (*p == '.' && v_type != VAR_DICT ! && v_type != VAR_OBJECT ! && v_type != VAR_CLASS) { if (!quiet) semsg(_(e_dot_can_only_be_used_on_dictionary_str), name); return NULL; } ! if (v_type != VAR_LIST ! && v_type != VAR_DICT ! && v_type != VAR_BLOB ! && v_type != VAR_OBJECT ! && v_type != VAR_CLASS) { if (!quiet) emsg(_(e_can_only_index_list_dictionary_or_blob)); *************** *** 1214,1222 **** // A NULL list/blob works like an empty list/blob, allocate one now. int r = OK; ! if (lp->ll_tv->v_type == VAR_LIST && lp->ll_tv->vval.v_list == NULL) r = rettv_list_alloc(lp->ll_tv); ! else if (lp->ll_tv->v_type == VAR_BLOB && lp->ll_tv->vval.v_blob == NULL) r = rettv_blob_alloc(lp->ll_tv); if (r == FAIL) --- 1216,1224 ---- // A NULL list/blob works like an empty list/blob, allocate one now. int r = OK; ! if (v_type == VAR_LIST && lp->ll_tv->vval.v_list == NULL) r = rettv_list_alloc(lp->ll_tv); ! else if (v_type == VAR_BLOB && lp->ll_tv->vval.v_blob == NULL) r = rettv_blob_alloc(lp->ll_tv); if (r == FAIL) *************** *** 1278,1284 **** // Optionally get the second index [ :expr]. if (*p == ':') { ! if (lp->ll_tv->v_type == VAR_DICT) { if (!quiet) emsg(_(e_cannot_slice_dictionary)); --- 1280,1286 ---- // Optionally get the second index [ :expr]. if (*p == ':') { ! if (v_type == VAR_DICT) { if (!quiet) emsg(_(e_cannot_slice_dictionary)); *************** *** 1334,1340 **** ++p; } ! if (lp->ll_tv->v_type == VAR_DICT) { if (len == -1) { --- 1336,1342 ---- ++p; } ! if (v_type == VAR_DICT) { if (len == -1) { *************** *** 1435,1441 **** clear_tv(&var1); lp->ll_tv = &lp->ll_di->di_tv; } ! else if (lp->ll_tv->v_type == VAR_BLOB) { long bloblen = blob_len(lp->ll_tv->vval.v_blob); --- 1437,1443 ---- clear_tv(&var1); lp->ll_tv = &lp->ll_di->di_tv; } ! else if (v_type == VAR_BLOB) { long bloblen = blob_len(lp->ll_tv->vval.v_blob); *************** *** 1466,1472 **** lp->ll_tv = NULL; break; } ! else if (lp->ll_tv->v_type == VAR_LIST) { /* * Get the number and item for the only or first index of the List. --- 1468,1474 ---- lp->ll_tv = NULL; break; } ! else if (v_type == VAR_LIST) { /* * Get the number and item for the only or first index of the List. *************** *** 1513,1519 **** } else // v_type == VAR_CLASS || v_type == VAR_OBJECT { ! class_T *cl = (lp->ll_tv->v_type == VAR_OBJECT && lp->ll_tv->vval.v_object != NULL) ? lp->ll_tv->vval.v_object->obj_class : lp->ll_tv->vval.v_class; --- 1515,1521 ---- } else // v_type == VAR_CLASS || v_type == VAR_OBJECT { ! class_T *cl = (v_type == VAR_OBJECT && lp->ll_tv->vval.v_object != NULL) ? lp->ll_tv->vval.v_object->obj_class : lp->ll_tv->vval.v_class; *************** *** 1521,1543 **** if (cl != NULL) { lp->ll_valtype = NULL; ! for (int i = 0; i < cl->class_obj_member_count; ++i) ! { ! objmember_T *om = cl->class_obj_members + i; ! if (STRNCMP(om->om_name, key, p - key) == 0 ! && om->om_name[p - key] == NUL) { ! switch (om->om_access) { case ACCESS_PRIVATE: ! semsg(_(e_cannot_access_private_object_member_str), ! om->om_name); return NULL; case ACCESS_READ: if (!(flags & GLV_READ_ONLY)) { ! semsg(_(e_object_member_is_not_writable_str), ! om->om_name); return NULL; } break; --- 1523,1550 ---- if (cl != NULL) { lp->ll_valtype = NULL; ! int count = v_type == VAR_OBJECT ? cl->class_obj_member_count ! : cl->class_class_member_count; ! ocmember_T *members = v_type == VAR_OBJECT ! ? cl->class_obj_members ! : cl->class_class_members; ! for (int i = 0; i < count; ++i) ! { ! ocmember_T *om = members + i; ! if (STRNCMP(om->ocm_name, key, p - key) == 0 ! && om->ocm_name[p - key] == NUL) { ! switch (om->ocm_access) { case ACCESS_PRIVATE: ! semsg(_(e_cannot_access_private_member_str), ! om->ocm_name); return NULL; case ACCESS_READ: if (!(flags & GLV_READ_ONLY)) { ! semsg(_(e_member_is_not_writable_str), ! om->ocm_name); return NULL; } break; *************** *** 1545,1562 **** break; } ! lp->ll_valtype = om->om_type; ! if (lp->ll_tv->v_type == VAR_OBJECT) lp->ll_tv = ((typval_T *)( lp->ll_tv->vval.v_object + 1)) + i; ! // TODO: what about a class? break; } } if (lp->ll_valtype == NULL) { ! semsg(_(e_object_member_not_found_str), key); return NULL; } } --- 1552,1573 ---- break; } ! lp->ll_valtype = om->ocm_type; ! if (v_type == VAR_OBJECT) lp->ll_tv = ((typval_T *)( lp->ll_tv->vval.v_object + 1)) + i; ! else ! lp->ll_tv = &cl->class_members_tv[i]; break; } } if (lp->ll_valtype == NULL) { ! if (v_type == VAR_OBJECT) ! semsg(_(e_object_member_not_found_str), key); ! else ! semsg(_(e_class_member_not_found_str), key); return NULL; } } *************** *** 5936,5943 **** { if (i > 0) ga_concat(&ga, (char_u *)", "); ! objmember_T *m = &cl->class_obj_members[i]; ! ga_concat(&ga, m->om_name); ga_concat(&ga, (char_u *)": "); char_u *tf = NULL; ga_concat(&ga, echo_string_core( --- 5947,5954 ---- { if (i > 0) ga_concat(&ga, (char_u *)", "); ! ocmember_T *m = &cl->class_obj_members[i]; ! ga_concat(&ga, m->ocm_name); ga_concat(&ga, (char_u *)": "); char_u *tf = NULL; ga_concat(&ga, echo_string_core( *** ../vim-9.0.1073/src/structs.h 2022-12-14 20:54:52.411699476 +0000 --- src/structs.h 2022-12-18 21:15:20.235962840 +0000 *************** *** 1387,1392 **** --- 1387,1393 ---- typedef double float_T; + typedef struct typval_S typval_T; typedef struct listvar_S list_T; typedef struct dictvar_S dict_T; typedef struct partial_S partial_T; *************** *** 1466,1479 **** } omacc_T; /* ! * Entry for an object member variable. */ typedef struct { ! char_u *om_name; // allocated ! omacc_T om_access; ! type_T *om_type; ! char_u *om_init; // allocated ! } objmember_T; // "class_T": used for v_class of typval of VAR_CLASS struct class_S --- 1467,1480 ---- } omacc_T; /* ! * Entry for an object or class member variable. */ typedef struct { ! char_u *ocm_name; // allocated ! omacc_T ocm_access; ! type_T *ocm_type; ! char_u *ocm_init; // allocated ! } ocmember_T; // "class_T": used for v_class of typval of VAR_CLASS struct class_S *************** *** 1481,1494 **** char_u *class_name; // allocated int class_refcount; int class_obj_member_count; ! objmember_T *class_obj_members; // allocated int class_obj_method_count; ufunc_T **class_obj_methods; // allocated garray_T class_type_list; // used for type pointers ! type_T class_type; type_T class_object_type; // same as class_type but VAR_OBJECT }; --- 1482,1506 ---- char_u *class_name; // allocated int class_refcount; + // class members: "static varname" + int class_class_member_count; + ocmember_T *class_class_members; // allocated + typval_T *class_members_tv; // allocated array of class member vals + + // class methods: "static def SomeMethod()" + int class_class_method_count; + ufunc_T **class_class_methods; // allocated + + // object members: "this.varname" int class_obj_member_count; ! ocmember_T *class_obj_members; // allocated + // object methods: "def SomeMethod()" int class_obj_method_count; ufunc_T **class_obj_methods; // allocated garray_T class_type_list; // used for type pointers ! type_T class_type; // type used for the class type_T class_object_type; // same as class_type but VAR_OBJECT }; *************** *** 1513,1519 **** /* * Structure to hold an internal variable without a name. */ ! typedef struct { vartype_T v_type; char v_lock; // see below: VAR_LOCKED, VAR_FIXED --- 1525,1531 ---- /* * Structure to hold an internal variable without a name. */ ! struct typval_S { vartype_T v_type; char v_lock; // see below: VAR_LOCKED, VAR_FIXED *************** *** 1534,1540 **** class_T *v_class; // class value (can be NULL) object_T *v_object; // object value (can be NULL) } vval; ! } typval_T; // Values for "dv_scope". #define VAR_SCOPE 1 // a:, v:, s:, etc. scope dictionaries --- 1546,1552 ---- class_T *v_class; // class value (can be NULL) object_T *v_object; // object value (can be NULL) } vval; ! }; // Values for "dv_scope". #define VAR_SCOPE 1 // a:, v:, s:, etc. scope dictionaries *** ../vim-9.0.1073/src/vim9.h 2022-12-13 18:42:19.749879633 +0000 --- src/vim9.h 2022-12-18 21:30:58.774980527 +0000 *************** *** 36,41 **** --- 36,43 ---- ISN_GET_OBJ_MEMBER, // object member, index is isn_arg.number ISN_STORE_THIS, // store value in "this" object member, index is // isn_arg.number + ISN_LOAD_CLASSMEMBER, // load class member, using classmember_T + ISN_STORE_CLASSMEMBER, // store in class member, using classmember_T // get and set variables ISN_LOAD, // push local variable isn_arg.number *************** *** 476,481 **** --- 478,489 ---- class_T *construct_class; // class the object is created from } construct_T; + // arguments to ISN_STORE_CLASSMEMBER and ISN_LOAD_CLASSMEMBER + typedef struct { + class_T *cm_class; + int cm_idx; + } classmember_T; + /* * Instruction */ *************** *** 528,533 **** --- 536,542 ---- deferins_T defer; echowin_T echowin; construct_T construct; + classmember_T classmember; } isn_arg; }; *************** *** 538,544 **** ufunc_T *df_ufunc; // struct containing most stuff int df_refcount; // how many ufunc_T point to this dfunc_T int df_idx; // index in def_functions ! int df_deleted; // if TRUE function was deleted int df_script_seq; // Value of sctx_T sc_seq when the function // was compiled. char_u *df_name; // name used for error messages --- 547,555 ---- ufunc_T *df_ufunc; // struct containing most stuff int df_refcount; // how many ufunc_T point to this dfunc_T int df_idx; // index in def_functions ! char df_deleted; // if TRUE function was deleted ! char df_delete_busy; // TRUE when in ! // delete_def_function_contents() int df_script_seq; // Value of sctx_T sc_seq when the function // was compiled. char_u *df_name; // name used for error messages *************** *** 735,740 **** --- 746,752 ---- dest_window, dest_tab, dest_vimvar, + dest_class_member, dest_script, dest_reg, dest_expr, *************** *** 766,771 **** --- 778,787 ---- lvar_T lhs_local_lvar; // used for existing local destination lvar_T lhs_arg_lvar; // used for argument destination lvar_T *lhs_lvar; // points to destination lvar + + class_T *lhs_class; // for dest_class_member + int lhs_classmember_idx; // for dest_class_member + int lhs_scriptvar_sid; int lhs_scriptvar_idx; *** ../vim-9.0.1073/src/vim9class.c 2022-12-14 20:54:52.407699483 +0000 --- src/vim9class.c 2022-12-18 21:19:06.883718087 +0000 *************** *** 22,27 **** --- 22,175 ---- #endif /* + * Parse a member declaration, both object and class member. + * Returns OK or FAIL. When OK then "varname_end" is set to just after the + * variable name and "type_ret" is set to the decleared or detected type. + * "init_expr" is set to the initialisation expression (allocated), if there is + * one. + */ + static int + parse_member( + exarg_T *eap, + char_u *line, + char_u *varname, + int has_public, // TRUE if "public" seen before "varname" + char_u **varname_end, + garray_T *type_list, + type_T **type_ret, + char_u **init_expr) + { + *varname_end = to_name_end(varname, FALSE); + if (*varname == '_' && has_public) + { + semsg(_(e_public_member_name_cannot_start_with_underscore_str), line); + return FAIL; + } + + char_u *colon = skipwhite(*varname_end); + char_u *type_arg = colon; + type_T *type = NULL; + if (*colon == ':') + { + if (VIM_ISWHITE(**varname_end)) + { + semsg(_(e_no_white_space_allowed_before_colon_str), varname); + return FAIL; + } + if (!VIM_ISWHITE(colon[1])) + { + semsg(_(e_white_space_required_after_str_str), ":", varname); + return FAIL; + } + type_arg = skipwhite(colon + 1); + type = parse_type(&type_arg, type_list, TRUE); + if (type == NULL) + return FAIL; + } + + char_u *expr_start = skipwhite(type_arg); + char_u *expr_end = expr_start; + if (type == NULL && *expr_start != '=') + { + emsg(_(e_type_or_initialization_required)); + return FAIL; + } + + if (*expr_start == '=') + { + if (!VIM_ISWHITE(expr_start[-1]) || !VIM_ISWHITE(expr_start[1])) + { + semsg(_(e_white_space_required_before_and_after_str_at_str), + "=", type_arg); + return FAIL; + } + expr_start = skipwhite(expr_start + 1); + + expr_end = expr_start; + evalarg_T evalarg; + fill_evalarg_from_eap(&evalarg, eap, FALSE); + skip_expr(&expr_end, NULL); + + if (type == NULL) + { + // No type specified, use the type of the initializer. + typval_T tv; + tv.v_type = VAR_UNKNOWN; + char_u *expr = expr_start; + int res = eval0(expr, &tv, eap, &evalarg); + + if (res == OK) + type = typval2type(&tv, get_copyID(), type_list, + TVTT_DO_MEMBER); + if (type == NULL) + { + semsg(_(e_cannot_get_object_member_type_from_initializer_str), + expr_start); + clear_evalarg(&evalarg, NULL); + return FAIL; + } + } + clear_evalarg(&evalarg, NULL); + } + if (!valid_declaration_type(type)) + return FAIL; + + *type_ret = type; + if (expr_end > expr_start) + *init_expr = vim_strnsave(expr_start, expr_end - expr_start); + return OK; + } + + /* + * 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". + */ + static int + add_member( + garray_T *gap, + char_u *varname, + char_u *varname_end, + int has_public, + type_T *type, + char_u *init_expr) + { + if (ga_grow(gap, 1) == FAIL) + return FAIL; + ocmember_T *m = ((ocmember_T *)gap->ga_data) + gap->ga_len; + m->ocm_name = vim_strnsave(varname, varname_end - varname); + m->ocm_access = has_public ? ACCESS_ALL + : *varname == '_' ? ACCESS_PRIVATE : ACCESS_READ; + m->ocm_type = type; + if (init_expr != NULL) + m->ocm_init = init_expr; + ++gap->ga_len; + return OK; + } + + /* + * Move the class or object members found while parsing a class into the class. + * "gap" contains the found members. + * "members" will be set to the newly allocated array of members and + * "member_count" set to the number of members. + * Returns OK or FAIL. + */ + static int + add_members_to_class( + garray_T *gap, + 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) + return FAIL; + mch_memmove(*members, gap->ga_data, sizeof(ocmember_T) * gap->ga_len); + VIM_CLEAR(gap->ga_data); + return OK; + } + + /* * Handle ":class" and ":abstract class" up to ":endclass". */ void *************** *** 64,79 **** // extends SomeClass // implements SomeInterface // specifies SomeInterface ! // check nothing follows ! ! // TODO: handle "is_export" if it is set garray_T type_list; // list of pointers to allocated types ga_init2(&type_list, sizeof(type_T *), 10); // Growarray with object members declared in the class. garray_T objmembers; ! ga_init2(&objmembers, sizeof(objmember_T), 10); // Growarray with object methods declared in the class. garray_T objmethods; --- 212,234 ---- // extends SomeClass // implements SomeInterface // specifies SomeInterface ! // check that nothing follows ! // handle "is_export" if it is set garray_T type_list; // list of pointers to allocated types ga_init2(&type_list, sizeof(type_T *), 10); + // Growarray with class members declared in the class. + garray_T classmembers; + ga_init2(&classmembers, sizeof(ocmember_T), 10); + + // Growarray with object methods declared in the class. + garray_T classmethods; + ga_init2(&classmethods, sizeof(ufunc_T *), 10); + // Growarray with object members declared in the class. garray_T objmembers; ! ga_init2(&objmembers, sizeof(ocmember_T), 10); // Growarray with object methods declared in the class. garray_T objmethods; *************** *** 92,103 **** break; char_u *line = skipwhite(theline); - // TODO: - // class members (public, read access, private): - // static varname - // public static varname - // static _varname - char_u *p = line; if (checkforcmd(&p, "endclass", 4)) { --- 247,252 ---- *************** *** 110,118 **** break; } - // "this._varname" - // "this.varname" - // "public this.varname" int has_public = FALSE; if (checkforcmd(&p, "public", 3)) { --- 259,264 ---- *************** *** 124,135 **** has_public = TRUE; p = skipwhite(line + 6); ! if (STRNCMP(p, "this", 4) != 0) { ! emsg(_(e_public_must_be_followed_by_this)); break; } } if (STRNCMP(p, "this", 4) == 0) { if (p[4] != '.' || !eval_isnamec1(p[5])) --- 270,286 ---- has_public = TRUE; p = skipwhite(line + 6); ! if (STRNCMP(p, "this", 4) != 0 && STRNCMP(p, "static", 6) != 0) { ! emsg(_(e_public_must_be_followed_by_this_or_static)); break; } } + + // object members (public, read access, private): + // "this._varname" + // "this.varname" + // "public this.varname" if (STRNCMP(p, "this", 4) == 0) { if (p[4] != '.' || !eval_isnamec1(p[5])) *************** *** 138,232 **** break; } char_u *varname = p + 5; ! char_u *varname_end = to_name_end(varname, FALSE); ! if (*varname == '_' && has_public) ! { ! semsg(_(e_public_object_member_name_cannot_start_with_underscore_str), line); ! break; ! } ! ! char_u *colon = skipwhite(varname_end); ! char_u *type_arg = colon; type_T *type = NULL; ! if (*colon == ':') { ! if (VIM_ISWHITE(*varname_end)) ! { ! semsg(_(e_no_white_space_allowed_before_colon_str), ! varname); ! break; ! } ! if (!VIM_ISWHITE(colon[1])) ! { ! semsg(_(e_white_space_required_after_str_str), ":", ! varname); ! break; ! } ! type_arg = skipwhite(colon + 1); ! type = parse_type(&type_arg, &type_list, TRUE); ! if (type == NULL) ! break; } ! char_u *expr_start = skipwhite(type_arg); ! char_u *expr_end = expr_start; ! if (type == NULL && *expr_start != '=') { ! emsg(_(e_type_or_initialization_required)); ! break; } ! ! if (*expr_start == '=') { ! if (!VIM_ISWHITE(expr_start[-1]) || !VIM_ISWHITE(expr_start[1])) ! { ! semsg(_(e_white_space_required_before_and_after_str_at_str), ! "=", type_arg); break; ! } ! expr_start = skipwhite(expr_start + 1); ! ! expr_end = expr_start; ! evalarg_T evalarg; ! fill_evalarg_from_eap(&evalarg, eap, FALSE); ! skip_expr(&expr_end, NULL); ! ! if (type == NULL) { ! // No type specified, use the type of the initializer. ! typval_T tv; ! tv.v_type = VAR_UNKNOWN; ! char_u *expr = expr_start; ! int res = eval0(expr, &tv, eap, &evalarg); ! ! if (res == OK) ! type = typval2type(&tv, get_copyID(), &type_list, ! TVTT_DO_MEMBER); ! if (type == NULL) ! { ! semsg(_(e_cannot_get_object_member_type_from_initializer_str), ! expr_start); ! clear_evalarg(&evalarg, NULL); ! break; ! } } - clear_evalarg(&evalarg, NULL); } - if (!valid_declaration_type(type)) - break; - - if (ga_grow(&objmembers, 1) == FAIL) - break; - objmember_T *m = ((objmember_T *)objmembers.ga_data) - + objmembers.ga_len; - m->om_name = vim_strnsave(varname, varname_end - varname); - m->om_access = has_public ? ACCESS_ALL - : *varname == '_' ? ACCESS_PRIVATE - : ACCESS_READ; - m->om_type = type; - if (expr_end > expr_start) - m->om_init = vim_strnsave(expr_start, expr_end - expr_start); - ++objmembers.ga_len; } // constructors: --- 289,340 ---- break; } char_u *varname = p + 5; ! char_u *varname_end = NULL; type_T *type = NULL; ! char_u *init_expr = NULL; ! if (parse_member(eap, line, varname, has_public, ! &varname_end, &type_list, &type, &init_expr) == FAIL) ! break; ! if (add_member(&objmembers, varname, varname_end, ! has_public, type, init_expr) == FAIL) { ! vim_free(init_expr); ! break; } + } ! // class members and methods ! else if (checkforcmd(&p, "static", 6)) ! { ! p = skipwhite(p); ! if (checkforcmd(&p, "def", 3)) { ! // TODO: class method ! // static def someMethod() ! // enddef ! // static def someMethod() ! // enddef } ! else { ! // class members (public, read access, private): ! // "static _varname" ! // "static varname" ! // "public static varname" ! char_u *varname = p; ! char_u *varname_end = NULL; ! type_T *type = NULL; ! char_u *init_expr = NULL; ! if (parse_member(eap, line, varname, has_public, ! &varname_end, &type_list, &type, &init_expr) == FAIL) break; ! if (add_member(&classmembers, varname, varname_end, ! has_public, type, init_expr) == FAIL) { ! vim_free(init_expr); ! break; } } } // constructors: *************** *** 238,249 **** // def someMethod() // enddef // TODO: - // static def someMethod() - // enddef // def someMethod() // enddef - // static def someMethod() - // enddef else if (checkforcmd(&p, "def", 3)) { exarg_T ea; --- 346,353 ---- *************** *** 282,303 **** class_T *cl = NULL; if (success) { cl = ALLOC_CLEAR_ONE(class_T); if (cl == NULL) goto cleanup; cl->class_refcount = 1; cl->class_name = vim_strnsave(arg, name_end - arg); ! // Members are used by the new() function, add them here. ! cl->class_obj_member_count = objmembers.ga_len; ! cl->class_obj_members = objmembers.ga_len == 0 ? NULL ! : ALLOC_MULT(objmember_T, objmembers.ga_len); ! if (cl->class_name == NULL ! || (objmembers.ga_len > 0 && cl->class_obj_members == NULL)) goto cleanup; ! mch_memmove(cl->class_obj_members, objmembers.ga_data, ! sizeof(objmember_T) * objmembers.ga_len); ! vim_free(objmembers.ga_data); int have_new = FALSE; for (int i = 0; i < objmethods.ga_len; ++i) --- 386,437 ---- class_T *cl = NULL; if (success) { + // "endclass" encountered without failures: Create the class. + cl = ALLOC_CLEAR_ONE(class_T); if (cl == NULL) goto cleanup; cl->class_refcount = 1; cl->class_name = vim_strnsave(arg, name_end - arg); + if (cl->class_name == NULL) + goto cleanup; ! // Add class and object members to "cl". ! if (add_members_to_class(&classmembers, ! &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) goto cleanup; ! ! if (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; ! } ! } ! } int have_new = FALSE; for (int i = 0; i < objmethods.ga_len; ++i) *************** *** 318,325 **** if (i > 0) ga_concat(&fga, (char_u *)", "); ga_concat(&fga, (char_u *)"this."); ! objmember_T *m = cl->class_obj_members + i; ! ga_concat(&fga, (char_u *)m->om_name); ga_concat(&fga, (char_u *)" = v:none"); } ga_concat(&fga, (char_u *)")\nenddef\n"); --- 452,459 ---- 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"); *************** *** 355,360 **** --- 489,495 ---- } } + // TODO: class methods cl->class_obj_method_count = objmethods.ga_len; cl->class_obj_methods = ALLOC_MULT(ufunc_T *, objmethods.ga_len); if (cl->class_obj_methods == NULL) *************** *** 378,390 **** cl->class_type_list = type_list; // TODO: ! // - Add the methods to the class ! // - array with ufunc_T pointers ! // - Fill hashtab with object members and methods ! // - Generate the default new() method, if needed. ! // Later: ! // - class members ! // - class methods // Add the class to the script-local variables. typval_T tv; --- 513,519 ---- cl->class_type_list = type_list; // TODO: ! // - Fill hashtab with object members and methods ? // Add the class to the script-local variables. typval_T tv; *************** *** 404,416 **** vim_free(cl); } ! for (int i = 0; i < objmembers.ga_len; ++i) { ! objmember_T *m = ((objmember_T *)objmembers.ga_data) + i; ! vim_free(m->om_name); ! vim_free(m->om_init); } - ga_clear(&objmembers); for (int i = 0; i < objmethods.ga_len; ++i) { --- 533,552 ---- vim_free(cl); } ! for (int round = 1; round <= 2; ++round) { ! garray_T *gap = round == 1 ? &classmembers : &objmembers; ! if (gap->ga_len == 0 || gap->ga_data == NULL) ! continue; ! ! for (int i = 0; i < gap->ga_len; ++i) ! { ! ocmember_T *m = ((ocmember_T *)gap->ga_data) + i; ! vim_free(m->ocm_name); ! vim_free(m->ocm_init); ! } ! ga_clear(gap); } for (int i = 0; i < objmethods.ga_len; ++i) { *************** *** 437,447 **** for (int i = 0; i < cl->class_obj_member_count; ++i) { ! objmember_T *m = cl->class_obj_members + i; ! if (STRNCMP(m->om_name, name, len) == 0 && m->om_name[len] == NUL) { *member_idx = i; ! return m->om_type; } } return &t_any; --- 573,583 ---- for (int i = 0; i < cl->class_obj_member_count; ++i) { ! ocmember_T *m = cl->class_obj_members + i; ! if (STRNCMP(m->ocm_name, name, len) == 0 && m->ocm_name[len] == NUL) { *member_idx = i; ! return m->ocm_type; } } return &t_any; *************** *** 572,584 **** { for (int i = 0; i < cl->class_obj_member_count; ++i) { ! objmember_T *m = &cl->class_obj_members[i]; ! if (STRNCMP(name, m->om_name, len) == 0 && m->om_name[len] == NUL) { if (*name == '_') { ! semsg(_(e_cannot_access_private_object_member_str), ! m->om_name); return FAIL; } --- 708,719 ---- { for (int i = 0; i < cl->class_obj_member_count; ++i) { ! ocmember_T *m = &cl->class_obj_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; } *************** *** 597,603 **** semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name); } ! // TODO: class member return FAIL; } --- 732,762 ---- semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name); } ! else if (rettv->v_type == VAR_CLASS) ! { ! // class member ! 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 (*name == '_') ! { ! semsg(_(e_cannot_access_private_member_str), 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; } *************** *** 708,722 **** void class_unref(class_T *cl) { ! if (cl != NULL && --cl->class_refcount <= 0) { ! vim_free(cl->class_name); for (int i = 0; i < cl->class_obj_member_count; ++i) { ! objmember_T *m = &cl->class_obj_members[i]; ! vim_free(m->om_name); ! vim_free(m->om_init); } vim_free(cl->class_obj_members); --- 867,895 ---- void class_unref(class_T *cl) { ! if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL) { ! // Freeing what the class contains may recursively come back here. ! // Clear "class_name" first, if it is NULL the class does not need to ! // be freed. ! VIM_CLEAR(cl->class_name); ! ! for (int i = 0; i < cl->class_class_member_count; ++i) ! { ! ocmember_T *m = &cl->class_class_members[i]; ! vim_free(m->ocm_name); ! vim_free(m->ocm_init); ! if (cl->class_members_tv != NULL) ! clear_tv(&cl->class_members_tv[i]); ! } ! vim_free(cl->class_class_members); ! vim_free(cl->class_members_tv); for (int i = 0; i < cl->class_obj_member_count; ++i) { ! ocmember_T *m = &cl->class_obj_members[i]; ! vim_free(m->ocm_name); ! vim_free(m->ocm_init); } vim_free(cl->class_obj_members); *** ../vim-9.0.1073/src/vim9compile.c 2022-12-14 13:49:56.214667568 +0000 --- src/vim9compile.c 2022-12-18 21:30:03.843036521 +0000 *************** *** 302,307 **** --- 302,329 ---- } /* + * If "name" is a class member in cctx->ctx_ufunc->uf_class return the index in + * class.class_class_members[]. + * Otherwise return -1; + */ + static int + class_member_index(char_u *name, size_t len, cctx_T *cctx) + { + 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) + return i; + } + return -1; + } + + /* * Return TRUE if "name" is a local variable, argument, script variable or * imported. */ *************** *** 316,321 **** --- 338,344 ---- && (cctx->ctx_ufunc->uf_flags & FC_OBJECT) && STRNCMP(name, "this", 4) == 0))) || script_var_exists(name, len, cctx, NULL) == OK + || class_member_index(name, len, cctx) >= 0 || find_imported(name, len, FALSE) != NULL; } *************** *** 353,358 **** --- 376,384 ---- if (len == 1 && *p == '_') return OK; + if (class_member_index(p, len, cctx) >= 0) + return OK; + if (script_var_exists(p, len, cctx, cstack) == OK) { if (is_arg) *************** *** 1195,1208 **** * Generate the load instruction for "name". */ static void ! generate_loadvar( ! cctx_T *cctx, ! assign_dest_T dest, ! char_u *name, ! lvar_T *lvar, ! type_T *type) { ! switch (dest) { case dest_option: case dest_func_option: --- 1221,1232 ---- * Generate the load instruction for "name". */ static void ! generate_loadvar(cctx_T *cctx, lhs_T *lhs) { ! char_u *name = lhs->lhs_name; ! type_T *type = lhs->lhs_type; ! ! switch (lhs->lhs_dest) { case dest_option: case dest_func_option: *************** *** 1245,1250 **** --- 1269,1275 ---- case dest_local: if (cctx->ctx_skip != SKIP_YES) { + lvar_T *lvar = lhs->lhs_lvar; if (lvar->lv_from_outer > 0) generate_LOADOUTER(cctx, lvar->lv_idx, lvar->lv_from_outer, lvar->lv_loop_depth, lvar->lv_loop_idx, type); *************** *** 1252,1257 **** --- 1277,1286 ---- generate_LOAD(cctx, ISN_LOAD, lvar->lv_idx, NULL, type); } break; + case dest_class_member: + generate_CLASSMEMBER(cctx, TRUE, lhs->lhs_class, + lhs->lhs_classmember_idx); + break; case dest_expr: // list or dict value should already be on the stack. break; *************** *** 1533,1539 **** --- 1562,1570 ---- if (lookup_local(var_start, lhs->lhs_varlen, &lhs->lhs_local_lvar, cctx) == OK) + { lhs->lhs_lvar = &lhs->lhs_local_lvar; + } else { CLEAR_FIELD(lhs->lhs_arg_lvar); *************** *** 1549,1554 **** --- 1580,1586 ---- lhs->lhs_lvar = &lhs->lhs_arg_lvar; } } + if (lhs->lhs_lvar != NULL) { if (is_decl) *************** *** 1557,1562 **** --- 1589,1600 ---- return FAIL; } } + else if ((lhs->lhs_classmember_idx = class_member_index( + var_start, lhs->lhs_varlen, cctx)) >= 0) + { + lhs->lhs_dest = dest_class_member; + lhs->lhs_class = cctx->ctx_ufunc->uf_class; + } else { int script_namespace = lhs->lhs_varlen > 1 *************** *** 1965,1972 **** return FAIL; } else ! generate_loadvar(cctx, lhs->lhs_dest, lhs->lhs_name, ! lhs->lhs_lvar, lhs->lhs_type); return OK; } --- 2003,2009 ---- return FAIL; } else ! generate_loadvar(cctx, lhs); return OK; } *************** *** 2998,3017 **** for (int i = 0; i < ufunc->uf_class->class_obj_member_count; ++i) { ! objmember_T *m = &ufunc->uf_class->class_obj_members[i]; ! if (m->om_init != NULL) { ! char_u *expr = m->om_init; if (compile_expr0(&expr, &cctx) == FAIL) goto erret; ! if (!ends_excmd2(m->om_init, expr)) { semsg(_(e_trailing_characters_str), expr); goto erret; } } else ! push_default_value(&cctx, m->om_type->tt_type, FALSE, NULL); generate_STORE_THIS(&cctx, i); } --- 3035,3054 ---- for (int i = 0; i < ufunc->uf_class->class_obj_member_count; ++i) { ! ocmember_T *m = &ufunc->uf_class->class_obj_members[i]; ! if (m->ocm_init != NULL) { ! char_u *expr = m->ocm_init; if (compile_expr0(&expr, &cctx) == FAIL) goto erret; ! if (!ends_excmd2(m->ocm_init, expr)) { semsg(_(e_trailing_characters_str), expr); goto erret; } } else ! push_default_value(&cctx, m->ocm_type->tt_type, FALSE, NULL); generate_STORE_THIS(&cctx, i); } *************** *** 3792,3797 **** --- 3829,3841 ---- { int idx; + // In same cases the instructions may refer to a class in which the + // function is defined and unreferencing the class may call back here + // recursively. Set the df_delete_busy to avoid problems. + if (dfunc->df_delete_busy) + return; + dfunc->df_delete_busy = TRUE; + ga_clear(&dfunc->df_def_args_isn); ga_clear_strings(&dfunc->df_var_names); *************** *** 3800,3813 **** for (idx = 0; idx < dfunc->df_instr_count; ++idx) delete_instr(dfunc->df_instr + idx); VIM_CLEAR(dfunc->df_instr); - dfunc->df_instr = NULL; } if (dfunc->df_instr_debug != NULL) { for (idx = 0; idx < dfunc->df_instr_debug_count; ++idx) delete_instr(dfunc->df_instr_debug + idx); VIM_CLEAR(dfunc->df_instr_debug); - dfunc->df_instr_debug = NULL; } #ifdef FEAT_PROFILE if (dfunc->df_instr_prof != NULL) --- 3844,3855 ---- *************** *** 3815,3821 **** for (idx = 0; idx < dfunc->df_instr_prof_count; ++idx) delete_instr(dfunc->df_instr_prof + idx); VIM_CLEAR(dfunc->df_instr_prof); - dfunc->df_instr_prof = NULL; } #endif --- 3857,3862 ---- *************** *** 3823,3828 **** --- 3864,3871 ---- dfunc->df_deleted = TRUE; if (dfunc->df_ufunc != NULL) dfunc->df_ufunc->uf_def_status = UF_NOT_COMPILED; + + dfunc->df_delete_busy = FALSE; } /* *** ../vim-9.0.1073/src/vim9expr.c 2022-12-14 20:54:52.411699476 +0000 --- src/vim9expr.c 2022-12-18 18:27:03.819479601 +0000 *************** *** 278,294 **** { for (int i = 0; i < cl->class_obj_member_count; ++i) { ! objmember_T *m = &cl->class_obj_members[i]; ! if (STRNCMP(name, m->om_name, len) == 0 && m->om_name[len] == NUL) { if (*name == '_' && cctx->ctx_ufunc->uf_class != cl) { ! semsg(_(e_cannot_access_private_object_member_str), ! m->om_name); return FAIL; } ! generate_GET_OBJ_MEMBER(cctx, i, m->om_type); *arg = name_end; return OK; --- 278,293 ---- { for (int i = 0; i < cl->class_obj_member_count; ++i) { ! ocmember_T *m = &cl->class_obj_members[i]; ! if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL) { if (*name == '_' && cctx->ctx_ufunc->uf_class != cl) { ! semsg(_(e_cannot_access_private_member_str), m->ocm_name); return FAIL; } ! generate_GET_OBJ_MEMBER(cctx, i, m->ocm_type); *arg = name_end; return OK; *** ../vim-9.0.1073/src/vim9instr.c 2022-12-13 18:42:19.749879633 +0000 --- src/vim9instr.c 2022-12-18 20:53:32.093494839 +0000 *************** *** 957,962 **** --- 957,994 ---- } /* + * Generate an ISN_LOAD_CLASSMEMBER ("load" == TRUE) or ISN_STORE_CLASSMEMBER + * ("load" == FALSE) instruction. + */ + int + generate_CLASSMEMBER( + cctx_T *cctx, + int load, + class_T *cl, + int idx) + { + isn_T *isn; + + RETURN_OK_IF_SKIP(cctx); + if (load) + { + ocmember_T *m = &cl->class_class_members[idx]; + isn = generate_instr_type(cctx, ISN_LOAD_CLASSMEMBER, m->ocm_type); + } + else + { + isn = generate_instr_drop(cctx, ISN_STORE_CLASSMEMBER, 1); + } + if (isn == NULL) + return FAIL; + isn->isn_arg.classmember.cm_class = cl; + ++cl->class_refcount; + isn->isn_arg.classmember.cm_idx = idx; + + return OK; + } + + /* * Generate an ISN_STOREOUTER instruction. */ static int *************** *** 2114,2119 **** --- 2146,2152 ---- /* * Generate a STORE instruction for "dest", not being "dest_local". + * "lhs" might be NULL. * Return FAIL when out of memory. */ int *************** *** 2122,2131 **** assign_dest_T dest, int opt_flags, int vimvaridx, - int scriptvar_idx, - int scriptvar_sid, type_T *type, ! char_u *name) { switch (dest) { --- 2155,2163 ---- assign_dest_T dest, int opt_flags, int vimvaridx, type_T *type, ! char_u *name, ! lhs_T *lhs) { switch (dest) { *************** *** 2156,2164 **** case dest_vimvar: return generate_STORE(cctx, ISN_STOREV, vimvaridx, NULL); case dest_script: if (scriptvar_idx < 0) { ! isntype_T isn_type = ISN_STORES; if (SCRIPT_ID_VALID(scriptvar_sid) && SCRIPT_ITEM(scriptvar_sid)->sn_import_autoload --- 2188,2198 ---- case dest_vimvar: return generate_STORE(cctx, ISN_STOREV, vimvaridx, NULL); case dest_script: + int scriptvar_idx = lhs->lhs_scriptvar_idx; + int scriptvar_sid = lhs->lhs_scriptvar_sid; if (scriptvar_idx < 0) { ! isntype_T isn_type = ISN_STORES; if (SCRIPT_ID_VALID(scriptvar_sid) && SCRIPT_ITEM(scriptvar_sid)->sn_import_autoload *************** *** 2177,2182 **** --- 2211,2220 ---- } return generate_VIM9SCRIPT(cctx, ISN_STORESCRIPT, scriptvar_sid, scriptvar_idx, type); + case dest_class_member: + return generate_CLASSMEMBER(cctx, FALSE, + lhs->lhs_class, lhs->lhs_classmember_idx); + case dest_local: case dest_expr: // cannot happen *************** *** 2210,2217 **** if (lhs->lhs_dest != dest_local) return generate_store_var(cctx, lhs->lhs_dest, lhs->lhs_opt_flags, lhs->lhs_vimvaridx, ! lhs->lhs_scriptvar_idx, lhs->lhs_scriptvar_sid, ! lhs->lhs_type, lhs->lhs_name); if (lhs->lhs_lvar != NULL) { --- 2248,2254 ---- if (lhs->lhs_dest != dest_local) return generate_store_var(cctx, lhs->lhs_dest, lhs->lhs_opt_flags, lhs->lhs_vimvaridx, ! lhs->lhs_type, lhs->lhs_name, lhs); if (lhs->lhs_lvar != NULL) { *************** *** 2422,2427 **** --- 2459,2469 ---- vim_free(isn->isn_arg.script.scriptref); break; + case ISN_LOAD_CLASSMEMBER: + case ISN_STORE_CLASSMEMBER: + class_unref(isn->isn_arg.classmember.cm_class); + break; + case ISN_TRY: vim_free(isn->isn_arg.tryref.try_ref); break; *** ../vim-9.0.1073/src/proto/vim9instr.pro 2022-12-13 18:42:19.749879633 +0000 --- src/proto/vim9instr.pro 2022-12-18 21:15:25.863956663 +0000 *************** *** 32,37 **** --- 32,38 ---- int generate_SLICE(cctx_T *cctx, int count); int generate_CHECKLEN(cctx_T *cctx, int min_len, int more_OK); int generate_STORE(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name); + int generate_CLASSMEMBER(cctx_T *cctx, int load, class_T *cl, int idx); int generate_STORENR(cctx_T *cctx, int idx, varnumber_T value); int generate_LOAD(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name, type_T *type); int generate_LOADOUTER(cctx_T *cctx, int idx, int nesting, int loop_depth, int loop_idx, type_T *type); *************** *** 74,80 **** int generate_UNPACK(cctx_T *cctx, int var_count, int semicolon); int generate_cmdmods(cctx_T *cctx, cmdmod_T *cmod); int generate_undo_cmdmods(cctx_T *cctx); ! int generate_store_var(cctx_T *cctx, assign_dest_T dest, int opt_flags, int vimvaridx, int scriptvar_idx, int scriptvar_sid, type_T *type, char_u *name); int inside_loop_scope(cctx_T *cctx); int generate_store_lhs(cctx_T *cctx, lhs_T *lhs, int instr_count, int is_decl); void may_generate_prof_end(cctx_T *cctx, int prof_lnum); --- 75,81 ---- int generate_UNPACK(cctx_T *cctx, int var_count, int semicolon); int generate_cmdmods(cctx_T *cctx, cmdmod_T *cmod); int generate_undo_cmdmods(cctx_T *cctx); ! int generate_store_var(cctx_T *cctx, assign_dest_T dest, int opt_flags, int vimvaridx, type_T *type, char_u *name, lhs_T *lhs); int inside_loop_scope(cctx_T *cctx); int generate_store_lhs(cctx_T *cctx, lhs_T *lhs, int instr_count, int is_decl); void may_generate_prof_end(cctx_T *cctx, int prof_lnum); *** ../vim-9.0.1073/src/vim9cmds.c 2022-12-02 18:12:01.018476816 +0000 --- src/vim9cmds.c 2022-12-18 20:05:02.684604705 +0000 *************** *** 1013,1019 **** if (dest != dest_local) { if (generate_store_var(cctx, dest, opt_flags, vimvaridx, ! 0, 0, type, name) == FAIL) goto failed; } else if (varlen == 1 && *arg == '_') --- 1013,1019 ---- if (dest != dest_local) { if (generate_store_var(cctx, dest, opt_flags, vimvaridx, ! type, name, NULL) == FAIL) goto failed; } else if (varlen == 1 && *arg == '_') *** ../vim-9.0.1073/src/vim9execute.c 2022-12-13 18:42:19.749879633 +0000 --- src/vim9execute.c 2022-12-18 20:43:29.722073643 +0000 *************** *** 3817,3822 **** --- 3817,3843 ---- goto on_error; break; + case ISN_LOAD_CLASSMEMBER: + { + if (GA_GROW_FAILS(&ectx->ec_stack, 1)) + goto theend; + classmember_T *cm = &iptr->isn_arg.classmember; + *STACK_TV_BOT(0) = + cm->cm_class->class_members_tv[cm->cm_idx]; + ++ectx->ec_stack.ga_len; + } + break; + + case ISN_STORE_CLASSMEMBER: + { + classmember_T *cm = &iptr->isn_arg.classmember; + tv = &cm->cm_class->class_members_tv[cm->cm_idx]; + clear_tv(tv); + *tv = *STACK_TV_BOT(-1); + --ectx->ec_stack.ga_len; + } + break; + // Load or store variable or argument from outer scope. case ISN_LOADOUTER: case ISN_STOREOUTER: *************** *** 6403,6408 **** --- 6424,6442 ---- smsg("%s%4d STORERANGE", pfx, current); break; + case ISN_LOAD_CLASSMEMBER: + case ISN_STORE_CLASSMEMBER: + { + class_T *cl = iptr->isn_arg.classmember.cm_class; + int idx = iptr->isn_arg.classmember.cm_idx; + ocmember_T *ocm = &cl->class_class_members[idx]; + smsg("%s%4d %s CLASSMEMBER %s.%s", pfx, current, + iptr->isn_type == ISN_LOAD_CLASSMEMBER + ? "LOAD" : "STORE", + cl->class_name, ocm->ocm_name); + } + break; + // constants case ISN_PUSHNR: smsg("%s%4d PUSHNR %lld", pfx, current, *** ../vim-9.0.1073/src/testdir/test_vim9_class.vim 2022-12-14 20:54:52.411699476 +0000 --- src/testdir/test_vim9_class.vim 2022-12-18 21:17:55.343794543 +0000 *************** *** 306,311 **** --- 306,335 ---- assert_fails('trip.two = 22', 'E1335') trip.three = 33 assert_equal(33, trip.three) + + assert_fails('trip.four = 4', 'E1334') + END + v9.CheckScriptSuccess(lines) + enddef + + def Test_class_member_access() + var lines =<< trim END + vim9script + class TextPos + this.lnum = 1 + this.col = 1 + static counter = 0 + + def AddToCounter(nr: number) + counter += nr + enddef + endclass + + assert_equal(0, TextPos.counter) + TextPos.AddToCounter(3) + assert_equal(3, TextPos.counter) + + assert_fails('TextPos.counter += 5', 'E1335') END v9.CheckScriptSuccess(lines) enddef *** ../vim-9.0.1073/src/version.c 2022-12-18 17:47:11.430957013 +0000 --- src/version.c 2022-12-18 18:30:47.947164802 +0000 *************** *** 697,698 **** --- 697,700 ---- { /* Add new patch number below this line */ + /**/ + 1074, /**/ -- Shit makes the flowers grow and that's beautiful /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///