]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.0.1045: in a class object members cannot be initialized v9.0.1045
authorBram Moolenaar <Bram@vim.org>
Sat, 10 Dec 2022 18:42:12 +0000 (18:42 +0000)
committerBram Moolenaar <Bram@vim.org>
Sat, 10 Dec 2022 18:42:12 +0000 (18:42 +0000)
Problem:    In a class object members cannot be initialized.
Solution:   Support initializing object members. Make "dissassemble" work on
            an object method.

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

index 0a0a1e11d66e58e1f1702ab2aa9d063395fc3224..b19cd9483560a315d03d7baa8fc287a6425f8a27 100644 (file)
@@ -1193,9 +1193,8 @@ get_lval(
     var2.v_type = VAR_UNKNOWN;
     while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.'))
     {
-       int r = OK;
-
-       if (*p == '.' && lp->ll_tv->v_type != VAR_DICT)
+       if (*p == '.' && lp->ll_tv->v_type != VAR_DICT
+                     && lp->ll_tv->v_type != VAR_CLASS)
        {
            if (!quiet)
                semsg(_(e_dot_can_only_be_used_on_dictionary_str), name);
@@ -1203,7 +1202,8 @@ get_lval(
        }
        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_BLOB
+               && lp->ll_tv->v_type != VAR_CLASS)
        {
            if (!quiet)
                emsg(_(e_can_only_index_list_dictionary_or_blob));
@@ -1211,6 +1211,7 @@ 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)
            r = rettv_list_alloc(lp->ll_tv);
        else if (lp->ll_tv->v_type == VAR_BLOB
@@ -1463,7 +1464,7 @@ get_lval(
            lp->ll_tv = NULL;
            break;
        }
-       else
+       else if (lp->ll_tv->v_type == VAR_LIST)
        {
            /*
             * Get the number and item for the only or first index of the List.
@@ -1508,6 +1509,11 @@ get_lval(
 
            lp->ll_tv = &lp->ll_li->li_tv;
        }
+       else  // v_type == VAR_CLASS
+       {
+           // TODO: check object members and methods if
+           // "key" points name start, "p" to the end
+       }
     }
 
     clear_tv(&var1);
index 9a6b23fcb44418e303de2c019a7fa767a7c4282f..8ae20eba81d81d3c99fa6d2c4172bcd36a44b0ce 100644 (file)
@@ -5,6 +5,7 @@ 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);
+ufunc_T *find_class_func(char_u **arg);
 void copy_object(typval_T *from, typval_T *to);
 void object_unref(object_T *obj);
 void copy_class(typval_T *from, typval_T *to);
index d0b0d86a39458c6d81ce2a56df8227198df0ded5..e26ca259dec6cdba9cf4890002de905e9f571f4f 100644 (file)
@@ -4,7 +4,8 @@ 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 generate_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type);
+int generate_GET_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type);
+int generate_STORE_THIS(cctx_T *cctx, int idx);
 int may_generate_2STRING(int offset, int tolerant, cctx_T *cctx);
 int generate_add_instr(cctx_T *cctx, vartype_T vartype, type_T *type1, type_T *type2, exprtype_T expr_type);
 vartype_T operator_type(type_T *type1, type_T *type2);
index 1395a202781afd9d4f8f3454e32c9ef1aa008e81..46f9b0c3d1fa5b7c078d33fcf92404d5343b1cb5 100644 (file)
@@ -1463,8 +1463,9 @@ typedef struct {
  * Entry for an object member variable.
  */
 typedef struct {
-    char_u     *om_name;  // allocated
+    char_u     *om_name;   // allocated
     type_T     *om_type;
+    char_u     *om_init;   // allocated
 } objmember_T;
 
 // "class_T": used for v_class of typval of VAR_CLASS
index 2ed83085816521fbdf8ff25cc719020e3242e011..b18572d4e1859acb0dfdf7f63109a8a01e1823b2 100644 (file)
@@ -129,7 +129,7 @@ def Test_class_basic()
 
       class TextPosition
         this.lnum: number
-       this.col: number
+        this.col: number
 
         def ToString(): string
           return $'({this.lnum}, {this.col})'
@@ -147,5 +147,40 @@ def Test_class_basic()
   v9.CheckScriptSuccess(lines)
 enddef
 
+def Test_class_member_initializer()
+  var lines =<< trim END
+      vim9script
+
+      class TextPosition
+        this.lnum: number = 1
+        this.col: number = 1
+
+        def new(lnum: number)
+          this.lnum = lnum
+        enddef
+      endclass
+
+      var pos = TextPosition.new(3)
+      assert_equal(3, pos.lnum)
+      assert_equal(1, pos.col)
+
+      var instr = execute('disassemble TextPosition.new')
+      assert_match('new\_s*' ..
+            '0 NEW TextPosition size 72\_s*' ..
+            '\d PUSHNR 1\_s*' ..
+            '\d STORE_THIS 0\_s*' ..
+            '\d PUSHNR 1\_s*' ..
+            '\d STORE_THIS 1\_s*' ..
+            'this.lnum = lnum\_s*' ..
+            '\d LOAD arg\[-1]\_s*' ..
+            '\d PUSHNR 0\_s*' ..
+            '\d LOAD $0\_s*' ..
+            '\d\+ STOREINDEX object\_s*' ..
+            '\d\+ RETURN object.*',
+            instr)
+  END
+  v9.CheckScriptSuccess(lines)
+enddef
+
 
 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
index ac7c4fde512dc2033dce53a99a40b16946e385b2..9ac1274a48b127cddcd8fa3983ac8ffcfe973864 100644 (file)
@@ -4047,6 +4047,12 @@ trans_function_name(
            name = vim_strsave(lv.ll_tv->vval.v_string);
            *pp = end;
        }
+       else if (lv.ll_tv->v_type == VAR_CLASS
+                                            && lv.ll_tv->vval.v_class != NULL)
+       {
+           name = vim_strsave(lv.ll_tv->vval.v_class->class_name);
+           *pp = end;
+       }
        else if (lv.ll_tv->v_type == VAR_PARTIAL
                                          && lv.ll_tv->vval.v_partial != NULL)
        {
@@ -5240,8 +5246,17 @@ find_func_by_name(char_u *name, compiletype_T *compile_type)
        fname = vim_strnsave(name, arg - name);
     }
     else
+    {
+       // First try finding a method in a class, find_func_by_name() will give
+       // an error if the function is not found.
+       ufunc = find_class_func(&arg);
+       if (ufunc != NULL)
+           return ufunc;
+
        fname = trans_function_name(&arg, &is_global, FALSE,
-                     TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD, NULL, NULL, NULL);
+                     TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DECL,
+                     NULL, NULL, NULL);
+    }
     if (fname == NULL)
     {
        semsg(_(e_invalid_argument_str), name);
index bc22ab127722387c4d2aa8b36f73cab5390dc86c..fa77295ec8311cc5097cb394f7db975bff67a8a2 100644 (file)
@@ -695,6 +695,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1045,
 /**/
     1044,
 /**/
index 91ff6caa38d955bb103fa9b493c46cf55bf46404..c1d11fc7091c604a4b6210b2758c3c3e15bd872f 100644 (file)
@@ -33,7 +33,9 @@ typedef enum {
     ISN_SOURCE,            // source autoload script, isn_arg.number is the script ID
     ISN_INSTR,     // instructions compiled from expression
     ISN_CONSTRUCT,  // construct an object, using contstruct_T
-    ISN_OBJ_MEMBER, // object member, index is isn_arg.number
+    ISN_GET_OBJ_MEMBER, // object member, index is isn_arg.number
+    ISN_STORE_THIS, // store value in "this" object member, index is
+                   // isn_arg.number
 
     // get and set variables
     ISN_LOAD,      // push local variable isn_arg.number
index 8f2f09f3b5ad736f250a1e48d98a6b57f38ec7de..c8cea3c1a5b0138445054b571ba9430eee8a8b56 100644 (file)
@@ -147,12 +147,30 @@ ex_class(exarg_T *eap)
            if (type == NULL)
                break;
 
+           char_u *expr_start = skipwhite(type_arg);
+           if (*expr_start == '=' && (!VIM_ISWHITE(expr_start[-1])
+                                              || !VIM_ISWHITE(expr_start[1])))
+           {
+               semsg(_(e_white_space_required_before_and_after_str_at_str),
+                                                               "=", type_arg);
+               break;
+           }
+           expr_start = skipwhite(expr_start + 1);
+
+           char_u *expr_end = expr_start;
+           evalarg_T   evalarg;
+           init_evalarg(&evalarg);
+           skip_expr(&expr_end, &evalarg);
+           clear_evalarg(&evalarg, NULL);
+
            if (ga_grow(&objmembers, 1) == FAIL)
                break;
            objmember_T *m = ((objmember_T *)objmembers.ga_data)
                                                          + objmembers.ga_len;
            m->om_name = vim_strnsave(varname, varname_end - varname);
            m->om_type = type;
+           if (expr_end > expr_start)
+               m->om_init = vim_strnsave(expr_start, expr_end - expr_start);
            ++objmembers.ga_len;
        }
 
@@ -190,6 +208,9 @@ ex_class(exarg_T *eap)
            // TODO: how about errors?
            if (uf != NULL && ga_grow(&objmethods, 1) == OK)
            {
+               if (STRNCMP(uf->uf_name, "new", 3) == 0)
+                   uf->uf_flags |= FC_NEW;
+
                ((ufunc_T **)objmethods.ga_data)[objmethods.ga_len] = uf;
                ++objmethods.ga_len;
            }
@@ -333,6 +354,7 @@ cleanup:
     {
        objmember_T *m = ((objmember_T *)objmembers.ga_data) + i;
        vim_free(m->om_name);
+       vim_free(m->om_init);
     }
     ga_clear(&objmembers);
 
@@ -519,6 +541,52 @@ class_object_index(
     return FAIL;
 }
 
+/*
+ * If "arg" points to a class or object method, return it.
+ * Otherwise return NULL.
+ */
+    ufunc_T *
+find_class_func(char_u **arg)
+{
+    char_u *name = *arg;
+    char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
+    if (name_end == name || *name_end != '.')
+       return NULL;
+
+    size_t len = name_end - name;
+    typval_T tv;
+    tv.v_type = VAR_UNKNOWN;
+    if (eval_variable(name, len, 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
+       return NULL;
+    if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
+    {
+       clear_tv(&tv);
+       return NULL;
+    }
+
+    class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
+                                                : tv.vval.v_object->obj_class;
+    if (cl == NULL)
+       return NULL;
+    char_u *fname = name_end + 1;
+    char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
+    if (fname_end == fname)
+       return NULL;
+    len = fname_end - fname;
+
+    for (int i = 0; i < cl->class_obj_method_count; ++i)
+    {
+       ufunc_T *fp = cl->class_obj_methods[i];
+       // Use a separate pointer to avoid that ASAN complains about
+       // uf_name[] only being 4 characters.
+       char_u *ufname = (char_u *)fp->uf_name;
+       if (STRNCMP(fname, ufname, len) == 0 && ufname[len] == NUL)
+           return fp;
+    }
+
+    return NULL;
+}
+
 /*
  * Make a copy of an object.
  */
@@ -585,6 +653,7 @@ class_unref(class_T *cl)
        {
            objmember_T *m = &cl->class_obj_members[i];
            vim_free(m->om_name);
+           vim_free(m->om_init);
        }
        vim_free(cl->class_obj_members);
 
index 08e273dd024600feeaf6ce2b32cfc0c28461b6ee..e27bf29c6f5ca852c331a084410efc7b15527e88 100644 (file)
@@ -2117,6 +2117,71 @@ compile_assign_unlet(
     return OK;
 }
 
+/*
+ * Generate an instruction to push the default value for "vartype".
+ * if "dest_local" is TRUE then for some types no instruction is generated.
+ * "skip_store" is set to TRUE if no PUSH instruction is generated.
+ * Returns OK or FAIL.
+ */
+    static int
+push_default_value(
+       cctx_T      *cctx,
+       vartype_T   vartype,
+       int         dest_is_local,
+       int         *skip_store)
+{
+    int r = OK;
+
+    switch (vartype)
+    {
+       case VAR_BOOL:
+           r = generate_PUSHBOOL(cctx, VVAL_FALSE);
+           break;
+       case VAR_FLOAT:
+           r = generate_PUSHF(cctx, 0.0);
+           break;
+       case VAR_STRING:
+           r = generate_PUSHS(cctx, NULL);
+           break;
+       case VAR_BLOB:
+           r = generate_PUSHBLOB(cctx, blob_alloc());
+           break;
+       case VAR_FUNC:
+           r = generate_PUSHFUNC(cctx, NULL, &t_func_void, TRUE);
+           break;
+       case VAR_LIST:
+           r = generate_NEWLIST(cctx, 0, FALSE);
+           break;
+       case VAR_DICT:
+           r = generate_NEWDICT(cctx, 0, FALSE);
+           break;
+       case VAR_JOB:
+           r = generate_PUSHJOB(cctx);
+           break;
+       case VAR_CHANNEL:
+           r = generate_PUSHCHANNEL(cctx);
+           break;
+       case VAR_NUMBER:
+       case VAR_UNKNOWN:
+       case VAR_ANY:
+       case VAR_PARTIAL:
+       case VAR_VOID:
+       case VAR_INSTR:
+       case VAR_CLASS:
+       case VAR_OBJECT:
+       case VAR_SPECIAL:  // cannot happen
+           // This is skipped for local variables, they are always
+           // initialized to zero.  But in a "for" or "while" loop
+           // the value may have been changed.
+           if (dest_is_local && !inside_loop_scope(cctx))
+               *skip_store = TRUE;
+           else
+               r = generate_PUSHNR(cctx, 0);
+           break;
+    }
+    return r;
+}
+
 /*
  * Compile declaration and assignment:
  * "let name"
@@ -2462,62 +2527,12 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
            }
            else
            {
-               int r = OK;
-
                // variables are always initialized
                if (GA_GROW_FAILS(instr, 1))
                    goto theend;
-               switch (lhs.lhs_member_type->tt_type)
-               {
-                   case VAR_BOOL:
-                       r = generate_PUSHBOOL(cctx, VVAL_FALSE);
-                       break;
-                   case VAR_FLOAT:
-                       r = generate_PUSHF(cctx, 0.0);
-                       break;
-                   case VAR_STRING:
-                       r = generate_PUSHS(cctx, NULL);
-                       break;
-                   case VAR_BLOB:
-                       r = generate_PUSHBLOB(cctx, blob_alloc());
-                       break;
-                   case VAR_FUNC:
-                       r = generate_PUSHFUNC(cctx, NULL, &t_func_void, TRUE);
-                       break;
-                   case VAR_LIST:
-                       r = generate_NEWLIST(cctx, 0, FALSE);
-                       break;
-                   case VAR_DICT:
-                       r = generate_NEWDICT(cctx, 0, FALSE);
-                       break;
-                   case VAR_JOB:
-                       r = generate_PUSHJOB(cctx);
-                       break;
-                   case VAR_CHANNEL:
-                       r = generate_PUSHCHANNEL(cctx);
-                       break;
-                   case VAR_NUMBER:
-                   case VAR_UNKNOWN:
-                   case VAR_ANY:
-                   case VAR_PARTIAL:
-                   case VAR_VOID:
-                   case VAR_INSTR:
-                   case VAR_CLASS:
-                   case VAR_OBJECT:
-                   case VAR_SPECIAL:  // cannot happen
-                       // This is skipped for local variables, they are always
-                       // initialized to zero.  But in a "for" or "while" loop
-                       // the value may have been changed.
-                       if (lhs.lhs_dest == dest_local
-                                                  && !inside_loop_scope(cctx))
-                           skip_store = TRUE;
-                       else
-                       {
-                           instr_count = instr->ga_len;
-                           r = generate_PUSHNR(cctx, 0);
-                       }
-                       break;
-               }
+               instr_count = instr->ga_len;
+               int r = push_default_value(cctx, lhs.lhs_member_type->tt_type,
+                                     lhs.lhs_dest == dest_local, &skip_store);
                if (r == FAIL)
                    goto theend;
            }
@@ -2946,9 +2961,32 @@ compile_def_function(
                                                 vim_strsave((char_u *)"this");
        ++dfunc->df_var_names.ga_len;
 
-       // In the constructor allocate memory for the object.
+       // In the constructor allocate memory for the object and initialize the
+       // object members.
        if ((ufunc->uf_flags & FC_NEW) == FC_NEW)
+       {
            generate_CONSTRUCT(&cctx, ufunc->uf_class);
+
+           for (int i = 0; i < ufunc->uf_class->class_obj_member_count; ++i)
+           {
+               objmember_T *m = &ufunc->uf_class->class_obj_members[i];
+               if (m->om_init != NULL)
+               {
+                   char_u *expr = m->om_init;
+                   if (compile_expr0(&expr, &cctx) == FAIL)
+                       goto erret;
+                   if (!ends_excmd2(m->om_init, expr))
+                   {
+                       semsg(_(e_trailing_characters_str), expr);
+                       goto erret;
+                   }
+               }
+               else
+                   push_default_value(&cctx, m->om_type->tt_type,
+                                                                 FALSE, NULL);
+               generate_STORE_THIS(&cctx, i);
+           }
+       }
     }
 
     if (ufunc->uf_def_args.ga_len > 0)
@@ -3564,7 +3602,10 @@ nextline:
        // Return void if there is no return at the end.
        // For a constructor return the object.
        if ((ufunc->uf_flags & FC_NEW) == FC_NEW)
+       {
            generate_instr(&cctx, ISN_RETURN_OBJECT);
+           ufunc->uf_ret_type = &ufunc->uf_class->class_object_type;
+       }
        else
            generate_instr(&cctx, ISN_RETURN_VOID);
     }
index 96983bbdc35687e5637371e900f5e60bacada0a5..6a6ec41e1a772d7fa6bb8a0896be760b3717d195 100644 (file)
@@ -3009,7 +3009,7 @@ exec_instructions(ectx_T *ectx)
        iptr = &ectx->ec_instr[ectx->ec_iidx++];
        switch (iptr->isn_type)
        {
-           // Constructor, new() method.
+           // Constructor, first instruction in a new() method.
            case ISN_CONSTRUCT:
                // "this" is always the local variable at index zero
                tv = STACK_TV_VAR(0);
@@ -5114,7 +5114,7 @@ exec_instructions(ectx_T *ectx)
                }
                break;
 
-           case ISN_OBJ_MEMBER:
+           case ISN_GET_OBJ_MEMBER:
                {
                    tv = STACK_TV_BOT(-1);
                    if (tv->v_type != VAR_OBJECT)
@@ -5143,6 +5143,18 @@ exec_instructions(ectx_T *ectx)
                }
                break;
 
+           case ISN_STORE_THIS:
+               {
+                   int idx = iptr->isn_arg.number;
+                   object_T *obj = STACK_TV_VAR(0)->vval.v_object;
+                   // the members are located right after the object struct
+                   typval_T *mtv = ((typval_T *)(obj + 1)) + idx;
+                   clear_tv(mtv);
+                   *mtv = *STACK_TV_BOT(-1);
+                   --ectx->ec_stack.ga_len;
+               }
+               break;
+
            case ISN_CLEARDICT:
                dict_stack_drop();
                break;
@@ -6805,7 +6817,9 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
            case ISN_MEMBER: smsg("%s%4d MEMBER", pfx, current); break;
            case ISN_STRINGMEMBER: smsg("%s%4d MEMBER %s", pfx, current,
                                                  iptr->isn_arg.string); break;
-           case ISN_OBJ_MEMBER: smsg("%s%4d OBJ_MEMBER %d", pfx, current,
+           case ISN_GET_OBJ_MEMBER: smsg("%s%4d OBJ_MEMBER %d", pfx, current,
+                                            (int)iptr->isn_arg.number); break;
+           case ISN_STORE_THIS: smsg("%s%4d STORE_THIS %d", pfx, current,
                                             (int)iptr->isn_arg.number); break;
            case ISN_CLEARDICT: smsg("%s%4d CLEARDICT", pfx, current); break;
            case ISN_USEDICT: smsg("%s%4d USEDICT", pfx, current); break;
index ca2d81ce640e914ce659177100c998336f044efc..c8054e52d0055c819d2fb30a6417919cba86a03e 100644 (file)
@@ -253,7 +253,6 @@ compile_member(int is_slice, int *keeping_dict, cctx_T *cctx)
 /*
  * Compile ".member" coming after an object or class.
  */
-
     static int
 compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
 {
@@ -282,7 +281,7 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
            objmember_T *m = &cl->class_obj_members[i];
            if (STRNCMP(name, m->om_name, len) == 0 && m->om_name[len] == NUL)
            {
-               generate_OBJ_MEMBER(cctx, i, m->om_type);
+               generate_GET_OBJ_MEMBER(cctx, i, m->om_type);
 
                *arg = name_end;
                return OK;
index 88bd2ca42a3e36a0400249ed5b8d510e3e00d2fb..49f8c5212602f84fe189807489bf9fc1275155c1 100644 (file)
@@ -132,15 +132,16 @@ generate_CONSTRUCT(cctx_T *cctx, class_T *cl)
 }
 
 /*
- * Generate ISN_OBJ_MEMBER - access object member by indes.
+ * Generate ISN_GET_OBJ_MEMBER - access member of object at bottom of stack by
+ * index.
  */
     int
-generate_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type)
+generate_GET_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type)
 {
     RETURN_OK_IF_SKIP(cctx);
 
     // drop the object type
-    isn_T *isn = generate_instr_drop(cctx, ISN_OBJ_MEMBER, 1);
+    isn_T *isn = generate_instr_drop(cctx, ISN_GET_OBJ_MEMBER, 1);
     if (isn == NULL)
        return FAIL;
 
@@ -148,6 +149,24 @@ generate_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type)
     return push_type_stack2(cctx, type, &t_any);
 }
 
+/*
+ * Generate ISN_STORE_THIS - store value in member of "this" object with member
+ * index "idx".
+ */
+    int
+generate_STORE_THIS(cctx_T *cctx, int idx)
+{
+    RETURN_OK_IF_SKIP(cctx);
+
+    // drop the value type
+    isn_T *isn = generate_instr_drop(cctx, ISN_STORE_THIS, 1);
+    if (isn == NULL)
+       return FAIL;
+
+    isn->isn_arg.number = idx;
+    return OK;
+}
+
 /*
  * If type at "offset" isn't already VAR_STRING then generate ISN_2STRING.
  * But only for simple types.
@@ -2458,6 +2477,7 @@ delete_instr(isn_T *isn)
        case ISN_FINISH:
        case ISN_FOR:
        case ISN_GETITEM:
+       case ISN_GET_OBJ_MEMBER:
        case ISN_JUMP:
        case ISN_JUMP_IF_ARG_SET:
        case ISN_LISTAPPEND:
@@ -2477,7 +2497,6 @@ delete_instr(isn_T *isn)
        case ISN_NEWDICT:
        case ISN_NEWLIST:
        case ISN_NEWPARTIAL:
-       case ISN_OBJ_MEMBER:
        case ISN_OPANY:
        case ISN_OPFLOAT:
        case ISN_OPNR:
@@ -2495,8 +2514,8 @@ delete_instr(isn_T *isn)
        case ISN_REDIREND:
        case ISN_REDIRSTART:
        case ISN_RETURN:
-       case ISN_RETURN_VOID:
        case ISN_RETURN_OBJECT:
+       case ISN_RETURN_VOID:
        case ISN_SHUFFLE:
        case ISN_SLICE:
        case ISN_SOURCE:
@@ -2504,6 +2523,7 @@ delete_instr(isn_T *isn)
        case ISN_STOREINDEX:
        case ISN_STORENR:
        case ISN_STOREOUTER:
+       case ISN_STORE_THIS:
        case ISN_STORERANGE:
        case ISN_STOREREG:
        case ISN_STOREV: