To: vim_dev@googlegroups.com Subject: Patch 9.0.1041 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.1041 Problem: Cannot define a method in a class. Solution: Implement defining an object method. Make calling an object method work. Files: src/errors.h, src/eval.c, src/structs.h, src/userfunc.c, src/proto/userfunc.pro, src/vim.h, src/vim9.h, src/vim9class.c, src/vim9compile.c, src/vim9execute.c, src/proto/vim9execute.pro, src/vim9expr.c, src/vim9instr.c, src/proto/vim9instr.pro, src/vim9type.c, src/testdir/test_vim9_class.vim *** ../vim-9.0.1040/src/errors.h 2022-12-08 15:32:11.075034154 +0000 --- src/errors.h 2022-12-09 17:56:38.878996744 +0000 *************** *** 3370,3373 **** --- 3370,3375 ---- INIT(= N_("E1325: Method not found on class \"%s\": %s")); EXTERN char e_member_not_found_on_object_str_str[] INIT(= N_("E1326: Member not found on object \"%s\": %s")); + EXTERN char e_object_required_found_str[] + INIT(= N_("E1327: Object required, found %s")); #endif *** ../vim-9.0.1040/src/eval.c 2022-12-08 20:41:55.433288306 +0000 --- src/eval.c 2022-12-09 20:02:41.878156184 +0000 *************** *** 295,301 **** // Shortcut to call a compiled function with minimal overhead. r = call_def_function(partial->pt_func, argc, argv, ! DEF_USE_PT_ARGV, partial, fc, rettv); if (fc_arg == NULL) remove_funccal(); if (r == FAIL) --- 295,301 ---- // Shortcut to call a compiled function with minimal overhead. r = call_def_function(partial->pt_func, argc, argv, ! DEF_USE_PT_ARGV, partial, NULL, fc, rettv); if (fc_arg == NULL) remove_funccal(); if (r == FAIL) *** ../vim-9.0.1040/src/structs.h 2022-12-08 20:41:55.433288306 +0000 --- src/structs.h 2022-12-09 21:31:16.853056878 +0000 *************** *** 1478,1492 **** int class_obj_method_count; ufunc_T **class_obj_methods; // allocated - ufunc_T *class_new_func; // new() function that was created garray_T class_type_list; // used for type pointers type_T class_type; }; // Used for v_object of typval of VAR_OBJECT. // The member variables follow in an array of typval_T. ! struct object_S { class_T *obj_class; // class this object is created for; // pointer adds to class_refcount int obj_refcount; --- 1478,1493 ---- 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 }; // Used for v_object of typval of VAR_OBJECT. // The member variables follow in an array of typval_T. ! struct object_S ! { class_T *obj_class; // class this object is created for; // pointer adds to class_refcount int obj_refcount; *************** *** 2123,2128 **** --- 2124,2130 ---- int fe_evaluate; // actually evaluate expressions partial_T *fe_partial; // for extra arguments dict_T *fe_selfdict; // Dictionary for "self" + object_T *fe_object; // object, e.g. for "this.Func()" typval_T *fe_basetv; // base for base->method() type_T *fe_check_type; // type from funcref or NULL int fe_found_var; // if the function is not found then give an *** ../vim-9.0.1040/src/userfunc.c 2022-12-08 15:32:11.083034191 +0000 --- src/userfunc.c 2022-12-09 20:03:16.322042924 +0000 *************** *** 188,193 **** --- 188,194 ---- if (theline != NULL) { if (lines_to_free->ga_len > 0 + && eap->cmdlinep != NULL && *eap->cmdlinep == ((char_u **)lines_to_free->ga_data) [lines_to_free->ga_len - 1]) *eap->cmdlinep = theline; *************** *** 214,220 **** garray_T *default_args, int skip, exarg_T *eap, // can be NULL ! class_T *class_arg, garray_T *newlines, // function body lines garray_T *lines_to_free) { --- 215,221 ---- garray_T *default_args, int skip, exarg_T *eap, // can be NULL ! int in_class, // TRUE when inside a class garray_T *newlines, // function body lines garray_T *lines_to_free) { *************** *** 294,300 **** } } } ! else if (class_arg != NULL && STRNCMP(p, "this.", 5) == 0) { // this.memberName p += 5; --- 295,301 ---- } } } ! else if (in_class && STRNCMP(p, "this.", 5) == 0) { // this.memberName p += 5; *************** *** 1436,1442 **** s = *arg + 1; ret = get_function_args(&s, equal_arrow ? ')' : '-', NULL, types_optional ? &argtypes : NULL, types_optional, evalarg, ! NULL, &default_args, TRUE, NULL, NULL, NULL, NULL); if (ret == FAIL || skip_arrow(s, equal_arrow, &ret_type, NULL) == NULL) { if (types_optional) --- 1437,1443 ---- s = *arg + 1; ret = get_function_args(&s, equal_arrow ? ')' : '-', NULL, types_optional ? &argtypes : NULL, types_optional, evalarg, ! NULL, &default_args, TRUE, NULL, FALSE, NULL, NULL); if (ret == FAIL || skip_arrow(s, equal_arrow, &ret_type, NULL) == NULL) { if (types_optional) *************** *** 1453,1459 **** ret = get_function_args(arg, equal_arrow ? ')' : '-', pnewargs, types_optional ? &argtypes : NULL, types_optional, evalarg, &varargs, &default_args, ! FALSE, NULL, NULL, NULL, NULL); if (ret == FAIL || (s = skip_arrow(*arg, equal_arrow, &ret_type, equal_arrow || vim9script ? &white_error : NULL)) == NULL) --- 1454,1460 ---- ret = get_function_args(arg, equal_arrow ? ')' : '-', pnewargs, types_optional ? &argtypes : NULL, types_optional, evalarg, &varargs, &default_args, ! FALSE, NULL, FALSE, NULL, NULL); if (ret == FAIL || (s = skip_arrow(*arg, equal_arrow, &ret_type, equal_arrow || vim9script ? &white_error : NULL)) == NULL) *************** *** 2733,2740 **** profile_may_start_func(&profile_info, fp, caller); #endif sticky_cmdmod_flags = 0; ! call_def_function(fp, argcount, argvars, 0, funcexe->fe_partial, ! fc, rettv); funcdepth_decrement(); #ifdef FEAT_PROFILE if (do_profiling == PROF_YES && (fp->uf_profiling --- 2734,2741 ---- profile_may_start_func(&profile_info, fp, caller); #endif sticky_cmdmod_flags = 0; ! call_def_function(fp, argcount, argvars, 0, ! funcexe->fe_partial, funcexe->fe_object, fc, rettv); funcdepth_decrement(); #ifdef FEAT_PROFILE if (do_profiling == PROF_YES && (fp->uf_profiling *************** *** 3957,3962 **** --- 3958,3964 ---- * "is_global" is NULL. * flags: * TFN_INT: internal function name OK + * TFN_IN_CLASS: function in a class * TFN_QUIET: be quiet * TFN_NO_AUTOLOAD: do not use script autoloading * TFN_NO_DEREF: do not dereference a Funcref *************** *** 4172,4179 **** } // In Vim9 script a user function is script-local by default, unless it ! // starts with a lower case character: dict.func(). ! vim9_local = ASCII_ISUPPER(*start) && vim9script; /* * Copy the function name to allocated memory. --- 4174,4182 ---- } // In Vim9 script a user function is script-local by default, unless it ! // starts with a lower case character: dict.func(). Or when in a class. ! vim9_local = ASCII_ISUPPER(*start) && vim9script ! && (flags & TFN_IN_CLASS) == 0; /* * Copy the function name to allocated memory. *************** *** 4211,4218 **** lead += (int)STRLEN(sid_buf); } } ! else if (!(flags & TFN_INT) && (builtin_function(lv.ll_name, len) ! || (vim9script && *lv.ll_name == '_'))) { semsg(_(vim9script ? e_function_name_must_start_with_capital_str : e_function_name_must_start_with_capital_or_s_str), --- 4214,4223 ---- lead += (int)STRLEN(sid_buf); } } ! else if (!(flags & TFN_INT) ! && (builtin_function(lv.ll_name, len) ! || (vim9script && *lv.ll_name == '_')) ! && !((flags & TFN_IN_CLASS) && STRNCMP(lv.ll_name, "new", 3) == 0)) { semsg(_(vim9script ? e_function_name_must_start_with_capital_str : e_function_name_must_start_with_capital_or_s_str), *************** *** 4417,4423 **** * When "name_arg" is not NULL this is a nested function, using "name_arg" for * the function name. * "lines_to_free" is a list of strings to be freed later. ! * If "class_arg" is not NULL then the function is defined in this class. * Returns a pointer to the function or NULL if no function defined. */ ufunc_T * --- 4422,4428 ---- * When "name_arg" is not NULL this is a nested function, using "name_arg" for * the function name. * "lines_to_free" is a list of strings to be freed later. ! * If "in_class" is TRUE then the function is defined inside a class. * Returns a pointer to the function or NULL if no function defined. */ ufunc_T * *************** *** 4425,4431 **** exarg_T *eap, char_u *name_arg, garray_T *lines_to_free, ! class_T *class_arg) { int j; int c; --- 4430,4436 ---- exarg_T *eap, char_u *name_arg, garray_T *lines_to_free, ! int in_class) { int j; int c; *************** *** 4500,4506 **** /* * Get the function name. There are these situations: ! * func normal function name * "name" == func, "fudi.fd_dict" == NULL * dict.func new dictionary entry * "name" == NULL, "fudi.fd_dict" set, --- 4505,4511 ---- /* * Get the function name. There are these situations: ! * func normal function name, also when "in_class" is TRUE * "name" == func, "fudi.fd_dict" == NULL * dict.func new dictionary entry * "name" == NULL, "fudi.fd_dict" set, *************** *** 4541,4547 **** } int tfn_flags = TFN_NO_AUTOLOAD | TFN_NEW_FUNC ! | (class_arg == 0 ? 0 : TFN_INT); name = save_function_name(&p, &is_global, eap->skip, tfn_flags, &fudi); paren = (vim_strchr(p, '(') != NULL); if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip) --- 4546,4552 ---- } int tfn_flags = TFN_NO_AUTOLOAD | TFN_NEW_FUNC ! | (in_class ? TFN_IN_CLASS : 0); name = save_function_name(&p, &is_global, eap->skip, tfn_flags, &fudi); paren = (vim_strchr(p, '(') != NULL); if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip) *************** *** 4743,4749 **** if (get_function_args(&p, ')', &newargs, eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE, NULL, &varargs, &default_args, eap->skip, ! eap, class_arg, &newlines, lines_to_free) == FAIL) goto errret_2; whitep = p; --- 4748,4754 ---- if (get_function_args(&p, ')', &newargs, eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE, NULL, &varargs, &default_args, eap->skip, ! eap, in_class, &newlines, lines_to_free) == FAIL) goto errret_2; whitep = p; *************** *** 4860,4866 **** /* * If there are no errors, add the function */ ! if (fudi.fd_dict == NULL) { hashtab_T *ht; char_u *find_name = name; --- 4865,4899 ---- /* * If there are no errors, add the function */ ! if (fudi.fd_dict != NULL) ! { ! char numbuf[20]; ! ! fp = NULL; ! if (fudi.fd_newkey == NULL && !eap->forceit) ! { ! emsg(_(e_dictionary_entry_already_exists)); ! goto erret; ! } ! if (fudi.fd_di == NULL) ! { ! // Can't add a function to a locked dictionary ! if (value_check_lock(fudi.fd_dict->dv_lock, eap->arg, FALSE)) ! goto erret; ! } ! // Can't change an existing function if it is locked ! else if (value_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg, FALSE)) ! goto erret; ! ! // Give the function a sequential number. Can only be used with a ! // Funcref! ! vim_free(name); ! sprintf(numbuf, "%d", ++func_nr); ! name = vim_strsave((char_u *)numbuf); ! if (name == NULL) ! goto erret; ! } ! else if (!in_class) { hashtab_T *ht; char_u *find_name = name; *************** *** 4967,5000 **** } } } - else - { - char numbuf[20]; - - fp = NULL; - if (fudi.fd_newkey == NULL && !eap->forceit) - { - emsg(_(e_dictionary_entry_already_exists)); - goto erret; - } - if (fudi.fd_di == NULL) - { - // Can't add a function to a locked dictionary - if (value_check_lock(fudi.fd_dict->dv_lock, eap->arg, FALSE)) - goto erret; - } - // Can't change an existing function if it is locked - else if (value_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg, FALSE)) - goto erret; - - // Give the function a sequential number. Can only be used with a - // Funcref! - vim_free(name); - sprintf(numbuf, "%d", ++func_nr); - name = vim_strsave((char_u *)numbuf); - if (name == NULL) - goto erret; - } if (fp == NULL) { --- 5000,5005 ---- *************** *** 5113,5119 **** hi = hash_find(&func_hashtab, name); hi->hi_key = UF2HIKEY(fp); } ! else if (hash_add(&func_hashtab, UF2HIKEY(fp), "add function") == FAIL) { free_fp = TRUE; goto erret; --- 5118,5125 ---- hi = hash_find(&func_hashtab, name); hi->hi_key = UF2HIKEY(fp); } ! else if (!in_class && hash_add(&func_hashtab, ! UF2HIKEY(fp), "add function") == FAIL) { free_fp = TRUE; goto erret; *************** *** 5198,5204 **** garray_T lines_to_free; ga_init2(&lines_to_free, sizeof(char_u *), 50); ! (void)define_function(eap, NULL, &lines_to_free, NULL); ga_clear_strings(&lines_to_free); } --- 5204,5210 ---- garray_T lines_to_free; ga_init2(&lines_to_free, sizeof(char_u *), 50); ! (void)define_function(eap, NULL, &lines_to_free, FALSE); ga_clear_strings(&lines_to_free); } *** ../vim-9.0.1040/src/proto/userfunc.pro 2022-12-08 15:32:11.083034191 +0000 --- src/proto/userfunc.pro 2022-12-09 13:24:16.921434007 +0000 *************** *** 46,52 **** char_u *alloc_printable_func_name(char_u *fname); char_u *save_function_name(char_u **name, int *is_global, int skip, int flags, funcdict_T *fudi); void list_functions(regmatch_T *regmatch); ! ufunc_T *define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free, class_T *class_arg); void ex_function(exarg_T *eap); ufunc_T *find_func_by_name(char_u *name, compiletype_T *compile_type); void ex_defcompile(exarg_T *eap); --- 46,52 ---- char_u *alloc_printable_func_name(char_u *fname); char_u *save_function_name(char_u **name, int *is_global, int skip, int flags, funcdict_T *fudi); void list_functions(regmatch_T *regmatch); ! ufunc_T *define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free, int in_class); void ex_function(exarg_T *eap); ufunc_T *find_func_by_name(char_u *name, compiletype_T *compile_type); void ex_defcompile(exarg_T *eap); *** ../vim-9.0.1040/src/vim.h 2022-12-08 15:32:11.083034191 +0000 --- src/vim.h 2022-12-09 13:54:25.823292463 +0000 *************** *** 2676,2682 **** #define TFN_NO_DECL 0x20 // only used for GLV_NO_DECL #define TFN_COMPILING 0x40 // only used for GLV_COMPILING #define TFN_NEW_FUNC 0x80 // defining a new function ! #define TFN_ASSIGN_WITH_OP 0x100 // only for GLV_ASSIGN_WITH_OP // Values for get_lval() flags argument: #define GLV_QUIET TFN_QUIET // no error messages --- 2676,2683 ---- #define TFN_NO_DECL 0x20 // only used for GLV_NO_DECL #define TFN_COMPILING 0x40 // only used for GLV_COMPILING #define TFN_NEW_FUNC 0x80 // defining a new function ! #define TFN_ASSIGN_WITH_OP 0x100 // only for GLV_ASSIGN_WITH_OP ! #define TFN_IN_CLASS 0x200 // function in a class // Values for get_lval() flags argument: #define GLV_QUIET TFN_QUIET // no error messages *** ../vim-9.0.1040/src/vim9.h 2022-12-08 15:32:11.083034191 +0000 --- src/vim9.h 2022-12-09 16:45:17.127587972 +0000 *************** *** 33,38 **** --- 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 *** ../vim-9.0.1040/src/vim9class.c 2022-12-08 22:09:09.839635611 +0000 --- src/vim9class.c 2022-12-09 21:34:38.897153597 +0000 *************** *** 77,83 **** // Growarray with object methods declared in the class. garray_T objmethods; ! ga_init2(&objmethods, sizeof(ufunc_T), 10); /* * Go over the body of the class until "endclass" is found. --- 77,83 ---- // Growarray with object methods declared in the class. garray_T objmethods; ! ga_init2(&objmethods, sizeof(ufunc_T *), 10); /* * Go over the body of the class until "endclass" is found. *************** *** 97,118 **** // static varname // public static varname // static _varname - // - // constructors: - // def new() - // enddef - // def newOther() - // enddef - // - // methods (object, class, generics): - // def someMethod() - // enddef - // static def someMethod() - // enddef - // def someMethod() - // enddef - // static def someMethod() - // enddef char_u *p = line; if (checkforcmd(&p, "endclass", 4)) --- 97,102 ---- *************** *** 172,177 **** --- 156,200 ---- ++objmembers.ga_len; } + // constructors: + // def new() + // enddef + // def newOther() + // enddef + // methods: + // def someMethod() + // enddef + // TODO: + // static def someMethod() + // enddef + // def someMethod() + // enddef + // static def someMethod() + // enddef + else if (checkforcmd(&p, "def", 3)) + { + exarg_T ea; + garray_T lines_to_free; + + CLEAR_FIELD(ea); + ea.cmd = line; + ea.arg = p; + ea.cmdidx = CMD_def; + ea.getline = eap->getline; + ea.cookie = eap->cookie; + + ga_init2(&lines_to_free, sizeof(char_u *), 50); + ufunc_T *uf = define_function(&ea, NULL, &lines_to_free, TRUE); + ga_clear_strings(&lines_to_free); + + // TODO: how about errors? + if (uf != NULL && ga_grow(&objmethods, 1) == OK) + { + ((ufunc_T **)objmethods.ga_data)[objmethods.ga_len] = uf; + ++objmethods.ga_len; + } + } + else { semsg(_(e_not_valid_command_in_class_str), line); *************** *** 206,212 **** int have_new = FALSE; for (int i = 0; i < objmethods.ga_len; ++i) ! if (STRCMP((((ufunc_T *)objmethods.ga_data) + i)->uf_name, "new") == 0) { have_new = TRUE; --- 229,235 ---- int have_new = FALSE; for (int i = 0; i < objmethods.ga_len; ++i) ! if (STRCMP(((ufunc_T **)objmethods.ga_data)[i]->uf_name, "new") == 0) { have_new = TRUE; *************** *** 237,243 **** garray_T lines_to_free; ga_init2(&lines_to_free, sizeof(char_u *), 50); ! ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, cl); ga_clear_strings(&lines_to_free); vim_free(fga.ga_data); --- 260,266 ---- garray_T lines_to_free; ga_init2(&lines_to_free, sizeof(char_u *), 50); ! ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, TRUE); ga_clear_strings(&lines_to_free); vim_free(fga.ga_data); *************** *** 248,254 **** ++objmethods.ga_len; nf->uf_flags |= FC_NEW; - nf->uf_class = cl; nf->uf_ret_type = get_type_ptr(&type_list); if (nf->uf_ret_type != NULL) { --- 271,276 ---- *************** *** 257,263 **** nf->uf_ret_type->tt_argcount = 0; nf->uf_ret_type->tt_args = NULL; } - cl->class_new_func = nf; } } --- 279,284 ---- *************** *** 275,282 **** --- 296,313 ---- sizeof(ufunc_T *) * objmethods.ga_len); vim_free(objmethods.ga_data); + // Set the class pointer on all the object methods. + for (int i = 0; i < objmethods.ga_len; ++i) + { + ufunc_T *fp = cl->class_obj_methods[i]; + fp->uf_class = cl; + fp->uf_flags |= FC_OBJECT; // TODO: not for class method + } + cl->class_type.tt_type = VAR_CLASS; cl->class_type.tt_member = (type_T *)cl; + cl->class_object_type.tt_type = VAR_OBJECT; + cl->class_object_type.tt_member = (type_T *)cl; cl->class_type_list = type_list; // TODO: *************** *** 305,310 **** --- 336,346 ---- } ga_clear(&objmembers); + for (int i = 0; i < objmethods.ga_len; ++i) + { + ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i]; + func_clear_free(uf, FALSE); + } ga_clear(&objmethods); clear_type_list(&type_list); } *************** *** 419,424 **** --- 455,465 ---- funcexe_T funcexe; CLEAR_FIELD(funcexe); funcexe.fe_evaluate = TRUE; + if (rettv->v_type == VAR_OBJECT) + { + funcexe.fe_object = rettv->vval.v_object; + ++funcexe.fe_object->obj_refcount; + } // Clear the class or object after calling the function, in // case the refcount is one. *************** *** 426,432 **** rettv->v_type = VAR_UNKNOWN; // Call the user function. Result goes into rettv; - // TODO: pass the object int error = call_user_func_check(fp, argcount, argvars, rettv, &funcexe, NULL); --- 467,472 ---- *************** *** 545,555 **** } vim_free(cl->class_obj_members); vim_free(cl->class_obj_methods); - if (cl->class_new_func != NULL) - func_ptr_unref(cl->class_new_func); - clear_type_list(&cl->class_type_list); vim_free(cl); --- 585,597 ---- } vim_free(cl->class_obj_members); + for (int i = 0; i < cl->class_obj_method_count; ++i) + { + ufunc_T *uf = cl->class_obj_methods[i]; + func_clear_free(uf, FALSE); + } vim_free(cl->class_obj_methods); clear_type_list(&cl->class_type_list); vim_free(cl); *** ../vim-9.0.1040/src/vim9compile.c 2022-12-08 15:32:11.083034191 +0000 --- src/vim9compile.c 2022-12-09 19:36:51.559936425 +0000 *************** *** 52,58 **** CLEAR_POINTER(lvar); lvar->lv_name = (char_u *)"this"; if (cctx->ctx_ufunc->uf_class != NULL) ! lvar->lv_type = &cctx->ctx_ufunc->uf_class->class_type; } return OK; } --- 52,58 ---- CLEAR_POINTER(lvar); lvar->lv_name = (char_u *)"this"; if (cctx->ctx_ufunc->uf_class != NULL) ! lvar->lv_type = &cctx->ctx_ufunc->uf_class->class_object_type; } return OK; } *************** *** 975,981 **** goto theend; } ! ufunc = define_function(eap, lambda_name, lines_to_free, NULL); if (ufunc == NULL) { r = eap->skip ? OK : FAIL; --- 975,981 ---- goto theend; } ! ufunc = define_function(eap, lambda_name, lines_to_free, FALSE); if (ufunc == NULL) { r = eap->skip ? OK : FAIL; *** ../vim-9.0.1040/src/vim9execute.c 2022-12-08 20:41:55.437288302 +0000 --- src/vim9execute.c 2022-12-09 20:07:30.161359867 +0000 *************** *** 4225,4231 **** CLEAR_FIELD(ea); ea.cmd = ea.arg = iptr->isn_arg.string; ga_init2(&lines_to_free, sizeof(char_u *), 50); ! define_function(&ea, NULL, &lines_to_free, NULL); ga_clear_strings(&lines_to_free); } break; --- 4225,4231 ---- CLEAR_FIELD(ea); ea.cmd = ea.arg = iptr->isn_arg.string; ga_init2(&lines_to_free, sizeof(char_u *), 50); ! define_function(&ea, NULL, &lines_to_free, FALSE); ga_clear_strings(&lines_to_free); } break; *************** *** 5114,5119 **** --- 5114,5148 ---- } break; + case ISN_OBJ_MEMBER: + { + tv = STACK_TV_BOT(-1); + if (tv->v_type != VAR_OBJECT) + { + SOURCING_LNUM = iptr->isn_lnum; + garray_T type_list; + ga_init2(&type_list, sizeof(type_T *), 10); + type_T *type = typval2type(tv, get_copyID(), + &type_list, TVTT_DO_MEMBER); + char *tofree = NULL; + char *typename = type_name(type, &tofree); + semsg(_(e_object_required_found_str), typename); + vim_free(tofree); + clear_type_list(&type_list); + goto on_error; + } + int idx = iptr->isn_arg.number; + object_T *obj = tv->vval.v_object; + // the members are located right after the object struct + typval_T *mtv = ((typval_T *)(obj + 1)) + idx; + *tv = *mtv; + + // Unreference the object after getting the member, it may + // be freed. + object_unref(obj); + } + break; + case ISN_CLEARDICT: dict_stack_drop(); break; *************** *** 5577,5582 **** --- 5606,5612 ---- typval_T *argv, // arguments int flags, // DEF_ flags partial_T *partial, // optional partial for context + object_T *object, // object, e.g. for this.Func() funccall_T *funccal, typval_T *rettv) // return value { *************** *** 5818,5823 **** --- 5848,5862 ---- STACK_TV_VAR(idx)->vval.v_number = 0; } ectx.ec_stack.ga_len += dfunc->df_varcount; + + if (object != NULL) + { + // the object is always the variable at index zero + tv = STACK_TV_VAR(0); + tv->v_type = VAR_OBJECT; + tv->vval.v_object = object; + } + if (dfunc->df_has_closure) { // Initialize the variable that counts how many closures were *************** *** 6766,6771 **** --- 6805,6812 ---- 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; *** ../vim-9.0.1040/src/proto/vim9execute.pro 2022-11-02 13:30:37.538314551 +0000 --- src/proto/vim9execute.pro 2022-12-09 20:07:33.041353385 +0000 *************** *** 9,15 **** int add_defer_function(char_u *name, int argcount, typval_T *argvars); char_u *char_from_string(char_u *str, varnumber_T index); char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last, int exclusive); ! int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, loopvarinfo_T *loopvarinfo, ectx_T *ectx); int may_load_script(int sid, int *loaded); typval_T *lookup_debug_var(char_u *name); int may_break_in_function(ufunc_T *ufunc); --- 9,15 ---- int add_defer_function(char_u *name, int argcount, typval_T *argvars); char_u *char_from_string(char_u *str, varnumber_T index); char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last, int exclusive); ! int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, loopvarinfo_T *lvi, ectx_T *ectx); int may_load_script(int sid, int *loaded); typval_T *lookup_debug_var(char_u *name); int may_break_in_function(ufunc_T *ufunc); *************** *** 17,23 **** int set_ref_in_loopvars(int copyID); int exe_typval_instr(typval_T *tv, typval_T *rettv); char_u *exe_substitute_instr(void); ! int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, int flags, partial_T *partial, funccall_T *funccal, typval_T *rettv); void unwind_def_callstack(ectx_T *ectx); void may_invoke_defer_funcs(ectx_T *ectx); void set_context_in_disassemble_cmd(expand_T *xp, char_u *arg); --- 17,23 ---- int set_ref_in_loopvars(int copyID); int exe_typval_instr(typval_T *tv, typval_T *rettv); char_u *exe_substitute_instr(void); ! int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, int flags, partial_T *partial, object_T *object, funccall_T *funccal, typval_T *rettv); void unwind_def_callstack(ectx_T *ectx); void may_invoke_defer_funcs(ectx_T *ectx); void set_context_in_disassemble_cmd(expand_T *xp, char_u *arg); *** ../vim-9.0.1040/src/vim9expr.c 2022-12-08 15:32:11.087034211 +0000 --- src/vim9expr.c 2022-12-09 19:41:01.871966405 +0000 *************** *** 251,256 **** --- 251,306 ---- } /* + * Compile ".member" coming after an object or class. + */ + + static int + compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type) + { + if (VIM_ISWHITE((*arg)[1])) + { + semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg); + return FAIL; + } + + ++*arg; + char_u *name = *arg; + char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START); + if (name_end == name) + return FAIL; + size_t len = name_end - name; + + class_T *cl = (class_T *)type->tt_member; + if (*name_end == '(') + { + // TODO + } + else if (type->tt_type == VAR_OBJECT) + { + 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) + { + generate_OBJ_MEMBER(cctx, i, m->om_type); + + *arg = name_end; + return OK; + } + } + + semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name); + } + else + { + // TODO: class member + emsg("compile_class_object_index(): not handled"); + } + + return FAIL; + } + + /* * Generate an instruction to load script-local variable "name", without the * leading "s:". * Also finds imported variables. *************** *** 1797,1802 **** --- 1847,1853 ---- for (;;) { char_u *p = skipwhite(*arg); + type_T *type; if (*p == NUL || (VIM_ISWHITE(**arg) && vim9_comment_start(p))) { *************** *** 1824,1830 **** // is not a function call. if (**arg == '(') { - type_T *type; int argcount = 0; if (generate_ppconst(cctx, ppconst) == FAIL) --- 1875,1880 ---- *************** *** 1911,1917 **** int argcount = 1; garray_T *stack = &cctx->ctx_type_stack; int type_idx_start = stack->ga_len; - type_T *type; int expr_isn_start = cctx->ctx_instr.ga_len; int expr_isn_end; int arg_isn_count; --- 1961,1966 ---- *************** *** 2097,2102 **** --- 2146,2163 ---- if (compile_member(is_slice, &keeping_dict, cctx) == FAIL) return FAIL; } + else if (*p == '.' + && (type = get_type_on_stack(cctx, 0)) != &t_unknown + && (type->tt_type == VAR_CLASS || type->tt_type == VAR_OBJECT)) + { + // class member: SomeClass.varname + // class method: SomeClass.SomeMethod() + // class constructor: SomeClass.new() + // object member: someObject.varname, this.varname + // object method: someObject.SomeMethod(), this.SomeMethod() + if (compile_class_object_index(cctx, arg, type) == FAIL) + return FAIL; + } else if (*p == '.' && p[1] != '.') { // dictionary member: dict.name *** ../vim-9.0.1040/src/vim9instr.c 2022-12-08 15:32:11.087034211 +0000 --- src/vim9instr.c 2022-12-09 20:40:02.854658616 +0000 *************** *** 132,137 **** --- 132,154 ---- } /* + * 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; + + isn->isn_arg.number = idx; + return push_type_stack2(cctx, type, &t_any); + } + + /* * 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. *************** *** 2460,2465 **** --- 2477,2483 ---- case ISN_NEWDICT: case ISN_NEWLIST: case ISN_NEWPARTIAL: + case ISN_OBJ_MEMBER: case ISN_OPANY: case ISN_OPFLOAT: case ISN_OPNR: *** ../vim-9.0.1040/src/proto/vim9instr.pro 2022-12-08 15:32:11.087034211 +0000 --- src/proto/vim9instr.pro 2022-12-09 17:47:46.271275591 +0000 *************** *** 4,9 **** --- 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); *** ../vim-9.0.1040/src/vim9type.c 2022-12-08 15:32:11.087034211 +0000 --- src/vim9type.c 2022-12-09 18:01:32.590912145 +0000 *************** *** 1585,1594 **** if (tofree != NULL) rettv->vval.v_string = (char_u *)tofree; else - { rettv->vval.v_string = vim_strsave((char_u *)name); - vim_free(tofree); - } clear_type_list(&type_list); } --- 1585,1591 ---- *** ../vim-9.0.1040/src/testdir/test_vim9_class.vim 2022-12-08 20:41:55.437288302 +0000 --- src/testdir/test_vim9_class.vim 2022-12-09 21:32:25.597089460 +0000 *************** *** 130,141 **** --- 130,148 ---- class TextPosition this.lnum: number this.col: number + + def ToString(): string + return $'({this.lnum}, {this.col})' + enddef endclass # use the automatically generated new() method var pos = TextPosition.new(2, 12) assert_equal(2, pos.lnum) assert_equal(12, pos.col) + + # call an object method + assert_equal('(2, 12)', pos.ToString()) END v9.CheckScriptSuccess(lines) enddef *** ../vim-9.0.1040/src/version.c 2022-12-09 12:41:28.606855470 +0000 --- src/version.c 2022-12-09 21:39:56.440780864 +0000 *************** *** 697,698 **** --- 697,700 ---- { /* Add new patch number below this line */ + /**/ + 1041, /**/ -- MAN: Fetchez la vache! GUARD: Quoi? MAN: Fetchez la vache! "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD /// 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 ///