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 <Tval> someMethod()
-       //        enddef
-       //        static def <Tval> 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 <Tval> someMethod()
+       //        enddef
+       //        static def <Tval> 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 -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20221209214220.E19871C1742%40moolenaar.net.

Raspunde prin e-mail lui