]> granicus.if.org Git - vim/commitdiff
patch 8.2.0200: Vim9 script commands not sufficiently tested v8.2.0200
authorBram Moolenaar <Bram@vim.org>
Sun, 2 Feb 2020 21:24:04 +0000 (22:24 +0100)
committerBram Moolenaar <Bram@vim.org>
Sun, 2 Feb 2020 21:24:04 +0000 (22:24 +0100)
Problem:    Vim9 script commands not sufficiently tested.
Solution:   Add more tests.  Fix storing global variable.  Make script
            variables work.

src/evalvars.c
src/misc1.c
src/proto/evalvars.pro
src/proto/misc1.pro
src/testdir/test_vim9_script.vim
src/version.c
src/vim9.h
src/vim9compile.c
src/vim9execute.c

index a5768599acbcc3019cb80831db37314190151233..32c803fd9fb47096eb694ab9d426325017174a08 100644 (file)
@@ -1206,14 +1206,7 @@ ex_let_one(
                }
                if (p != NULL)
                {
-                   vim_setenv(name, p);
-                   if (STRICMP(name, "HOME") == 0)
-                       init_homedir();
-                   else if (didset_vim && STRICMP(name, "VIM") == 0)
-                       didset_vim = FALSE;
-                   else if (didset_vimruntime
-                                       && STRICMP(name, "VIMRUNTIME") == 0)
-                       didset_vimruntime = FALSE;
+                   vim_setenv_ext(name, p);
                    arg_end = arg;
                }
                name[len] = c1;
@@ -1966,6 +1959,24 @@ get_vim_var_tv(int idx)
     return &vimvars[idx].vv_tv;
 }
 
+/*
+ * Set v: variable to "tv".  Only accepts the same type.
+ * Takes over the value of "tv".
+ */
+    int
+set_vim_var_tv(int idx, typval_T *tv)
+{
+    if (vimvars[idx].vv_type != tv->v_type)
+    {
+       emsg(_("E1063: type mismatch for v: variable"));
+       clear_tv(tv);
+       return FAIL;
+    }
+    clear_tv(&vimvars[idx].vv_di.di_tv);
+    vimvars[idx].vv_di.di_tv = *tv;
+    return OK;
+}
+
 /*
  * Get number v: variable value.
  */
index 85ab727d323774a47b2e1de046f2c47ec8b52ee8..bb7137dc3a0e18bccebc56821bdfb9d39da7cb7b 100644 (file)
@@ -1853,6 +1853,22 @@ vim_unsetenv(char_u *var)
 #endif
 
 
+/*
+ * Set environment variable "name" and take care of side effects.
+ */
+    void
+vim_setenv_ext(char_u *name, char_u *val)
+{
+    vim_setenv(name, val);
+    if (STRICMP(name, "HOME") == 0)
+       init_homedir();
+    else if (didset_vim && STRICMP(name, "VIM") == 0)
+       didset_vim = FALSE;
+    else if (didset_vimruntime
+           && STRICMP(name, "VIMRUNTIME") == 0)
+       didset_vimruntime = FALSE;
+}
+
 /*
  * Our portable version of setenv.
  */
index 1d557f528405576e267c9762ac4701f8b61ba271..ff2f58ff903987e50e646024457c88a2491889d9 100644 (file)
@@ -33,6 +33,7 @@ void set_vim_var_type(int idx, vartype_T type);
 void set_vim_var_nr(int idx, varnumber_T val);
 char *get_vim_var_name(int idx);
 typval_T *get_vim_var_tv(int idx);
+int set_vim_var_tv(int idx, typval_T *tv);
 varnumber_T get_vim_var_nr(int idx);
 char_u *get_vim_var_str(int idx);
 list_T *get_vim_var_list(int idx);
index 2f7bd03885a467e7c03b3a2048e90b57fc00b4eb..e4f2c3d8b608097d44b26cd573080423d98596f9 100644 (file)
@@ -31,6 +31,7 @@ void expand_env(char_u *src, char_u *dst, int dstlen);
 void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, int esc, int one, char_u *startstr);
 char_u *vim_getenv(char_u *name, int *mustfree);
 void vim_unsetenv(char_u *var);
+void vim_setenv_ext(char_u *name, char_u *val);
 void vim_setenv(char_u *name, char_u *val);
 char_u *get_env_name(expand_T *xp, int idx);
 char_u *get_users(expand_T *xp, int idx);
index ffeec5dbdfaa5bac6c161d8ffe6c5e798d3b17d9..023fc0f751d64ebc918d6cf0fa88a9de690819a3 100644 (file)
@@ -42,6 +42,13 @@ def Test_assignment()
 
   let dict1: dict<string> = #{key: 'value'}
   let dict2: dict<number> = #{one: 1, two: 2}
+
+  v:char = 'abc'
+  call assert_equal('abc', v:char)
+
+  $ENVVAR = 'foobar'
+  call assert_equal('foobar', $ENVVAR)
+  $ENVVAR = ''
 enddef
 
 func Test_assignment_failure()
@@ -106,7 +113,7 @@ def Test_call_ufunc_count()
   Increment()
   " works with and without :call
   assert_equal(4, g:counter)
-  assert_equal(4, g:counter)
+  call assert_equal(4, g:counter)
   unlet g:counter
 enddef
 
@@ -311,7 +318,11 @@ def Test_import_absolute()
   let import_lines = [
         \ 'vim9script',
         \ 'import exported from "' .. escape(getcwd(), '\') .. '/Xexport_abs.vim"',
-        \ 'g:imported_abs = exported',
+        \ 'def UseExported()',
+        \ '  g:imported_abs = exported',
+        \ 'enddef',
+        \ 'UseExported()',
+        \ 'g:import_disassabled = execute("disass UseExported")',
         \ ]
   writefile(import_lines, 'Ximport_abs.vim')
   writefile(s:export_script_lines, 'Xexport_abs.vim')
@@ -319,7 +330,12 @@ def Test_import_absolute()
   source Ximport_abs.vim
 
   assert_equal(9876, g:imported_abs)
+  assert_match('<SNR>\d\+_UseExported.*'
+        \ .. 'g:imported_abs = exported.*'
+        \ .. '0 LOADSCRIPT exported from .*Xexport_abs.vim.*'
+        \ .. '1 STOREG g:imported_abs', g:import_disassabled)
   unlet g:imported_abs
+  unlet g:import_disassabled
 
   delete('Ximport_abs.vim')
   delete('Xexport_abs.vim')
@@ -405,7 +421,7 @@ endfunc
 let s:scriptvar = 4
 let g:globalvar = 'g'
 
-def s:ScriptFunc(arg: string)
+def s:ScriptFuncLoad(arg: string)
   let local = 1
   buffers
   echo arg
@@ -418,12 +434,25 @@ def s:ScriptFunc(arg: string)
   echo @z
 enddef
 
+def s:ScriptFuncStore()
+  let localnr = 1
+  localnr = 2
+  let localstr = 'abc'
+  localstr = 'xyz'
+  v:char = 'abc'
+  s:scriptvar = 'sv'
+  g:globalvar = 'gv'
+  &tabstop = 8
+  $ENVVAR = 'ev'
+  @z = 'rv'
+enddef
+
 def Test_disassemble()
   assert_fails('disass NoFunc', 'E1061:')
   assert_fails('disass NotCompiled', 'E1062:')
 
-  let res = execute('disass s:ScriptFunc')
-  assert_match('<SNR>\d*_ScriptFunc.*'
+  let res = execute('disass s:ScriptFuncLoad')
+  assert_match('<SNR>\d*_ScriptFuncLoad.*'
         \ .. 'buffers.*'
         \ .. ' EXEC \+buffers.*'
         \ .. ' LOAD arg\[-1\].*'
@@ -432,7 +461,31 @@ def Test_disassemble()
         \ .. ' LOADS s:scriptvar from .*test_vim9_script.vim.*'
         \ .. ' LOADG g:globalvar.*'
         \ .. ' LOADENV $ENVVAR.*'
-        \ .. ' LOADREG @z.*', res)
+        \ .. ' LOADREG @z.*'
+        \, res)
+
+  " TODO:
+  " v:char =
+  " s:scriptvar =
+  res = execute('disass s:ScriptFuncStore')
+  assert_match('<SNR>\d*_ScriptFuncStore.*'
+        \ .. 'localnr = 2.*'
+        \ .. ' STORE 2 in $0.*'
+        \ .. 'localstr = ''xyz''.*'
+        \ .. ' STORE $1.*'
+        \ .. 'v:char = ''abc''.*'
+        \ .. 'STOREV v:char.*'
+        \ .. 's:scriptvar = ''sv''.*'
+        \ .. ' STORES s:scriptvar in .*test_vim9_script.vim.*'
+        \ .. 'g:globalvar = ''gv''.*'
+        \ .. ' STOREG g:globalvar.*'
+        \ .. '&tabstop = 8.*'
+        \ .. ' STOREOPT &tabstop.*'
+        \ .. '$ENVVAR = ''ev''.*'
+        \ .. ' STOREENV $ENVVAR.*'
+        \ .. '@z = ''rv''.*'
+        \ .. ' STOREREG @z.*'
+        \, res)
 enddef
 
 
index 297e74498c6e50e9d2d2dbfdf5a6f4d645645b28..6b7fe09526ee8ef432d74c769fb4537a464c2ce3 100644 (file)
@@ -742,6 +742,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    200,
 /**/
     199,
 /**/
index 97941cad2065c2f81ee7c7c399d0d17097d48ffb..a9f0710438c17adfc213fda3213930ce40d5c643 100644 (file)
@@ -18,17 +18,21 @@ typedef enum {
     // get and set variables
     ISN_LOAD,      // push local variable isn_arg.number
     ISN_LOADV,     // push v: variable isn_arg.number
-    ISN_LOADSCRIPT, // push script-local variable isn_arg.script.
-    ISN_LOADS,     // push s: variable isn_arg.string
     ISN_LOADG,     // push g: variable isn_arg.string
+    ISN_LOADS,     // push s: variable isn_arg.loadstore
+    ISN_LOADSCRIPT, // push script-local variable isn_arg.script.
     ISN_LOADOPT,    // push option isn_arg.string
     ISN_LOADENV,    // push environment variable isn_arg.string
     ISN_LOADREG,    // push register isn_arg.number
 
     ISN_STORE,     // pop into local variable isn_arg.number
+    ISN_STOREV,            // pop into v: variable isn_arg.number
     ISN_STOREG,            // pop into global variable isn_arg.string
+    ISN_STORES,            // pop into scirpt variable isn_arg.loadstore
     ISN_STORESCRIPT, // pop into scirpt variable isn_arg.script
     ISN_STOREOPT,   // pop into option isn_arg.string
+    ISN_STOREENV,    // pop into environment variable isn_arg.string
+    ISN_STOREREG,    // pop into register isn_arg.number
     // ISN_STOREOTHER, // pop into other script variable isn_arg.other.
 
     ISN_STORENR,    // store number into local variable isn_arg.storenr.str_idx
@@ -180,13 +184,13 @@ typedef struct {
     int                so_flags;
 } storeopt_T;
 
-// arguments to ISN_LOADS
+// arguments to ISN_LOADS and ISN_STORES
 typedef struct {
     char_u     *ls_name;       // variable name
     int                ls_sid;         // script ID
-} loads_T;
+} loadstore_T;
 
-// arguments to ISN_LOADSCRIPT
+// arguments to ISN_LOADSCRIPT and ISN_STORESCRIPT
 typedef struct {
     int                script_sid;     // script ID
     int                script_idx;     // index in sn_var_vals
@@ -217,7 +221,7 @@ typedef struct {
        checktype_T         type;
        storenr_T           storenr;
        storeopt_T          storeopt;
-       loads_T             loads;
+       loadstore_T         loadstore;
        script_T            script;
     } isn_arg;
 } isn_T;
index 651c5ba92dfbbbad2010f13a6a2a1ccc660c0ede..570c6a494830490fdf5d8487d7cad7d24eef7d81 100644 (file)
@@ -335,7 +335,7 @@ check_number_or_float(vartype_T type1, vartype_T type2, char_u *op)
                                                     || type2 == VAR_UNKNOWN)))
     {
        if (*op == '+')
-           semsg(_("E1035: wrong argument type for +"));
+           emsg(_("E1035: wrong argument type for +"));
        else
            semsg(_("E1036: %c requires number or float arguments"), *op);
        return FAIL;
@@ -720,21 +720,50 @@ generate_LOAD(
     return OK;
 }
 
+/*
+ * Generate an ISN_LOADV instruction.
+ */
+    static int
+generate_LOADV(
+       cctx_T      *cctx,
+       char_u      *name,
+       int         error)
+{
+    // load v:var
+    int vidx = find_vim_var(name);
+
+    if (vidx < 0)
+    {
+       if (error)
+           semsg(_(e_var_notfound), name);
+       return FAIL;
+    }
+
+    // TODO: get actual type
+    return generate_LOAD(cctx, ISN_LOADV, vidx, NULL, &t_any);
+}
+
 /*
  * Generate an ISN_LOADS instruction.
  */
     static int
-generate_LOADS(
+generate_OLDSCRIPT(
        cctx_T      *cctx,
+       isntype_T   isn_type,
        char_u      *name,
-       int         sid)
+       int         sid,
+       type_T      *type)
 {
     isn_T      *isn;
 
-    if ((isn = generate_instr_type(cctx, ISN_LOADS, &t_any)) == NULL)
+    if (isn_type == ISN_LOADS)
+       isn = generate_instr_type(cctx, isn_type, type);
+    else
+       isn = generate_instr_drop(cctx, isn_type, 1);
+    if (isn == NULL)
        return FAIL;
-    isn->isn_arg.loads.ls_name = vim_strsave(name);
-    isn->isn_arg.loads.ls_sid = sid;
+    isn->isn_arg.loadstore.ls_name = vim_strsave(name);
+    isn->isn_arg.loadstore.ls_sid = sid;
 
     return OK;
 }
@@ -743,7 +772,7 @@ generate_LOADS(
  * Generate an ISN_LOADSCRIPT or ISN_STORESCRIPT instruction.
  */
     static int
-generate_SCRIPT(
+generate_VIM9SCRIPT(
        cctx_T      *cctx,
        isntype_T   isn_type,
        int         sid,
@@ -1476,13 +1505,14 @@ compile_load_scriptvar(cctx_T *cctx, char_u *name)
     if (idx == -1)
     {
        // variable exists but is not in sn_var_vals: old style script.
-       return generate_LOADS(cctx, name, current_sctx.sc_sid);
+       return generate_OLDSCRIPT(cctx, ISN_LOADS, name, current_sctx.sc_sid,
+                                                                      &t_any);
     }
     if (idx >= 0)
     {
        svar_T          *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx;
 
-       generate_SCRIPT(cctx, ISN_LOADSCRIPT,
+       generate_VIM9SCRIPT(cctx, ISN_LOADSCRIPT,
                                        current_sctx.sc_sid, idx, sv->sv_type);
        return OK;
     }
@@ -1491,7 +1521,7 @@ compile_load_scriptvar(cctx_T *cctx, char_u *name)
     if (import != NULL)
     {
        // TODO: check this is a variable, not a function
-       generate_SCRIPT(cctx, ISN_LOADSCRIPT,
+       generate_VIM9SCRIPT(cctx, ISN_LOADSCRIPT,
                import->imp_sid,
                import->imp_var_vals_idx,
                import->imp_type);
@@ -1523,18 +1553,7 @@ compile_load(char_u **arg, char_u *end, cctx_T *cctx, int error)
 
        if (**arg == 'v')
        {
-           // load v:var
-           int vidx = find_vim_var(name);
-
-           if (vidx < 0)
-           {
-               if (error)
-                   semsg(_(e_var_notfound), name);
-               goto theend;
-           }
-
-           // TODO: get actual type
-           res = generate_LOAD(cctx, ISN_LOADV, vidx, NULL, &t_any);
+           res = generate_LOADV(cctx, name, error);
        }
        else if (**arg == 'g')
        {
@@ -3071,6 +3090,9 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
     int                opt_type;
     int                opt_flags = 0;
     int                global = FALSE;
+    int                env = FALSE;
+    int                reg = FALSE;
+    int                vimvaridx = -1;
     int                script = FALSE;
     int                oplen = 0;
     int                heredoc = FALSE;
@@ -3135,6 +3157,29 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
        else
            type = &t_number;   // both number and boolean option
     }
+    else if (*arg == '$')
+    {
+       env = TRUE;
+       if (is_decl)
+       {
+           semsg(_("E1065: Cannot declare an environment variable: %s"), name);
+           goto theend;
+       }
+    }
+    else if (*arg == '@')
+    {
+       if (!valid_yank_reg(arg[1], TRUE))
+       {
+           emsg_invreg(arg[1]);
+           return FAIL;
+       }
+       reg = TRUE;
+       if (is_decl)
+       {
+           semsg(_("E1066: Cannot declare a register: %s"), name);
+           goto theend;
+       }
+    }
     else if (STRNCMP(arg, "g:", 2) == 0)
     {
        global = TRUE;
@@ -3144,6 +3189,20 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
            goto theend;
        }
     }
+    else if (STRNCMP(arg, "v:", 2) == 0)
+    {
+       vimvaridx = find_vim_var(name + 2);
+       if (vimvaridx < 0)
+       {
+           semsg(_(e_var_notfound), arg);
+           goto theend;
+       }
+       if (is_decl)
+       {
+           semsg(_("E1064: Cannot declare a v: variable: %s"), name);
+           goto theend;
+       }
+    }
     else
     {
        for (idx = 0; reserved[idx] != NULL; ++idx)
@@ -3171,7 +3230,9 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
                }
            }
        }
-       else if (lookup_script(arg, varlen) == OK)
+       else if ((STRNCMP(arg, "s:", 2) == 0
+                   ? lookup_script(arg + 2, varlen - 2)
+                   : lookup_script(arg, varlen)) == OK)
        {
            script = TRUE;
            if (is_decl)
@@ -3226,7 +3287,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
     }
 
     // +=, /=, etc. require an existing variable
-    if (idx < 0 && !global && !option)
+    if (idx < 0 && !global && !env && !reg && !option)
     {
        if (oplen > 1 && !heredoc)
        {
@@ -3272,6 +3333,13 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
                generate_LOAD(cctx, ISN_LOADOPT, 0, name + 1, type);
            else if (global)
                generate_LOAD(cctx, ISN_LOADG, 0, name + 2, type);
+           else if (env)
+               // Include $ in the name here
+               generate_LOAD(cctx, ISN_LOADENV, 0, name, type);
+           else if (reg)
+               generate_LOAD(cctx, ISN_LOADREG, arg[1], NULL, &t_string);
+           else if (vimvaridx >= 0)
+               generate_LOADV(cctx, name + 2, TRUE);
            else
                generate_LOAD(cctx, ISN_LOAD, idx, NULL, type);
        }
@@ -3362,12 +3430,25 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
     if (option)
        generate_STOREOPT(cctx, name + 1, opt_flags);
     else if (global)
-       generate_STORE(cctx, ISN_STOREG, 0, name + 2);
+       // include g: with the name, easier to execute that way
+       generate_STORE(cctx, ISN_STOREG, 0, name);
+    else if (env)
+       generate_STORE(cctx, ISN_STOREENV, 0, name + 1);
+    else if (reg)
+       generate_STORE(cctx, ISN_STOREREG, name[1], NULL);
+    else if (vimvaridx >= 0)
+       generate_STORE(cctx, ISN_STOREV, vimvaridx, NULL);
     else if (script)
     {
-       idx = get_script_item_idx(current_sctx.sc_sid, name, TRUE);
+       char_u *rawname = name + (name[1] == ':' ? 2 : 0);
+
+       idx = get_script_item_idx(current_sctx.sc_sid, rawname, TRUE);
        // TODO: specific type
-       generate_SCRIPT(cctx, ISN_STORESCRIPT,
+       if (idx < 0)
+           generate_OLDSCRIPT(cctx, ISN_STORES, rawname,
+                                                 current_sctx.sc_sid, &t_any);
+       else
+           generate_VIM9SCRIPT(cctx, ISN_STORESCRIPT,
                                             current_sctx.sc_sid, idx, &t_any);
     }
     else
@@ -4527,8 +4608,9 @@ compile_def_function(ufunc_T *ufunc, int set_return_type)
            ea.cmd = skipwhite(ea.cmd);
 
        // Assuming the command starts with a variable or function name, find
-       // what follows.  Also "&opt = value".
-       p = (*ea.cmd == '&') ? ea.cmd + 1 : ea.cmd;
+       // what follows.  Also "&opt = val", "$ENV = val" and "@r = val".
+       p = (*ea.cmd == '&' || *ea.cmd == '$' || *ea.cmd == '@')
+                                                        ? ea.cmd + 1 : ea.cmd;
        p = to_name_end(p);
        if (p > ea.cmd && *p != NUL)
        {
@@ -4554,7 +4636,11 @@ compile_def_function(ufunc_T *ufunc, int set_return_type)
                // "g:var = expr"
                // "var = expr"  where "var" is a local var name.
                // "&opt = expr"
+               // "$ENV = expr"
+               // "@r = expr"
                if (*ea.cmd == '&'
+                       || *ea.cmd == '$'
+                       || *ea.cmd == '@'
                        || ((p - ea.cmd) > 2 && ea.cmd[1] == ':')
                        || lookup_local(ea.cmd, p - ea.cmd, &cctx) >= 0
                        || lookup_script(ea.cmd, p - ea.cmd) == OK)
@@ -4776,12 +4862,14 @@ delete_instr(isn_T *isn)
        case ISN_MEMBER:
        case ISN_PUSHEXC:
        case ISN_PUSHS:
+       case ISN_STOREENV:
        case ISN_STOREG:
            vim_free(isn->isn_arg.string);
            break;
 
        case ISN_LOADS:
-           vim_free(isn->isn_arg.loads.ls_name);
+       case ISN_STORES:
+           vim_free(isn->isn_arg.loadstore.ls_name);
            break;
 
        case ISN_STOREOPT:
@@ -4841,7 +4929,9 @@ delete_instr(isn_T *isn)
        case ISN_PUSHSPEC:
        case ISN_RETURN:
        case ISN_STORE:
+       case ISN_STOREV:
        case ISN_STORENR:
+       case ISN_STOREREG:
        case ISN_STORESCRIPT:
        case ISN_THROW:
        case ISN_TRY:
index 3d7a2fc44b9da9f0672021f83619955e53115ab9..e4aa7fe8d0dd1c77491abb2ca4756ab34cdc6cca 100644 (file)
@@ -488,7 +488,7 @@ call_def_function(
                ++ectx.ec_stack.ga_len;
                break;
 
-           // load s: variable in vim9script
+           // load s: variable in Vim9 script
            case ISN_LOADSCRIPT:
                {
                    scriptitem_T *si =
@@ -507,12 +507,13 @@ call_def_function(
            // load s: variable in old script
            case ISN_LOADS:
                {
-                   hashtab_T   *ht = &SCRIPT_VARS(iptr->isn_arg.loads.ls_sid);
-                   char_u      *name = iptr->isn_arg.loads.ls_name;
+                   hashtab_T   *ht = &SCRIPT_VARS(
+                                              iptr->isn_arg.loadstore.ls_sid);
+                   char_u      *name = iptr->isn_arg.loadstore.ls_name;
                    dictitem_T  *di = find_var_in_ht(ht, 0, name, TRUE);
                    if (di == NULL)
                    {
-                       semsg(_("E121: Undefined variable: s:%s"), name);
+                       semsg(_(e_undefvar), name);
                        goto failed;
                    }
                    else
@@ -601,7 +602,26 @@ call_def_function(
                *tv = *STACK_TV_BOT(0);
                break;
 
-           // store script-local variable
+           // store s: variable in old script
+           case ISN_STORES:
+               {
+                   hashtab_T   *ht = &SCRIPT_VARS(
+                                              iptr->isn_arg.loadstore.ls_sid);
+                   char_u      *name = iptr->isn_arg.loadstore.ls_name;
+                   dictitem_T  *di = find_var_in_ht(ht, 0, name, TRUE);
+
+                   if (di == NULL)
+                   {
+                       semsg(_(e_undefvar), name);
+                       goto failed;
+                   }
+                   --ectx.ec_stack.ga_len;
+                   clear_tv(&di->di_tv);
+                   di->di_tv = *STACK_TV_BOT(0);
+               }
+               break;
+
+           // store script-local variable in Vim9 script
            case ISN_STORESCRIPT:
                {
                    scriptitem_T *si = SCRIPT_ITEM(
@@ -648,6 +668,32 @@ call_def_function(
                }
                break;
 
+           // store $ENV
+           case ISN_STOREENV:
+               --ectx.ec_stack.ga_len;
+               vim_setenv_ext(iptr->isn_arg.string,
+                                              tv_get_string(STACK_TV_BOT(0)));
+               break;
+
+           // store @r
+           case ISN_STOREREG:
+               {
+                   int reg = iptr->isn_arg.number;
+
+                   --ectx.ec_stack.ga_len;
+                   write_reg_contents(reg == '@' ? '"' : reg,
+                                   tv_get_string(STACK_TV_BOT(0)), -1, FALSE);
+               }
+               break;
+
+           // store v: variable
+           case ISN_STOREV:
+               --ectx.ec_stack.ga_len;
+               if (set_vim_var_tv(iptr->isn_arg.number, STACK_TV_BOT(0))
+                                                                      == FAIL)
+                   goto failed;
+               break;
+
            // store g: variable
            case ISN_STOREG:
                {
@@ -1583,7 +1629,8 @@ ex_disassemble(exarg_T *eap)
                break;
            case ISN_LOADS:
                {
-                   scriptitem_T *si = SCRIPT_ITEM(iptr->isn_arg.loads.ls_sid);
+                   scriptitem_T *si = SCRIPT_ITEM(
+                                              iptr->isn_arg.loadstore.ls_sid);
 
                    smsg("%4d LOADS s:%s from %s", current,
                                            iptr->isn_arg.string, si->sn_name);
@@ -1605,8 +1652,21 @@ ex_disassemble(exarg_T *eap)
            case ISN_STORE:
                smsg("%4d STORE $%lld", current, iptr->isn_arg.number);
                break;
+           case ISN_STOREV:
+               smsg("%4d STOREV v:%s", current,
+                                      get_vim_var_name(iptr->isn_arg.number));
+               break;
            case ISN_STOREG:
-               smsg("%4d STOREG g:%s", current, iptr->isn_arg.string);
+               smsg("%4d STOREG %s", current, iptr->isn_arg.string);
+               break;
+           case ISN_STORES:
+               {
+                   scriptitem_T *si = SCRIPT_ITEM(
+                                              iptr->isn_arg.loadstore.ls_sid);
+
+                   smsg("%4d STORES s:%s in %s", current,
+                                           iptr->isn_arg.string, si->sn_name);
+               }
                break;
            case ISN_STORESCRIPT:
                {
@@ -1623,7 +1683,12 @@ ex_disassemble(exarg_T *eap)
                smsg("%4d STOREOPT &%s", current,
                                               iptr->isn_arg.storeopt.so_name);
                break;
-
+           case ISN_STOREENV:
+               smsg("%4d STOREENV $%s", current, iptr->isn_arg.string);
+               break;
+           case ISN_STOREREG:
+               smsg("%4d STOREREG @%c", current, iptr->isn_arg.number);
+               break;
            case ISN_STORENR:
                smsg("%4d STORE %lld in $%d", current,
                                iptr->isn_arg.storenr.str_val,