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 -- [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/20221210184245.1E0131C1742%40moolenaar.net.

Raspunde prin e-mail lui