To: vim_dev@googlegroups.com Subject: Patch 9.0.1140 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.1140 Problem: Cannot call an object method in a compiled function. Solution: Compile the instructins to invoke an object method. Files: src/vim9expr.c, src/vim9execute.c, src/vim9compile.c, src/structs.h, src/vim9script.c, src/testdir/test_vim9_class.vim *** ../vim-9.0.1139/src/vim9expr.c 2023-01-03 14:01:15.961750392 +0000 --- src/vim9expr.c 2023-01-03 17:34:35.308341508 +0000 *************** *** 273,281 **** class_T *cl = (class_T *)type->tt_member; if (*name_end == '(') { if (type->tt_type == VAR_CLASS) { ! garray_T *instr = &cctx->ctx_instr; if (instr->ga_len > 0) { isn_T *isn = ((isn_T *)instr->ga_data) + instr->ga_len - 1; --- 273,284 ---- class_T *cl = (class_T *)type->tt_member; if (*name_end == '(') { + int function_count; + ufunc_T **functions; + if (type->tt_type == VAR_CLASS) { ! garray_T *instr = &cctx->ctx_instr; if (instr->ga_len > 0) { isn_T *isn = ((isn_T *)instr->ga_data) + instr->ga_len - 1; *************** *** 288,321 **** } } ! for (int i = 0; i < cl->class_class_function_count; ++i) { ! ufunc_T *fp = cl->class_class_functions[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(name, ufname, len) == 0 && ufname[len] == NUL) ! { ! *arg = skipwhite(name_end + 1); ! int argcount = 0; ! if (compile_arguments(arg, cctx, &argcount, ! CA_NOT_SPECIAL) == FAIL) ! return FAIL; ! return generate_CALL(cctx, fp, argcount); ! } } - - semsg(_(e_method_not_found_on_class_str_str), - cl->class_name, name); - return FAIL; } ! else { ! // TODO: method call ! emsg("compile_class_object_index(): object call not handled yet"); } } ! else if (type->tt_type == VAR_OBJECT) { for (int i = 0; i < cl->class_obj_member_count; ++i) { --- 291,337 ---- } } ! function_count = cl->class_class_function_count; ! functions = cl->class_class_functions; ! } ! else ! { ! // type->tt_type == VAR_OBJECT: method call ! function_count = cl->class_obj_method_count; ! functions = cl->class_obj_methods; ! } ! ! ufunc_T *ufunc = NULL; ! for (int i = 0; i < function_count; ++i) ! { ! ufunc_T *fp = functions[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(name, ufname, len) == 0 && ufname[len] == NUL) { ! ufunc = fp; ! break; } } ! if (ufunc == NULL) { ! // TODO: different error for object method? ! semsg(_(e_method_not_found_on_class_str_str), cl->class_name, name); ! return FAIL; } + + // Compile the arguments and call the class function or object method. + // The object method will know that the object is on the stack, just + // before the arguments. + *arg = skipwhite(name_end + 1); + int argcount = 0; + if (compile_arguments(arg, cctx, &argcount, CA_NOT_SPECIAL) == FAIL) + return FAIL; + return generate_CALL(cctx, ufunc, argcount); } ! ! if (type->tt_type == VAR_OBJECT) { for (int i = 0; i < cl->class_obj_member_count; ++i) { *** ../vim-9.0.1139/src/vim9execute.c 2023-01-02 20:37:58.225452652 +0000 --- src/vim9execute.c 2023-01-03 18:05:40.044315107 +0000 *************** *** 532,537 **** --- 532,540 ---- return FAIL; } + // If this is an object method, the object is just before the arguments. + typval_T *obj = STACK_TV_BOT(0) - argcount - vararg_count - 1; + // Check the argument types. if (check_ufunc_arg_types(ufunc, argcount, vararg_count, ectx) == FAIL) return FAIL; *************** *** 594,599 **** --- 597,610 ---- } ectx->ec_stack.ga_len += STACK_FRAME_SIZE + varcount; + // For an object method move the object from just before the arguments to + // the first local variable. + if (ufunc->uf_flags & FC_OBJECT) + { + *STACK_TV_VAR(0) = *obj; + obj->v_type = VAR_UNKNOWN; + } + partial_T *pt = pt_arg != NULL ? pt_arg : ufunc->uf_partial; if (pt != NULL || (ufunc->uf_flags & FC_CLOSURE)) { *************** *** 1073,1079 **** dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + ectx->ec_dfunc_idx; int argcount = ufunc_argcount(dfunc->df_ufunc); - int top = ectx->ec_frame_idx - argcount; estack_T *entry; int prev_dfunc_idx = STACK_TV(ectx->ec_frame_idx + STACK_FRAME_FUNC_OFF)->vval.v_number; --- 1084,1089 ---- *************** *** 1111,1117 **** if (handle_closure_in_use(ectx, TRUE) == FAIL) return FAIL; ! // Clear the arguments. for (idx = top; idx < ectx->ec_frame_idx; ++idx) clear_tv(STACK_TV(idx)); --- 1121,1131 ---- if (handle_closure_in_use(ectx, TRUE) == FAIL) return FAIL; ! // Clear the arguments. If this was an object method also clear the ! // object, it is just before the arguments. ! int top = ectx->ec_frame_idx - argcount; ! if (dfunc->df_ufunc->uf_flags & FC_OBJECT) ! --top; for (idx = top; idx < ectx->ec_frame_idx; ++idx) clear_tv(STACK_TV(idx)); *** ../vim-9.0.1139/src/vim9compile.c 2023-01-02 18:10:00.023271223 +0000 --- src/vim9compile.c 2023-01-03 18:45:00.197069784 +0000 *************** *** 45,51 **** if (len == 4 && STRNCMP(name, "this", 4) == 0 && cctx->ctx_ufunc != NULL ! && (cctx->ctx_ufunc->uf_flags & FC_OBJECT)) { if (lvar != NULL) { --- 45,51 ---- if (len == 4 && STRNCMP(name, "this", 4) == 0 && cctx->ctx_ufunc != NULL ! && (cctx->ctx_ufunc->uf_flags & (FC_OBJECT|FC_NEW))) { if (lvar != NULL) { *************** *** 313,319 **** || arg_exists(name, len, NULL, NULL, NULL, cctx) == OK || (len == 4 && cctx->ctx_ufunc != NULL ! && (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, NULL, cctx) >= 0 --- 313,319 ---- || arg_exists(name, len, NULL, NULL, NULL, cctx) == OK || (len == 4 && cctx->ctx_ufunc != NULL ! && (cctx->ctx_ufunc->uf_flags & (FC_OBJECT|FC_NEW)) && STRNCMP(name, "this", 4) == 0))) || script_var_exists(name, len, cctx, NULL) == OK || class_member_index(name, len, NULL, cctx) >= 0 *************** *** 3018,3024 **** goto erret; // For an object method and constructor "this" is the first local variable. ! if (ufunc->uf_flags & FC_OBJECT) { dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx; --- 3018,3024 ---- goto erret; // For an object method and constructor "this" is the first local variable. ! if (ufunc->uf_flags & (FC_OBJECT|FC_NEW)) { dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx; *** ../vim-9.0.1139/src/structs.h 2023-01-01 12:58:29.474417546 +0000 --- src/structs.h 2023-01-03 18:47:50.577155426 +0000 *************** *** 1822,1829 **** // copy_lambda_to_global_func() #define FC_LAMBDA 0x2000 // one line "return {expr}" ! #define FC_OBJECT 010000 // object method ! #define FC_NEW 030000 // constructor (also an object method) #define MAX_FUNC_ARGS 20 // maximum number of function arguments #define VAR_SHORT_LEN 20 // short variable name length --- 1822,1829 ---- // copy_lambda_to_global_func() #define FC_LAMBDA 0x2000 // one line "return {expr}" ! #define FC_OBJECT 0x4000 // object method ! #define FC_NEW 0x8000 // constructor #define MAX_FUNC_ARGS 20 // maximum number of function arguments #define VAR_SHORT_LEN 20 // short variable name length *** ../vim-9.0.1139/src/vim9script.c 2023-01-02 18:10:00.023271223 +0000 --- src/vim9script.c 2023-01-03 18:46:08.221103621 +0000 *************** *** 1136,1142 **** && !(STRCMP("this", name) == 0 && cctx != NULL && cctx->ctx_ufunc != NULL ! && (cctx->ctx_ufunc->uf_flags & FC_OBJECT))) { semsg(_(e_cannot_use_reserved_name_str), name); return FAIL; --- 1136,1142 ---- && !(STRCMP("this", name) == 0 && cctx != NULL && cctx->ctx_ufunc != NULL ! && (cctx->ctx_ufunc->uf_flags & (FC_OBJECT|FC_NEW)))) { semsg(_(e_cannot_use_reserved_name_str), name); return FAIL; *** ../vim-9.0.1139/src/testdir/test_vim9_class.vim 2023-01-03 14:01:15.961750392 +0000 --- src/testdir/test_vim9_class.vim 2023-01-03 19:00:01.265185896 +0000 *************** *** 329,334 **** --- 329,335 ---- class MyCar this.make: string + this.age = 5 def new(make_arg: string) this.make = make_arg *************** *** 337,342 **** --- 338,346 ---- def GetMake(): string return $"make = {this.make}" enddef + def GetAge(): number + return this.age + enddef endclass var c = MyCar.new("abc") *************** *** 347,352 **** --- 351,362 ---- var c2 = MyCar.new("123") assert_equal('make = 123', c2.GetMake()) + + def CheckCar() + assert_equal("make = def", c.GetMake()) + assert_equal(5, c.GetAge()) + enddef + CheckCar() END v9.CheckScriptSuccess(lines) *** ../vim-9.0.1139/src/version.c 2023-01-03 14:01:15.961750392 +0000 --- src/version.c 2023-01-03 17:32:19.052097761 +0000 *************** *** 697,698 **** --- 697,700 ---- { /* Add new patch number below this line */ + /**/ + 1140, /**/ -- To keep milk from turning sour: Keep it in the cow. /// 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 ///