changeset 19283:9dc843109c97 v8.2.0200

patch 8.2.0200: Vim9 script commands not sufficiently tested Commit: https://github.com/vim/vim/commit/b283a8a6802ef8a46b17cb439f9514840c03698f Author: Bram Moolenaar <Bram@vim.org> Date: Sun Feb 2 22:24:04 2020 +0100 patch 8.2.0200: Vim9 script commands not sufficiently tested Problem: Vim9 script commands not sufficiently tested. Solution: Add more tests. Fix storing global variable. Make script variables work.
author Bram Moolenaar <Bram@vim.org>
date Sun, 02 Feb 2020 22:30:04 +0100
parents 0d513180baa1
children 938f4994ad87
files 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
diffstat 9 files changed, 301 insertions(+), 58 deletions(-) [+]
line wrap: on
line diff
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -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;
@@ -1967,6 +1960,24 @@ get_vim_var_tv(int idx)
 }
 
 /*
+ * 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.
  */
     varnumber_T
--- a/src/misc1.c
+++ b/src/misc1.c
@@ -1854,6 +1854,22 @@ vim_unsetenv(char_u *var)
 
 
 /*
+ * 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.
  */
     void
--- a/src/proto/evalvars.pro
+++ b/src/proto/evalvars.pro
@@ -33,6 +33,7 @@ void set_vim_var_type(int idx, vartype_T
 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);
--- a/src/proto/misc1.pro
+++ b/src/proto/misc1.pro
@@ -31,6 +31,7 @@ void expand_env(char_u *src, char_u *dst
 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);
--- a/src/testdir/test_vim9_script.vim
+++ b/src/testdir/test_vim9_script.vim
@@ -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
 
 
--- a/src/version.c
+++ b/src/version.c
@@ -743,6 +743,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    200,
+/**/
     199,
 /**/
     198,
--- a/src/vim9.h
+++ b/src/vim9.h
@@ -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_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_LOADS,	    // push s: variable isn_arg.string
-    ISN_LOADG,	    // push g: variable isn_arg.string
     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;
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -335,7 +335,7 @@ check_number_or_float(vartype_T type1, v
 						     || 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;
@@ -721,20 +721,49 @@ generate_LOAD(
 }
 
 /*
+ * 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, cha
     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, cha
     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, 
 
 	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 
     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 
 	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 
 	    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 
 		}
 	    }
 	}
-	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 
     }
 
     // +=, /=, 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 
 		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 
     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
 	    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
 		// "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:
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -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,