To: vim_dev@googlegroups.com Subject: Patch 9.0.1045 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.1045 Problem: In a class object members cannot be initialized. Solution: Support initializing object members. Make "dissassemble" work on an object method. Files: src/vim9class.c, src/proto/vim9class.pro, src/vim9compile.c, src/vim9execute.c, src/vim9expr.c, src/vim9instr.c, src/proto/vim9instr.pro, src/structs.h, src/userfunc.c, src/eval.c, src/vim9.h, src/testdir/test_vim9_class.vim *** ../vim-9.0.1044/src/vim9class.c 2022-12-09 22:49:19.447580421 +0000 --- src/vim9class.c 2022-12-10 18:09:29.470008096 +0000 *************** *** 147,158 **** --- 147,176 ---- if (type == NULL) break; + char_u *expr_start = skipwhite(type_arg); + if (*expr_start == '=' && (!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); + + char_u *expr_end = expr_start; + evalarg_T evalarg; + init_evalarg(&evalarg); + skip_expr(&expr_end, &evalarg); + clear_evalarg(&evalarg, NULL); + 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_type = type; + if (expr_end > expr_start) + m->om_init = vim_strnsave(expr_start, expr_end - expr_start); ++objmembers.ga_len; } *************** *** 190,195 **** --- 208,216 ---- // TODO: how about errors? if (uf != NULL && ga_grow(&objmethods, 1) == OK) { + if (STRNCMP(uf->uf_name, "new", 3) == 0) + uf->uf_flags |= FC_NEW; + ((ufunc_T **)objmethods.ga_data)[objmethods.ga_len] = uf; ++objmethods.ga_len; } *************** *** 333,338 **** --- 354,360 ---- { objmember_T *m = ((objmember_T *)objmembers.ga_data) + i; vim_free(m->om_name); + vim_free(m->om_init); } ga_clear(&objmembers); *************** *** 520,525 **** --- 542,593 ---- } /* + * If "arg" points to a class or object method, return it. + * Otherwise return NULL. + */ + ufunc_T * + find_class_func(char_u **arg) + { + char_u *name = *arg; + char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START); + if (name_end == name || *name_end != '.') + return NULL; + + size_t len = name_end - name; + typval_T tv; + tv.v_type = VAR_UNKNOWN; + if (eval_variable(name, len, 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL) + return NULL; + if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT) + { + clear_tv(&tv); + return NULL; + } + + class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class + : tv.vval.v_object->obj_class; + if (cl == NULL) + return NULL; + char_u *fname = name_end + 1; + char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START); + if (fname_end == fname) + return NULL; + len = fname_end - fname; + + for (int i = 0; i < cl->class_obj_method_count; ++i) + { + ufunc_T *fp = cl->class_obj_methods[i]; + // Use a separate pointer to avoid that ASAN complains about + // uf_name[] only being 4 characters. + char_u *ufname = (char_u *)fp->uf_name; + if (STRNCMP(fname, ufname, len) == 0 && ufname[len] == NUL) + return fp; + } + + return NULL; + } + + /* * Make a copy of an object. */ void *************** *** 585,590 **** --- 653,659 ---- { objmember_T *m = &cl->class_obj_members[i]; vim_free(m->om_name); + vim_free(m->om_init); } vim_free(cl->class_obj_members); *** ../vim-9.0.1044/src/proto/vim9class.pro 2022-12-08 20:41:55.433288306 +0000 --- src/proto/vim9class.pro 2022-12-10 17:38:19.147340414 +0000 *************** *** 5,10 **** --- 5,11 ---- void ex_enum(exarg_T *eap); void ex_type(exarg_T *eap); int class_object_index(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose); + ufunc_T *find_class_func(char_u **arg); void copy_object(typval_T *from, typval_T *to); void object_unref(object_T *obj); void copy_class(typval_T *from, typval_T *to); *** ../vim-9.0.1044/src/vim9compile.c 2022-12-09 21:41:43.908327271 +0000 --- src/vim9compile.c 2022-12-10 16:24:08.356778584 +0000 *************** *** 2118,2123 **** --- 2118,2188 ---- } /* + * Generate an instruction to push the default value for "vartype". + * if "dest_local" is TRUE then for some types no instruction is generated. + * "skip_store" is set to TRUE if no PUSH instruction is generated. + * Returns OK or FAIL. + */ + static int + push_default_value( + cctx_T *cctx, + vartype_T vartype, + int dest_is_local, + int *skip_store) + { + int r = OK; + + switch (vartype) + { + case VAR_BOOL: + r = generate_PUSHBOOL(cctx, VVAL_FALSE); + break; + case VAR_FLOAT: + r = generate_PUSHF(cctx, 0.0); + break; + case VAR_STRING: + r = generate_PUSHS(cctx, NULL); + break; + case VAR_BLOB: + r = generate_PUSHBLOB(cctx, blob_alloc()); + break; + case VAR_FUNC: + r = generate_PUSHFUNC(cctx, NULL, &t_func_void, TRUE); + break; + case VAR_LIST: + r = generate_NEWLIST(cctx, 0, FALSE); + break; + case VAR_DICT: + r = generate_NEWDICT(cctx, 0, FALSE); + break; + case VAR_JOB: + r = generate_PUSHJOB(cctx); + break; + case VAR_CHANNEL: + r = generate_PUSHCHANNEL(cctx); + break; + case VAR_NUMBER: + case VAR_UNKNOWN: + case VAR_ANY: + case VAR_PARTIAL: + case VAR_VOID: + case VAR_INSTR: + case VAR_CLASS: + case VAR_OBJECT: + case VAR_SPECIAL: // cannot happen + // This is skipped for local variables, they are always + // initialized to zero. But in a "for" or "while" loop + // the value may have been changed. + if (dest_is_local && !inside_loop_scope(cctx)) + *skip_store = TRUE; + else + r = generate_PUSHNR(cctx, 0); + break; + } + return r; + } + + /* * Compile declaration and assignment: * "let name" * "var name = expr" *************** *** 2462,2523 **** } else { - int r = OK; - // variables are always initialized if (GA_GROW_FAILS(instr, 1)) goto theend; ! switch (lhs.lhs_member_type->tt_type) ! { ! case VAR_BOOL: ! r = generate_PUSHBOOL(cctx, VVAL_FALSE); ! break; ! case VAR_FLOAT: ! r = generate_PUSHF(cctx, 0.0); ! break; ! case VAR_STRING: ! r = generate_PUSHS(cctx, NULL); ! break; ! case VAR_BLOB: ! r = generate_PUSHBLOB(cctx, blob_alloc()); ! break; ! case VAR_FUNC: ! r = generate_PUSHFUNC(cctx, NULL, &t_func_void, TRUE); ! break; ! case VAR_LIST: ! r = generate_NEWLIST(cctx, 0, FALSE); ! break; ! case VAR_DICT: ! r = generate_NEWDICT(cctx, 0, FALSE); ! break; ! case VAR_JOB: ! r = generate_PUSHJOB(cctx); ! break; ! case VAR_CHANNEL: ! r = generate_PUSHCHANNEL(cctx); ! break; ! case VAR_NUMBER: ! case VAR_UNKNOWN: ! case VAR_ANY: ! case VAR_PARTIAL: ! case VAR_VOID: ! case VAR_INSTR: ! case VAR_CLASS: ! case VAR_OBJECT: ! case VAR_SPECIAL: // cannot happen ! // This is skipped for local variables, they are always ! // initialized to zero. But in a "for" or "while" loop ! // the value may have been changed. ! if (lhs.lhs_dest == dest_local ! && !inside_loop_scope(cctx)) ! skip_store = TRUE; ! else ! { ! instr_count = instr->ga_len; ! r = generate_PUSHNR(cctx, 0); ! } ! break; ! } if (r == FAIL) goto theend; } --- 2527,2538 ---- } else { // variables are always initialized if (GA_GROW_FAILS(instr, 1)) goto theend; ! instr_count = instr->ga_len; ! int r = push_default_value(cctx, lhs.lhs_member_type->tt_type, ! lhs.lhs_dest == dest_local, &skip_store); if (r == FAIL) goto theend; } *************** *** 2946,2954 **** vim_strsave((char_u *)"this"); ++dfunc->df_var_names.ga_len; ! // In the constructor allocate memory for the object. if ((ufunc->uf_flags & FC_NEW) == FC_NEW) generate_CONSTRUCT(&cctx, ufunc->uf_class); } if (ufunc->uf_def_args.ga_len > 0) --- 2961,2992 ---- vim_strsave((char_u *)"this"); ++dfunc->df_var_names.ga_len; ! // In the constructor allocate memory for the object and initialize the ! // object members. if ((ufunc->uf_flags & FC_NEW) == FC_NEW) + { generate_CONSTRUCT(&cctx, ufunc->uf_class); + + 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); + } + } } if (ufunc->uf_def_args.ga_len > 0) *************** *** 3564,3570 **** --- 3602,3611 ---- // Return void if there is no return at the end. // For a constructor return the object. if ((ufunc->uf_flags & FC_NEW) == FC_NEW) + { generate_instr(&cctx, ISN_RETURN_OBJECT); + ufunc->uf_ret_type = &ufunc->uf_class->class_object_type; + } else generate_instr(&cctx, ISN_RETURN_VOID); } *** ../vim-9.0.1044/src/vim9execute.c 2022-12-09 21:41:43.908327271 +0000 --- src/vim9execute.c 2022-12-10 17:25:04.637876026 +0000 *************** *** 3009,3015 **** iptr = &ectx->ec_instr[ectx->ec_iidx++]; switch (iptr->isn_type) { ! // Constructor, new() method. case ISN_CONSTRUCT: // "this" is always the local variable at index zero tv = STACK_TV_VAR(0); --- 3009,3015 ---- iptr = &ectx->ec_instr[ectx->ec_iidx++]; switch (iptr->isn_type) { ! // Constructor, first instruction in a new() method. case ISN_CONSTRUCT: // "this" is always the local variable at index zero tv = STACK_TV_VAR(0); *************** *** 5114,5120 **** } break; ! case ISN_OBJ_MEMBER: { tv = STACK_TV_BOT(-1); if (tv->v_type != VAR_OBJECT) --- 5114,5120 ---- } break; ! case ISN_GET_OBJ_MEMBER: { tv = STACK_TV_BOT(-1); if (tv->v_type != VAR_OBJECT) *************** *** 5143,5148 **** --- 5143,5160 ---- } break; + case ISN_STORE_THIS: + { + int idx = iptr->isn_arg.number; + object_T *obj = STACK_TV_VAR(0)->vval.v_object; + // the members are located right after the object struct + typval_T *mtv = ((typval_T *)(obj + 1)) + idx; + clear_tv(mtv); + *mtv = *STACK_TV_BOT(-1); + --ectx->ec_stack.ga_len; + } + break; + case ISN_CLEARDICT: dict_stack_drop(); break; *************** *** 6805,6811 **** case ISN_MEMBER: smsg("%s%4d MEMBER", pfx, current); break; case ISN_STRINGMEMBER: smsg("%s%4d MEMBER %s", pfx, current, iptr->isn_arg.string); break; ! case ISN_OBJ_MEMBER: smsg("%s%4d OBJ_MEMBER %d", pfx, current, (int)iptr->isn_arg.number); break; case ISN_CLEARDICT: smsg("%s%4d CLEARDICT", pfx, current); break; case ISN_USEDICT: smsg("%s%4d USEDICT", pfx, current); break; --- 6817,6825 ---- case ISN_MEMBER: smsg("%s%4d MEMBER", pfx, current); break; case ISN_STRINGMEMBER: smsg("%s%4d MEMBER %s", pfx, current, iptr->isn_arg.string); break; ! case ISN_GET_OBJ_MEMBER: smsg("%s%4d OBJ_MEMBER %d", pfx, current, ! (int)iptr->isn_arg.number); break; ! case ISN_STORE_THIS: smsg("%s%4d STORE_THIS %d", pfx, current, (int)iptr->isn_arg.number); break; case ISN_CLEARDICT: smsg("%s%4d CLEARDICT", pfx, current); break; case ISN_USEDICT: smsg("%s%4d USEDICT", pfx, current); break; *** ../vim-9.0.1044/src/vim9expr.c 2022-12-09 21:41:43.908327271 +0000 --- src/vim9expr.c 2022-12-10 15:58:26.540459771 +0000 *************** *** 253,259 **** /* * Compile ".member" coming after an object or class. */ - static int compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type) { --- 253,258 ---- *************** *** 282,288 **** objmember_T *m = &cl->class_obj_members[i]; if (STRNCMP(name, m->om_name, len) == 0 && m->om_name[len] == NUL) { ! generate_OBJ_MEMBER(cctx, i, m->om_type); *arg = name_end; return OK; --- 281,287 ---- objmember_T *m = &cl->class_obj_members[i]; if (STRNCMP(name, m->om_name, len) == 0 && m->om_name[len] == NUL) { ! generate_GET_OBJ_MEMBER(cctx, i, m->om_type); *arg = name_end; return OK; *** ../vim-9.0.1044/src/vim9instr.c 2022-12-09 21:41:43.908327271 +0000 --- src/vim9instr.c 2022-12-10 16:02:18.204571906 +0000 *************** *** 132,146 **** } /* ! * Generate ISN_OBJ_MEMBER - access object member by indes. */ int ! generate_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type) { RETURN_OK_IF_SKIP(cctx); // drop the object type ! isn_T *isn = generate_instr_drop(cctx, ISN_OBJ_MEMBER, 1); if (isn == NULL) return FAIL; --- 132,147 ---- } /* ! * Generate ISN_GET_OBJ_MEMBER - access member of object at bottom of stack by ! * index. */ int ! generate_GET_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type) { RETURN_OK_IF_SKIP(cctx); // drop the object type ! isn_T *isn = generate_instr_drop(cctx, ISN_GET_OBJ_MEMBER, 1); if (isn == NULL) return FAIL; *************** *** 149,154 **** --- 150,173 ---- } /* + * Generate ISN_STORE_THIS - store value in member of "this" object with member + * index "idx". + */ + int + generate_STORE_THIS(cctx_T *cctx, int idx) + { + RETURN_OK_IF_SKIP(cctx); + + // drop the value type + isn_T *isn = generate_instr_drop(cctx, ISN_STORE_THIS, 1); + if (isn == NULL) + return FAIL; + + isn->isn_arg.number = idx; + return OK; + } + + /* * If type at "offset" isn't already VAR_STRING then generate ISN_2STRING. * But only for simple types. * When "tolerant" is TRUE convert most types to string, e.g. a List. *************** *** 2458,2463 **** --- 2477,2483 ---- case ISN_FINISH: case ISN_FOR: case ISN_GETITEM: + case ISN_GET_OBJ_MEMBER: case ISN_JUMP: case ISN_JUMP_IF_ARG_SET: case ISN_LISTAPPEND: *************** *** 2477,2483 **** case ISN_NEWDICT: case ISN_NEWLIST: case ISN_NEWPARTIAL: - case ISN_OBJ_MEMBER: case ISN_OPANY: case ISN_OPFLOAT: case ISN_OPNR: --- 2497,2502 ---- *************** *** 2495,2502 **** case ISN_REDIREND: case ISN_REDIRSTART: case ISN_RETURN: - case ISN_RETURN_VOID: case ISN_RETURN_OBJECT: case ISN_SHUFFLE: case ISN_SLICE: case ISN_SOURCE: --- 2514,2521 ---- case ISN_REDIREND: case ISN_REDIRSTART: case ISN_RETURN: case ISN_RETURN_OBJECT: + case ISN_RETURN_VOID: case ISN_SHUFFLE: case ISN_SLICE: case ISN_SOURCE: *************** *** 2504,2509 **** --- 2523,2529 ---- case ISN_STOREINDEX: case ISN_STORENR: case ISN_STOREOUTER: + case ISN_STORE_THIS: case ISN_STORERANGE: case ISN_STOREREG: case ISN_STOREV: *** ../vim-9.0.1044/src/proto/vim9instr.pro 2022-12-09 21:41:43.908327271 +0000 --- src/proto/vim9instr.pro 2022-12-10 15:58:24.476458533 +0000 *************** *** 4,10 **** isn_T *generate_instr_type(cctx_T *cctx, isntype_T isn_type, type_T *type); isn_T *generate_instr_debug(cctx_T *cctx); int generate_CONSTRUCT(cctx_T *cctx, class_T *cl); ! int generate_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type); int may_generate_2STRING(int offset, int tolerant, cctx_T *cctx); int generate_add_instr(cctx_T *cctx, vartype_T vartype, type_T *type1, type_T *type2, exprtype_T expr_type); vartype_T operator_type(type_T *type1, type_T *type2); --- 4,11 ---- isn_T *generate_instr_type(cctx_T *cctx, isntype_T isn_type, type_T *type); isn_T *generate_instr_debug(cctx_T *cctx); int generate_CONSTRUCT(cctx_T *cctx, class_T *cl); ! int generate_GET_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type); ! int generate_STORE_THIS(cctx_T *cctx, int idx); int may_generate_2STRING(int offset, int tolerant, cctx_T *cctx); int generate_add_instr(cctx_T *cctx, vartype_T vartype, type_T *type1, type_T *type2, exprtype_T expr_type); vartype_T operator_type(type_T *type1, type_T *type2); *** ../vim-9.0.1044/src/structs.h 2022-12-09 21:41:43.904327284 +0000 --- src/structs.h 2022-12-10 16:15:29.192738979 +0000 *************** *** 1463,1470 **** * Entry for an object member variable. */ typedef struct { ! char_u *om_name; // allocated type_T *om_type; } objmember_T; // "class_T": used for v_class of typval of VAR_CLASS --- 1463,1471 ---- * Entry for an object member variable. */ typedef struct { ! char_u *om_name; // allocated type_T *om_type; + char_u *om_init; // allocated } objmember_T; // "class_T": used for v_class of typval of VAR_CLASS *** ../vim-9.0.1044/src/userfunc.c 2022-12-09 21:41:43.904327284 +0000 --- src/userfunc.c 2022-12-10 17:27:31.237805351 +0000 *************** *** 4047,4052 **** --- 4047,4058 ---- name = vim_strsave(lv.ll_tv->vval.v_string); *pp = end; } + else if (lv.ll_tv->v_type == VAR_CLASS + && lv.ll_tv->vval.v_class != NULL) + { + name = vim_strsave(lv.ll_tv->vval.v_class->class_name); + *pp = end; + } else if (lv.ll_tv->v_type == VAR_PARTIAL && lv.ll_tv->vval.v_partial != NULL) { *************** *** 5240,5247 **** fname = vim_strnsave(name, arg - name); } else fname = trans_function_name(&arg, &is_global, FALSE, ! TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD, NULL, NULL, NULL); if (fname == NULL) { semsg(_(e_invalid_argument_str), name); --- 5246,5262 ---- fname = vim_strnsave(name, arg - name); } else + { + // First try finding a method in a class, find_func_by_name() will give + // an error if the function is not found. + ufunc = find_class_func(&arg); + if (ufunc != NULL) + return ufunc; + fname = trans_function_name(&arg, &is_global, FALSE, ! TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DECL, ! NULL, NULL, NULL); ! } if (fname == NULL) { semsg(_(e_invalid_argument_str), name); *** ../vim-9.0.1044/src/eval.c 2022-12-09 21:41:43.904327284 +0000 --- src/eval.c 2022-12-10 16:53:00.408481439 +0000 *************** *** 1193,1201 **** var2.v_type = VAR_UNKNOWN; while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.')) { ! int r = OK; ! ! if (*p == '.' && lp->ll_tv->v_type != VAR_DICT) { if (!quiet) semsg(_(e_dot_can_only_be_used_on_dictionary_str), name); --- 1193,1200 ---- 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_CLASS) { if (!quiet) semsg(_(e_dot_can_only_be_used_on_dictionary_str), name); *************** *** 1203,1209 **** } if (lp->ll_tv->v_type != VAR_LIST && lp->ll_tv->v_type != VAR_DICT ! && lp->ll_tv->v_type != VAR_BLOB) { if (!quiet) emsg(_(e_can_only_index_list_dictionary_or_blob)); --- 1202,1209 ---- } 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_CLASS) { if (!quiet) emsg(_(e_can_only_index_list_dictionary_or_blob)); *************** *** 1211,1216 **** --- 1211,1217 ---- } // 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 *************** *** 1463,1469 **** lp->ll_tv = NULL; break; } ! else { /* * Get the number and item for the only or first index of the List. --- 1464,1470 ---- 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. *************** *** 1508,1513 **** --- 1509,1519 ---- lp->ll_tv = &lp->ll_li->li_tv; } + else // v_type == VAR_CLASS + { + // TODO: check object members and methods if + // "key" points name start, "p" to the end + } } clear_tv(&var1); *** ../vim-9.0.1044/src/vim9.h 2022-12-09 21:41:43.908327271 +0000 --- src/vim9.h 2022-12-10 13:59:30.716854085 +0000 *************** *** 33,39 **** ISN_SOURCE, // source autoload script, isn_arg.number is the script ID ISN_INSTR, // instructions compiled from expression ISN_CONSTRUCT, // construct an object, using contstruct_T ! ISN_OBJ_MEMBER, // object member, index is isn_arg.number // get and set variables ISN_LOAD, // push local variable isn_arg.number --- 33,41 ---- ISN_SOURCE, // source autoload script, isn_arg.number is the script ID ISN_INSTR, // instructions compiled from expression ISN_CONSTRUCT, // construct an object, using contstruct_T ! 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 // get and set variables ISN_LOAD, // push local variable isn_arg.number *** ../vim-9.0.1044/src/testdir/test_vim9_class.vim 2022-12-09 21:41:43.908327271 +0000 --- src/testdir/test_vim9_class.vim 2022-12-10 18:29:25.714260061 +0000 *************** *** 129,135 **** class TextPosition this.lnum: number ! this.col: number def ToString(): string return $'({this.lnum}, {this.col})' --- 129,135 ---- class TextPosition this.lnum: number ! this.col: number def ToString(): string return $'({this.lnum}, {this.col})' *************** *** 146,151 **** --- 146,186 ---- END v9.CheckScriptSuccess(lines) enddef + + def Test_class_member_initializer() + var lines =<< trim END + vim9script + + class TextPosition + this.lnum: number = 1 + this.col: number = 1 + + def new(lnum: number) + this.lnum = lnum + enddef + endclass + + var pos = TextPosition.new(3) + assert_equal(3, pos.lnum) + assert_equal(1, pos.col) + + var instr = execute('disassemble TextPosition.new') + assert_match('new\_s*' .. + '0 NEW TextPosition size 72\_s*' .. + '\d PUSHNR 1\_s*' .. + '\d STORE_THIS 0\_s*' .. + '\d PUSHNR 1\_s*' .. + '\d STORE_THIS 1\_s*' .. + 'this.lnum = lnum\_s*' .. + '\d LOAD arg\[-1]\_s*' .. + '\d PUSHNR 0\_s*' .. + '\d LOAD $0\_s*' .. + '\d\+ STOREINDEX object\_s*' .. + '\d\+ RETURN object.*', + instr) + END + v9.CheckScriptSuccess(lines) + enddef " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker *** ../vim-9.0.1044/src/version.c 2022-12-10 11:17:07.426230828 +0000 --- src/version.c 2022-12-10 18:32:33.466334006 +0000 *************** *** 697,698 **** --- 697,700 ---- { /* Add new patch number below this line */ + /**/ + 1045, /**/ -- Microsoft: "Windows NT 4.0 now has the same user-interface as Windows 95" Windows 95: "Press CTRL-ALT-DEL to reboot" Windows NT 4.0: "Press CTRL-ALT-DEL to login" /// 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 ///