Patch 9.0.1031
Problem:    Vim9 class is not implemented yet.
Solution:   Add very basic class support.
Files:      runtime/doc/vim9class.txt, src/errors.h, src/eval.c,
            src/evalfunc.c, src/evalvars.c, src/if_py_both.h, src/json.c,
            src/structs.h, src/testing.c, src/typval.c, src/userfunc.c,
            src/proto/userfunc.pro, src/vim.h, src/vim9.h, src/vim9class.c,
            src/proto/vim9class.pro, src/vim9compile.c, src/vim9execute.c,
            src/vim9expr.c, src/vim9instr.c, src/proto/vim9instr.pro,
            src/vim9script.c, src/proto/vim9script.pro, src/vim9type.c,
            src/proto/vim9type.pro, src/viminfo.c, src/testdir/Make_all.mak,
            src/testdir/test_vim9_class.vim


*** ../vim-9.0.1030/runtime/doc/vim9class.txt   2022-12-04 20:11:12.791828025 
+0000
--- runtime/doc/vim9class.txt   2022-12-08 09:07:00.157532559 +0000
***************
*** 336,341 ****
--- 336,344 ----
  A class is defined between `:class` and `:endclass`.  The whole class is
  defined in one script file.  It is not possible to add to a class later.
  
+ A class can only be defined in a |Vim9| script file.  *E1315*
+ A class cannot be defined inside a function.
+ 
  It is possible to define more than one class in a script file.  Although it
  usually is better to export only one main class.  It can be useful to define
  types, enums and helper classes though.
***************
*** 369,377 ****
                                                        *implements*
  A class can implement one or more interfaces.
                                                        *specifies*
! A class can declare it's interface, the object members and methods, with a
  named interface.  This avoids the need for separately specifying the
! interface, which is often done an many languages, especially Java.
  
  
  Defining an interface ~
--- 372,380 ----
                                                        *implements*
  A class can implement one or more interfaces.
                                                        *specifies*
! A class can declare its interface, the object members and methods, with a
  named interface.  This avoids the need for separately specifying the
! interface, which is often done in many languages, especially Java.
  
  
  Defining an interface ~
***************
*** 634,640 ****
  to allow that.  This helps writing code with fewer mistakes.
  
  
! Making object membes private with an underscore ~
  
  When an object member is private, it can only be read and changed inside the
  class (and in sub-classes), then it cannot be used outside of the class.
--- 637,643 ----
  to allow that.  This helps writing code with fewer mistakes.
  
  
! Making object members private with an underscore ~
  
  When an object member is private, it can only be read and changed inside the
  class (and in sub-classes), then it cannot be used outside of the class.
*** ../vim-9.0.1030/src/errors.h        2022-12-04 21:40:48.467175257 +0000
--- src/errors.h        2022-12-08 12:21:36.131379099 +0000
***************
*** 3346,3349 ****
--- 3346,3373 ----
  #ifdef FEAT_EVAL
  EXTERN char e_class_name_must_start_with_uppercase_letter_str[]
        INIT(= N_("E1314: Class name must start with an uppercase letter: %s"));
+ EXTERN char e_white_space_required_after_class_name_str[]
+       INIT(= N_("E1315: White space required after class name: %s"));
+ EXTERN char e_class_can_only_be_defined_in_vim9_script[]
+       INIT(= N_("E1316: Class can only be defined in Vim9 script"));
+ EXTERN char e_invalid_object_member_declaration_str[]
+       INIT(= N_("E1317: Invalid object member declaration: %s"));
+ EXTERN char e_not_valid_command_in_class_str[]
+       INIT(= N_("E1318: Not a valid command in a class: %s"));
+ EXTERN char e_using_class_as_number[]
+       INIT(= N_("E1319: Using a class as a Number"));
+ EXTERN char e_using_object_as_number[]
+       INIT(= N_("E1320: Using an object as a Number"));
+ EXTERN char e_using_class_as_float[]
+       INIT(= N_("E1321: Using a class as a Float"));
+ EXTERN char e_using_object_as_float[]
+       INIT(= N_("E1322: Using an object as a Float"));
+ EXTERN char e_using_class_as_string[]
+       INIT(= N_("E1323: Using a class as a String"));
+ EXTERN char e_using_object_as_string[]
+       INIT(= N_("E1324: Using an object as a String"));
+ EXTERN char e_method_not_found_on_class_str_str[]
+       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"));
  #endif
*** ../vim-9.0.1030/src/eval.c  2022-11-28 20:34:47.704140309 +0000
--- src/eval.c  2022-12-08 14:06:38.808126638 +0000
***************
*** 1548,1554 ****
      {
        cc = *endp;
        *endp = NUL;
!       if (in_vim9script() && check_reserved_name(lp->ll_name) == FAIL)
            return;
  
        if (lp->ll_blob != NULL)
--- 1548,1554 ----
      {
        cc = *endp;
        *endp = NUL;
!       if (in_vim9script() && check_reserved_name(lp->ll_name, NULL) == FAIL)
            return;
  
        if (lp->ll_blob != NULL)
***************
*** 1724,1729 ****
--- 1724,1731 ----
            case VAR_JOB:
            case VAR_CHANNEL:
            case VAR_INSTR:
+           case VAR_CLASS:
+           case VAR_OBJECT:
                break;
  
            case VAR_BLOB:
***************
*** 3850,3861 ****
--- 3852,3876 ----
                    return OK;
                }
                break;
+       case 10: if (STRNCMP(s, "null_class", 10) == 0)
+               {
+                   rettv->v_type = VAR_CLASS;
+                   rettv->vval.v_class = NULL;
+                   return OK;
+               }
+                break;
        case 11: if (STRNCMP(s, "null_string", 11) == 0)
                {
                    rettv->v_type = VAR_STRING;
                    rettv->vval.v_string = NULL;
                    return OK;
                }
+               if (STRNCMP(s, "null_object", 11) == 0)
+               {
+                   rettv->v_type = VAR_OBJECT;
+                   rettv->vval.v_object = NULL;
+                   return OK;
+               }
                break;
        case 12:
                if (STRNCMP(s, "null_channel", 12) == 0)
***************
*** 4685,4690 ****
--- 4700,4707 ----
        case VAR_JOB:
        case VAR_CHANNEL:
        case VAR_INSTR:
+       case VAR_CLASS:
+       case VAR_OBJECT:
            if (verbose)
                emsg(_(e_cannot_index_special_variable));
            return FAIL;
***************
*** 4788,4793 ****
--- 4805,4812 ----
        case VAR_JOB:
        case VAR_CHANNEL:
        case VAR_INSTR:
+       case VAR_CLASS:
+       case VAR_OBJECT:
            break; // not evaluating, skipping over subscript
  
        case VAR_NUMBER:
***************
*** 5781,5786 ****
--- 5800,5815 ----
            r = (char_u *)"instructions";
            break;
  
+       case VAR_CLASS:
+           *tofree = NULL;
+           r = (char_u *)"class";
+           break;
+ 
+       case VAR_OBJECT:
+           *tofree = NULL;
+           r = (char_u *)"object";
+           break;
+ 
        case VAR_FLOAT:
            *tofree = NULL;
            vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", tv->vval.v_float);
***************
*** 6588,6593 ****
--- 6617,6636 ----
                ret = FAIL;
            }
        }
+       else if (**arg == '.' && (rettv->v_type == VAR_CLASS
+                                              || rettv->v_type == VAR_OBJECT))
+       {
+           // class member: SomeClass.varname
+           // class method: SomeClass.SomeMethod()
+           // class constructor: SomeClass.new()
+           // object member: someObject.varname
+           // object method: someObject.SomeMethod()
+           if (class_object_index(arg, rettv, evalarg, verbose) == FAIL)
+           {
+               clear_tv(rettv);
+               ret = FAIL;
+           }
+       }
        else
            break;
      }
***************
*** 6644,6649 ****
--- 6687,6694 ----
        case VAR_JOB:
        case VAR_CHANNEL:
        case VAR_INSTR:
+       case VAR_CLASS:
+       case VAR_OBJECT:
            copy_tv(from, to);
            break;
        case VAR_LIST:
*** ../vim-9.0.1030/src/evalfunc.c      2022-12-06 16:16:57.971656206 +0000
--- src/evalfunc.c      2022-12-07 09:49:43.662984070 +0000
***************
*** 3770,3775 ****
--- 3770,3781 ----
        case VAR_SPECIAL:
            n = argvars[0].vval.v_number != VVAL_TRUE;
            break;
+       case VAR_CLASS:
+           n = argvars[0].vval.v_class != NULL;
+           break;
+       case VAR_OBJECT:
+           n = argvars[0].vval.v_object != NULL;
+           break;
  
        case VAR_BLOB:
            n = argvars[0].vval.v_blob == NULL
***************
*** 7267,7272 ****
--- 7273,7280 ----
        case VAR_JOB:
        case VAR_CHANNEL:
        case VAR_INSTR:
+       case VAR_CLASS:
+       case VAR_OBJECT:
            emsg(_(e_invalid_type_for_len));
            break;
      }
***************
*** 10183,10189 ****
  
      if (argvars[2].v_type == VAR_FUNC
            || argvars[2].v_type == VAR_PARTIAL
!           || argvars[2].v_type == VAR_INSTR)
        expr = &argvars[2];
      else
        sub = tv_get_string_buf_chk(&argvars[2], subbuf);
--- 10191,10199 ----
  
      if (argvars[2].v_type == VAR_FUNC
            || argvars[2].v_type == VAR_PARTIAL
!           || argvars[2].v_type == VAR_INSTR
!           || argvars[2].v_type == VAR_CLASS
!           || argvars[2].v_type == VAR_OBJECT)
        expr = &argvars[2];
      else
        sub = tv_get_string_buf_chk(&argvars[2], subbuf);
***************
*** 10617,10622 ****
--- 10627,10634 ----
        case VAR_CHANNEL: n = VAR_TYPE_CHANNEL; break;
        case VAR_BLOB:    n = VAR_TYPE_BLOB; break;
        case VAR_INSTR:   n = VAR_TYPE_INSTR; break;
+       case VAR_CLASS:   n = VAR_TYPE_CLASS; break;
+       case VAR_OBJECT:  n = VAR_TYPE_OBJECT; break;
        case VAR_UNKNOWN:
        case VAR_ANY:
        case VAR_VOID:
*** ../vim-9.0.1030/src/evalvars.c      2022-12-02 15:58:34.602705473 +0000
--- src/evalvars.c      2022-12-07 09:51:22.714902172 +0000
***************
*** 2264,2269 ****
--- 2264,2271 ----
        case VAR_JOB:
        case VAR_CHANNEL:
        case VAR_INSTR:
+       case VAR_CLASS:
+       case VAR_OBJECT:
            break;
  
        case VAR_BLOB:
*** ../vim-9.0.1030/src/if_py_both.h    2022-11-29 13:46:43.122213356 +0000
--- src/if_py_both.h    2022-12-07 10:11:40.887756522 +0000
***************
*** 6422,6427 ****
--- 6422,6429 ----
        case VAR_CHANNEL:
        case VAR_JOB:
        case VAR_INSTR:
+       case VAR_CLASS:
+       case VAR_OBJECT:
            Py_INCREF(Py_None);
            return Py_None;
        case VAR_BOOL:
*** ../vim-9.0.1030/src/json.c  2022-09-17 21:07:52.099993159 +0100
--- src/json.c  2022-12-07 09:52:16.206857930 +0000
***************
*** 308,313 ****
--- 308,315 ----
        case VAR_JOB:
        case VAR_CHANNEL:
        case VAR_INSTR:
+       case VAR_CLASS:
+       case VAR_OBJECT:
            semsg(_(e_cannot_json_encode_str), vartype_name(val->v_type));
            return FAIL;
  
*** ../vim-9.0.1030/src/structs.h       2022-12-07 22:30:14.991089265 +0000
--- src/structs.h       2022-12-08 13:26:32.799900700 +0000
***************
*** 1406,1411 ****
--- 1406,1414 ----
  typedef struct isn_S isn_T;       // instruction
  typedef struct dfunc_S dfunc_T;           // :def function
  
+ typedef struct type_S type_T;
+ typedef struct ufunc_S ufunc_T;
+ 
  typedef struct jobvar_S job_T;
  typedef struct readq_S readq_T;
  typedef struct writeq_S writeq_T;
***************
*** 1415,1420 ****
--- 1418,1425 ----
  typedef struct cctx_S cctx_T;
  typedef struct ectx_S ectx_T;
  typedef struct instr_S instr_T;
+ typedef struct class_S class_T;
+ typedef struct object_S object_T;
  
  typedef enum
  {
***************
*** 1434,1449 ****
      VAR_JOB,          // "v_job" is used
      VAR_CHANNEL,      // "v_channel" is used
      VAR_INSTR,                // "v_instr" is used
  } vartype_T;
  
  // A type specification.
- typedef struct type_S type_T;
  struct type_S {
      vartype_T     tt_type;
      int8_T        tt_argcount;    // for func, incl. vararg, -1 for unknown
      int8_T        tt_min_argcount; // number of non-optional arguments
      char_u        tt_flags;       // TTFLAG_ values
      type_T        *tt_member;     // for list, dict, func return type
      type_T        **tt_args;      // func argument types, allocated
  };
  
--- 1439,1456 ----
      VAR_JOB,          // "v_job" is used
      VAR_CHANNEL,      // "v_channel" is used
      VAR_INSTR,                // "v_instr" is used
+     VAR_CLASS,                // "v_class" is used
+     VAR_OBJECT,               // "v_object" is used
  } vartype_T;
  
  // A type specification.
  struct type_S {
      vartype_T     tt_type;
      int8_T        tt_argcount;    // for func, incl. vararg, -1 for unknown
      int8_T        tt_min_argcount; // number of non-optional arguments
      char_u        tt_flags;       // TTFLAG_ values
      type_T        *tt_member;     // for list, dict, func return type
+                                   // for class: class_T
      type_T        **tt_args;      // func argument types, allocated
  };
  
***************
*** 1452,1457 ****
--- 1459,1496 ----
      type_T    *type_decl;         // declared type or equal to type_current
  } type2_T;
  
+ /*
+  * 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
+ struct class_S
+ {
+     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
+     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
+     int               obj_refcount;
+ };
+ 
  #define TTFLAG_VARARGS        0x01        // func args ends with "..."
  #define TTFLAG_BOOL_OK        0x02        // can be converted to bool
  #define TTFLAG_STATIC 0x04        // one of the static types, e.g. t_any
***************
*** 1467,1483 ****
      union
      {
        varnumber_T     v_number;       // number value
!       float_T         v_float;        // floating number value
!       char_u          *v_string;      // string value (can be NULL!)
!       list_T          *v_list;        // list value (can be NULL!)
!       dict_T          *v_dict;        // dict value (can be NULL!)
        partial_T       *v_partial;     // closure: function with args
  #ifdef FEAT_JOB_CHANNEL
!       job_T           *v_job;         // job value (can be NULL!)
!       channel_T       *v_channel;     // channel value (can be NULL!)
  #endif
!       blob_T          *v_blob;        // blob value (can be NULL!)
        instr_T         *v_instr;       // instructions to execute
      }         vval;
  } typval_T;
  
--- 1506,1524 ----
      union
      {
        varnumber_T     v_number;       // number value
!       float_T         v_float;        // floating point number value
!       char_u          *v_string;      // string value (can be NULL)
!       list_T          *v_list;        // list value (can be NULL)
!       dict_T          *v_dict;        // dict value (can be NULL)
        partial_T       *v_partial;     // closure: function with args
  #ifdef FEAT_JOB_CHANNEL
!       job_T           *v_job;         // job value (can be NULL)
!       channel_T       *v_channel;     // channel value (can be NULL)
  #endif
!       blob_T          *v_blob;        // blob value (can be NULL)
        instr_T         *v_instr;       // instructions to execute
+       class_T         *v_class;       // class value (can be NULL)
+       object_T        *v_object;      // object value (can be NULL)
      }         vval;
  } typval_T;
  
***************
*** 1663,1669 ****
   * Structure to hold info for a user function.
   * When adding a field check copy_lambda_to_global_func().
   */
! typedef struct
  {
      int               uf_varargs;     // variable nr of arguments (old style)
      int               uf_flags;       // FC_ flags
--- 1704,1710 ----
   * Structure to hold info for a user function.
   * When adding a field check copy_lambda_to_global_func().
   */
! struct ufunc_S
  {
      int               uf_varargs;     // variable nr of arguments (old style)
      int               uf_flags;       // FC_ flags
***************
*** 1671,1676 ****
--- 1712,1720 ----
      int               uf_cleared;     // func_clear() was already called
      def_status_T uf_def_status; // UF_NOT_COMPILED, UF_TO_BE_COMPILED, etc.
      int               uf_dfunc_idx;   // only valid if uf_def_status is 
UF_COMPILED
+ 
+     class_T   *uf_class;      // for object method and constructor
+ 
      garray_T  uf_args;        // arguments, including optional arguments
      garray_T  uf_def_args;    // default argument expressions
      int               uf_args_visible; // normally uf_args.ga_len, less when
***************
*** 1731,1737 ****
      char_u    uf_name[4];     // name of function (actual size equals name);
                                // can start with <SNR>123_ (<SNR> is K_SPECIAL
                                // KS_EXTRA KE_SNR)
! } ufunc_T;
  
  // flags used in uf_flags
  #define FC_ABORT    0x01      // abort function on error
--- 1775,1781 ----
      char_u    uf_name[4];     // name of function (actual size equals name);
                                // can start with <SNR>123_ (<SNR> is K_SPECIAL
                                // KS_EXTRA KE_SNR)
! };
  
  // flags used in uf_flags
  #define FC_ABORT    0x01      // abort function on error
***************
*** 1750,1755 ****
--- 1794,1802 ----
                                // copy_lambda_to_global_func()
  #define FC_LAMBDA   0x2000    // one line "return {expr}"
  
+ #define FC_OBJECT   010000    // object method
+ #define FC_NEW            030000      // constructor (also an object method)
+ 
  #define MAX_FUNC_ARGS 20      // maximum number of function arguments
  #define VAR_SHORT_LEN 20      // short variable name length
  #define FIXVAR_CNT    12      // number of fixed variables
*** ../vim-9.0.1030/src/testing.c       2022-11-09 00:44:25.315439173 +0000
--- src/testing.c       2022-12-07 09:52:56.990824217 +0000
***************
*** 1101,1106 ****
--- 1101,1108 ----
        case VAR_SPECIAL:
        case VAR_STRING:
        case VAR_INSTR:
+       case VAR_CLASS:
+       case VAR_OBJECT:
            break;
        case VAR_JOB:
  #ifdef FEAT_JOB_CHANNEL
*** ../vim-9.0.1030/src/typval.c        2022-11-30 20:20:52.755228276 +0000
--- src/typval.c        2022-12-08 13:21:44.371816108 +0000
***************
*** 84,89 ****
--- 84,96 ----
                channel_unref(varp->vval.v_channel);
                break;
  #endif
+           case VAR_CLASS:
+               class_unref(varp);
+               break;
+           case VAR_OBJECT:
+               object_unref(varp->vval.v_object);
+               break;
+ 
            case VAR_NUMBER:
            case VAR_FLOAT:
            case VAR_ANY:
***************
*** 153,158 ****
--- 160,171 ----
            case VAR_INSTR:
                VIM_CLEAR(varp->vval.v_instr);
                break;
+           case VAR_CLASS:
+               class_unref(varp);
+               break;
+           case VAR_OBJECT:
+               object_unref(varp->vval.v_object);
+               break;
            case VAR_UNKNOWN:
            case VAR_ANY:
            case VAR_VOID:
***************
*** 234,239 ****
--- 247,258 ----
        case VAR_BLOB:
            emsg(_(e_using_blob_as_number));
            break;
+       case VAR_CLASS:
+           emsg(_(e_using_class_as_number));
+           break;
+       case VAR_OBJECT:
+           emsg(_(e_using_object_as_number));
+           break;
        case VAR_VOID:
            emsg(_(e_cannot_use_void_value));
            break;
***************
*** 333,338 ****
--- 352,363 ----
        case VAR_BLOB:
            emsg(_(e_using_blob_as_float));
            break;
+       case VAR_CLASS:
+           emsg(_(e_using_class_as_float));
+           break;
+       case VAR_OBJECT:
+           emsg(_(e_using_object_as_float));
+           break;
        case VAR_VOID:
            emsg(_(e_cannot_use_void_value));
            break;
***************
*** 1029,1034 ****
--- 1054,1065 ----
        case VAR_BLOB:
            emsg(_(e_using_blob_as_string));
            break;
+       case VAR_CLASS:
+           emsg(_(e_using_class_as_string));
+           break;
+       case VAR_OBJECT:
+           emsg(_(e_using_object_as_string));
+           break;
        case VAR_JOB:
  #ifdef FEAT_JOB_CHANNEL
            if (in_vim9script())
***************
*** 1158,1163 ****
--- 1189,1202 ----
            to->vval.v_instr = from->vval.v_instr;
            break;
  
+       case VAR_CLASS:
+           copy_class(from, to);
+           break;
+ 
+       case VAR_OBJECT:
+           copy_object(from, to);
+           break;
+ 
        case VAR_STRING:
        case VAR_FUNC:
            if (from->vval.v_string == NULL)
***************
*** 1878,1883 ****
--- 1917,1929 ----
        case VAR_INSTR:
            return tv1->vval.v_instr == tv2->vval.v_instr;
  
+       case VAR_CLASS:
+           return tv1->vval.v_class == tv2->vval.v_class;
+ 
+       case VAR_OBJECT:
+           // TODO: compare values
+           return tv1->vval.v_object == tv2->vval.v_object;
+ 
        case VAR_PARTIAL:
            return tv1->vval.v_partial == tv2->vval.v_partial;
  
*** ../vim-9.0.1030/src/userfunc.c      2022-11-25 16:31:46.972606656 +0000
--- src/userfunc.c      2022-12-07 16:01:48.183339139 +0000
***************
*** 214,219 ****
--- 214,221 ----
      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)
  {
      int               mustend = FALSE;
***************
*** 292,297 ****
--- 294,344 ----
                }
            }
        }
+       else if (class_arg != NULL && STRNCMP(p, "this.", 5) == 0)
+       {
+           // this.memberName
+           p += 5;
+           arg = p;
+           while (ASCII_ISALNUM(*p) || *p == '_')
+               ++p;
+ 
+           // TODO: check the argument is indeed a member
+           if (newargs != NULL && ga_grow(newargs, 1) == FAIL)
+               return FAIL;
+           if (newargs != NULL)
+           {
+               ((char_u **)(newargs->ga_data))[newargs->ga_len] =
+                                                   vim_strnsave(arg, p - arg);
+               newargs->ga_len++;
+ 
+               if (argtypes != NULL && ga_grow(argtypes, 1) == OK)
+               {
+                   // TODO: use the actual type
+                   ((char_u **)argtypes->ga_data)[argtypes->ga_len++] =
+                                                 vim_strsave((char_u *)"any");
+ 
+                   // Add a line to the function body for the assignment.
+                   if (ga_grow(newlines, 1) == OK)
+                   {
+                       // "this.name = name"
+                       int len = 5 + (p - arg) + 3 + (p - arg) + 1;
+                       char_u *assignment = alloc(len);
+                       if (assignment != NULL)
+                       {
+                           c = *p;
+                           *p = NUL;
+                           vim_snprintf((char *)assignment, len,
+                                                    "this.%s = %s", arg, arg);
+                           *p = c;
+                           ((char_u **)(newlines->ga_data))[
+                                             newlines->ga_len++] = assignment;
+                       }
+                   }
+               }
+           }
+           if (*p == ',')
+               ++p;
+       }
        else
        {
            char_u *np;
***************
*** 1389,1395 ****
      s = *arg + 1;
      ret = get_function_args(&s, equal_arrow ? ')' : '-', NULL,
            types_optional ? &argtypes : NULL, types_optional, evalarg,
!                                       NULL, &default_args, TRUE, NULL, NULL);
      if (ret == FAIL || skip_arrow(s, equal_arrow, &ret_type, NULL) == NULL)
      {
        if (types_optional)
--- 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)
***************
*** 1406,1412 ****
      ret = get_function_args(arg, equal_arrow ? ')' : '-', pnewargs,
            types_optional ? &argtypes : NULL, types_optional, evalarg,
                                            &varargs, &default_args,
!                                           FALSE, NULL, NULL);
      if (ret == FAIL
                  || (s = skip_arrow(*arg, equal_arrow, &ret_type,
                equal_arrow || vim9script ? &white_error : NULL)) == NULL)
--- 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)
***************
*** 1733,1739 ****
   * Return them in "*argvars[MAX_FUNC_ARGS + 1]" and the count in "argcount".
   * On failure FAIL is returned but the "argvars[argcount]" are still set.
   */
!     static int
  get_func_arguments(
        char_u      **arg,
        evalarg_T   *evalarg,
--- 1780,1786 ----
   * Return them in "*argvars[MAX_FUNC_ARGS + 1]" and the count in "argcount".
   * On failure FAIL is returned but the "argvars[argcount]" are still set.
   */
!     int
  get_func_arguments(
        char_u      **arg,
        evalarg_T   *evalarg,
***************
*** 1809,1815 ****
      funcexe_T *funcexe)       // various values
  {
      char_u    *argp;
!     int               ret = OK;
      typval_T  argvars[MAX_FUNC_ARGS + 1];     // vars for arguments
      int               argcount = 0;                   // number of arguments 
found
      int               vim9script = in_vim9script();
--- 1856,1862 ----
      funcexe_T *funcexe)       // various values
  {
      char_u    *argp;
!     int               ret;
      typval_T  argvars[MAX_FUNC_ARGS + 1];     // vars for arguments
      int               argcount = 0;                   // number of arguments 
found
      int               vim9script = in_vim9script();
***************
*** 4370,4379 ****
   * 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.
   * Returns a pointer to the function or NULL if no function defined.
   */
      ufunc_T *
! define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free)
  {
      int               j;
      int               c;
--- 4417,4431 ----
   * 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 *
! define_function(
!       exarg_T     *eap,
!       char_u      *name_arg,
!       garray_T    *lines_to_free,
!       class_T     *class_arg)
  {
      int               j;
      int               c;
***************
*** 4488,4495 ****
            p = eap->arg;
        }
  
!       name = save_function_name(&p, &is_global, eap->skip,
!                                       TFN_NO_AUTOLOAD | TFN_NEW_FUNC, &fudi);
        paren = (vim_strchr(p, '(') != NULL);
        if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip)
        {
--- 4540,4548 ----
            p = eap->arg;
        }
  
!       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)
        {
***************
*** 4690,4696 ****
      if (get_function_args(&p, ')', &newargs,
                        eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE,
                         NULL, &varargs, &default_args, eap->skip,
!                        eap, lines_to_free) == FAIL)
        goto errret_2;
      whitep = p;
  
--- 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;
  
***************
*** 5145,5151 ****
      garray_T lines_to_free;
  
      ga_init2(&lines_to_free, sizeof(char_u *), 50);
!     (void)define_function(eap, NULL, &lines_to_free);
      ga_clear_strings(&lines_to_free);
  }
  
--- 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);
  }
  
*** ../vim-9.0.1030/src/proto/userfunc.pro      2022-10-01 15:32:42.539442245 
+0100
--- src/proto/userfunc.pro      2022-12-07 15:29:14.772312232 +0000
***************
*** 7,12 ****
--- 7,13 ----
  int get_lambda_tv(char_u **arg, typval_T *rettv, int types_optional, 
evalarg_T *evalarg);
  char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, type_T 
**type, int no_autoload, int new_function, int *found_var);
  void emsg_funcname(char *ermsg, char_u *name);
+ int get_func_arguments(char_u **arg, evalarg_T *evalarg, int partial_argc, 
typval_T *argvars, int *argcount);
  int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, 
evalarg_T *evalarg, funcexe_T *funcexe);
  char_u *fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int 
*error);
  void func_name_with_sid(char_u *name, int sid, char_u *buffer);
***************
*** 45,51 ****
  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);
  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, 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);
*** ../vim-9.0.1030/src/vim.h   2022-11-23 20:19:17.129682462 +0000
--- src/vim.h   2022-12-07 09:50:23.922950781 +0000
***************
*** 2120,2125 ****
--- 2120,2127 ----
  #define VAR_TYPE_CHANNEL    9
  #define VAR_TYPE_BLOB     10
  #define VAR_TYPE_INSTR            11
+ #define VAR_TYPE_CLASS            12
+ #define VAR_TYPE_OBJECT           13
  
  #define DICT_MAXNEST 100      // maximum nesting of lists and dicts
  
*** ../vim-9.0.1030/src/vim9.h  2022-10-07 14:31:04.320852668 +0100
--- src/vim9.h  2022-12-08 12:40:20.798899365 +0000
***************
*** 32,37 ****
--- 32,38 ----
  
      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
  
      // get and set variables
      ISN_LOAD,     // push local variable isn_arg.number
***************
*** 110,115 ****
--- 111,117 ----
      ISN_PCALL_END,  // cleanup after ISN_PCALL with cpf_top set
      ISN_RETURN,           // return, result is on top of stack
      ISN_RETURN_VOID, // Push void, then return
+     ISN_RETURN_OBJECT, // Push constructed object, then return
      ISN_FUNCREF,    // push a function ref to dfunc isn_arg.funcref
      ISN_NEWFUNC,    // create a global function from a lambda function
      ISN_DEF,      // list functions
***************
*** 463,468 ****
--- 465,476 ----
      long      ewin_time;          // time argument (msec)
  } echowin_T;
  
+ // arguments to ISN_CONSTRUCT
+ typedef struct {
+     int               construct_size;     // size of object in bytes
+     class_T   *construct_class;   // class the object is created from
+ } construct_T;
+ 
  /*
   * Instruction
   */
***************
*** 514,519 ****
--- 522,528 ----
        debug_T             debug;
        deferins_T          defer;
        echowin_T           echowin;
+       construct_T         construct;
      } isn_arg;
  };
  
***************
*** 757,763 ****
  
      int                   lhs_has_type;   // type was specified
      type_T        *lhs_type;
!     type_T        *lhs_member_type;
  
      int                   lhs_append;     // used by ISN_REDIREND
  } lhs_T;
--- 766,773 ----
  
      int                   lhs_has_type;   // type was specified
      type_T        *lhs_type;
!     int                   lhs_member_idx;    // object member index
!     type_T        *lhs_member_type;  // list/dict/object member type
  
      int                   lhs_append;     // used by ISN_REDIREND
  } lhs_T;
*** ../vim-9.0.1030/src/vim9class.c     2022-12-04 20:11:12.791828025 +0000
--- src/vim9class.c     2022-12-08 14:07:59.048131829 +0000
***************
*** 27,35 ****
      void
  ex_class(exarg_T *eap)
  {
!     int is_abstract = eap->cmdidx == CMD_abstract;
  
      char_u *arg = eap->arg;
      if (is_abstract)
      {
        if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
--- 27,42 ----
      void
  ex_class(exarg_T *eap)
  {
!     if (!current_script_is_vim9()
!               || (cmdmod.cmod_flags & CMOD_LEGACY)
!               || !getline_equal(eap->getline, eap->cookie, getsourceline))
!     {
!       emsg(_(e_class_can_only_be_defined_in_vim9_script));
!       return;
!     }
  
      char_u *arg = eap->arg;
+     int is_abstract = eap->cmdidx == CMD_abstract;
      if (is_abstract)
      {
        if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
***************
*** 45,82 ****
        semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
        return;
      }
  
      // TODO:
!     // generics: <Tkey, Tentry>
      //    extends SomeClass
      //    implements SomeInterface
      //    specifies SomeInterface
  
  
!     // TODO: handle until "endclass" is found:
!     // object and class members (public, read access, private):
!     //          public this.varname
!     //          public static varname
!     //          this.varname
!     //          static varname
!     //          this._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
  }
  
  /*
--- 52,337 ----
        semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
        return;
      }
+     char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
+     if (!IS_WHITE_OR_NUL(*name_end))
+     {
+       semsg(_(e_white_space_required_after_class_name_str), arg);
+       return;
+     }
  
      // TODO:
!     //    generics: <Tkey, Tentry>
      //    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;
+     ga_init2(&objmethods, sizeof(ufunc_T), 10);
+ 
+     /*
+      * Go over the body of the class until "endclass" is found.
+      */
+     char_u *theline = NULL;
+     int success = FALSE;
+     for (;;)
+     {
+       vim_free(theline);
+       theline = eap->getline(':', eap->cookie, 0, GETLINE_CONCAT_ALL);
+       if (theline == NULL)
+           break;
+       char_u *line = skipwhite(theline);
+ 
+       // TODO:
+       // class members (public, read access, private):
+       //        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))
+       {
+           if (STRNCMP(line, "endclass", 8) != 0)
+               semsg(_(e_command_cannot_be_shortened_str), line);
+           else if (*p == '|' || !ends_excmd2(line, p))
+               semsg(_(e_trailing_characters_str), p);
+ 
+           success = TRUE;
+           break;
+       }
+ 
+       // "this.varname"
+       // "this._varname"
+       // TODO:
+       //      "public this.varname"
+       if (STRNCMP(line, "this", 4) == 0)
+       {
+           if (line[4] != '.' || !eval_isnamec1(line[5]))
+           {
+               semsg(_(e_invalid_object_member_declaration_str), line);
+               break;
+           }
+           char_u *varname = line + 5;
+           char_u *varname_end = to_name_end(varname, FALSE);
+ 
+           char_u *colon = skipwhite(varname_end);
+           // TODO: accept initialization and figure out type from it
+           if (*colon != ':')
+           {
+               emsg(_(e_type_or_initialization_required));
+               break;
+           }
+           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;
+           }
+ 
+           char_u *type_arg = skipwhite(colon + 1);
+           type_T *type = parse_type(&type_arg, &type_list, TRUE);
+           if (type == NULL)
+               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_type = type;
+           ++objmembers.ga_len;
+       }
+ 
+       else
+       {
+           semsg(_(e_not_valid_command_in_class_str), line);
+           break;
+       }
+     }
+     vim_free(theline);
+ 
+     if (success)
+     {
+       class_T *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 = ALLOC_MULT(objmember_T, objmembers.ga_len);
+       if (cl->class_name == NULL
+               || cl->class_obj_members == NULL)
+       {
+           vim_free(cl->class_name);
+           vim_free(cl->class_obj_members);
+           vim_free(cl);
+           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)
+           if (STRCMP((((ufunc_T *)objmethods.ga_data) + i)->uf_name,
+                                                                  "new") == 0)
+           {
+               have_new = TRUE;
+               break;
+           }
+       if (!have_new)
+       {
+           // No new() method was defined, add the default constructor.
+           garray_T fga;
+           ga_init2(&fga, 1, 1000);
+           ga_concat(&fga, (char_u *)"new(");
+           for (int i = 0; i < cl->class_obj_member_count; ++i)
+           {
+               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 *)")\nenddef\n");
+           ga_append(&fga, NUL);
+ 
+           exarg_T fea;
+           CLEAR_FIELD(fea);
+           fea.cmdidx = CMD_def;
+           fea.cmd = fea.arg = fga.ga_data;
+ 
+           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);
+ 
+           if (nf != NULL && ga_grow(&objmethods, 1) == OK)
+           {
+               ((ufunc_T **)objmethods.ga_data)[objmethods.ga_len] = nf;
+               ++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)
+               {
+                   nf->uf_ret_type->tt_type = VAR_OBJECT;
+                   nf->uf_ret_type->tt_member = (type_T *)cl;
+                   nf->uf_ret_type->tt_argcount = 0;
+                   nf->uf_ret_type->tt_args = NULL;
+               }
+               cl->class_new_func = nf;
+           }
+       }
+ 
+       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)
+       {
+           vim_free(cl->class_name);
+           vim_free(cl->class_obj_members);
+           vim_free(cl->class_obj_methods);
+           vim_free(cl);
+           goto cleanup;
+       }
+       mch_memmove(cl->class_obj_methods, objmethods.ga_data,
+                                       sizeof(ufunc_T *) * objmethods.ga_len);
+       vim_free(objmethods.ga_data);
+ 
+       cl->class_type.tt_type = VAR_CLASS;
+       cl->class_type.tt_member = (type_T *)cl;
+       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;
+       tv.v_type = VAR_CLASS;
+       tv.vval.v_class = cl;
+       set_var_const(cl->class_name, current_sctx.sc_sid,
+                                            NULL, &tv, FALSE, ASSIGN_DECL, 0);
+       return;
+     }
  
! cleanup:
!     for (int i = 0; i < objmembers.ga_len; ++i)
!     {
!       objmember_T *m = ((objmember_T *)objmembers.ga_data) + i;
!       vim_free(m->om_name);
!     }
!     ga_clear(&objmembers);
! 
!     ga_clear(&objmethods);
!     clear_type_list(&type_list);
! }
! 
! /*
!  * Find member "name" in class "cl" and return its type.
!  * When not found t_any is returned.
!  */
!     type_T *
! class_member_type(
!       class_T *cl,
!       char_u  *name,
!       char_u  *name_end,
!       int     *member_idx)
! {
!     *member_idx = -1;  // not found (yet)
!     size_t len = name_end - name;
! 
!     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;
  }
  
  /*
***************
*** 106,110 ****
--- 361,551 ----
      // TODO
  }
  
+ /*
+  * Evaluate what comes after a class:
+  * - class member: SomeClass.varname
+  * - class method: SomeClass.SomeMethod()
+  * - class constructor: SomeClass.new()
+  * - object member: someObject.varname
+  * - object method: someObject.SomeMethod()
+  *
+  * "*arg" points to the '.'.
+  * "*arg" is advanced to after the member name or method call.
+  *
+  * Returns FAIL or OK.
+  */
+     int
+ class_object_index(
+     char_u    **arg,
+     typval_T  *rettv,
+     evalarg_T *evalarg,
+     int               verbose UNUSED) // give error messages
+ {
+     // int            evaluate = evalarg != NULL
+     //                                      && (evalarg->eval_flags & 
EVAL_EVALUATE);
+ 
+     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 = rettv->v_type == VAR_CLASS ? rettv->vval.v_class
+                                            : rettv->vval.v_object->obj_class;
+     if (*name_end == '(')
+     {
+       for (int i = 0; i < cl->class_obj_method_count; ++i)
+       {
+           ufunc_T *fp = cl->class_obj_methods[i];
+           if (STRNCMP(name, fp->uf_name, len) == 0 && fp->uf_name[len] == NUL)
+           {
+               typval_T    argvars[MAX_FUNC_ARGS + 1];
+               int         argcount = 0;
+ 
+               char_u *argp = name_end;
+               int ret = get_func_arguments(&argp, evalarg, 0,
+                                                          argvars, &argcount);
+               if (ret == FAIL)
+                   return FAIL;
+ 
+               funcexe_T   funcexe;
+               CLEAR_FIELD(funcexe);
+               funcexe.fe_evaluate = TRUE;
+ 
+               // Call the user function.  Result goes into rettv;
+               // TODO: pass the object
+               rettv->v_type = VAR_UNKNOWN;
+               int error = call_user_func_check(fp, argcount, argvars,
+                                                       rettv, &funcexe, NULL);
+ 
+               // Clear the arguments.
+               for (int idx = 0; idx < argcount; ++idx)
+                   clear_tv(&argvars[idx]);
+ 
+               if (error != FCERR_NONE)
+               {
+                   user_func_error(error, printable_func_name(fp),
+                                                        funcexe.fe_found_var);
+                   return FAIL;
+               }
+               *arg = argp;
+               return OK;
+           }
+       }
+ 
+       semsg(_(e_method_not_found_on_class_str_str), cl->class_name, name);
+     }
+ 
+     else if (rettv->v_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)
+           {
+               // The object only contains a pointer to the class, the member
+               // values array follows right after that.
+               object_T *obj = rettv->vval.v_object;
+               typval_T *tv = (typval_T *)(obj + 1) + i;
+               copy_tv(tv, rettv);
+               object_unref(obj);
+ 
+               *arg = name_end;
+               return OK;
+           }
+       }
+ 
+       semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name);
+     }
+ 
+     // TODO: class member
+ 
+     return FAIL;
+ }
+ 
+ /*
+  * Make a copy of an object.
+  */
+     void
+ copy_object(typval_T *from, typval_T *to)
+ {
+     *to = *from;
+     if (to->vval.v_object != NULL)
+       ++to->vval.v_object->obj_refcount;
+ }
+ 
+ /*
+  * Free an object.
+  */
+     static void
+ object_clear(object_T *obj)
+ {
+     class_T *cl = obj->obj_class;
+ 
+     // the member values are just after the object structure
+     typval_T *tv = (typval_T *)(obj + 1);
+     for (int i = 0; i < cl->class_obj_member_count; ++i)
+       clear_tv(tv + i);
+ 
+     vim_free(obj);
+ }
+ 
+ /*
+  * Unreference an object.
+  */
+     void
+ object_unref(object_T *obj)
+ {
+     if (obj != NULL && --obj->obj_refcount <= 0)
+       object_clear(obj);
+ }
+ 
+ /*
+  * Make a copy of a class.
+  */
+     void
+ copy_class(typval_T *from, typval_T *to)
+ {
+     *to = *from;
+     if (to->vval.v_class != NULL)
+       ++to->vval.v_class->class_refcount;
+ }
+ 
+ /*
+  * Unreference a class.  Free it when the reference count goes down to zero.
+  */
+     void
+ class_unref(typval_T *tv)
+ {
+     class_T *cl = tv->vval.v_class;
+     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(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);
+     }
+ }
+ 
  
  #endif // FEAT_EVAL
*** ../vim-9.0.1030/src/proto/vim9class.pro     2022-12-04 20:11:12.791828025 
+0000
--- src/proto/vim9class.pro     2022-12-08 12:55:49.451427123 +0000
***************
*** 1,6 ****
--- 1,12 ----
  /* vim9class.c */
  void ex_class(exarg_T *eap);
+ type_T *class_member_type(class_T *cl, char_u *name, char_u *name_end, int 
*member_idx);
  void ex_interface(exarg_T *eap);
  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);
+ void copy_object(typval_T *from, typval_T *to);
+ void object_unref(object_T *obj);
+ void copy_class(typval_T *from, typval_T *to);
+ void class_unref(typval_T *tv);
  /* vim: set ft=c : */
*** ../vim-9.0.1030/src/vim9compile.c   2022-11-22 18:12:40.833924528 +0000
--- src/vim9compile.c   2022-12-08 12:40:53.882939900 +0000
***************
*** 43,48 ****
--- 43,62 ----
      if (len == 0)
        return FAIL;
  
+     if (len == 4 && STRNCMP(name, "this", 4) == 0
+           && cctx->ctx_ufunc != NULL
+           && (cctx->ctx_ufunc->uf_flags & FC_OBJECT))
+     {
+       if (lvar != NULL)
+       {
+           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;
+     }
+ 
      // Find local in current function scope.
      for (idx = 0; idx < cctx->ctx_locals.ga_len; ++idx)
      {
***************
*** 296,302 ****
  {
      return (cctx != NULL
                && (lookup_local(name, len, NULL, cctx) == OK
!                   || arg_exists(name, len, NULL, NULL, NULL, cctx) == OK))
            || script_var_exists(name, len, cctx, NULL) == OK
            || find_imported(name, len, FALSE) != NULL;
  }
--- 310,320 ----
  {
      return (cctx != NULL
                && (lookup_local(name, len, NULL, cctx) == OK
!                   || arg_exists(name, len, NULL, NULL, NULL, cctx) == OK
!                   || (len == 4
!                       && cctx->ctx_ufunc != NULL
!                       && (cctx->ctx_ufunc->uf_flags & FC_OBJECT)
!                       && STRNCMP(name, "this", 4) == 0)))
            || script_var_exists(name, len, cctx, NULL) == OK
            || find_imported(name, len, FALSE) != NULL;
  }
***************
*** 957,963 ****
        goto theend;
      }
  
!     ufunc = define_function(eap, lambda_name, lines_to_free);
      if (ufunc == NULL)
      {
        r = eap->skip ? OK : FAIL;
--- 975,981 ----
        goto theend;
      }
  
!     ufunc = define_function(eap, lambda_name, lines_to_free, NULL);
      if (ufunc == NULL)
      {
        r = eap->skip ? OK : FAIL;
***************
*** 1450,1455 ****
--- 1468,1474 ----
      lhs->lhs_dest = dest_local;
      lhs->lhs_vimvaridx = -1;
      lhs->lhs_scriptvar_idx = -1;
+     lhs->lhs_member_idx = -1;
  
      // "dest_end" is the end of the destination, including "[expr]" or
      // ".name".
***************
*** 1509,1515 ****
        else
        {
            // No specific kind of variable recognized, just a name.
!           if (check_reserved_name(lhs->lhs_name) == FAIL)
                return FAIL;
  
            if (lookup_local(var_start, lhs->lhs_varlen,
--- 1528,1534 ----
        else
        {
            // No specific kind of variable recognized, just a name.
!           if (check_reserved_name(lhs->lhs_name, cctx) == FAIL)
                return FAIL;
  
            if (lookup_local(var_start, lhs->lhs_varlen,
***************
*** 1757,1764 ****
            lhs->lhs_type = &t_any;
        }
  
!       if (lhs->lhs_type->tt_member == NULL)
            lhs->lhs_member_type = &t_any;
        else
            lhs->lhs_member_type = lhs->lhs_type->tt_member;
      }
--- 1776,1791 ----
            lhs->lhs_type = &t_any;
        }
  
!       if (lhs->lhs_type == NULL || lhs->lhs_type->tt_member == NULL)
            lhs->lhs_member_type = &t_any;
+       else if (lhs->lhs_type->tt_type == VAR_CLASS
+               || lhs->lhs_type->tt_type == VAR_OBJECT)
+       {
+           // for an object or class member get the type of the member
+           class_T *cl = (class_T *)lhs->lhs_type->tt_member;
+           lhs->lhs_member_type = class_member_type(cl, after + 1,
+                                          lhs->lhs_end, &lhs->lhs_member_idx);
+       }
        else
            lhs->lhs_member_type = lhs->lhs_type->tt_member;
      }
***************
*** 1880,1885 ****
--- 1907,1917 ----
            r = FAIL;
        }
      }
+     else if (lhs->lhs_member_idx >= 0)
+     {
+       // object member index
+       r = generate_PUSHNR(cctx, lhs->lhs_member_idx);
+     }
      else // if (*p == '.')
      {
        char_u *key_end = to_name_end(p + 1, TRUE);
***************
*** 1996,2002 ****
        return FAIL;
      }
  
!     if (lhs->lhs_type == &t_any)
      {
        // Index on variable of unknown type: check at runtime.
        dest_type = VAR_ANY;
--- 2028,2034 ----
        return FAIL;
      }
  
!     if (lhs->lhs_type == NULL || lhs->lhs_type == &t_any)
      {
        // Index on variable of unknown type: check at runtime.
        dest_type = VAR_ANY;
***************
*** 2042,2049 ****
      if (compile_load_lhs(lhs, var_start, rhs_type, cctx) == FAIL)
        return FAIL;
  
!     if (dest_type == VAR_LIST || dest_type == VAR_DICT
!                             || dest_type == VAR_BLOB || dest_type == VAR_ANY)
      {
        if (is_assign)
        {
--- 2074,2085 ----
      if (compile_load_lhs(lhs, var_start, rhs_type, cctx) == FAIL)
        return FAIL;
  
!     if (dest_type == VAR_LIST
!           || dest_type == VAR_DICT
!           || dest_type == VAR_BLOB
!           || dest_type == VAR_CLASS
!           || dest_type == VAR_OBJECT
!           || dest_type == VAR_ANY)
      {
        if (is_assign)
        {
***************
*** 2466,2471 ****
--- 2502,2509 ----
                    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
***************
*** 2897,2902 ****
--- 2935,2956 ----
      if (check_args_shadowing(ufunc, &cctx) == FAIL)
        goto erret;
  
+     // For an object method and constructor "this" is the first local 
variable.
+     if (ufunc->uf_flags & FC_OBJECT)
+     {
+       dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+                                                        + ufunc->uf_dfunc_idx;
+       if (GA_GROW_FAILS(&dfunc->df_var_names, 1))
+           goto erret;
+       ((char_u **)dfunc->df_var_names.ga_data)[0] =
+                                                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)
      {
        int     count = ufunc->uf_def_args.ga_len;
***************
*** 3500,3513 ****
      {
        if (ufunc->uf_ret_type->tt_type == VAR_UNKNOWN)
            ufunc->uf_ret_type = &t_void;
!       else if (ufunc->uf_ret_type->tt_type != VAR_VOID)
        {
            emsg(_(e_missing_return_statement));
            goto erret;
        }
  
        // Return void if there is no return at the end.
!       generate_instr(&cctx, ISN_RETURN_VOID);
      }
  
      // When compiled with ":silent!" and there was an error don't consider the
--- 3554,3572 ----
      {
        if (ufunc->uf_ret_type->tt_type == VAR_UNKNOWN)
            ufunc->uf_ret_type = &t_void;
!       else if (ufunc->uf_ret_type->tt_type != VAR_VOID
!               && (ufunc->uf_flags & FC_NEW) != FC_NEW)
        {
            emsg(_(e_missing_return_statement));
            goto erret;
        }
  
        // 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);
!       else
!           generate_instr(&cctx, ISN_RETURN_VOID);
      }
  
      // When compiled with ":silent!" and there was an error don't consider the
*** ../vim-9.0.1030/src/vim9execute.c   2022-11-25 16:31:46.968606662 +0000
--- src/vim9execute.c   2022-12-08 13:01:19.175468604 +0000
***************
*** 2029,2034 ****
--- 2029,2035 ----
      for (ni = iptr + 1; ni->isn_type != ISN_FINISH; ++ni)
        if (ni->isn_type == ISN_DEBUG
                  || ni->isn_type == ISN_RETURN
+                 || ni->isn_type == ISN_RETURN_OBJECT
                  || ni->isn_type == ISN_RETURN_VOID)
        {
            end_lnum = ni->isn_lnum + (ni->isn_type == ISN_DEBUG ? 0 : 1);
***************
*** 2082,2088 ****
      // Stack contains:
      // -3 value to be stored
      // -2 index
!     // -1 dict or list
      tv = STACK_TV_BOT(-3);
      SOURCING_LNUM = iptr->isn_lnum;
      if (dest_type == VAR_ANY)
--- 2083,2089 ----
      // Stack contains:
      // -3 value to be stored
      // -2 index
!     // -1 dict, list, blob or object
      tv = STACK_TV_BOT(-3);
      SOURCING_LNUM = iptr->isn_lnum;
      if (dest_type == VAR_ANY)
***************
*** 2203,2208 ****
--- 2204,2216 ----
                return FAIL;
            blob_set_append(blob, lidx, nr);
        }
+       else if (dest_type == VAR_CLASS || dest_type == VAR_OBJECT)
+       {
+           long            idx = (long)tv_idx->vval.v_number;
+           object_T        *obj = tv_dest->vval.v_object;
+           typval_T        *otv = (typval_T *)(obj + 1);
+           otv[idx] = *tv;
+       }
        else
        {
            status = FAIL;
***************
*** 3001,3006 ****
--- 3009,3026 ----
        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);
+               tv->v_type = VAR_OBJECT;
+               tv->vval.v_object = alloc_clear(
+                                      iptr->isn_arg.construct.construct_size);
+               tv->vval.v_object->obj_class =
+                                      iptr->isn_arg.construct.construct_class;
+               tv->vval.v_object->obj_refcount = 1;
+               break;
+ 
            // execute Ex command line
            case ISN_EXEC:
                if (exec_command(iptr) == FAIL)
***************
*** 4092,4106 ****
                    goto on_error;
                break;
  
!           // return from a :def function call without a value
            case ISN_RETURN_VOID:
                if (GA_GROW_FAILS(&ectx->ec_stack, 1))
                    goto theend;
                tv = STACK_TV_BOT(0);
                ++ectx->ec_stack.ga_len;
!               tv->v_type = VAR_VOID;
!               tv->vval.v_number = 0;
!               tv->v_lock = 0;
                // FALLTHROUGH
  
            // return from a :def function call with what is on the stack
--- 4112,4136 ----
                    goto on_error;
                break;
  
!           // Return from a :def function call without a value.
!           // Return from a constructor.
            case ISN_RETURN_VOID:
+           case ISN_RETURN_OBJECT:
                if (GA_GROW_FAILS(&ectx->ec_stack, 1))
                    goto theend;
                tv = STACK_TV_BOT(0);
                ++ectx->ec_stack.ga_len;
!               if (iptr->isn_type == ISN_RETURN_VOID)
!               {
!                   tv->v_type = VAR_VOID;
!                   tv->vval.v_number = 0;
!                   tv->v_lock = 0;
!               }
!               else
!               {
!                   *tv = *STACK_TV_VAR(0);
!                   ++tv->vval.v_object->obj_refcount;
!               }
                // FALLTHROUGH
  
            // return from a :def function call with what is on the stack
***************
*** 4193,4199 ****
                    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);
                    ga_clear_strings(&lines_to_free);
                }
                break;
--- 4223,4229 ----
                    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;
***************
*** 6018,6023 ****
--- 6048,6058 ----
  
        switch (iptr->isn_type)
        {
+           case ISN_CONSTRUCT:
+               smsg("%s%4d NEW %s size %d", pfx, current,
+                       iptr->isn_arg.construct.construct_class->class_name,
+                                 (int)iptr->isn_arg.construct.construct_size);
+               break;
            case ISN_EXEC:
                smsg("%s%4d EXEC %s", pfx, current, iptr->isn_arg.string);
                break;
***************
*** 6447,6452 ****
--- 6482,6490 ----
            case ISN_RETURN_VOID:
                smsg("%s%4d RETURN void", pfx, current);
                break;
+           case ISN_RETURN_OBJECT:
+               smsg("%s%4d RETURN object", pfx, current);
+               break;
            case ISN_FUNCREF:
                {
                    funcref_T           *funcref = &iptr->isn_arg.funcref;
***************
*** 6979,6984 ****
--- 7017,7024 ----
        case VAR_ANY:
        case VAR_VOID:
        case VAR_INSTR:
+       case VAR_CLASS:
+       case VAR_OBJECT:
            break;
      }
      return FALSE;
*** ../vim-9.0.1030/src/vim9expr.c      2022-11-06 18:27:09.363922860 +0000
--- src/vim9expr.c      2022-12-07 10:09:42.463753806 +0000
***************
*** 235,240 ****
--- 235,242 ----
            case VAR_JOB:
            case VAR_CHANNEL:
            case VAR_INSTR:
+           case VAR_CLASS:
+           case VAR_OBJECT:
            case VAR_UNKNOWN:
            case VAR_ANY:
            case VAR_VOID:
*** ../vim-9.0.1030/src/vim9instr.c     2022-10-08 14:39:31.966903597 +0100
--- src/vim9instr.c     2022-12-08 12:38:37.182753606 +0000
***************
*** 114,119 ****
--- 114,137 ----
  }
  
  /*
+  * Generate an ISN_CONSTRUCT instruction.
+  * The object will have "size" members.
+  */
+     int
+ generate_CONSTRUCT(cctx_T *cctx, class_T *cl)
+ {
+     isn_T     *isn;
+ 
+     RETURN_OK_IF_SKIP(cctx);
+     if ((isn = generate_instr(cctx, ISN_CONSTRUCT)) == NULL)
+       return FAIL;
+     isn->isn_arg.construct.construct_size = sizeof(object_T)
+                              + cl->class_obj_member_count * sizeof(typval_T);
+     isn->isn_arg.construct.construct_class = cl;
+     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.
***************
*** 163,168 ****
--- 181,188 ----
        case VAR_JOB:
        case VAR_CHANNEL:
        case VAR_INSTR:
+       case VAR_CLASS:
+       case VAR_OBJECT:
                         to_string_error(type->tt_type);
                         return FAIL;
      }
***************
*** 2403,2408 ****
--- 2423,2429 ----
        case ISN_COMPARESPECIAL:
        case ISN_COMPARESTRING:
        case ISN_CONCAT:
+       case ISN_CONSTRUCT:
        case ISN_COND2BOOL:
        case ISN_DEBUG:
        case ISN_DEFER:
***************
*** 2457,2462 ****
--- 2478,2484 ----
        case ISN_REDIRSTART:
        case ISN_RETURN:
        case ISN_RETURN_VOID:
+       case ISN_RETURN_OBJECT:
        case ISN_SHUFFLE:
        case ISN_SLICE:
        case ISN_SOURCE:
*** ../vim-9.0.1030/src/proto/vim9instr.pro     2022-10-07 14:31:04.324852691 
+0100
--- src/proto/vim9instr.pro     2022-12-08 12:40:52.634938418 +0000
***************
*** 3,8 ****
--- 3,9 ----
  isn_T *generate_instr_drop(cctx_T *cctx, isntype_T isn_type, int drop);
  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 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.1030/src/vim9script.c    2022-11-25 16:31:46.968606662 +0000
--- src/vim9script.c    2022-12-07 18:10:31.103199234 +0000
***************
*** 838,844 ****
      // parse type, check for reserved name
      p = skipwhite(p + 1);
      type = parse_type(&p, &si->sn_type_list, TRUE);
!     if (type == NULL || check_reserved_name(name) == FAIL)
      {
        vim_free(name);
        return p;
--- 838,844 ----
      // parse type, check for reserved name
      p = skipwhite(p + 1);
      type = parse_type(&p, &si->sn_type_list, TRUE);
!     if (type == NULL || check_reserved_name(name, NULL) == FAIL)
      {
        vim_free(name);
        return p;
***************
*** 1126,1137 ****
  };
  
      int
! check_reserved_name(char_u *name)
  {
      int idx;
  
      for (idx = 0; reserved[idx] != NULL; ++idx)
!       if (STRCMP(reserved[idx], name) == 0)
        {
            semsg(_(e_cannot_use_reserved_name), name);
            return FAIL;
--- 1126,1142 ----
  };
  
      int
! check_reserved_name(char_u *name, cctx_T *cctx)
  {
      int idx;
  
      for (idx = 0; reserved[idx] != NULL; ++idx)
!       if (STRCMP(reserved[idx], name) == 0
!               // "this" can be used in an object method
!               && !(STRCMP("this", name) == 0
!                   && cctx != NULL
!                   && cctx->ctx_ufunc != NULL
!                   && (cctx->ctx_ufunc->uf_flags & FC_OBJECT)))
        {
            semsg(_(e_cannot_use_reserved_name), name);
            return FAIL;
*** ../vim-9.0.1030/src/proto/vim9script.pro    2022-08-24 16:30:30.690752449 
+0100
--- src/proto/vim9script.pro    2022-12-07 17:56:10.911342014 +0000
***************
*** 19,23 ****
  void hide_script_var(scriptitem_T *si, int idx, int func_defined);
  svar_T *find_typval_in_script(typval_T *dest, scid_T sid, int must_find);
  int check_script_var_type(svar_T *sv, typval_T *value, char_u *name, where_T 
where);
! int check_reserved_name(char_u *name);
  /* vim: set ft=c : */
--- 19,23 ----
  void hide_script_var(scriptitem_T *si, int idx, int func_defined);
  svar_T *find_typval_in_script(typval_T *dest, scid_T sid, int must_find);
  int check_script_var_type(svar_T *sv, typval_T *value, char_u *name, where_T 
where);
! int check_reserved_name(char_u *name, cctx_T *cctx);
  /* vim: set ft=c : */
*** ../vim-9.0.1030/src/vim9type.c      2022-10-09 12:55:29.590190644 +0100
--- src/vim9type.c      2022-12-08 13:45:12.936060332 +0000
***************
*** 29,35 ****
   * Allocate memory for a type_T and add the pointer to type_gap, so that it 
can
   * be easily freed later.
   */
!     static type_T *
  get_type_ptr(garray_T *type_gap)
  {
      type_T *type;
--- 29,35 ----
   * Allocate memory for a type_T and add the pointer to type_gap, so that it 
can
   * be easily freed later.
   */
!     type_T *
  get_type_ptr(garray_T *type_gap)
  {
      type_T *type;
***************
*** 94,100 ****
      *ret = *type;
  
      if (ret->tt_member != NULL)
!       ret->tt_member = alloc_type(ret->tt_member);
      if (type->tt_args != NULL)
      {
        int i;
--- 94,105 ----
      *ret = *type;
  
      if (ret->tt_member != NULL)
!     {
!       // tt_member points to the class_T for VAR_CLASS and VAR_OBJECT
!       if (type->tt_type != VAR_CLASS && type->tt_type != VAR_OBJECT)
!           ret->tt_member = alloc_type(ret->tt_member);
!     }
! 
      if (type->tt_args != NULL)
      {
        int i;
***************
*** 124,130 ****
            free_type(type->tt_args[i]);
        vim_free(type->tt_args);
      }
!     free_type(type->tt_member);
      vim_free(type);
  }
  
--- 129,139 ----
            free_type(type->tt_args[i]);
        vim_free(type->tt_args);
      }
! 
!     // for an object and class tt_member is a pointer to the class
!     if (type->tt_type != VAR_OBJECT && type->tt_type != VAR_CLASS)
!       free_type(type->tt_member);
! 
      vim_free(type);
  }
  
***************
*** 1203,1208 ****
--- 1212,1219 ----
        case VAR_JOB:
        case VAR_CHANNEL:
        case VAR_INSTR:
+       case VAR_CLASS:
+       case VAR_OBJECT:
            break;  // not composite is always OK
        case VAR_LIST:
        case VAR_DICT:
***************
*** 1451,1456 ****
--- 1462,1469 ----
        case VAR_LIST: return "list";
        case VAR_DICT: return "dict";
        case VAR_INSTR: return "instr";
+       case VAR_CLASS: return "class";
+       case VAR_OBJECT: return "object";
  
        case VAR_FUNC:
        case VAR_PARTIAL: return "func";
*** ../vim-9.0.1030/src/proto/vim9type.pro      2022-10-09 12:55:29.590190644 
+0100
--- src/proto/vim9type.pro      2022-12-07 21:22:10.052795209 +0000
***************
*** 1,4 ****
--- 1,5 ----
  /* vim9type.c */
+ type_T *get_type_ptr(garray_T *type_gap);
  type_T *copy_type(type_T *type, garray_T *type_gap);
  void clear_type_list(garray_T *gap);
  type_T *alloc_type(type_T *type);
*** ../vim-9.0.1030/src/viminfo.c       2022-09-17 21:07:52.103993150 +0100
--- src/viminfo.c       2022-12-07 10:11:25.767756863 +0000
***************
*** 1370,1375 ****
--- 1370,1377 ----
                    case VAR_JOB:
                    case VAR_CHANNEL:
                    case VAR_INSTR:
+                   case VAR_CLASS:
+                   case VAR_OBJECT:
                                     continue;
                }
                fprintf(fp, "!%s\t%s\t", this_var->di_key, s);
*** ../vim-9.0.1030/src/testdir/Make_all.mak    2022-11-17 11:34:33.333726348 
+0000
--- src/testdir/Make_all.mak    2022-12-06 18:52:19.893770307 +0000
***************
*** 37,42 ****
--- 37,43 ----
  TEST_VIM9 = \
        test_vim9_assign \
        test_vim9_builtin \
+       test_vim9_class \
        test_vim9_cmd \
        test_vim9_disassemble \
        test_vim9_expr \
***************
*** 48,53 ****
--- 49,55 ----
  TEST_VIM9_RES = \
        test_vim9_assign.res \
        test_vim9_builtin.res \
+       test_vim9_class.res \
        test_vim9_cmd.res \
        test_vim9_disassemble.res \
        test_vim9_expr.res \
*** ../vim-9.0.1030/src/testdir/test_vim9_class.vim     2022-12-08 
15:28:29.853992301 +0000
--- src/testdir/test_vim9_class.vim     2022-12-08 15:22:38.214234840 +0000
***************
*** 0 ****
--- 1,145 ----
+ " Test Vim9 classes
+ 
+ source check.vim
+ import './vim9.vim' as v9
+ 
+ def Test_class_basic()
+   var lines =<< trim END
+       class NotWorking
+       endclass
+   END
+   v9.CheckScriptFailure(lines, 'E1316:')
+ 
+   lines =<< trim END
+       vim9script
+       class notWorking
+       endclass
+   END
+   v9.CheckScriptFailure(lines, 'E1314:')
+ 
+   lines =<< trim END
+       vim9script
+       class Not@working
+       endclass
+   END
+   v9.CheckScriptFailure(lines, 'E1315:')
+ 
+   lines =<< trim END
+       vim9script
+       abstract noclass Something
+       endclass
+   END
+   v9.CheckScriptFailure(lines, 'E475:')
+ 
+   lines =<< trim END
+       vim9script
+       abstract classy Something
+       endclass
+   END
+   v9.CheckScriptFailure(lines, 'E475:')
+ 
+   lines =<< trim END
+       vim9script
+       class Something
+       endcl
+   END
+   v9.CheckScriptFailure(lines, 'E1065:')
+ 
+   lines =<< trim END
+       vim9script
+       class Something
+       endclass school's out 
+   END
+   v9.CheckScriptFailure(lines, 'E488:')
+ 
+   lines =<< trim END
+       vim9script
+       class Something
+       endclass | echo 'done'
+   END
+   v9.CheckScriptFailure(lines, 'E488:')
+ 
+   lines =<< trim END
+       vim9script
+       class Something
+         this
+       endclass
+   END
+   v9.CheckScriptFailure(lines, 'E1317:')
+ 
+   lines =<< trim END
+       vim9script
+       class Something
+         this.
+       endclass
+   END
+   v9.CheckScriptFailure(lines, 'E1317:')
+ 
+   lines =<< trim END
+       vim9script
+       class Something
+         this .count
+       endclass
+   END
+   v9.CheckScriptFailure(lines, 'E1317:')
+ 
+   lines =<< trim END
+       vim9script
+       class Something
+         this. count
+       endclass
+   END
+   v9.CheckScriptFailure(lines, 'E1317:')
+ 
+   lines =<< trim END
+       vim9script
+       class Something
+         this.count: number
+         that.count
+       endclass
+   END
+   v9.CheckScriptFailure(lines, 'E1318: Not a valid command in a class: 
that.count')
+ 
+   lines =<< trim END
+       vim9script
+       class Something
+         this.count
+       endclass
+   END
+   v9.CheckScriptFailure(lines, 'E1022:')
+ 
+   lines =<< trim END
+       vim9script
+       class Something
+         this.count : number
+       endclass
+   END
+   v9.CheckScriptFailure(lines, 'E1059:')
+ 
+   lines =<< trim END
+       vim9script
+       class Something
+         this.count:number
+       endclass
+   END
+   v9.CheckScriptFailure(lines, 'E1069:')
+ 
+   lines =<< trim END
+       vim9script
+ 
+       class TextPosition
+         this.lnum: number
+       this.col: number
+       endclass
+ 
+       # # FIXME: this works but leaks memory
+       # # use the automatically generated new() method
+       # var pos = TextPosition.new(2, 12)
+       # assert_equal(2, pos.lnum)
+       # assert_equal(12, pos.col)
+   END
+   v9.CheckScriptSuccess(lines)
+ enddef
+ 
+ 
+ " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
*** ../vim-9.0.1030/src/version.c       2022-12-08 12:00:32.414054432 +0000
--- src/version.c       2022-12-08 15:27:58.250013050 +0000
***************
*** 697,698 ****
--- 697,700 ----
  {   /* Add new patch number below this line */
+ /**/
+     1031,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
264. You turn to the teletext page "surfing report" and are surprised that it
     is about sizes of waves and a weather forecast for seaside resorts.

 /// 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/20221208153300.93AE81C0678%40moolenaar.net.

Raspunde prin e-mail lui