Patch 9.0.1074
Problem:    Class members are not supported yet.
Solution:   Add initial support for class members.
Files:      src/errors.h, src/eval.c, src/structs.h, src/vim9.h,
            src/vim9class.c, src/vim9compile.c, src/vim9expr.c,
            src/vim9instr.c, src/proto/vim9instr.pro,
            src/vim9cmds.c, src/vim9execute.c, src/testdir/test_vim9_class.vim


*** ../vim-9.0.1073/src/errors.h        2022-12-16 16:41:19.204714805 +0000
--- src/errors.h        2022-12-18 18:30:04.387224250 +0000
***************
*** 3378,3393 ****
        INIT(= N_("E1329: Cannot get object member type from initializer: %s"));
  EXTERN char e_invalid_type_for_object_member_str[]
        INIT(= N_("E1330: Invalid type for object member: %s"));
! EXTERN char e_public_must_be_followed_by_this[]
!       INIT(= N_("E1331: Public must be followed by \"this\""));
! EXTERN char e_public_object_member_name_cannot_start_with_underscore_str[]
!       INIT(= N_("E1332: Public object member name cannot start with 
underscore: %s"));
! EXTERN char e_cannot_access_private_object_member_str[]
!       INIT(= N_("E1333: Cannot access private object member: %s"));
  EXTERN char e_object_member_not_found_str[]
        INIT(= N_("E1334: Object member not found: %s"));
! EXTERN char e_object_member_is_not_writable_str[]
!       INIT(= N_("E1335: Object member is not writable: %s"));
  #endif
  EXTERN char e_internal_error_shortmess_too_long[]
        INIT(= N_("E1336: Internal error: shortmess too long"));
--- 3378,3399 ----
        INIT(= N_("E1329: Cannot get object member type from initializer: %s"));
  EXTERN char e_invalid_type_for_object_member_str[]
        INIT(= N_("E1330: Invalid type for object member: %s"));
! EXTERN char e_public_must_be_followed_by_this_or_static[]
!       INIT(= N_("E1331: Public must be followed by \"this\" or \"static\""));
! EXTERN char e_public_member_name_cannot_start_with_underscore_str[]
!       INIT(= N_("E1332: Public member name cannot start with underscore: 
%s"));
! EXTERN char e_cannot_access_private_member_str[]
!       INIT(= N_("E1333: Cannot access private member: %s"));
  EXTERN char e_object_member_not_found_str[]
        INIT(= N_("E1334: Object member not found: %s"));
! EXTERN char e_member_is_not_writable_str[]
!       INIT(= N_("E1335: Member is not writable: %s"));
  #endif
  EXTERN char e_internal_error_shortmess_too_long[]
        INIT(= N_("E1336: Internal error: shortmess too long"));
+ #ifdef FEAT_EVAL
+ EXTERN char e_class_member_not_found_str[]
+       INIT(= N_("E1337: Class member not found: %s"));
+ EXTERN char e_member_not_found_on_class_str_str[]
+       INIT(= N_("E1338: Member not found on class \"%s\": %s"));
+ #endif
*** ../vim-9.0.1073/src/eval.c  2022-12-14 20:54:52.411699476 +0000
--- src/eval.c  2022-12-18 18:23:43.039785841 +0000
***************
*** 1193,1211 ****
      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_OBJECT
!                     && lp->ll_tv->v_type != VAR_CLASS)
        {
            if (!quiet)
                semsg(_(e_dot_can_only_be_used_on_dictionary_str), name);
            return NULL;
        }
!       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_OBJECT
!               && lp->ll_tv->v_type != VAR_CLASS)
        {
            if (!quiet)
                emsg(_(e_can_only_index_list_dictionary_or_blob));
--- 1193,1213 ----
      var2.v_type = VAR_UNKNOWN;
      while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.'))
      {
!       vartype_T v_type = lp->ll_tv->v_type;
! 
!       if (*p == '.' && v_type != VAR_DICT
!                     && v_type != VAR_OBJECT
!                     && v_type != VAR_CLASS)
        {
            if (!quiet)
                semsg(_(e_dot_can_only_be_used_on_dictionary_str), name);
            return NULL;
        }
!       if (v_type != VAR_LIST
!               && v_type != VAR_DICT
!               && v_type != VAR_BLOB
!               && v_type != VAR_OBJECT
!               && v_type != VAR_CLASS)
        {
            if (!quiet)
                emsg(_(e_can_only_index_list_dictionary_or_blob));
***************
*** 1214,1222 ****
  
        // 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
                                             && lp->ll_tv->vval.v_blob == NULL)
            r = rettv_blob_alloc(lp->ll_tv);
        if (r == FAIL)
--- 1216,1224 ----
  
        // A NULL list/blob works like an empty list/blob, allocate one now.
        int r = OK;
!       if (v_type == VAR_LIST && lp->ll_tv->vval.v_list == NULL)
            r = rettv_list_alloc(lp->ll_tv);
!       else if (v_type == VAR_BLOB
                                             && lp->ll_tv->vval.v_blob == NULL)
            r = rettv_blob_alloc(lp->ll_tv);
        if (r == FAIL)
***************
*** 1278,1284 ****
            // Optionally get the second index [ :expr].
            if (*p == ':')
            {
!               if (lp->ll_tv->v_type == VAR_DICT)
                {
                    if (!quiet)
                        emsg(_(e_cannot_slice_dictionary));
--- 1280,1286 ----
            // Optionally get the second index [ :expr].
            if (*p == ':')
            {
!               if (v_type == VAR_DICT)
                {
                    if (!quiet)
                        emsg(_(e_cannot_slice_dictionary));
***************
*** 1334,1340 ****
            ++p;
        }
  
!       if (lp->ll_tv->v_type == VAR_DICT)
        {
            if (len == -1)
            {
--- 1336,1342 ----
            ++p;
        }
  
!       if (v_type == VAR_DICT)
        {
            if (len == -1)
            {
***************
*** 1435,1441 ****
            clear_tv(&var1);
            lp->ll_tv = &lp->ll_di->di_tv;
        }
!       else if (lp->ll_tv->v_type == VAR_BLOB)
        {
            long bloblen = blob_len(lp->ll_tv->vval.v_blob);
  
--- 1437,1443 ----
            clear_tv(&var1);
            lp->ll_tv = &lp->ll_di->di_tv;
        }
!       else if (v_type == VAR_BLOB)
        {
            long bloblen = blob_len(lp->ll_tv->vval.v_blob);
  
***************
*** 1466,1472 ****
            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.
--- 1468,1474 ----
            lp->ll_tv = NULL;
            break;
        }
!       else if (v_type == VAR_LIST)
        {
            /*
             * Get the number and item for the only or first index of the List.
***************
*** 1513,1519 ****
        }
        else  // v_type == VAR_CLASS || v_type == VAR_OBJECT
        {
!           class_T *cl = (lp->ll_tv->v_type == VAR_OBJECT
                                           && lp->ll_tv->vval.v_object != NULL)
                            ? lp->ll_tv->vval.v_object->obj_class
                            : lp->ll_tv->vval.v_class;
--- 1515,1521 ----
        }
        else  // v_type == VAR_CLASS || v_type == VAR_OBJECT
        {
!           class_T *cl = (v_type == VAR_OBJECT
                                           && lp->ll_tv->vval.v_object != NULL)
                            ? lp->ll_tv->vval.v_object->obj_class
                            : lp->ll_tv->vval.v_class;
***************
*** 1521,1543 ****
            if (cl != NULL)
            {
                lp->ll_valtype = NULL;
!               for (int i = 0; i < cl->class_obj_member_count; ++i)
!               {
!                   objmember_T *om = cl->class_obj_members + i;
!                   if (STRNCMP(om->om_name, key, p - key) == 0
!                                               && om->om_name[p - key] == NUL)
                    {
!                       switch (om->om_access)
                        {
                            case ACCESS_PRIVATE:
!                                   
semsg(_(e_cannot_access_private_object_member_str),
!                                           om->om_name);
                                    return NULL;
                            case ACCESS_READ:
                                    if (!(flags & GLV_READ_ONLY))
                                    {
!                                       
semsg(_(e_object_member_is_not_writable_str),
!                                               om->om_name);
                                        return NULL;
                                    }
                                    break;
--- 1523,1550 ----
            if (cl != NULL)
            {
                lp->ll_valtype = NULL;
!               int count = v_type == VAR_OBJECT ? cl->class_obj_member_count
!                                               : cl->class_class_member_count;
!               ocmember_T *members = v_type == VAR_OBJECT
!                                                    ? cl->class_obj_members
!                                                    : cl->class_class_members;
!               for (int i = 0; i < count; ++i)
!               {
!                   ocmember_T *om = members + i;
!                   if (STRNCMP(om->ocm_name, key, p - key) == 0
!                                              && om->ocm_name[p - key] == NUL)
                    {
!                       switch (om->ocm_access)
                        {
                            case ACCESS_PRIVATE:
!                                   semsg(_(e_cannot_access_private_member_str),
!                                                                om->ocm_name);
                                    return NULL;
                            case ACCESS_READ:
                                    if (!(flags & GLV_READ_ONLY))
                                    {
!                                       semsg(_(e_member_is_not_writable_str),
!                                                                om->ocm_name);
                                        return NULL;
                                    }
                                    break;
***************
*** 1545,1562 ****
                                    break;
                        }
  
!                       lp->ll_valtype = om->om_type;
  
!                       if (lp->ll_tv->v_type == VAR_OBJECT)
                            lp->ll_tv = ((typval_T *)(
                                            lp->ll_tv->vval.v_object + 1)) + i;
!                       // TODO: what about a class?
                        break;
                    }
                }
                if (lp->ll_valtype == NULL)
                {
!                   semsg(_(e_object_member_not_found_str), key);
                    return NULL;
                }
            }
--- 1552,1573 ----
                                    break;
                        }
  
!                       lp->ll_valtype = om->ocm_type;
  
!                       if (v_type == VAR_OBJECT)
                            lp->ll_tv = ((typval_T *)(
                                            lp->ll_tv->vval.v_object + 1)) + i;
!                       else
!                           lp->ll_tv = &cl->class_members_tv[i];
                        break;
                    }
                }
                if (lp->ll_valtype == NULL)
                {
!                   if (v_type == VAR_OBJECT)
!                       semsg(_(e_object_member_not_found_str), key);
!                   else
!                       semsg(_(e_class_member_not_found_str), key);
                    return NULL;
                }
            }
***************
*** 5936,5943 ****
                    {
                        if (i > 0)
                            ga_concat(&ga, (char_u *)", ");
!                       objmember_T *m = &cl->class_obj_members[i];
!                       ga_concat(&ga, m->om_name);
                        ga_concat(&ga, (char_u *)": ");
                        char_u *tf = NULL;
                        ga_concat(&ga, echo_string_core(
--- 5947,5954 ----
                    {
                        if (i > 0)
                            ga_concat(&ga, (char_u *)", ");
!                       ocmember_T *m = &cl->class_obj_members[i];
!                       ga_concat(&ga, m->ocm_name);
                        ga_concat(&ga, (char_u *)": ");
                        char_u *tf = NULL;
                        ga_concat(&ga, echo_string_core(
*** ../vim-9.0.1073/src/structs.h       2022-12-14 20:54:52.411699476 +0000
--- src/structs.h       2022-12-18 21:15:20.235962840 +0000
***************
*** 1387,1392 ****
--- 1387,1393 ----
  
  typedef double        float_T;
  
+ typedef struct typval_S typval_T;
  typedef struct listvar_S list_T;
  typedef struct dictvar_S dict_T;
  typedef struct partial_S partial_T;
***************
*** 1466,1479 ****
  } omacc_T;
  
  /*
!  * Entry for an object member variable.
   */
  typedef struct {
!     char_u    *om_name;   // allocated
!     omacc_T   om_access;
!     type_T    *om_type;
!     char_u    *om_init;   // allocated
! } objmember_T;
  
  // "class_T": used for v_class of typval of VAR_CLASS
  struct class_S
--- 1467,1480 ----
  } omacc_T;
  
  /*
!  * Entry for an object or class member variable.
   */
  typedef struct {
!     char_u    *ocm_name;   // allocated
!     omacc_T   ocm_access;
!     type_T    *ocm_type;
!     char_u    *ocm_init;   // allocated
! } ocmember_T;
  
  // "class_T": used for v_class of typval of VAR_CLASS
  struct class_S
***************
*** 1481,1494 ****
      char_u    *class_name;            // allocated
      int               class_refcount;
  
      int               class_obj_member_count;
!     objmember_T       *class_obj_members;     // allocated
  
      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
  };
  
--- 1482,1506 ----
      char_u    *class_name;            // allocated
      int               class_refcount;
  
+     // class members: "static varname"
+     int               class_class_member_count;
+     ocmember_T        *class_class_members;   // allocated
+     typval_T  *class_members_tv;      // allocated array of class member vals
+ 
+     // class methods: "static def SomeMethod()"
+     int               class_class_method_count;
+     ufunc_T   **class_class_methods;  // allocated
+ 
+     // object members: "this.varname"
      int               class_obj_member_count;
!     ocmember_T        *class_obj_members;     // allocated
  
+     // object methods: "def SomeMethod()"
      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 used for the class
      type_T    class_object_type;      // same as class_type but VAR_OBJECT
  };
  
***************
*** 1513,1519 ****
  /*
   * Structure to hold an internal variable without a name.
   */
! typedef struct
  {
      vartype_T v_type;
      char      v_lock;     // see below: VAR_LOCKED, VAR_FIXED
--- 1525,1531 ----
  /*
   * Structure to hold an internal variable without a name.
   */
! struct typval_S
  {
      vartype_T v_type;
      char      v_lock;     // see below: VAR_LOCKED, VAR_FIXED
***************
*** 1534,1540 ****
        class_T         *v_class;       // class value (can be NULL)
        object_T        *v_object;      // object value (can be NULL)
      }         vval;
! } typval_T;
  
  // Values for "dv_scope".
  #define VAR_SCOPE     1       // a:, v:, s:, etc. scope dictionaries
--- 1546,1552 ----
        class_T         *v_class;       // class value (can be NULL)
        object_T        *v_object;      // object value (can be NULL)
      }         vval;
! };
  
  // Values for "dv_scope".
  #define VAR_SCOPE     1       // a:, v:, s:, etc. scope dictionaries
*** ../vim-9.0.1073/src/vim9.h  2022-12-13 18:42:19.749879633 +0000
--- src/vim9.h  2022-12-18 21:30:58.774980527 +0000
***************
*** 36,41 ****
--- 36,43 ----
      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
+     ISN_LOAD_CLASSMEMBER,  // load class member, using classmember_T
+     ISN_STORE_CLASSMEMBER,  // store in class member, using classmember_T
  
      // get and set variables
      ISN_LOAD,     // push local variable isn_arg.number
***************
*** 476,481 ****
--- 478,489 ----
      class_T   *construct_class;   // class the object is created from
  } construct_T;
  
+ // arguments to ISN_STORE_CLASSMEMBER and ISN_LOAD_CLASSMEMBER
+ typedef struct {
+     class_T   *cm_class;
+     int               cm_idx;
+ } classmember_T;
+ 
  /*
   * Instruction
   */
***************
*** 528,533 ****
--- 536,542 ----
        deferins_T          defer;
        echowin_T           echowin;
        construct_T         construct;
+       classmember_T       classmember;
      } isn_arg;
  };
  
***************
*** 538,544 ****
      ufunc_T   *df_ufunc;          // struct containing most stuff
      int               df_refcount;        // how many ufunc_T point to this 
dfunc_T
      int               df_idx;             // index in def_functions
!     int               df_deleted;         // if TRUE function was deleted
      int               df_script_seq;      // Value of sctx_T sc_seq when the 
function
                                    // was compiled.
      char_u    *df_name;           // name used for error messages
--- 547,555 ----
      ufunc_T   *df_ufunc;          // struct containing most stuff
      int               df_refcount;        // how many ufunc_T point to this 
dfunc_T
      int               df_idx;             // index in def_functions
!     char      df_deleted;         // if TRUE function was deleted
!     char      df_delete_busy;     // TRUE when in
!                                   // delete_def_function_contents()
      int               df_script_seq;      // Value of sctx_T sc_seq when the 
function
                                    // was compiled.
      char_u    *df_name;           // name used for error messages
***************
*** 735,740 ****
--- 746,752 ----
      dest_window,
      dest_tab,
      dest_vimvar,
+     dest_class_member,
      dest_script,
      dest_reg,
      dest_expr,
***************
*** 766,771 ****
--- 778,787 ----
      lvar_T        lhs_local_lvar; // used for existing local destination
      lvar_T        lhs_arg_lvar;   // used for argument destination
      lvar_T        *lhs_lvar;      // points to destination lvar
+ 
+     class_T       *lhs_class;             // for dest_class_member
+     int                   lhs_classmember_idx;    // for dest_class_member
+ 
      int                   lhs_scriptvar_sid;
      int                   lhs_scriptvar_idx;
  
*** ../vim-9.0.1073/src/vim9class.c     2022-12-14 20:54:52.407699483 +0000
--- src/vim9class.c     2022-12-18 21:19:06.883718087 +0000
***************
*** 22,27 ****
--- 22,175 ----
  #endif
  
  /*
+  * Parse a member declaration, both object and class member.
+  * Returns OK or FAIL.  When OK then "varname_end" is set to just after the
+  * variable name and "type_ret" is set to the decleared or detected type.
+  * "init_expr" is set to the initialisation expression (allocated), if there 
is
+  * one.
+  */
+     static int
+ parse_member(
+       exarg_T *eap,
+       char_u  *line,
+       char_u  *varname,
+       int     has_public,         // TRUE if "public" seen before "varname"
+       char_u  **varname_end,
+       garray_T *type_list,
+       type_T  **type_ret,
+       char_u  **init_expr)
+ {
+     *varname_end = to_name_end(varname, FALSE);
+     if (*varname == '_' && has_public)
+     {
+       semsg(_(e_public_member_name_cannot_start_with_underscore_str), line);
+       return FAIL;
+     }
+ 
+     char_u *colon = skipwhite(*varname_end);
+     char_u *type_arg = colon;
+     type_T *type = NULL;
+     if (*colon == ':')
+     {
+       if (VIM_ISWHITE(**varname_end))
+       {
+           semsg(_(e_no_white_space_allowed_before_colon_str), varname);
+           return FAIL;
+       }
+       if (!VIM_ISWHITE(colon[1]))
+       {
+           semsg(_(e_white_space_required_after_str_str), ":", varname);
+           return FAIL;
+       }
+       type_arg = skipwhite(colon + 1);
+       type = parse_type(&type_arg, type_list, TRUE);
+       if (type == NULL)
+           return FAIL;
+     }
+ 
+     char_u *expr_start = skipwhite(type_arg);
+     char_u *expr_end = expr_start;
+     if (type == NULL && *expr_start != '=')
+     {
+       emsg(_(e_type_or_initialization_required));
+       return FAIL;
+     }
+ 
+     if (*expr_start == '=')
+     {
+       if (!VIM_ISWHITE(expr_start[-1]) || !VIM_ISWHITE(expr_start[1]))
+       {
+           semsg(_(e_white_space_required_before_and_after_str_at_str),
+                                                       "=", type_arg);
+           return FAIL;
+       }
+       expr_start = skipwhite(expr_start + 1);
+ 
+       expr_end = expr_start;
+       evalarg_T evalarg;
+       fill_evalarg_from_eap(&evalarg, eap, FALSE);
+       skip_expr(&expr_end, NULL);
+ 
+       if (type == NULL)
+       {
+           // No type specified, use the type of the initializer.
+           typval_T tv;
+           tv.v_type = VAR_UNKNOWN;
+           char_u *expr = expr_start;
+           int res = eval0(expr, &tv, eap, &evalarg);
+ 
+           if (res == OK)
+               type = typval2type(&tv, get_copyID(), type_list,
+                                                      TVTT_DO_MEMBER);
+           if (type == NULL)
+           {
+               semsg(_(e_cannot_get_object_member_type_from_initializer_str),
+                       expr_start);
+               clear_evalarg(&evalarg, NULL);
+               return FAIL;
+           }
+       }
+       clear_evalarg(&evalarg, NULL);
+     }
+     if (!valid_declaration_type(type))
+       return FAIL;
+ 
+     *type_ret = type;
+     if (expr_end > expr_start)
+       *init_expr = vim_strnsave(expr_start, expr_end - expr_start);
+     return OK;
+ }
+ 
+ /*
+  * Add a member to an object or a class.
+  * Returns OK when successful, "init_expr" will be consumed then.
+  * Returns FAIL otherwise, caller might need to free "init_expr".
+  */
+     static int
+ add_member(
+       garray_T    *gap,
+       char_u      *varname,
+       char_u      *varname_end,
+       int         has_public,
+       type_T      *type,
+       char_u      *init_expr)
+ {
+     if (ga_grow(gap, 1) == FAIL)
+       return FAIL;
+     ocmember_T *m = ((ocmember_T *)gap->ga_data) + gap->ga_len;
+     m->ocm_name = vim_strnsave(varname, varname_end - varname);
+     m->ocm_access = has_public ? ACCESS_ALL
+                             : *varname == '_' ? ACCESS_PRIVATE : ACCESS_READ;
+     m->ocm_type = type;
+     if (init_expr != NULL)
+       m->ocm_init = init_expr;
+     ++gap->ga_len;
+     return OK;
+ }
+ 
+ /*
+  * Move the class or object members found while parsing a class into the 
class.
+  * "gap" contains the found members.
+  * "members" will be set to the newly allocated array of members and
+  * "member_count" set to the number of members.
+  * Returns OK or FAIL.
+  */
+     static int
+ add_members_to_class(
+     garray_T  *gap,
+     ocmember_T        **members,
+     int               *member_count)
+ {
+     *member_count = gap->ga_len;
+     *members = gap->ga_len == 0 ? NULL : ALLOC_MULT(ocmember_T, gap->ga_len);
+     if (gap->ga_len > 0 && *members == NULL)
+       return FAIL;
+     mch_memmove(*members, gap->ga_data, sizeof(ocmember_T) * gap->ga_len);
+     VIM_CLEAR(gap->ga_data);
+     return OK;
+ }
+ 
+ /*
   * Handle ":class" and ":abstract class" up to ":endclass".
   */
      void
***************
*** 64,79 ****
      //    extends SomeClass
      //    implements SomeInterface
      //    specifies SomeInterface
!     //    check nothing follows
! 
!     // TODO: handle "is_export" if it is set
  
      garray_T  type_list;          // list of pointers to allocated types
      ga_init2(&type_list, sizeof(type_T *), 10);
  
      // Growarray with object members declared in the class.
      garray_T objmembers;
!     ga_init2(&objmembers, sizeof(objmember_T), 10);
  
      // Growarray with object methods declared in the class.
      garray_T objmethods;
--- 212,234 ----
      //    extends SomeClass
      //    implements SomeInterface
      //    specifies SomeInterface
!     //    check that nothing follows
!     //          handle "is_export" if it is set
  
      garray_T  type_list;          // list of pointers to allocated types
      ga_init2(&type_list, sizeof(type_T *), 10);
  
+     // Growarray with class members declared in the class.
+     garray_T classmembers;
+     ga_init2(&classmembers, sizeof(ocmember_T), 10);
+ 
+     // Growarray with object methods declared in the class.
+     garray_T classmethods;
+     ga_init2(&classmethods, sizeof(ufunc_T *), 10);
+ 
      // Growarray with object members declared in the class.
      garray_T objmembers;
!     ga_init2(&objmembers, sizeof(ocmember_T), 10);
  
      // Growarray with object methods declared in the class.
      garray_T objmethods;
***************
*** 92,103 ****
            break;
        char_u *line = skipwhite(theline);
  
-       // TODO:
-       // class members (public, read access, private):
-       //        static varname
-       //        public static varname
-       //        static _varname
- 
        char_u *p = line;
        if (checkforcmd(&p, "endclass", 4))
        {
--- 247,252 ----
***************
*** 110,118 ****
            break;
        }
  
-       // "this._varname"
-       // "this.varname"
-       // "public this.varname"
        int has_public = FALSE;
        if (checkforcmd(&p, "public", 3))
        {
--- 259,264 ----
***************
*** 124,135 ****
            has_public = TRUE;
            p = skipwhite(line + 6);
  
!           if (STRNCMP(p, "this", 4) != 0)
            {
!               emsg(_(e_public_must_be_followed_by_this));
                break;
            }
        }
        if (STRNCMP(p, "this", 4) == 0)
        {
            if (p[4] != '.' || !eval_isnamec1(p[5]))
--- 270,286 ----
            has_public = TRUE;
            p = skipwhite(line + 6);
  
!           if (STRNCMP(p, "this", 4) != 0 && STRNCMP(p, "static", 6) != 0)
            {
!               emsg(_(e_public_must_be_followed_by_this_or_static));
                break;
            }
        }
+ 
+       // object members (public, read access, private):
+       //      "this._varname"
+       //      "this.varname"
+       //      "public this.varname"
        if (STRNCMP(p, "this", 4) == 0)
        {
            if (p[4] != '.' || !eval_isnamec1(p[5]))
***************
*** 138,232 ****
                break;
            }
            char_u *varname = p + 5;
!           char_u *varname_end = to_name_end(varname, FALSE);
!           if (*varname == '_' && has_public)
!           {
!               
semsg(_(e_public_object_member_name_cannot_start_with_underscore_str), line);
!               break;
!           }
! 
!           char_u *colon = skipwhite(varname_end);
!           char_u *type_arg = colon;
            type_T *type = NULL;
!           if (*colon == ':')
            {
!               if (VIM_ISWHITE(*varname_end))
!               {
!                   semsg(_(e_no_white_space_allowed_before_colon_str),
!                                                                     varname);
!                   break;
!               }
!               if (!VIM_ISWHITE(colon[1]))
!               {
!                   semsg(_(e_white_space_required_after_str_str), ":",
!                                                                     varname);
!                   break;
!               }
!               type_arg = skipwhite(colon + 1);
!               type = parse_type(&type_arg, &type_list, TRUE);
!               if (type == NULL)
!                   break;
            }
  
!           char_u *expr_start = skipwhite(type_arg);
!           char_u *expr_end = expr_start;
!           if (type == NULL && *expr_start != '=')
            {
!               emsg(_(e_type_or_initialization_required));
!               break;
            }
! 
!           if (*expr_start == '=')
            {
!               if (!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);
! 
!               expr_end = expr_start;
!               evalarg_T evalarg;
!               fill_evalarg_from_eap(&evalarg, eap, FALSE);
!               skip_expr(&expr_end, NULL);
! 
!               if (type == NULL)
                {
!                   // No type specified, use the type of the initializer.
!                   typval_T tv;
!                   tv.v_type = VAR_UNKNOWN;
!                   char_u *expr = expr_start;
!                   int res = eval0(expr, &tv, eap, &evalarg);
! 
!                   if (res == OK)
!                       type = typval2type(&tv, get_copyID(), &type_list,
!                                                              TVTT_DO_MEMBER);
!                   if (type == NULL)
!                   {
!                       
semsg(_(e_cannot_get_object_member_type_from_initializer_str),
!                               expr_start);
!                       clear_evalarg(&evalarg, NULL);
!                       break;
!                   }
                }
-               clear_evalarg(&evalarg, NULL);
            }
-           if (!valid_declaration_type(type))
-               break;
- 
-           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_access = has_public ? ACCESS_ALL
-                           : *varname == '_' ? ACCESS_PRIVATE
-                           : ACCESS_READ;
-           m->om_type = type;
-           if (expr_end > expr_start)
-               m->om_init = vim_strnsave(expr_start, expr_end - expr_start);
-           ++objmembers.ga_len;
        }
  
        // constructors:
--- 289,340 ----
                break;
            }
            char_u *varname = p + 5;
!           char_u *varname_end = NULL;
            type_T *type = NULL;
!           char_u *init_expr = NULL;
!           if (parse_member(eap, line, varname, has_public,
!                         &varname_end, &type_list, &type, &init_expr) == FAIL)
!               break;
!           if (add_member(&objmembers, varname, varname_end,
!                                         has_public, type, init_expr) == FAIL)
            {
!               vim_free(init_expr);
!               break;
            }
+       }
  
!       // class members and methods
!       else if (checkforcmd(&p, "static", 6))
!       {
!           p = skipwhite(p);
!           if (checkforcmd(&p, "def", 3))
            {
!               // TODO: class method
!               //        static def someMethod()
!               //        enddef
!               //        static def <Tval> someMethod()
!               //        enddef
            }
!           else
            {
!               // class members (public, read access, private):
!               //      "static _varname"
!               //      "static varname"
!               //      "public static varname"
!               char_u *varname = p;
!               char_u *varname_end = NULL;
!               type_T *type = NULL;
!               char_u *init_expr = NULL;
!               if (parse_member(eap, line, varname, has_public,
!                         &varname_end, &type_list, &type, &init_expr) == FAIL)
                    break;
!               if (add_member(&classmembers, varname, varname_end,
!                                         has_public, type, init_expr) == FAIL)
                {
!                   vim_free(init_expr);
!                   break;
                }
            }
        }
  
        // constructors:
***************
*** 238,249 ****
        //        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;
--- 346,353 ----
***************
*** 282,303 ****
      class_T *cl = NULL;
      if (success)
      {
        cl = ALLOC_CLEAR_ONE(class_T);
        if (cl == NULL)
            goto cleanup;
        cl->class_refcount = 1;
        cl->class_name = vim_strnsave(arg, name_end - arg);
  
!       // Members are used by the new() function, add them here.
!       cl->class_obj_member_count = objmembers.ga_len;
!       cl->class_obj_members = objmembers.ga_len == 0 ? NULL
!                                 : ALLOC_MULT(objmember_T, objmembers.ga_len);
!       if (cl->class_name == NULL
!               || (objmembers.ga_len > 0 && cl->class_obj_members == NULL))
            goto cleanup;
!       mch_memmove(cl->class_obj_members, objmembers.ga_data,
!                                     sizeof(objmember_T) * objmembers.ga_len);
!       vim_free(objmembers.ga_data);
  
        int have_new = FALSE;
        for (int i = 0; i < objmethods.ga_len; ++i)
--- 386,437 ----
      class_T *cl = NULL;
      if (success)
      {
+       // "endclass" encountered without failures: Create the class.
+ 
        cl = ALLOC_CLEAR_ONE(class_T);
        if (cl == NULL)
            goto cleanup;
        cl->class_refcount = 1;
        cl->class_name = vim_strnsave(arg, name_end - arg);
+       if (cl->class_name == NULL)
+           goto cleanup;
  
!       // Add class and object members to "cl".
!       if (add_members_to_class(&classmembers,
!                                   &cl->class_class_members,
!                                   &cl->class_class_member_count) == FAIL
!               || add_members_to_class(&objmembers,
!                                   &cl->class_obj_members,
!                                   &cl->class_obj_member_count) == FAIL)
            goto cleanup;
! 
!       if (cl->class_class_member_count > 0)
!       {
!           // Allocate a typval for each class member and initialize it.
!           cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
!                                                cl->class_class_member_count);
!           if (cl->class_members_tv != NULL)
!               for (int i = 0; i < cl->class_class_member_count; ++i)
!               {
!                   ocmember_T *m = &cl->class_class_members[i];
!                   typval_T *tv = &cl->class_members_tv[i];
!                   if (m->ocm_init != NULL)
!                   {
!                       typval_T *etv = eval_expr(m->ocm_init, eap);
!                       if (etv != NULL)
!                       {
!                           *tv = *etv;
!                           vim_free(etv);
!                       }
!                   }
!                   else
!                   {
!                       // TODO: proper default value
!                       tv->v_type = m->ocm_type->tt_type;
!                       tv->vval.v_string = NULL;
!                   }
!               }
!       }
  
        int have_new = FALSE;
        for (int i = 0; i < objmethods.ga_len; ++i)
***************
*** 318,325 ****
                if (i > 0)
                    ga_concat(&fga, (char_u *)", ");
                ga_concat(&fga, (char_u *)"this.");
!               objmember_T *m = cl->class_obj_members + i;
!               ga_concat(&fga, (char_u *)m->om_name);
                ga_concat(&fga, (char_u *)" = v:none");
            }
            ga_concat(&fga, (char_u *)")\nenddef\n");
--- 452,459 ----
                if (i > 0)
                    ga_concat(&fga, (char_u *)", ");
                ga_concat(&fga, (char_u *)"this.");
!               ocmember_T *m = cl->class_obj_members + i;
!               ga_concat(&fga, (char_u *)m->ocm_name);
                ga_concat(&fga, (char_u *)" = v:none");
            }
            ga_concat(&fga, (char_u *)")\nenddef\n");
***************
*** 355,360 ****
--- 489,495 ----
            }
        }
  
+       // TODO: class methods
        cl->class_obj_method_count = objmethods.ga_len;
        cl->class_obj_methods = ALLOC_MULT(ufunc_T *, objmethods.ga_len);
        if (cl->class_obj_methods == NULL)
***************
*** 378,390 ****
        cl->class_type_list = type_list;
  
        // TODO:
!       // - Add the methods to the class
!       //      - array with ufunc_T pointers
!       // - Fill hashtab with object members and methods
!       // - Generate the default new() method, if needed.
!       // Later:
!       // - class members
!       // - class methods
  
        // Add the class to the script-local variables.
        typval_T tv;
--- 513,519 ----
        cl->class_type_list = type_list;
  
        // TODO:
!       // - Fill hashtab with object members and methods ?
  
        // Add the class to the script-local variables.
        typval_T tv;
***************
*** 404,416 ****
        vim_free(cl);
      }
  
!     for (int i = 0; i < objmembers.ga_len; ++i)
      {
!       objmember_T *m = ((objmember_T *)objmembers.ga_data) + i;
!       vim_free(m->om_name);
!       vim_free(m->om_init);
      }
-     ga_clear(&objmembers);
  
      for (int i = 0; i < objmethods.ga_len; ++i)
      {
--- 533,552 ----
        vim_free(cl);
      }
  
!     for (int round = 1; round <= 2; ++round)
      {
!       garray_T *gap = round == 1 ? &classmembers : &objmembers;
!       if (gap->ga_len == 0 || gap->ga_data == NULL)
!           continue;
! 
!       for (int i = 0; i < gap->ga_len; ++i)
!       {
!           ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
!           vim_free(m->ocm_name);
!           vim_free(m->ocm_init);
!       }
!       ga_clear(gap);
      }
  
      for (int i = 0; i < objmethods.ga_len; ++i)
      {
***************
*** 437,447 ****
  
      for (int i = 0; i < cl->class_obj_member_count; ++i)
      {
!       objmember_T *m = cl->class_obj_members + i;
!       if (STRNCMP(m->om_name, name, len) == 0 && m->om_name[len] == NUL)
        {
            *member_idx = i;
!           return m->om_type;
        }
      }
      return &t_any;
--- 573,583 ----
  
      for (int i = 0; i < cl->class_obj_member_count; ++i)
      {
!       ocmember_T *m = cl->class_obj_members + i;
!       if (STRNCMP(m->ocm_name, name, len) == 0 && m->ocm_name[len] == NUL)
        {
            *member_idx = i;
!           return m->ocm_type;
        }
      }
      return &t_any;
***************
*** 572,584 ****
      {
        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)
            {
                if (*name == '_')
                {
!                   semsg(_(e_cannot_access_private_object_member_str),
!                                                                  m->om_name);
                    return FAIL;
                }
  
--- 708,719 ----
      {
        for (int i = 0; i < cl->class_obj_member_count; ++i)
        {
!           ocmember_T *m = &cl->class_obj_members[i];
!           if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
            {
                if (*name == '_')
                {
!                   semsg(_(e_cannot_access_private_member_str), m->ocm_name);
                    return FAIL;
                }
  
***************
*** 597,603 ****
        semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name);
      }
  
!     // TODO: class member
  
      return FAIL;
  }
--- 732,762 ----
        semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name);
      }
  
!     else if (rettv->v_type == VAR_CLASS)
!     {
!       // class member
!       for (int i = 0; i < cl->class_class_member_count; ++i)
!       {
!           ocmember_T *m = &cl->class_class_members[i];
!           if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
!           {
!               if (*name == '_')
!               {
!                   semsg(_(e_cannot_access_private_member_str), m->ocm_name);
!                   return FAIL;
!               }
! 
!               typval_T *tv = &cl->class_members_tv[i];
!               copy_tv(tv, rettv);
!               class_unref(cl);
! 
!               *arg = name_end;
!               return OK;
!           }
!       }
! 
!       semsg(_(e_member_not_found_on_class_str_str), cl->class_name, name);
!     }
  
      return FAIL;
  }
***************
*** 708,722 ****
      void
  class_unref(class_T *cl)
  {
!     if (cl != NULL && --cl->class_refcount <= 0)
      {
!       vim_free(cl->class_name);
  
        for (int i = 0; i < cl->class_obj_member_count; ++i)
        {
!           objmember_T *m = &cl->class_obj_members[i];
!           vim_free(m->om_name);
!           vim_free(m->om_init);
        }
        vim_free(cl->class_obj_members);
  
--- 867,895 ----
      void
  class_unref(class_T *cl)
  {
!     if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
      {
!       // Freeing what the class contains may recursively come back here.
!       // Clear "class_name" first, if it is NULL the class does not need to
!       // be freed.
!       VIM_CLEAR(cl->class_name);
! 
!       for (int i = 0; i < cl->class_class_member_count; ++i)
!       {
!           ocmember_T *m = &cl->class_class_members[i];
!           vim_free(m->ocm_name);
!           vim_free(m->ocm_init);
!           if (cl->class_members_tv != NULL)
!               clear_tv(&cl->class_members_tv[i]);
!       }
!       vim_free(cl->class_class_members);
!       vim_free(cl->class_members_tv);
  
        for (int i = 0; i < cl->class_obj_member_count; ++i)
        {
!           ocmember_T *m = &cl->class_obj_members[i];
!           vim_free(m->ocm_name);
!           vim_free(m->ocm_init);
        }
        vim_free(cl->class_obj_members);
  
*** ../vim-9.0.1073/src/vim9compile.c   2022-12-14 13:49:56.214667568 +0000
--- src/vim9compile.c   2022-12-18 21:30:03.843036521 +0000
***************
*** 302,307 ****
--- 302,329 ----
  }
  
  /*
+  * If "name" is a class member in cctx->ctx_ufunc->uf_class return the index 
in
+  * class.class_class_members[].
+  * Otherwise return -1;
+  */
+     static int
+ class_member_index(char_u *name, size_t len, cctx_T *cctx)
+ {
+     if (cctx == NULL || cctx->ctx_ufunc == NULL
+                                         || cctx->ctx_ufunc->uf_class == NULL)
+       return -1;
+     class_T *cl = cctx->ctx_ufunc->uf_class;
+     for (int i = 0; i < cl->class_class_member_count; ++i)
+     {
+       ocmember_T *m = &cl->class_class_members[i];
+       if (STRNCMP(name, m->ocm_name, len) == 0
+               && m->ocm_name[len] == NUL)
+           return i;
+     }
+     return -1;
+ }
+ 
+ /*
   * Return TRUE if "name" is a local variable, argument, script variable or
   * imported.
   */
***************
*** 316,321 ****
--- 338,344 ----
                        && (cctx->ctx_ufunc->uf_flags & FC_OBJECT)
                        && STRNCMP(name, "this", 4) == 0)))
            || script_var_exists(name, len, cctx, NULL) == OK
+           || class_member_index(name, len, cctx) >= 0
            || find_imported(name, len, FALSE) != NULL;
  }
  
***************
*** 353,358 ****
--- 376,384 ----
      if (len == 1 && *p == '_')
        return OK;
  
+     if (class_member_index(p, len, cctx) >= 0)
+       return OK;
+ 
      if (script_var_exists(p, len, cctx, cstack) == OK)
      {
        if (is_arg)
***************
*** 1195,1208 ****
   * Generate the load instruction for "name".
   */
      static void
! generate_loadvar(
!       cctx_T          *cctx,
!       assign_dest_T   dest,
!       char_u          *name,
!       lvar_T          *lvar,
!       type_T          *type)
  {
!     switch (dest)
      {
        case dest_option:
        case dest_func_option:
--- 1221,1232 ----
   * Generate the load instruction for "name".
   */
      static void
! generate_loadvar(cctx_T *cctx, lhs_T *lhs)
  {
!     char_u    *name = lhs->lhs_name;
!     type_T    *type = lhs->lhs_type;
! 
!     switch (lhs->lhs_dest)
      {
        case dest_option:
        case dest_func_option:
***************
*** 1245,1250 ****
--- 1269,1275 ----
        case dest_local:
            if (cctx->ctx_skip != SKIP_YES)
            {
+               lvar_T  *lvar = lhs->lhs_lvar;
                if (lvar->lv_from_outer > 0)
                    generate_LOADOUTER(cctx, lvar->lv_idx, lvar->lv_from_outer,
                                 lvar->lv_loop_depth, lvar->lv_loop_idx, type);
***************
*** 1252,1257 ****
--- 1277,1286 ----
                    generate_LOAD(cctx, ISN_LOAD, lvar->lv_idx, NULL, type);
            }
            break;
+       case dest_class_member:
+           generate_CLASSMEMBER(cctx, TRUE, lhs->lhs_class,
+                                                    lhs->lhs_classmember_idx);
+           break;
        case dest_expr:
            // list or dict value should already be on the stack.
            break;
***************
*** 1533,1539 ****
--- 1562,1570 ----
  
            if (lookup_local(var_start, lhs->lhs_varlen,
                                             &lhs->lhs_local_lvar, cctx) == OK)
+           {
                lhs->lhs_lvar = &lhs->lhs_local_lvar;
+           }
            else
            {
                CLEAR_FIELD(lhs->lhs_arg_lvar);
***************
*** 1549,1554 ****
--- 1580,1586 ----
                    lhs->lhs_lvar = &lhs->lhs_arg_lvar;
                }
            }
+ 
            if (lhs->lhs_lvar != NULL)
            {
                if (is_decl)
***************
*** 1557,1562 ****
--- 1589,1600 ----
                    return FAIL;
                }
            }
+           else if ((lhs->lhs_classmember_idx = class_member_index(
+                                      var_start, lhs->lhs_varlen, cctx)) >= 0)
+           {
+               lhs->lhs_dest = dest_class_member;
+               lhs->lhs_class = cctx->ctx_ufunc->uf_class;
+           }
            else
            {
                int script_namespace = lhs->lhs_varlen > 1
***************
*** 1965,1972 ****
            return FAIL;
      }
      else
!       generate_loadvar(cctx, lhs->lhs_dest, lhs->lhs_name,
!                                                lhs->lhs_lvar, lhs->lhs_type);
      return OK;
  }
  
--- 2003,2009 ----
            return FAIL;
      }
      else
!       generate_loadvar(cctx, lhs);
      return OK;
  }
  
***************
*** 2998,3017 ****
  
            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);
            }
--- 3035,3054 ----
  
            for (int i = 0; i < ufunc->uf_class->class_obj_member_count; ++i)
            {
!               ocmember_T *m = &ufunc->uf_class->class_obj_members[i];
!               if (m->ocm_init != NULL)
                {
!                   char_u *expr = m->ocm_init;
                    if (compile_expr0(&expr, &cctx) == FAIL)
                        goto erret;
!                   if (!ends_excmd2(m->ocm_init, expr))
                    {
                        semsg(_(e_trailing_characters_str), expr);
                        goto erret;
                    }
                }
                else
!                   push_default_value(&cctx, m->ocm_type->tt_type,
                                                                  FALSE, NULL);
                generate_STORE_THIS(&cctx, i);
            }
***************
*** 3792,3797 ****
--- 3829,3841 ----
  {
      int idx;
  
+     // In same cases the instructions may refer to a class in which the
+     // function is defined and unreferencing the class may call back here
+     // recursively.  Set the df_delete_busy to avoid problems.
+     if (dfunc->df_delete_busy)
+       return;
+     dfunc->df_delete_busy = TRUE;
+ 
      ga_clear(&dfunc->df_def_args_isn);
      ga_clear_strings(&dfunc->df_var_names);
  
***************
*** 3800,3813 ****
        for (idx = 0; idx < dfunc->df_instr_count; ++idx)
            delete_instr(dfunc->df_instr + idx);
        VIM_CLEAR(dfunc->df_instr);
-       dfunc->df_instr = NULL;
      }
      if (dfunc->df_instr_debug != NULL)
      {
        for (idx = 0; idx < dfunc->df_instr_debug_count; ++idx)
            delete_instr(dfunc->df_instr_debug + idx);
        VIM_CLEAR(dfunc->df_instr_debug);
-       dfunc->df_instr_debug = NULL;
      }
  #ifdef FEAT_PROFILE
      if (dfunc->df_instr_prof != NULL)
--- 3844,3855 ----
***************
*** 3815,3821 ****
        for (idx = 0; idx < dfunc->df_instr_prof_count; ++idx)
            delete_instr(dfunc->df_instr_prof + idx);
        VIM_CLEAR(dfunc->df_instr_prof);
-       dfunc->df_instr_prof = NULL;
      }
  #endif
  
--- 3857,3862 ----
***************
*** 3823,3828 ****
--- 3864,3871 ----
        dfunc->df_deleted = TRUE;
      if (dfunc->df_ufunc != NULL)
        dfunc->df_ufunc->uf_def_status = UF_NOT_COMPILED;
+ 
+     dfunc->df_delete_busy = FALSE;
  }
  
  /*
*** ../vim-9.0.1073/src/vim9expr.c      2022-12-14 20:54:52.411699476 +0000
--- src/vim9expr.c      2022-12-18 18:27:03.819479601 +0000
***************
*** 278,294 ****
      {
        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)
            {
                if (*name == '_' && cctx->ctx_ufunc->uf_class != cl)
                {
!                   semsg(_(e_cannot_access_private_object_member_str),
!                                                                  m->om_name);
                    return FAIL;
                }
  
!               generate_GET_OBJ_MEMBER(cctx, i, m->om_type);
  
                *arg = name_end;
                return OK;
--- 278,293 ----
      {
        for (int i = 0; i < cl->class_obj_member_count; ++i)
        {
!           ocmember_T *m = &cl->class_obj_members[i];
!           if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
            {
                if (*name == '_' && cctx->ctx_ufunc->uf_class != cl)
                {
!                   semsg(_(e_cannot_access_private_member_str), m->ocm_name);
                    return FAIL;
                }
  
!               generate_GET_OBJ_MEMBER(cctx, i, m->ocm_type);
  
                *arg = name_end;
                return OK;
*** ../vim-9.0.1073/src/vim9instr.c     2022-12-13 18:42:19.749879633 +0000
--- src/vim9instr.c     2022-12-18 20:53:32.093494839 +0000
***************
*** 957,962 ****
--- 957,994 ----
  }
  
  /*
+  * Generate an ISN_LOAD_CLASSMEMBER ("load" == TRUE) or ISN_STORE_CLASSMEMBER
+  * ("load" == FALSE) instruction.
+  */
+     int
+ generate_CLASSMEMBER(
+       cctx_T      *cctx,
+       int         load,
+       class_T     *cl,
+       int         idx)
+ {
+     isn_T     *isn;
+ 
+     RETURN_OK_IF_SKIP(cctx);
+     if (load)
+     {
+       ocmember_T *m = &cl->class_class_members[idx];
+       isn = generate_instr_type(cctx, ISN_LOAD_CLASSMEMBER, m->ocm_type);
+     }
+     else
+     {
+       isn = generate_instr_drop(cctx, ISN_STORE_CLASSMEMBER, 1);
+     }
+     if (isn == NULL)
+       return FAIL;
+     isn->isn_arg.classmember.cm_class = cl;
+     ++cl->class_refcount;
+     isn->isn_arg.classmember.cm_idx = idx;
+ 
+     return OK;
+ }
+ 
+ /*
   * Generate an ISN_STOREOUTER instruction.
   */
      static int
***************
*** 2114,2119 ****
--- 2146,2152 ----
  
  /*
   * Generate a STORE instruction for "dest", not being "dest_local".
+  * "lhs" might be NULL.
   * Return FAIL when out of memory.
   */
      int
***************
*** 2122,2131 ****
        assign_dest_T   dest,
        int             opt_flags,
        int             vimvaridx,
-       int             scriptvar_idx,
-       int             scriptvar_sid,
        type_T          *type,
!       char_u          *name)
  {
      switch (dest)
      {
--- 2155,2163 ----
        assign_dest_T   dest,
        int             opt_flags,
        int             vimvaridx,
        type_T          *type,
!       char_u          *name,
!       lhs_T           *lhs)
  {
      switch (dest)
      {
***************
*** 2156,2164 ****
        case dest_vimvar:
            return generate_STORE(cctx, ISN_STOREV, vimvaridx, NULL);
        case dest_script:
            if (scriptvar_idx < 0)
            {
!               isntype_T isn_type = ISN_STORES;
  
                if (SCRIPT_ID_VALID(scriptvar_sid)
                         && SCRIPT_ITEM(scriptvar_sid)->sn_import_autoload
--- 2188,2198 ----
        case dest_vimvar:
            return generate_STORE(cctx, ISN_STOREV, vimvaridx, NULL);
        case dest_script:
+           int     scriptvar_idx = lhs->lhs_scriptvar_idx;
+           int     scriptvar_sid = lhs->lhs_scriptvar_sid;
            if (scriptvar_idx < 0)
            {
!               isntype_T   isn_type = ISN_STORES;
  
                if (SCRIPT_ID_VALID(scriptvar_sid)
                         && SCRIPT_ITEM(scriptvar_sid)->sn_import_autoload
***************
*** 2177,2182 ****
--- 2211,2220 ----
            }
            return generate_VIM9SCRIPT(cctx, ISN_STORESCRIPT,
                                           scriptvar_sid, scriptvar_idx, type);
+       case dest_class_member:
+           return generate_CLASSMEMBER(cctx, FALSE,
+                                    lhs->lhs_class, lhs->lhs_classmember_idx);
+ 
        case dest_local:
        case dest_expr:
            // cannot happen
***************
*** 2210,2217 ****
      if (lhs->lhs_dest != dest_local)
        return generate_store_var(cctx, lhs->lhs_dest,
                            lhs->lhs_opt_flags, lhs->lhs_vimvaridx,
!                           lhs->lhs_scriptvar_idx, lhs->lhs_scriptvar_sid,
!                           lhs->lhs_type, lhs->lhs_name);
  
      if (lhs->lhs_lvar != NULL)
      {
--- 2248,2254 ----
      if (lhs->lhs_dest != dest_local)
        return generate_store_var(cctx, lhs->lhs_dest,
                            lhs->lhs_opt_flags, lhs->lhs_vimvaridx,
!                           lhs->lhs_type, lhs->lhs_name, lhs);
  
      if (lhs->lhs_lvar != NULL)
      {
***************
*** 2422,2427 ****
--- 2459,2469 ----
            vim_free(isn->isn_arg.script.scriptref);
            break;
  
+       case ISN_LOAD_CLASSMEMBER:
+       case ISN_STORE_CLASSMEMBER:
+           class_unref(isn->isn_arg.classmember.cm_class);
+           break;
+ 
        case ISN_TRY:
            vim_free(isn->isn_arg.tryref.try_ref);
            break;
*** ../vim-9.0.1073/src/proto/vim9instr.pro     2022-12-13 18:42:19.749879633 
+0000
--- src/proto/vim9instr.pro     2022-12-18 21:15:25.863956663 +0000
***************
*** 32,37 ****
--- 32,38 ----
  int generate_SLICE(cctx_T *cctx, int count);
  int generate_CHECKLEN(cctx_T *cctx, int min_len, int more_OK);
  int generate_STORE(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name);
+ int generate_CLASSMEMBER(cctx_T *cctx, int load, class_T *cl, int idx);
  int generate_STORENR(cctx_T *cctx, int idx, varnumber_T value);
  int generate_LOAD(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name, 
type_T *type);
  int generate_LOADOUTER(cctx_T *cctx, int idx, int nesting, int loop_depth, 
int loop_idx, type_T *type);
***************
*** 74,80 ****
  int generate_UNPACK(cctx_T *cctx, int var_count, int semicolon);
  int generate_cmdmods(cctx_T *cctx, cmdmod_T *cmod);
  int generate_undo_cmdmods(cctx_T *cctx);
! int generate_store_var(cctx_T *cctx, assign_dest_T dest, int opt_flags, int 
vimvaridx, int scriptvar_idx, int scriptvar_sid, type_T *type, char_u *name);
  int inside_loop_scope(cctx_T *cctx);
  int generate_store_lhs(cctx_T *cctx, lhs_T *lhs, int instr_count, int 
is_decl);
  void may_generate_prof_end(cctx_T *cctx, int prof_lnum);
--- 75,81 ----
  int generate_UNPACK(cctx_T *cctx, int var_count, int semicolon);
  int generate_cmdmods(cctx_T *cctx, cmdmod_T *cmod);
  int generate_undo_cmdmods(cctx_T *cctx);
! int generate_store_var(cctx_T *cctx, assign_dest_T dest, int opt_flags, int 
vimvaridx, type_T *type, char_u *name, lhs_T *lhs);
  int inside_loop_scope(cctx_T *cctx);
  int generate_store_lhs(cctx_T *cctx, lhs_T *lhs, int instr_count, int 
is_decl);
  void may_generate_prof_end(cctx_T *cctx, int prof_lnum);
*** ../vim-9.0.1073/src/vim9cmds.c      2022-12-02 18:12:01.018476816 +0000
--- src/vim9cmds.c      2022-12-18 20:05:02.684604705 +0000
***************
*** 1013,1019 ****
            if (dest != dest_local)
            {
                if (generate_store_var(cctx, dest, opt_flags, vimvaridx,
!                                                    0, 0, type, name) == FAIL)
                    goto failed;
            }
            else if (varlen == 1 && *arg == '_')
--- 1013,1019 ----
            if (dest != dest_local)
            {
                if (generate_store_var(cctx, dest, opt_flags, vimvaridx,
!                                                    type, name, NULL) == FAIL)
                    goto failed;
            }
            else if (varlen == 1 && *arg == '_')
*** ../vim-9.0.1073/src/vim9execute.c   2022-12-13 18:42:19.749879633 +0000
--- src/vim9execute.c   2022-12-18 20:43:29.722073643 +0000
***************
*** 3817,3822 ****
--- 3817,3843 ----
                    goto on_error;
                break;
  
+           case ISN_LOAD_CLASSMEMBER:
+               {
+                   if (GA_GROW_FAILS(&ectx->ec_stack, 1))
+                       goto theend;
+                   classmember_T *cm = &iptr->isn_arg.classmember;
+                   *STACK_TV_BOT(0) =
+                                   cm->cm_class->class_members_tv[cm->cm_idx];
+                   ++ectx->ec_stack.ga_len;
+               }
+               break;
+ 
+           case ISN_STORE_CLASSMEMBER:
+               {
+                   classmember_T *cm = &iptr->isn_arg.classmember;
+                   tv = &cm->cm_class->class_members_tv[cm->cm_idx];
+                   clear_tv(tv);
+                   *tv = *STACK_TV_BOT(-1);
+                   --ectx->ec_stack.ga_len;
+               }
+               break;
+ 
            // Load or store variable or argument from outer scope.
            case ISN_LOADOUTER:
            case ISN_STOREOUTER:
***************
*** 6403,6408 ****
--- 6424,6442 ----
                smsg("%s%4d STORERANGE", pfx, current);
                break;
  
+           case ISN_LOAD_CLASSMEMBER:
+           case ISN_STORE_CLASSMEMBER:
+               {
+                   class_T *cl = iptr->isn_arg.classmember.cm_class;
+                   int     idx = iptr->isn_arg.classmember.cm_idx;
+                   ocmember_T *ocm = &cl->class_class_members[idx];
+                   smsg("%s%4d %s CLASSMEMBER %s.%s", pfx, current,
+                           iptr->isn_type == ISN_LOAD_CLASSMEMBER
+                                                           ? "LOAD" : "STORE",
+                           cl->class_name, ocm->ocm_name);
+               }
+               break;
+ 
            // constants
            case ISN_PUSHNR:
                smsg("%s%4d PUSHNR %lld", pfx, current,
*** ../vim-9.0.1073/src/testdir/test_vim9_class.vim     2022-12-14 
20:54:52.411699476 +0000
--- src/testdir/test_vim9_class.vim     2022-12-18 21:17:55.343794543 +0000
***************
*** 306,311 ****
--- 306,335 ----
        assert_fails('trip.two = 22', 'E1335')
        trip.three = 33
        assert_equal(33, trip.three)
+ 
+       assert_fails('trip.four = 4', 'E1334')
+   END
+   v9.CheckScriptSuccess(lines)
+ enddef
+ 
+ def Test_class_member_access()
+   var lines =<< trim END
+       vim9script
+       class TextPos
+          this.lnum = 1
+          this.col = 1
+          static counter = 0
+ 
+          def AddToCounter(nr: number)
+            counter += nr
+          enddef
+       endclass
+ 
+       assert_equal(0, TextPos.counter)
+       TextPos.AddToCounter(3)
+       assert_equal(3, TextPos.counter)
+ 
+       assert_fails('TextPos.counter += 5', 'E1335')
    END
    v9.CheckScriptSuccess(lines)
  enddef
*** ../vim-9.0.1073/src/version.c       2022-12-18 17:47:11.430957013 +0000
--- src/version.c       2022-12-18 18:30:47.947164802 +0000
***************
*** 697,698 ****
--- 697,700 ----
  {   /* Add new patch number below this line */
+ /**/
+     1074,
  /**/

-- 
Shit makes the flowers grow and that's beautiful

 /// 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/20221218214324.657731C0950%40moolenaar.net.

Raspunde prin e-mail lui