]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.0.1074: class members are not supported yet v9.0.1074
authorBram Moolenaar <Bram@vim.org>
Sun, 18 Dec 2022 21:42:55 +0000 (21:42 +0000)
committerBram Moolenaar <Bram@vim.org>
Sun, 18 Dec 2022 21:42:55 +0000 (21:42 +0000)
Problem:    Class members are not supported yet.
Solution:   Add initial support for class members.

13 files changed:
src/errors.h
src/eval.c
src/proto/vim9instr.pro
src/structs.h
src/testdir/test_vim9_class.vim
src/version.c
src/vim9.h
src/vim9class.c
src/vim9cmds.c
src/vim9compile.c
src/vim9execute.c
src/vim9expr.c
src/vim9instr.c

index 23071e10a65d966bfae7731653b4a34f3fc6d542..345b4ac2e58be9d9ff26f6d185e55071b14a0d23 100644 (file)
@@ -3378,16 +3378,22 @@ EXTERN char e_cannot_get_object_member_type_from_initializer_str[]
        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_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_object_member_is_not_writable_str[]
-       INIT(= N_("E1335: Object member is not writable: %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
index 5baaa99062ccea557ff4eb2291055bb5a778f7c9..934d6402690f8344390018757f58127b6fde7ba0 100644 (file)
@@ -1193,19 +1193,21 @@ get_lval(
     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)
+       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 (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 (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,9 +1216,9 @@ get_lval(
 
        // 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)
+       if (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
+       else if (v_type == VAR_BLOB
                                             && lp->ll_tv->vval.v_blob == NULL)
            r = rettv_blob_alloc(lp->ll_tv);
        if (r == FAIL)
@@ -1278,7 +1280,7 @@ get_lval(
            // Optionally get the second index [ :expr].
            if (*p == ':')
            {
-               if (lp->ll_tv->v_type == VAR_DICT)
+               if (v_type == VAR_DICT)
                {
                    if (!quiet)
                        emsg(_(e_cannot_slice_dictionary));
@@ -1334,7 +1336,7 @@ get_lval(
            ++p;
        }
 
-       if (lp->ll_tv->v_type == VAR_DICT)
+       if (v_type == VAR_DICT)
        {
            if (len == -1)
            {
@@ -1435,7 +1437,7 @@ get_lval(
            clear_tv(&var1);
            lp->ll_tv = &lp->ll_di->di_tv;
        }
-       else if (lp->ll_tv->v_type == VAR_BLOB)
+       else if (v_type == VAR_BLOB)
        {
            long bloblen = blob_len(lp->ll_tv->vval.v_blob);
 
@@ -1466,7 +1468,7 @@ get_lval(
            lp->ll_tv = NULL;
            break;
        }
-       else if (lp->ll_tv->v_type == VAR_LIST)
+       else if (v_type == VAR_LIST)
        {
            /*
             * Get the number and item for the only or first index of the List.
@@ -1513,7 +1515,7 @@ get_lval(
        }
        else  // v_type == VAR_CLASS || v_type == VAR_OBJECT
        {
-           class_T *cl = (lp->ll_tv->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,23 +1523,28 @@ get_lval(
            if (cl != NULL)
            {
                lp->ll_valtype = NULL;
-               for (int i = 0; i < cl->class_obj_member_count; ++i)
+               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)
                {
-                   objmember_T *om = cl->class_obj_members + i;
-                   if (STRNCMP(om->om_name, key, p - key) == 0
-                                               && om->om_name[p - key] == NUL)
+                   ocmember_T *om = members + i;
+                   if (STRNCMP(om->ocm_name, key, p - key) == 0
+                                              && om->ocm_name[p - key] == NUL)
                    {
-                       switch (om->om_access)
+                       switch (om->ocm_access)
                        {
                            case ACCESS_PRIVATE:
-                                   semsg(_(e_cannot_access_private_object_member_str),
-                                           om->om_name);
+                                   semsg(_(e_cannot_access_private_member_str),
+                                                                om->ocm_name);
                                    return NULL;
                            case ACCESS_READ:
                                    if (!(flags & GLV_READ_ONLY))
                                    {
-                                       semsg(_(e_object_member_is_not_writable_str),
-                                               om->om_name);
+                                       semsg(_(e_member_is_not_writable_str),
+                                                                om->ocm_name);
                                        return NULL;
                                    }
                                    break;
@@ -1545,18 +1552,22 @@ get_lval(
                                    break;
                        }
 
-                       lp->ll_valtype = om->om_type;
+                       lp->ll_valtype = om->ocm_type;
 
-                       if (lp->ll_tv->v_type == VAR_OBJECT)
+                       if (v_type == VAR_OBJECT)
                            lp->ll_tv = ((typval_T *)(
                                            lp->ll_tv->vval.v_object + 1)) + i;
-                       // TODO: what about a class?
+                       else
+                           lp->ll_tv = &cl->class_members_tv[i];
                        break;
                    }
                }
                if (lp->ll_valtype == NULL)
                {
-                   semsg(_(e_object_member_not_found_str), key);
+                   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,8 +5947,8 @@ echo_string_core(
                    {
                        if (i > 0)
                            ga_concat(&ga, (char_u *)", ");
-                       objmember_T *m = &cl->class_obj_members[i];
-                       ga_concat(&ga, m->om_name);
+                       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(
index eb7baabf0406223079365bcecd496cf1caf4cde4..c242148e31604883733e51c67fd537879791471c 100644 (file)
@@ -32,6 +32,7 @@ int generate_GETITEM(cctx_T *cctx, int index, int with_op);
 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,7 +75,7 @@ int generate_RANGE(cctx_T *cctx, char_u *range);
 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 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);
index 2f9a78268a30a4ac2bfe6f50c091e045fbcec552..f4bd386393447b4207b4d15eec7882e615da49b7 100644 (file)
@@ -1387,6 +1387,7 @@ typedef signed char int8_T;
 
 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,14 +1467,14 @@ typedef enum {
 } omacc_T;
 
 /*
- * Entry for an object member variable.
+ * Entry for an object or class member variable.
  */
 typedef struct {
-    char_u     *om_name;   // allocated
-    omacc_T    om_access;
-    type_T     *om_type;
-    char_u     *om_init;   // allocated
-} objmember_T;
+    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,14 +1482,25 @@ struct class_S
     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;
-    objmember_T        *class_obj_members;     // allocated
+    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_T     class_type;             // type used for the class
     type_T     class_object_type;      // same as class_type but VAR_OBJECT
 };
 
@@ -1513,7 +1525,7 @@ struct object_S
 /*
  * Structure to hold an internal variable without a name.
  */
-typedef struct
+struct typval_S
 {
     vartype_T  v_type;
     char       v_lock;     // see below: VAR_LOCKED, VAR_FIXED
@@ -1534,7 +1546,7 @@ typedef struct
        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
index d30c7b468b1bbf70fea98354d2a9c6ed947d458d..5ec606e0969caffa40e93282a05927facbb14269 100644 (file)
@@ -306,6 +306,30 @@ def Test_class_object_member_access()
       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
index 0fbc65789a8824a4a8f0f9488efa458ef268cfb5..0c7c50f99f4136bad3e86dd2883b2a0b4addb342 100644 (file)
@@ -695,6 +695,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1074,
 /**/
     1073,
 /**/
index bddeb1559512348d10f4494a1b3658fd10f3e2e2..f8e5e5634f6ba79fa7ed6bb0b3beb6c2f12cb390 100644 (file)
@@ -36,6 +36,8 @@ typedef enum {
     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,6 +478,12 @@ typedef struct {
     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,6 +536,7 @@ struct isn_S {
        deferins_T          defer;
        echowin_T           echowin;
        construct_T         construct;
+       classmember_T       classmember;
     } isn_arg;
 };
 
@@ -538,7 +547,9 @@ struct dfunc_S {
     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
+    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,6 +746,7 @@ typedef enum {
     dest_window,
     dest_tab,
     dest_vimvar,
+    dest_class_member,
     dest_script,
     dest_reg,
     dest_expr,
@@ -766,6 +778,10 @@ typedef struct {
     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;
 
index da44c3ddf451e2f920c753a442f165d64fa7adea..4a975858190a5d06f9167735565750d78fb38c96 100644 (file)
 # include "vim9.h"
 #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".
  */
@@ -64,16 +212,23 @@ ex_class(exarg_T *eap)
     //    extends SomeClass
     //    implements SomeInterface
     //    specifies SomeInterface
-    //    check nothing follows
-
-    // TODO: handle "is_export" if it is set
+    //    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(objmember_T), 10);
+    ga_init2(&objmembers, sizeof(ocmember_T), 10);
 
     // Growarray with object methods declared in the class.
     garray_T objmethods;
@@ -92,12 +247,6 @@ ex_class(exarg_T *eap)
            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))
        {
@@ -110,9 +259,6 @@ ex_class(exarg_T *eap)
            break;
        }
 
-       // "this._varname"
-       // "this.varname"
-       // "public this.varname"
        int has_public = FALSE;
        if (checkforcmd(&p, "public", 3))
        {
@@ -124,12 +270,17 @@ ex_class(exarg_T *eap)
            has_public = TRUE;
            p = skipwhite(line + 6);
 
-           if (STRNCMP(p, "this", 4) != 0)
+           if (STRNCMP(p, "this", 4) != 0 && STRNCMP(p, "static", 6) != 0)
            {
-               emsg(_(e_public_must_be_followed_by_this));
+               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,95 +289,52 @@ ex_class(exarg_T *eap)
                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;
+           char_u *varname_end = NULL;
            type_T *type = NULL;
-           if (*colon == ':')
+           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)
            {
-               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;
+               vim_free(init_expr);
+               break;
            }
+       }
 
-           char_u *expr_start = skipwhite(type_arg);
-           char_u *expr_end = expr_start;
-           if (type == NULL && *expr_start != '=')
+       // class members and methods
+       else if (checkforcmd(&p, "static", 6))
+       {
+           p = skipwhite(p);
+           if (checkforcmd(&p, "def", 3))
            {
-               emsg(_(e_type_or_initialization_required));
-               break;
+               // TODO: class method
+               //        static def someMethod()
+               //        enddef
+               //        static def <Tval> someMethod()
+               //        enddef
            }
-
-           if (*expr_start == '=')
+           else
            {
-               if (!VIM_ISWHITE(expr_start[-1]) || !VIM_ISWHITE(expr_start[1]))
-               {
-                   semsg(_(e_white_space_required_before_and_after_str_at_str),
-                                                               "=", type_arg);
+               // 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;
-               }
-               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)
+               if (add_member(&classmembers, varname, varname_end,
+                                         has_public, type, init_expr) == FAIL)
                {
-                   // 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;
-                   }
+                   vim_free(init_expr);
+                   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:
@@ -238,12 +346,8 @@ ex_class(exarg_T *eap)
        //        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;
@@ -282,22 +386,52 @@ ex_class(exarg_T *eap)
     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;
 
-       // 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))
+       // 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;
-       mch_memmove(cl->class_obj_members, objmembers.ga_data,
-                                     sizeof(objmember_T) * objmembers.ga_len);
-       vim_free(objmembers.ga_data);
+
+       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,8 +452,8 @@ ex_class(exarg_T *eap)
                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);
+               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,6 +489,7 @@ ex_class(exarg_T *eap)
            }
        }
 
+       // 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,13 +513,7 @@ ex_class(exarg_T *eap)
        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
+       // - Fill hashtab with object members and methods ?
 
        // Add the class to the script-local variables.
        typval_T tv;
@@ -404,13 +533,20 @@ cleanup:
        vim_free(cl);
     }
 
-    for (int i = 0; i < objmembers.ga_len; ++i)
+    for (int round = 1; round <= 2; ++round)
     {
-       objmember_T *m = ((objmember_T *)objmembers.ga_data) + i;
-       vim_free(m->om_name);
-       vim_free(m->om_init);
+       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);
     }
-    ga_clear(&objmembers);
 
     for (int i = 0; i < objmethods.ga_len; ++i)
     {
@@ -437,11 +573,11 @@ class_member_type(
 
     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)
+       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->om_type;
+           return m->ocm_type;
        }
     }
     return &t_any;
@@ -572,13 +708,12 @@ class_object_index(
     {
        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)
+           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_object_member_str),
-                                                                  m->om_name);
+                   semsg(_(e_cannot_access_private_member_str), m->ocm_name);
                    return FAIL;
                }
 
@@ -597,7 +732,31 @@ class_object_index(
        semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name);
     }
 
-    // TODO: class member
+    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,15 +867,29 @@ copy_class(typval_T *from, typval_T *to)
     void
 class_unref(class_T *cl)
 {
-    if (cl != NULL && --cl->class_refcount <= 0)
+    if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
     {
-       vim_free(cl->class_name);
+       // 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)
        {
-           objmember_T *m = &cl->class_obj_members[i];
-           vim_free(m->om_name);
-           vim_free(m->om_init);
+           ocmember_T *m = &cl->class_obj_members[i];
+           vim_free(m->ocm_name);
+           vim_free(m->ocm_init);
        }
        vim_free(cl->class_obj_members);
 
index ecf31dca5bc9c24f251dff5f34f54708f628bba4..4fbae4fbd632ecbea546e29d60b79c2f40828e24 100644 (file)
@@ -1013,7 +1013,7 @@ compile_for(char_u *arg_start, cctx_T *cctx)
            if (dest != dest_local)
            {
                if (generate_store_var(cctx, dest, opt_flags, vimvaridx,
-                                                    0, 0, type, name) == FAIL)
+                                                    type, name, NULL) == FAIL)
                    goto failed;
            }
            else if (varlen == 1 && *arg == '_')
index ac321a90e30910648fce9c982f31f483cff3742c..44403ece292b5270147382db6a036c07287e69b6 100644 (file)
@@ -301,6 +301,28 @@ script_var_exists(char_u *name, size_t len, cctx_T *cctx, cstack_T *cstack)
     return FAIL;
 }
 
+/*
+ * 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,6 +338,7 @@ variable_exists(char_u *name, size_t len, cctx_T *cctx)
                        && (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,6 +376,9 @@ check_defined(
     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,14 +1221,12 @@ assignment_len(char_u *p, int *heredoc)
  * 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)
+generate_loadvar(cctx_T *cctx, lhs_T *lhs)
 {
-    switch (dest)
+    char_u     *name = lhs->lhs_name;
+    type_T     *type = lhs->lhs_type;
+
+    switch (lhs->lhs_dest)
     {
        case dest_option:
        case dest_func_option:
@@ -1245,6 +1269,7 @@ generate_loadvar(
        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,6 +1277,10 @@ generate_loadvar(
                    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,7 +1562,9 @@ compile_lhs(
 
            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,6 +1580,7 @@ compile_lhs(
                    lhs->lhs_lvar = &lhs->lhs_arg_lvar;
                }
            }
+
            if (lhs->lhs_lvar != NULL)
            {
                if (is_decl)
@@ -1557,6 +1589,12 @@ compile_lhs(
                    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,8 +2003,7 @@ compile_load_lhs(
            return FAIL;
     }
     else
-       generate_loadvar(cctx, lhs->lhs_dest, lhs->lhs_name,
-                                                lhs->lhs_lvar, lhs->lhs_type);
+       generate_loadvar(cctx, lhs);
     return OK;
 }
 
@@ -2998,20 +3035,20 @@ compile_def_function(
 
            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)
+               ocmember_T *m = &ufunc->uf_class->class_obj_members[i];
+               if (m->ocm_init != NULL)
                {
-                   char_u *expr = m->om_init;
+                   char_u *expr = m->ocm_init;
                    if (compile_expr0(&expr, &cctx) == FAIL)
                        goto erret;
-                   if (!ends_excmd2(m->om_init, expr))
+                   if (!ends_excmd2(m->ocm_init, expr))
                    {
                        semsg(_(e_trailing_characters_str), expr);
                        goto erret;
                    }
                }
                else
-                   push_default_value(&cctx, m->om_type->tt_type,
+                   push_default_value(&cctx, m->ocm_type->tt_type,
                                                                  FALSE, NULL);
                generate_STORE_THIS(&cctx, i);
            }
@@ -3792,6 +3829,13 @@ delete_def_function_contents(dfunc_T *dfunc, int mark_deleted)
 {
     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,14 +3844,12 @@ delete_def_function_contents(dfunc_T *dfunc, int mark_deleted)
        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)
@@ -3815,7 +3857,6 @@ delete_def_function_contents(dfunc_T *dfunc, int mark_deleted)
        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
 
@@ -3823,6 +3864,8 @@ delete_def_function_contents(dfunc_T *dfunc, int mark_deleted)
        dfunc->df_deleted = TRUE;
     if (dfunc->df_ufunc != NULL)
        dfunc->df_ufunc->uf_def_status = UF_NOT_COMPILED;
+
+    dfunc->df_delete_busy = FALSE;
 }
 
 /*
index 1a8058a4bec517a674a96d4c618524682fe03788..cdaeb5b844557589547b2234f167155542c8575f 100644 (file)
@@ -3817,6 +3817,27 @@ exec_instructions(ectx_T *ectx)
                    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,6 +6424,19 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
                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,
index 6c9385c8381c58af27dfc268417cd082a888e227..7ec3ee9de760954d4142caef29e543ad5d80283a 100644 (file)
@@ -278,17 +278,16 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
     {
        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)
+           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_object_member_str),
-                                                                  m->om_name);
+                   semsg(_(e_cannot_access_private_member_str), m->ocm_name);
                    return FAIL;
                }
 
-               generate_GET_OBJ_MEMBER(cctx, i, m->om_type);
+               generate_GET_OBJ_MEMBER(cctx, i, m->ocm_type);
 
                *arg = name_end;
                return OK;
index 47114f08cc47dda217833365acefcd1cbfad561b..8bd2485f6c6240f07191b5b87fe7f7027405a6a7 100644 (file)
@@ -956,6 +956,38 @@ generate_STORE(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name)
     return OK;
 }
 
+/*
+ * 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.
  */
@@ -2114,6 +2146,7 @@ generate_undo_cmdmods(cctx_T *cctx)
 
 /*
  * Generate a STORE instruction for "dest", not being "dest_local".
+ * "lhs" might be NULL.
  * Return FAIL when out of memory.
  */
     int
@@ -2122,10 +2155,9 @@ generate_store_var(
        assign_dest_T   dest,
        int             opt_flags,
        int             vimvaridx,
-       int             scriptvar_idx,
-       int             scriptvar_sid,
        type_T          *type,
-       char_u          *name)
+       char_u          *name,
+       lhs_T           *lhs)
 {
     switch (dest)
     {
@@ -2156,9 +2188,11 @@ generate_store_var(
        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;
+               isntype_T   isn_type = ISN_STORES;
 
                if (SCRIPT_ID_VALID(scriptvar_sid)
                         && SCRIPT_ITEM(scriptvar_sid)->sn_import_autoload
@@ -2177,6 +2211,10 @@ generate_store_var(
            }
            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,8 +2248,7 @@ generate_store_lhs(cctx_T *cctx, lhs_T *lhs, int instr_count, int is_decl)
     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);
+                           lhs->lhs_type, lhs->lhs_name, lhs);
 
     if (lhs->lhs_lvar != NULL)
     {
@@ -2422,6 +2459,11 @@ delete_instr(isn_T *isn)
            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;