Patch 8.2.2784
Problem:    Vim9: cannot use \=expr in :substitute.
Solution:   Compile the expression into instructions and execute them when
            invoked.
Files:      src/vim9.h, src/vim9compile.c, src/vim9execute.c,
            src/proto/vim9execute.pro, src/regexp.c, src/ex_cmds.c,
            src/proto/ex_cmds.pro, src/globals.h,
            src/testdir/test_vim9_cmd.vim,
            src/testdir/test_vim9_disassemble.vim


*** ../vim-8.2.2783/src/vim9.h  2021-04-12 21:20:58.634708976 +0200
--- src/vim9.h  2021-04-19 11:23:35.186017736 +0200
***************
*** 19,24 ****
--- 19,25 ----
      ISN_ECHOMSG,    // echo Ex commands isn_arg.number items on top of stack
      ISN_ECHOERR,    // echo Ex commands isn_arg.number items on top of stack
      ISN_RANGE,            // compute range from isn_arg.string, push to stack
+     ISN_SUBSTITUTE, // :s command with expression
  
      // get and set variables
      ISN_LOAD,     // push local variable isn_arg.number
***************
*** 94,100 ****
  
      // expression operations
      ISN_JUMP,     // jump if condition is matched isn_arg.jump
!     ISN_JUMP_IF_ARG_SET, // jump if argument is already set, uses 
isn_arg.jumparg
  
      // loop
      ISN_FOR,      // get next item from a list, uses isn_arg.forloop
--- 95,102 ----
  
      // expression operations
      ISN_JUMP,     // jump if condition is matched isn_arg.jump
!     ISN_JUMP_IF_ARG_SET, // jump if argument is already set, uses
!                        // isn_arg.jumparg
  
      // loop
      ISN_FOR,      // get next item from a list, uses isn_arg.forloop
***************
*** 165,171 ****
  
      ISN_UNPACK,           // unpack list into items, uses isn_arg.unpack
      ISN_SHUFFLE,    // move item on stack up or down
!     ISN_DROP      // pop stack and discard value
  } isntype_T;
  
  
--- 167,175 ----
  
      ISN_UNPACK,           // unpack list into items, uses isn_arg.unpack
      ISN_SHUFFLE,    // move item on stack up or down
!     ISN_DROP,     // pop stack and discard value
! 
!     ISN_FINISH            // end marker in list of instructions
  } isntype_T;
  
  
***************
*** 339,344 ****
--- 343,354 ----
      int               outer_depth;    // nesting level, stack frames to go up
  } isn_outer_T;
  
+ // arguments to ISN_SUBSTITUTE
+ typedef struct {
+     char_u    *subs_cmd;      // :s command
+     isn_T     *subs_instr;    // sequence of instructions
+ } subs_T;
+ 
  /*
   * Instruction
   */
***************
*** 381,386 ****
--- 391,397 ----
        cmod_T              cmdmod;
        unpack_T            unpack;
        isn_outer_T         outer;
+       subs_T              subs;
      } isn_arg;
  };
  
*** ../vim-8.2.2783/src/vim9compile.c   2021-04-18 13:15:54.524840780 +0200
--- src/vim9compile.c   2021-04-19 16:38:22.825424128 +0200
***************
*** 2130,2135 ****
--- 2130,2162 ----
      return OK;
  }
  
+     static int
+ generate_substitute(char_u *cmd, int instr_start, cctx_T *cctx)
+ {
+     isn_T     *isn;
+     isn_T     *instr;
+     int               instr_count = cctx->ctx_instr.ga_len - instr_start;
+ 
+     instr = ALLOC_MULT(isn_T, instr_count + 1);
+     if (instr == NULL)
+       return FAIL;
+     // Move the generated instructions into the ISN_SUBSTITUTE instructions,
+     // then truncate the list of instructions, so they are used only once.
+     mch_memmove(instr, ((isn_T *)cctx->ctx_instr.ga_data) + instr_start,
+                                             instr_count * sizeof(isn_T));
+     instr[instr_count].isn_type = ISN_FINISH;
+     cctx->ctx_instr.ga_len = instr_start;
+ 
+     if ((isn = generate_instr(cctx, ISN_SUBSTITUTE)) == NULL)
+     {
+       vim_free(instr);
+       return FAIL;
+     }
+     isn->isn_arg.subs.subs_cmd = vim_strsave(cmd);
+     isn->isn_arg.subs.subs_instr = instr;
+     return OK;
+ }
+ 
  /*
   * Generate ISN_RANGE.  Consumes "range".  Return OK/FAIL.
   */
***************
*** 8466,8471 ****
--- 8493,8547 ----
  }
  
  /*
+  * :s/pat/repl/
+  */
+     static char_u *
+ compile_substitute(char_u *arg, exarg_T *eap, cctx_T *cctx)
+ {
+     char_u  *cmd = eap->arg;
+     char_u  *expr = (char_u *)strstr((char *)cmd, "\\=");
+ 
+     if (expr != NULL)
+     {
+       int delimiter = *cmd++;
+ 
+       // There is a \=expr, find it in the substitute part.
+       cmd = skip_regexp_ex(cmd, delimiter, magic_isset(),
+                                                            NULL, NULL, NULL);
+       if (cmd[0] == delimiter && cmd[1] == '\\' && cmd[2] == '=')
+       {
+           int     instr_count = cctx->ctx_instr.ga_len;
+           char_u  *end;
+ 
+           cmd += 3;
+           end = skip_substitute(cmd, delimiter);
+ 
+           compile_expr0(&cmd, cctx);
+           if (end[-1] == NUL)
+               end[-1] = delimiter;
+           cmd = skipwhite(cmd);
+           if (*cmd != delimiter && *cmd != NUL)
+           {
+               semsg(_(e_trailing_arg), cmd);
+               return NULL;
+           }
+ 
+           if (generate_substitute(arg, instr_count, cctx) == FAIL)
+               return NULL;
+ 
+           // skip over flags
+           if (*end == '&')
+               ++end;
+           while (ASCII_ISALPHA(*end) || *end == '#')
+               ++end;
+           return end;
+       }
+     }
+ 
+     return compile_exec(arg, eap, cctx);
+ }
+ 
+ /*
   * Add a function to the list of :def functions.
   * This sets "ufunc->uf_dfunc_idx" but the function isn't compiled yet.
   */
***************
*** 8996,9001 ****
--- 9072,9087 ----
                    line = compile_put(p, &ea, &cctx);
                    break;
  
+           case CMD_substitute:
+                   if (cctx.ctx_skip == SKIP_YES)
+                       line = (char_u *)"";
+                   else
+                   {
+                       ea.arg = p;
+                       line = compile_substitute(line, &ea, &cctx);
+                   }
+                   break;
+ 
            // TODO: any other commands with an expression argument?
  
            case CMD_append:
***************
*** 9223,9228 ****
--- 9309,9319 ----
            vim_free(isn->isn_arg.string);
            break;
  
+       case ISN_SUBSTITUTE:
+           vim_free(isn->isn_arg.subs.subs_cmd);
+           vim_free(isn->isn_arg.subs.subs_instr);
+           break;
+ 
        case ISN_LOADS:
        case ISN_STORES:
            vim_free(isn->isn_arg.loadstore.ls_name);
***************
*** 9400,9405 ****
--- 9491,9497 ----
        case ISN_UNLETINDEX:
        case ISN_UNLETRANGE:
        case ISN_UNPACK:
+       case ISN_FINISH:
            // nothing allocated
            break;
      }
*** ../vim-8.2.2783/src/vim9execute.c   2021-04-18 14:12:27.707697058 +0200
--- src/vim9execute.c   2021-04-19 15:17:40.505007587 +0200
***************
*** 34,39 ****
--- 34,47 ----
      int           tcd_return;         // when TRUE return from end of :finally
  } trycmd_T;
  
+ // Data local to a function.
+ // On a function call, if not empty, is saved on the stack and restored when
+ // returning.
+ typedef struct {
+     int               floc_restore_cmdmod;
+     cmdmod_T  floc_save_cmdmod;
+     int               floc_restore_cmdmod_stacklen;
+ } funclocal_T;
  
  // A stack is used to store:
  // - arguments passed to a :def function
***************
*** 60,67 ****
--- 68,77 ----
  struct ectx_S {
      garray_T  ec_stack;       // stack of typval_T values
      int               ec_frame_idx;   // index in ec_stack: context of 
ec_dfunc_idx
+     int               ec_initial_frame_idx;   // frame index when called
  
      outer_T   *ec_outer;      // outer scope used for closures, allocated
+     funclocal_T ec_funclocal;
  
      garray_T  ec_trystack;    // stack of trycmd_T values
      int               ec_in_catch;    // when TRUE in catch or finally block
***************
*** 71,76 ****
--- 81,90 ----
      int               ec_iidx;        // index in ec_instr: instruction to 
execute
  
      garray_T  ec_funcrefs;    // partials that might be a closure
+ 
+     int               ec_did_emsg_before;
+     int               ec_trylevel_at_start;
+     where_T   ec_where;
  };
  
  #ifdef FEAT_PROFILE
***************
*** 125,139 ****
      return OK;
  }
  
- // Data local to a function.
- // On a function call, if not empty, is saved on the stack and restored when
- // returning.
- typedef struct {
-     int               floc_restore_cmdmod;
-     cmdmod_T  floc_save_cmdmod;
-     int               floc_restore_cmdmod_stacklen;
- } funclocal_T;
- 
  /*
   * Call compiled function "cdf_idx" from compiled code.
   * This adds a stack frame and sets the instruction pointer to the start of 
the
--- 139,144 ----
***************
*** 154,160 ****
        int             cdf_idx,
        partial_T       *pt,
        int             argcount_arg,
-       funclocal_T     *funclocal,
        ectx_T          *ectx)
  {
      int               argcount = argcount_arg;
--- 159,164 ----
***************
*** 254,266 ****
        return FAIL;
  
      // Only make a copy of funclocal if it contains something to restore.
!     if (funclocal->floc_restore_cmdmod)
      {
        floc = ALLOC_ONE(funclocal_T);
        if (floc == NULL)
            return FAIL;
!       *floc = *funclocal;
!       funclocal->floc_restore_cmdmod = FALSE;
      }
  
      // Move the vararg-list to below the missing optional arguments.
--- 258,270 ----
        return FAIL;
  
      // Only make a copy of funclocal if it contains something to restore.
!     if (ectx->ec_funclocal.floc_restore_cmdmod)
      {
        floc = ALLOC_ONE(funclocal_T);
        if (floc == NULL)
            return FAIL;
!       *floc = ectx->ec_funclocal;
!       ectx->ec_funclocal.floc_restore_cmdmod = FALSE;
      }
  
      // Move the vararg-list to below the missing optional arguments.
***************
*** 527,533 ****
   * Return from the current function.
   */
      static int
! func_return(funclocal_T *funclocal, ectx_T *ectx)
  {
      int               idx;
      int               ret_idx;
--- 531,537 ----
   * Return from the current function.
   */
      static int
! func_return(ectx_T *ectx)
  {
      int               idx;
      int               ret_idx;
***************
*** 598,607 ****
      ectx->ec_instr = INSTRUCTIONS(prev_dfunc);
  
      if (floc == NULL)
!       funclocal->floc_restore_cmdmod = FALSE;
      else
      {
!       *funclocal = *floc;
        vim_free(floc);
      }
  
--- 602,611 ----
      ectx->ec_instr = INSTRUCTIONS(prev_dfunc);
  
      if (floc == NULL)
!       ectx->ec_funclocal.floc_restore_cmdmod = FALSE;
      else
      {
!       ectx->ec_funclocal = *floc;
        vim_free(floc);
      }
  
***************
*** 698,704 ****
        ufunc_T     *ufunc,
        partial_T   *pt,
        int         argcount,
-       funclocal_T *funclocal,
        ectx_T      *ectx,
        isn_T       *iptr)
  {
--- 702,707 ----
***************
*** 738,744 ****
            iptr->isn_arg.dfunc.cdf_idx = ufunc->uf_dfunc_idx;
            iptr->isn_arg.dfunc.cdf_argcount = argcount;
        }
!       return call_dfunc(ufunc->uf_dfunc_idx, pt, argcount, funclocal, ectx);
      }
  
      if (call_prepare(argcount, argvars, ectx) == FAIL)
--- 741,747 ----
            iptr->isn_arg.dfunc.cdf_idx = ufunc->uf_dfunc_idx;
            iptr->isn_arg.dfunc.cdf_argcount = argcount;
        }
!       return call_dfunc(ufunc->uf_dfunc_idx, pt, argcount, ectx);
      }
  
      if (call_prepare(argcount, argvars, ectx) == FAIL)
***************
*** 800,806 ****
  call_by_name(
        char_u      *name,
        int         argcount,
-       funclocal_T *funclocal,
        ectx_T      *ectx,
        isn_T       *iptr)
  {
--- 803,808 ----
***************
*** 853,859 ****
            }
        }
  
!       return call_ufunc(ufunc, NULL, argcount, funclocal, ectx, iptr);
      }
  
      return FAIL;
--- 855,861 ----
            }
        }
  
!       return call_ufunc(ufunc, NULL, argcount, ectx, iptr);
      }
  
      return FAIL;
***************
*** 863,869 ****
  call_partial(
        typval_T    *tv,
        int         argcount_arg,
-       funclocal_T *funclocal,
        ectx_T      *ectx)
  {
      int               argcount = argcount_arg;
--- 865,870 ----
***************
*** 893,899 ****
        }
  
        if (pt->pt_func != NULL)
!           return call_ufunc(pt->pt_func, pt, argcount, funclocal, ectx, NULL);
  
        name = pt->pt_name;
      }
--- 894,900 ----
        }
  
        if (pt->pt_func != NULL)
!           return call_ufunc(pt->pt_func, pt, argcount, ectx, NULL);
  
        name = pt->pt_name;
      }
***************
*** 911,917 ****
        if (error != FCERR_NONE)
            res = FAIL;
        else
!           res = call_by_name(fname, argcount, funclocal, ectx, NULL);
        vim_free(tofree);
      }
  
--- 912,918 ----
        if (error != FCERR_NONE)
            res = FAIL;
        else
!           res = call_by_name(fname, argcount, ectx, NULL);
        vim_free(tofree);
      }
  
***************
*** 1184,1197 ****
  call_eval_func(
        char_u      *name,
        int         argcount,
-       funclocal_T *funclocal,
        ectx_T      *ectx,
        isn_T       *iptr)
  {
      int           called_emsg_before = called_emsg;
      int           res;
  
!     res = call_by_name(name, argcount, funclocal, ectx, iptr);
      if (res == FAIL && called_emsg == called_emsg_before)
      {
        dictitem_T      *v;
--- 1185,1197 ----
  call_eval_func(
        char_u      *name,
        int         argcount,
        ectx_T      *ectx,
        isn_T       *iptr)
  {
      int           called_emsg_before = called_emsg;
      int           res;
  
!     res = call_by_name(name, argcount, ectx, iptr);
      if (res == FAIL && called_emsg == called_emsg_before)
      {
        dictitem_T      *v;
***************
*** 1207,1213 ****
            semsg(_(e_unknownfunc), name);
            return FAIL;
        }
!       return call_partial(&v->di_tv, argcount, funclocal, ectx);
      }
      return res;
  }
--- 1207,1213 ----
            semsg(_(e_unknownfunc), name);
            return FAIL;
        }
!       return call_partial(&v->di_tv, argcount, ectx);
      }
      return res;
  }
***************
*** 1257,1511 ****
      return OK;
  }
  
! 
! /*
!  * Call a "def" function from old Vim script.
!  * Return OK or FAIL.
!  */
!     int
! call_def_function(
!     ufunc_T   *ufunc,
!     int               argc_arg,       // nr of arguments
!     typval_T  *argv,          // arguments
!     partial_T *partial,       // optional partial for context
!     typval_T  *rettv)         // return value
! {
!     ectx_T    ectx;           // execution context
!     int               argc = argc_arg;
!     int               initial_frame_idx;
!     typval_T  *tv;
!     int               idx;
!     int               ret = FAIL;
!     int               defcount = ufunc->uf_args.ga_len - argc;
!     sctx_T    save_current_sctx = current_sctx;
!     int               breakcheck_count = 0;
!     int               did_emsg_before = did_emsg_cumul + did_emsg;
!     int               save_suppress_errthrow = suppress_errthrow;
!     msglist_T **saved_msg_list = NULL;
!     msglist_T *private_msg_list = NULL;
!     funclocal_T funclocal;
!     int               save_emsg_silent_def = emsg_silent_def;
!     int               save_did_emsg_def = did_emsg_def;
!     int               trylevel_at_start = trylevel;
!     int               orig_funcdepth;
!     where_T   where;
  
  // Get pointer to item in the stack.
! #define STACK_TV(idx) (((typval_T *)ectx.ec_stack.ga_data) + idx)
  
  // Get pointer to item at the bottom of the stack, -1 is the bottom.
  #undef STACK_TV_BOT
! #define STACK_TV_BOT(idx) (((typval_T *)ectx.ec_stack.ga_data) + 
ectx.ec_stack.ga_len + idx)
  
  // Get pointer to a local variable on the stack.  Negative for arguments.
! #define STACK_TV_VAR(idx) (((typval_T *)ectx.ec_stack.ga_data) + 
ectx.ec_frame_idx + STACK_FRAME_SIZE + idx)
! 
!     if (ufunc->uf_def_status == UF_NOT_COMPILED
!           || ufunc->uf_def_status == UF_COMPILE_ERROR
!           || (func_needs_compiling(ufunc, PROFILING(ufunc))
!               && compile_def_function(ufunc, FALSE, PROFILING(ufunc), NULL)
!                                                                     == FAIL))
!     {
!       if (did_emsg_cumul + did_emsg == did_emsg_before)
!           semsg(_(e_function_is_not_compiled_str),
!                                                  printable_func_name(ufunc));
!       return FAIL;
!     }
! 
!     {
!       // Check the function was really compiled.
!       dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
!                                                        + ufunc->uf_dfunc_idx;
!       if (INSTRUCTIONS(dfunc) == NULL)
!       {
!           iemsg("using call_def_function() on not compiled function");
!           return FAIL;
!       }
!     }
! 
!     // If depth of calling is getting too high, don't execute the function.
!     orig_funcdepth = funcdepth_get();
!     if (funcdepth_increment() == FAIL)
!       return FAIL;
! 
!     CLEAR_FIELD(funclocal);
!     CLEAR_FIELD(ectx);
!     ectx.ec_dfunc_idx = ufunc->uf_dfunc_idx;
!     ga_init2(&ectx.ec_stack, sizeof(typval_T), 500);
!     if (ga_grow(&ectx.ec_stack, 20) == FAIL)
!     {
!       funcdepth_decrement();
!       return FAIL;
!     }
!     ga_init2(&ectx.ec_trystack, sizeof(trycmd_T), 10);
!     ga_init2(&ectx.ec_funcrefs, sizeof(partial_T *), 10);
! 
!     idx = argc - ufunc->uf_args.ga_len;
!     if (idx > 0 && ufunc->uf_va_name == NULL)
!     {
!       if (idx == 1)
!           emsg(_(e_one_argument_too_many));
!       else
!           semsg(_(e_nr_arguments_too_many), idx);
!       goto failed_early;
!     }
! 
!     // Put arguments on the stack, but no more than what the function expects.
!     // A lambda can be called with more arguments than it uses.
!     for (idx = 0; idx < argc
!           && (ufunc->uf_va_name != NULL || idx < ufunc->uf_args.ga_len);
!                                                                        ++idx)
!     {
!       if (idx >= ufunc->uf_args.ga_len - ufunc->uf_def_args.ga_len
!               && argv[idx].v_type == VAR_SPECIAL
!               && argv[idx].vval.v_number == VVAL_NONE)
!       {
!           // Use the default value.
!           STACK_TV_BOT(0)->v_type = VAR_UNKNOWN;
!       }
!       else
!       {
!           if (ufunc->uf_arg_types != NULL && idx < ufunc->uf_args.ga_len
!                   && check_typval_arg_type(
!                       ufunc->uf_arg_types[idx], &argv[idx], idx + 1) == FAIL)
!               goto failed_early;
!           copy_tv(&argv[idx], STACK_TV_BOT(0));
!       }
!       ++ectx.ec_stack.ga_len;
!     }
! 
!     // Turn varargs into a list.  Empty list if no args.
!     if (ufunc->uf_va_name != NULL)
!     {
!       int vararg_count = argc - ufunc->uf_args.ga_len;
! 
!       if (vararg_count < 0)
!           vararg_count = 0;
!       else
!           argc -= vararg_count;
!       if (exe_newlist(vararg_count, &ectx) == FAIL)
!           goto failed_early;
! 
!       // Check the type of the list items.
!       tv = STACK_TV_BOT(-1);
!       if (ufunc->uf_va_type != NULL
!               && ufunc->uf_va_type != &t_list_any
!               && ufunc->uf_va_type->tt_member != &t_any
!               && tv->vval.v_list != NULL)
!       {
!           type_T      *expected = ufunc->uf_va_type->tt_member;
!           listitem_T  *li = tv->vval.v_list->lv_first;
! 
!           for (idx = 0; idx < vararg_count; ++idx)
!           {
!               if (check_typval_arg_type(expected, &li->li_tv,
!                                                      argc + idx + 1) == FAIL)
!                   goto failed_early;
!               li = li->li_next;
!           }
!       }
! 
!       if (defcount > 0)
!           // Move varargs list to below missing default arguments.
!           *STACK_TV_BOT(defcount - 1) = *STACK_TV_BOT(-1);
!       --ectx.ec_stack.ga_len;
!     }
! 
!     // Make space for omitted arguments, will store default value below.
!     // Any varargs list goes after them.
!     if (defcount > 0)
!       for (idx = 0; idx < defcount; ++idx)
!       {
!           STACK_TV_BOT(0)->v_type = VAR_UNKNOWN;
!           ++ectx.ec_stack.ga_len;
!       }
!     if (ufunc->uf_va_name != NULL)
!       ++ectx.ec_stack.ga_len;
  
!     // Frame pointer points to just after arguments.
!     ectx.ec_frame_idx = ectx.ec_stack.ga_len;
!     initial_frame_idx = ectx.ec_frame_idx;
! 
!     {
!       dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
!                                                        + ufunc->uf_dfunc_idx;
!       ufunc_T *base_ufunc = dfunc->df_ufunc;
! 
!       // "uf_partial" is on the ufunc that "df_ufunc" points to, as is done
!       // by copy_func().
!       if (partial != NULL || base_ufunc->uf_partial != NULL)
!       {
!           ectx.ec_outer = ALLOC_CLEAR_ONE(outer_T);
!           if (ectx.ec_outer == NULL)
!               goto failed_early;
!           if (partial != NULL)
!           {
!               if (partial->pt_outer.out_stack == NULL && current_ectx != NULL)
!               {
!                   if (current_ectx->ec_outer != NULL)
!                       *ectx.ec_outer = *current_ectx->ec_outer;
!               }
!               else
!                   *ectx.ec_outer = partial->pt_outer;
!           }
!           else
!               *ectx.ec_outer = base_ufunc->uf_partial->pt_outer;
!           ectx.ec_outer->out_up_is_copy = TRUE;
!       }
!     }
! 
!     // dummy frame entries
!     for (idx = 0; idx < STACK_FRAME_SIZE; ++idx)
!     {
!       STACK_TV(ectx.ec_stack.ga_len)->v_type = VAR_UNKNOWN;
!       ++ectx.ec_stack.ga_len;
!     }
! 
!     {
!       // Reserve space for local variables and any closure reference count.
!       dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
!                                                        + ufunc->uf_dfunc_idx;
! 
!       for (idx = 0; idx < dfunc->df_varcount; ++idx)
!           STACK_TV_VAR(idx)->v_type = VAR_UNKNOWN;
!       ectx.ec_stack.ga_len += dfunc->df_varcount;
!       if (dfunc->df_has_closure)
!       {
!           STACK_TV_VAR(idx)->v_type = VAR_NUMBER;
!           STACK_TV_VAR(idx)->vval.v_number = 0;
!           ++ectx.ec_stack.ga_len;
!       }
! 
!       ectx.ec_instr = INSTRUCTIONS(dfunc);
!     }
! 
!     // Following errors are in the function, not the caller.
!     // Commands behave like vim9script.
!     estack_push_ufunc(ufunc, 1);
!     current_sctx = ufunc->uf_script_ctx;
!     current_sctx.sc_version = SCRIPT_VERSION_VIM9;
! 
!     // Use a specific location for storing error messages to be converted to 
an
!     // exception.
!     saved_msg_list = msg_list;
!     msg_list = &private_msg_list;
! 
!     // Do turn errors into exceptions.
!     suppress_errthrow = FALSE;
! 
!     // Do not delete the function while executing it.
!     ++ufunc->uf_calls;
! 
!     // When ":silent!" was used before calling then we still abort the
!     // function.  If ":silent!" is used in the function then we don't.
!     emsg_silent_def = emsg_silent;
!     did_emsg_def = 0;
! 
!     where.wt_index = 0;
!     where.wt_variable = FALSE;
  
      // Start execution at the first instruction.
!     ectx.ec_iidx = 0;
  
      for (;;)
      {
--- 1257,1291 ----
      return OK;
  }
  
! // used for substitute_instr
! typedef struct subs_expr_S {
!     ectx_T    *subs_ectx;
!     isn_T     *subs_instr;
!     int               subs_status;
! } subs_expr_T;
  
  // Get pointer to item in the stack.
! #define STACK_TV(idx) (((typval_T *)ectx->ec_stack.ga_data) + idx)
  
  // Get pointer to item at the bottom of the stack, -1 is the bottom.
  #undef STACK_TV_BOT
! #define STACK_TV_BOT(idx) (((typval_T *)ectx->ec_stack.ga_data) + 
ectx->ec_stack.ga_len + idx)
  
  // Get pointer to a local variable on the stack.  Negative for arguments.
! #define STACK_TV_VAR(idx) (((typval_T *)ectx->ec_stack.ga_data) + 
ectx->ec_frame_idx + STACK_FRAME_SIZE + idx)
  
! /*
!  * Execute instructions in execution context "ectx".
!  * Return OK or FAIL;
!  */
!     static int
! exec_instructions(ectx_T *ectx)
! {
!     int               breakcheck_count = 0;
!     typval_T  *tv;
  
      // Start execution at the first instruction.
!     ectx->ec_iidx = 0;
  
      for (;;)
      {
***************
*** 1521,1527 ****
            // Turn CTRL-C into an exception.
            got_int = FALSE;
            if (throw_exception("Vim:Interrupt", ET_INTERRUPT, NULL) == FAIL)
!               goto failed;
            did_throw = TRUE;
        }
  
--- 1301,1307 ----
            // Turn CTRL-C into an exception.
            got_int = FALSE;
            if (throw_exception("Vim:Interrupt", ET_INTERRUPT, NULL) == FAIL)
!               return FAIL;
            did_throw = TRUE;
        }
  
***************
*** 1530,1581 ****
            // Turn an error message into an exception.
            did_emsg = FALSE;
            if (throw_exception(*msg_list, ET_ERROR, NULL) == FAIL)
!               goto failed;
            did_throw = TRUE;
            *msg_list = NULL;
        }
  
!       if (did_throw && !ectx.ec_in_catch)
        {
!           garray_T    *trystack = &ectx.ec_trystack;
            trycmd_T    *trycmd = NULL;
  
            // An exception jumps to the first catch, finally, or returns from
            // the current function.
            if (trystack->ga_len > 0)
                trycmd = ((trycmd_T *)trystack->ga_data) + trystack->ga_len - 1;
!           if (trycmd != NULL && trycmd->tcd_frame_idx == ectx.ec_frame_idx)
            {
                // jump to ":catch" or ":finally"
!               ectx.ec_in_catch = TRUE;
!               ectx.ec_iidx = trycmd->tcd_catch_idx;
            }
            else
            {
                // Not inside try or need to return from current functions.
                // Push a dummy return value.
!               if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
!                   goto failed;
                tv = STACK_TV_BOT(0);
                tv->v_type = VAR_NUMBER;
                tv->vval.v_number = 0;
!               ++ectx.ec_stack.ga_len;
!               if (ectx.ec_frame_idx == initial_frame_idx)
                {
                    // At the toplevel we are done.
                    need_rethrow = TRUE;
!                   if (handle_closure_in_use(&ectx, FALSE) == FAIL)
!                       goto failed;
                    goto done;
                }
  
!               if (func_return(&funclocal, &ectx) == FAIL)
!                   goto failed;
            }
            continue;
        }
  
!       iptr = &ectx.ec_instr[ectx.ec_iidx++];
        switch (iptr->isn_type)
        {
            // execute Ex command line
--- 1310,1361 ----
            // Turn an error message into an exception.
            did_emsg = FALSE;
            if (throw_exception(*msg_list, ET_ERROR, NULL) == FAIL)
!               return FAIL;
            did_throw = TRUE;
            *msg_list = NULL;
        }
  
!       if (did_throw && !ectx->ec_in_catch)
        {
!           garray_T    *trystack = &ectx->ec_trystack;
            trycmd_T    *trycmd = NULL;
  
            // An exception jumps to the first catch, finally, or returns from
            // the current function.
            if (trystack->ga_len > 0)
                trycmd = ((trycmd_T *)trystack->ga_data) + trystack->ga_len - 1;
!           if (trycmd != NULL && trycmd->tcd_frame_idx == ectx->ec_frame_idx)
            {
                // jump to ":catch" or ":finally"
!               ectx->ec_in_catch = TRUE;
!               ectx->ec_iidx = trycmd->tcd_catch_idx;
            }
            else
            {
                // Not inside try or need to return from current functions.
                // Push a dummy return value.
!               if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
!                   return FAIL;
                tv = STACK_TV_BOT(0);
                tv->v_type = VAR_NUMBER;
                tv->vval.v_number = 0;
!               ++ectx->ec_stack.ga_len;
!               if (ectx->ec_frame_idx == ectx->ec_initial_frame_idx)
                {
                    // At the toplevel we are done.
                    need_rethrow = TRUE;
!                   if (handle_closure_in_use(ectx, FALSE) == FAIL)
!                       return FAIL;
                    goto done;
                }
  
!               if (func_return(ectx) == FAIL)
!                   return FAIL;
            }
            continue;
        }
  
!       iptr = &ectx->ec_instr[ectx->ec_iidx++];
        switch (iptr->isn_type)
        {
            // execute Ex command line
***************
*** 1597,1602 ****
--- 1377,1414 ----
                }
                break;
  
+           // execute :substitute with an expression
+           case ISN_SUBSTITUTE:
+               {
+                   subs_T              *subs = &iptr->isn_arg.subs;
+                   source_cookie_T     cookie;
+                   struct subs_expr_S  *save_instr = substitute_instr;
+                   struct subs_expr_S  subs_instr;
+                   int                 res;
+ 
+                   subs_instr.subs_ectx = ectx;
+                   subs_instr.subs_instr = subs->subs_instr;
+                   subs_instr.subs_status = OK;
+                   substitute_instr = &subs_instr;
+ 
+                   SOURCING_LNUM = iptr->isn_lnum;
+                   // This is very much like ISN_EXEC
+                   CLEAR_FIELD(cookie);
+                   cookie.sourcing_lnum = iptr->isn_lnum - 1;
+                   res = do_cmdline(subs->subs_cmd,
+                               getsourceline, &cookie,
+                                  DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED);
+                   substitute_instr = save_instr;
+ 
+                   if (res == FAIL || did_emsg
+                                            || subs_instr.subs_status == FAIL)
+                       goto on_error;
+               }
+               break;
+ 
+           case ISN_FINISH:
+               goto done;
+ 
            // execute Ex command from pieces on the stack
            case ISN_EXECCONCAT:
                {
***************
*** 1626,1632 ****
                        {
                            cmd = alloc(len + 1);
                            if (cmd == NULL)
!                               goto failed;
                            len = 0;
                        }
                    }
--- 1438,1444 ----
                        {
                            cmd = alloc(len + 1);
                            if (cmd == NULL)
!                               return FAIL;
                            len = 0;
                        }
                    }
***************
*** 1643,1648 ****
--- 1455,1461 ----
                    int count = iptr->isn_arg.echo.echo_count;
                    int atstart = TRUE;
                    int needclr = TRUE;
+                   int idx;
  
                    for (idx = 0; idx < count; ++idx)
                    {
***************
*** 1653,1659 ****
                    }
                    if (needclr)
                        msg_clr_eos();
!                   ectx.ec_stack.ga_len -= count;
                }
                break;
  
--- 1466,1472 ----
                    }
                    if (needclr)
                        msg_clr_eos();
!                   ectx->ec_stack.ga_len -= count;
                }
                break;
  
***************
*** 1670,1675 ****
--- 1483,1489 ----
                    char_u      *p;
                    int         len;
                    int         failed = FALSE;
+                   int         idx;
  
                    ga_init2(&ga, 1, 80);
                    for (idx = 0; idx < count; ++idx)
***************
*** 1702,1708 ****
                        }
                        clear_tv(tv);
                    }
!                   ectx.ec_stack.ga_len -= count;
                    if (failed)
                    {
                        ga_clear(&ga);
--- 1516,1522 ----
                        }
                        clear_tv(tv);
                    }
!                   ectx->ec_stack.ga_len -= count;
                    if (failed)
                    {
                        ga_clear(&ga);
***************
*** 1742,1759 ****
  
            // load local variable or argument
            case ISN_LOAD:
!               if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
!                   goto failed;
                copy_tv(STACK_TV_VAR(iptr->isn_arg.number), STACK_TV_BOT(0));
!               ++ectx.ec_stack.ga_len;
                break;
  
            // load v: variable
            case ISN_LOADV:
!               if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
!                   goto failed;
                copy_tv(get_vim_var_tv(iptr->isn_arg.number), STACK_TV_BOT(0));
!               ++ectx.ec_stack.ga_len;
                break;
  
            // load s: variable in Vim9 script
--- 1556,1573 ----
  
            // load local variable or argument
            case ISN_LOAD:
!               if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
!                   return FAIL;
                copy_tv(STACK_TV_VAR(iptr->isn_arg.number), STACK_TV_BOT(0));
!               ++ectx->ec_stack.ga_len;
                break;
  
            // load v: variable
            case ISN_LOADV:
!               if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
!                   return FAIL;
                copy_tv(get_vim_var_tv(iptr->isn_arg.number), STACK_TV_BOT(0));
!               ++ectx->ec_stack.ga_len;
                break;
  
            // load s: variable in Vim9 script
***************
*** 1762,1775 ****
                    scriptref_T *sref = iptr->isn_arg.script.scriptref;
                    svar_T       *sv;
  
!                   sv = get_script_svar(sref, &ectx);
                    if (sv == NULL)
!                       goto failed;
                    allocate_if_null(sv->sv_tv);
!                   if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
!                       goto failed;
                    copy_tv(sv->sv_tv, STACK_TV_BOT(0));
!                   ++ectx.ec_stack.ga_len;
                }
                break;
  
--- 1576,1589 ----
                    scriptref_T *sref = iptr->isn_arg.script.scriptref;
                    svar_T       *sv;
  
!                   sv = get_script_svar(sref, ectx);
                    if (sv == NULL)
!                       return FAIL;
                    allocate_if_null(sv->sv_tv);
!                   if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
!                       return FAIL;
                    copy_tv(sv->sv_tv, STACK_TV_BOT(0));
!                   ++ectx->ec_stack.ga_len;
                }
                break;
  
***************
*** 1789,1798 ****
                    }
                    else
                    {
!                       if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
!                           goto failed;
                        copy_tv(&di->di_tv, STACK_TV_BOT(0));
!                       ++ectx.ec_stack.ga_len;
                    }
                }
                break;
--- 1603,1612 ----
                    }
                    else
                    {
!                       if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
!                           return FAIL;
                        copy_tv(&di->di_tv, STACK_TV_BOT(0));
!                       ++ectx->ec_stack.ga_len;
                    }
                }
                break;
***************
*** 1826,1832 ****
                            namespace = 't';
                            break;
                        default:  // Cannot reach here
!                           goto failed;
                    }
                    di = find_var_in_ht(ht, 0, iptr->isn_arg.string, TRUE);
  
--- 1640,1646 ----
                            namespace = 't';
                            break;
                        default:  // Cannot reach here
!                           return FAIL;
                    }
                    di = find_var_in_ht(ht, 0, iptr->isn_arg.string, TRUE);
  
***************
*** 1839,1848 ****
                    }
                    else
                    {
!                       if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
!                           goto failed;
                        copy_tv(&di->di_tv, STACK_TV_BOT(0));
!                       ++ectx.ec_stack.ga_len;
                    }
                }
                break;
--- 1653,1662 ----
                    }
                    else
                    {
!                       if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
!                           return FAIL;
                        copy_tv(&di->di_tv, STACK_TV_BOT(0));
!                       ++ectx->ec_stack.ga_len;
                    }
                }
                break;
***************
*** 1852,1864 ****
                {
                    char_u *name = iptr->isn_arg.string;
  
!                   if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
!                       goto failed;
                    SOURCING_LNUM = iptr->isn_lnum;
                    if (eval_variable(name, (int)STRLEN(name),
                              STACK_TV_BOT(0), NULL, EVAL_VAR_VERBOSE) == FAIL)
                        goto on_error;
!                   ++ectx.ec_stack.ga_len;
                }
                break;
  
--- 1666,1678 ----
                {
                    char_u *name = iptr->isn_arg.string;
  
!                   if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
!                       return FAIL;
                    SOURCING_LNUM = iptr->isn_lnum;
                    if (eval_variable(name, (int)STRLEN(name),
                              STACK_TV_BOT(0), NULL, EVAL_VAR_VERBOSE) == FAIL)
                        goto on_error;
!                   ++ectx->ec_stack.ga_len;
                }
                break;
  
***************
*** 1877,1892 ****
                        case ISN_LOADWDICT: d = curwin->w_vars; break;
                        case ISN_LOADTDICT: d = curtab->tp_vars; break;
                        default:  // Cannot reach here
!                           goto failed;
                    }
!                   if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
!                       goto failed;
                    tv = STACK_TV_BOT(0);
                    tv->v_type = VAR_DICT;
                    tv->v_lock = 0;
                    tv->vval.v_dict = d;
                    ++d->dv_refcount;
!                   ++ectx.ec_stack.ga_len;
                }
                break;
  
--- 1691,1706 ----
                        case ISN_LOADWDICT: d = curwin->w_vars; break;
                        case ISN_LOADTDICT: d = curtab->tp_vars; break;
                        default:  // Cannot reach here
!                           return FAIL;
                    }
!                   if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
!                       return FAIL;
                    tv = STACK_TV_BOT(0);
                    tv->v_type = VAR_DICT;
                    tv->v_lock = 0;
                    tv->vval.v_dict = d;
                    ++d->dv_refcount;
!                   ++ectx->ec_stack.ga_len;
                }
                break;
  
***************
*** 1898,1909 ****
  
                    // This is not expected to fail, name is checked during
                    // compilation: don't set SOURCING_LNUM.
!                   if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
!                       goto failed;
                    if (eval_option(&name, &optval, TRUE) == FAIL)
!                       goto failed;
                    *STACK_TV_BOT(0) = optval;
!                   ++ectx.ec_stack.ga_len;
                }
                break;
  
--- 1712,1723 ----
  
                    // This is not expected to fail, name is checked during
                    // compilation: don't set SOURCING_LNUM.
!                   if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
!                       return FAIL;
                    if (eval_option(&name, &optval, TRUE) == FAIL)
!                       return FAIL;
                    *STACK_TV_BOT(0) = optval;
!                   ++ectx->ec_stack.ga_len;
                }
                break;
  
***************
*** 1913,1931 ****
                    typval_T    optval;
                    char_u      *name = iptr->isn_arg.string;
  
!                   if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
!                       goto failed;
                    // name is always valid, checked when compiling
                    (void)eval_env_var(&name, &optval, TRUE);
                    *STACK_TV_BOT(0) = optval;
!                   ++ectx.ec_stack.ga_len;
                }
                break;
  
            // load @register
            case ISN_LOADREG:
!               if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
!                   goto failed;
                tv = STACK_TV_BOT(0);
                tv->v_type = VAR_STRING;
                tv->v_lock = 0;
--- 1727,1745 ----
                    typval_T    optval;
                    char_u      *name = iptr->isn_arg.string;
  
!                   if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
!                       return FAIL;
                    // name is always valid, checked when compiling
                    (void)eval_env_var(&name, &optval, TRUE);
                    *STACK_TV_BOT(0) = optval;
!                   ++ectx->ec_stack.ga_len;
                }
                break;
  
            // load @register
            case ISN_LOADREG:
!               if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
!                   return FAIL;
                tv = STACK_TV_BOT(0);
                tv->v_type = VAR_STRING;
                tv->v_lock = 0;
***************
*** 1933,1944 ****
                // empty string.
                tv->vval.v_string = get_reg_contents(
                                          iptr->isn_arg.number, GREG_EXPR_SRC);
!               ++ectx.ec_stack.ga_len;
                break;
  
            // store local variable
            case ISN_STORE:
!               --ectx.ec_stack.ga_len;
                tv = STACK_TV_VAR(iptr->isn_arg.number);
                clear_tv(tv);
                *tv = *STACK_TV_BOT(0);
--- 1747,1758 ----
                // empty string.
                tv->vval.v_string = get_reg_contents(
                                          iptr->isn_arg.number, GREG_EXPR_SRC);
!               ++ectx->ec_stack.ga_len;
                break;
  
            // store local variable
            case ISN_STORE:
!               --ectx->ec_stack.ga_len;
                tv = STACK_TV_VAR(iptr->isn_arg.number);
                clear_tv(tv);
                *tv = *STACK_TV_BOT(0);
***************
*** 1952,1958 ****
                    char_u      *name = iptr->isn_arg.loadstore.ls_name;
                    dictitem_T  *di = find_var_in_ht(ht, 0, name + 2, TRUE);
  
!                   --ectx.ec_stack.ga_len;
                    if (di == NULL)
                        store_var(name, STACK_TV_BOT(0));
                    else
--- 1766,1772 ----
                    char_u      *name = iptr->isn_arg.loadstore.ls_name;
                    dictitem_T  *di = find_var_in_ht(ht, 0, name + 2, TRUE);
  
!                   --ectx->ec_stack.ga_len;
                    if (di == NULL)
                        store_var(name, STACK_TV_BOT(0));
                    else
***************
*** 1975,1984 ****
                    scriptref_T     *sref = iptr->isn_arg.script.scriptref;
                    svar_T          *sv;
  
!                   sv = get_script_svar(sref, &ectx);
                    if (sv == NULL)
!                       goto failed;
!                   --ectx.ec_stack.ga_len;
  
                    // "const" and "final" are checked at compile time, locking
                    // the value needs to be checked here.
--- 1789,1798 ----
                    scriptref_T     *sref = iptr->isn_arg.script.scriptref;
                    svar_T          *sv;
  
!                   sv = get_script_svar(sref, ectx);
                    if (sv == NULL)
!                       return FAIL;
!                   --ectx->ec_stack.ga_len;
  
                    // "const" and "final" are checked at compile time, locking
                    // the value needs to be checked here.
***************
*** 2001,2007 ****
                    char_u      *s = NULL;
                    char        *msg;
  
!                   --ectx.ec_stack.ga_len;
                    tv = STACK_TV_BOT(0);
                    if (tv->v_type == VAR_STRING)
                    {
--- 1815,1821 ----
                    char_u      *s = NULL;
                    char        *msg;
  
!                   --ectx->ec_stack.ga_len;
                    tv = STACK_TV_BOT(0);
                    if (tv->v_type == VAR_STRING)
                    {
***************
*** 2026,2032 ****
  
            // store $ENV
            case ISN_STOREENV:
!               --ectx.ec_stack.ga_len;
                tv = STACK_TV_BOT(0);
                vim_setenv_ext(iptr->isn_arg.string, tv_get_string(tv));
                clear_tv(tv);
--- 1840,1846 ----
  
            // store $ENV
            case ISN_STOREENV:
!               --ectx->ec_stack.ga_len;
                tv = STACK_TV_BOT(0);
                vim_setenv_ext(iptr->isn_arg.string, tv_get_string(tv));
                clear_tv(tv);
***************
*** 2037,2043 ****
                {
                    int reg = iptr->isn_arg.number;
  
!                   --ectx.ec_stack.ga_len;
                    tv = STACK_TV_BOT(0);
                    write_reg_contents(reg == '@' ? '"' : reg,
                                                 tv_get_string(tv), -1, FALSE);
--- 1851,1857 ----
                {
                    int reg = iptr->isn_arg.number;
  
!                   --ectx->ec_stack.ga_len;
                    tv = STACK_TV_BOT(0);
                    write_reg_contents(reg == '@' ? '"' : reg,
                                                 tv_get_string(tv), -1, FALSE);
***************
*** 2047,2053 ****
  
            // 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)
                    // should not happen, type is checked when compiling
--- 1861,1867 ----
  
            // 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)
                    // should not happen, type is checked when compiling
***************
*** 2079,2088 ****
                            ht = &curtab->tp_vars->dv_hashtab;
                            break;
                        default:  // Cannot reach here
!                           goto failed;
                    }
  
!                   --ectx.ec_stack.ga_len;
                    di = find_var_in_ht(ht, 0, name, TRUE);
                    if (di == NULL)
                        store_var(iptr->isn_arg.string, STACK_TV_BOT(0));
--- 1893,1902 ----
                            ht = &curtab->tp_vars->dv_hashtab;
                            break;
                        default:  // Cannot reach here
!                           return FAIL;
                    }
  
!                   --ectx->ec_stack.ga_len;
                    di = find_var_in_ht(ht, 0, name, TRUE);
                    if (di == NULL)
                        store_var(iptr->isn_arg.string, STACK_TV_BOT(0));
***************
*** 2102,2108 ****
                SOURCING_LNUM = iptr->isn_lnum;
                set_var(iptr->isn_arg.string, STACK_TV_BOT(-1), TRUE);
                clear_tv(STACK_TV_BOT(-1));
!               --ectx.ec_stack.ga_len;
                break;
  
            // store number in local variable
--- 1916,1922 ----
                SOURCING_LNUM = iptr->isn_lnum;
                set_var(iptr->isn_arg.string, STACK_TV_BOT(-1), TRUE);
                clear_tv(STACK_TV_BOT(-1));
!               --ectx->ec_stack.ga_len;
                break;
  
            // store number in local variable
***************
*** 2184,2190 ****
                                goto on_error;
                            // append to list, only fails when out of memory
                            if (list_append_tv(list, tv) == FAIL)
!                               goto failed;
                            clear_tv(tv);
                        }
                    }
--- 1998,2004 ----
                                goto on_error;
                            // append to list, only fails when out of memory
                            if (list_append_tv(list, tv) == FAIL)
!                               return FAIL;
                            clear_tv(tv);
                        }
                    }
***************
*** 2219,2225 ****
                                goto on_error;
                            // add to dict, only fails when out of memory
                            if (dict_add_tv(dict, (char *)key, tv) == FAIL)
!                               goto failed;
                            clear_tv(tv);
                        }
                    }
--- 2033,2039 ----
                                goto on_error;
                            // add to dict, only fails when out of memory
                            if (dict_add_tv(dict, (char *)key, tv) == FAIL)
!                               return FAIL;
                            clear_tv(tv);
                        }
                    }
***************
*** 2263,2269 ****
  
                    clear_tv(tv_idx);
                    clear_tv(tv_dest);
!                   ectx.ec_stack.ga_len -= 3;
                    if (status == FAIL)
                    {
                        clear_tv(tv);
--- 2077,2083 ----
  
                    clear_tv(tv_idx);
                    clear_tv(tv_dest);
!                   ectx->ec_stack.ga_len -= 3;
                    if (status == FAIL)
                    {
                        clear_tv(tv);
***************
*** 2328,2334 ****
                    clear_tv(tv_idx1);
                    clear_tv(tv_idx2);
                    clear_tv(tv_dest);
!                   ectx.ec_stack.ga_len -= 4;
                    clear_tv(tv);
  
                    if (status == FAIL)
--- 2142,2148 ----
                    clear_tv(tv_idx1);
                    clear_tv(tv_idx2);
                    clear_tv(tv_dest);
!                   ectx->ec_stack.ga_len -= 4;
                    clear_tv(tv);
  
                    if (status == FAIL)
***************
*** 2341,2347 ****
            case ISN_STOREOUTER:
                {
                    int         depth = iptr->isn_arg.outer.outer_depth;
!                   outer_T     *outer = ectx.ec_outer;
  
                    while (depth > 1 && outer != NULL)
                    {
--- 2155,2161 ----
            case ISN_STOREOUTER:
                {
                    int         depth = iptr->isn_arg.outer.outer_depth;
!                   outer_T     *outer = ectx->ec_outer;
  
                    while (depth > 1 && outer != NULL)
                    {
***************
*** 2352,2372 ****
                    {
                        SOURCING_LNUM = iptr->isn_lnum;
                        iemsg("LOADOUTER depth more than scope levels");
!                       goto failed;
                    }
                    tv = ((typval_T *)outer->out_stack->ga_data)
                                    + outer->out_frame_idx + STACK_FRAME_SIZE
                                    + iptr->isn_arg.outer.outer_idx;
                    if (iptr->isn_type == ISN_LOADOUTER)
                    {
!                       if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
!                           goto failed;
                        copy_tv(tv, STACK_TV_BOT(0));
!                       ++ectx.ec_stack.ga_len;
                    }
                    else
                    {
!                       --ectx.ec_stack.ga_len;
                        clear_tv(tv);
                        *tv = *STACK_TV_BOT(0);
                    }
--- 2166,2186 ----
                    {
                        SOURCING_LNUM = iptr->isn_lnum;
                        iemsg("LOADOUTER depth more than scope levels");
!                       return FAIL;
                    }
                    tv = ((typval_T *)outer->out_stack->ga_data)
                                    + outer->out_frame_idx + STACK_FRAME_SIZE
                                    + iptr->isn_arg.outer.outer_idx;
                    if (iptr->isn_type == ISN_LOADOUTER)
                    {
!                       if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
!                           return FAIL;
                        copy_tv(tv, STACK_TV_BOT(0));
!                       ++ectx->ec_stack.ga_len;
                    }
                    else
                    {
!                       --ectx->ec_stack.ga_len;
                        clear_tv(tv);
                        *tv = *STACK_TV_BOT(0);
                    }
***************
*** 2453,2459 ****
  
                    clear_tv(tv_idx);
                    clear_tv(tv_dest);
!                   ectx.ec_stack.ga_len -= 2;
                    if (status == FAIL)
                        goto on_error;
                }
--- 2267,2273 ----
  
                    clear_tv(tv_idx);
                    clear_tv(tv_dest);
!                   ectx->ec_stack.ga_len -= 2;
                    if (status == FAIL)
                        goto on_error;
                }
***************
*** 2505,2511 ****
                    clear_tv(tv_idx1);
                    clear_tv(tv_idx2);
                    clear_tv(tv_dest);
!                   ectx.ec_stack.ga_len -= 3;
                    if (status == FAIL)
                        goto on_error;
                }
--- 2319,2325 ----
                    clear_tv(tv_idx1);
                    clear_tv(tv_idx2);
                    clear_tv(tv_dest);
!                   ectx->ec_stack.ga_len -= 3;
                    if (status == FAIL)
                        goto on_error;
                }
***************
*** 2521,2531 ****
            case ISN_PUSHFUNC:
            case ISN_PUSHCHANNEL:
            case ISN_PUSHJOB:
!               if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
!                   goto failed;
                tv = STACK_TV_BOT(0);
                tv->v_lock = 0;
!               ++ectx.ec_stack.ga_len;
                switch (iptr->isn_type)
                {
                    case ISN_PUSHNR:
--- 2335,2345 ----
            case ISN_PUSHFUNC:
            case ISN_PUSHCHANNEL:
            case ISN_PUSHJOB:
!               if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
!                   return FAIL;
                tv = STACK_TV_BOT(0);
                tv->v_lock = 0;
!               ++ectx->ec_stack.ga_len;
                switch (iptr->isn_type)
                {
                    case ISN_PUSHNR:
***************
*** 2597,2604 ****
            // create a list from items on the stack; uses a single allocation
            // for the list header and the items
            case ISN_NEWLIST:
!               if (exe_newlist(iptr->isn_arg.number, &ectx) == FAIL)
!                   goto failed;
                break;
  
            // create a dict from items on the stack
--- 2411,2418 ----
            // create a list from items on the stack; uses a single allocation
            // for the list header and the items
            case ISN_NEWLIST:
!               if (exe_newlist(iptr->isn_arg.number, ectx) == FAIL)
!                   return FAIL;
                break;
  
            // create a dict from items on the stack
***************
*** 2608,2616 ****
                    dict_T      *dict = dict_alloc();
                    dictitem_T  *item;
                    char_u      *key;
  
                    if (dict == NULL)
!                       goto failed;
                    for (idx = 0; idx < count; ++idx)
                    {
                        // have already checked key type is VAR_STRING
--- 2422,2431 ----
                    dict_T      *dict = dict_alloc();
                    dictitem_T  *item;
                    char_u      *key;
+                   int         idx;
  
                    if (dict == NULL)
!                       return FAIL;
                    for (idx = 0; idx < count; ++idx)
                    {
                        // have already checked key type is VAR_STRING
***************
*** 2631,2637 ****
                        if (item == NULL)
                        {
                            dict_unref(dict);
!                           goto failed;
                        }
                        item->di_tv = *STACK_TV_BOT(2 * (idx - count) + 1);
                        item->di_tv.v_lock = 0;
--- 2446,2452 ----
                        if (item == NULL)
                        {
                            dict_unref(dict);
!                           return FAIL;
                        }
                        item->di_tv = *STACK_TV_BOT(2 * (idx - count) + 1);
                        item->di_tv.v_lock = 0;
***************
*** 2639,2654 ****
                        {
                            // can this ever happen?
                            dict_unref(dict);
!                           goto failed;
                        }
                    }
  
                    if (count > 0)
!                       ectx.ec_stack.ga_len -= 2 * count - 1;
!                   else if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
!                       goto failed;
                    else
!                       ++ectx.ec_stack.ga_len;
                    tv = STACK_TV_BOT(-1);
                    tv->v_type = VAR_DICT;
                    tv->v_lock = 0;
--- 2454,2469 ----
                        {
                            // can this ever happen?
                            dict_unref(dict);
!                           return FAIL;
                        }
                    }
  
                    if (count > 0)
!                       ectx->ec_stack.ga_len -= 2 * count - 1;
!                   else if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
!                       return FAIL;
                    else
!                       ++ectx->ec_stack.ga_len;
                    tv = STACK_TV_BOT(-1);
                    tv->v_type = VAR_DICT;
                    tv->v_lock = 0;
***************
*** 2663,2670 ****
                if (call_dfunc(iptr->isn_arg.dfunc.cdf_idx,
                                NULL,
                                iptr->isn_arg.dfunc.cdf_argcount,
!                               &funclocal,
!                               &ectx) == FAIL)
                    goto on_error;
                break;
  
--- 2478,2484 ----
                if (call_dfunc(iptr->isn_arg.dfunc.cdf_idx,
                                NULL,
                                iptr->isn_arg.dfunc.cdf_argcount,
!                               ectx) == FAIL)
                    goto on_error;
                break;
  
***************
*** 2673,2679 ****
                SOURCING_LNUM = iptr->isn_lnum;
                if (call_bfunc(iptr->isn_arg.bfunc.cbf_idx,
                              iptr->isn_arg.bfunc.cbf_argcount,
!                             &ectx) == FAIL)
                    goto on_error;
                break;
  
--- 2487,2493 ----
                SOURCING_LNUM = iptr->isn_lnum;
                if (call_bfunc(iptr->isn_arg.bfunc.cbf_idx,
                              iptr->isn_arg.bfunc.cbf_argcount,
!                             ectx) == FAIL)
                    goto on_error;
                break;
  
***************
*** 2693,2704 ****
                    else
                    {
                        // Get the funcref from the stack.
!                       --ectx.ec_stack.ga_len;
                        partial_tv = *STACK_TV_BOT(0);
                        tv = &partial_tv;
                    }
!                   r = call_partial(tv, pfunc->cpf_argcount,
!                                                           &funclocal, &ectx);
                    if (tv == &partial_tv)
                        clear_tv(&partial_tv);
                    if (r == FAIL)
--- 2507,2517 ----
                    else
                    {
                        // Get the funcref from the stack.
!                       --ectx->ec_stack.ga_len;
                        partial_tv = *STACK_TV_BOT(0);
                        tv = &partial_tv;
                    }
!                   r = call_partial(tv, pfunc->cpf_argcount, ectx);
                    if (tv == &partial_tv)
                        clear_tv(&partial_tv);
                    if (r == FAIL)
***************
*** 2710,2716 ****
                // PCALL finished, arguments have been consumed and replaced by
                // the return value.  Now clear the funcref from the stack,
                // and move the return value in its place.
!               --ectx.ec_stack.ga_len;
                clear_tv(STACK_TV_BOT(-1));
                *STACK_TV_BOT(-1) = *STACK_TV_BOT(0);
                break;
--- 2523,2529 ----
                // PCALL finished, arguments have been consumed and replaced by
                // the return value.  Now clear the funcref from the stack,
                // and move the return value in its place.
!               --ectx->ec_stack.ga_len;
                clear_tv(STACK_TV_BOT(-1));
                *STACK_TV_BOT(-1) = *STACK_TV_BOT(0);
                break;
***************
*** 2722,2738 ****
  
                    SOURCING_LNUM = iptr->isn_lnum;
                    if (call_eval_func(cufunc->cuf_name, cufunc->cuf_argcount,
!                                             &funclocal, &ectx, iptr) == FAIL)
                        goto on_error;
                }
                break;
  
            // return from a :def function call
            case ISN_RETURN_ZERO:
!               if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
!                   goto failed;
                tv = STACK_TV_BOT(0);
!               ++ectx.ec_stack.ga_len;
                tv->v_type = VAR_NUMBER;
                tv->vval.v_number = 0;
                tv->v_lock = 0;
--- 2535,2551 ----
  
                    SOURCING_LNUM = iptr->isn_lnum;
                    if (call_eval_func(cufunc->cuf_name, cufunc->cuf_argcount,
!                                                          ectx, iptr) == FAIL)
                        goto on_error;
                }
                break;
  
            // return from a :def function call
            case ISN_RETURN_ZERO:
!               if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
!                   return FAIL;
                tv = STACK_TV_BOT(0);
!               ++ectx->ec_stack.ga_len;
                tv->v_type = VAR_NUMBER;
                tv->vval.v_number = 0;
                tv->v_lock = 0;
***************
*** 2740,2759 ****
  
            case ISN_RETURN:
                {
!                   garray_T    *trystack = &ectx.ec_trystack;
                    trycmd_T    *trycmd = NULL;
  
                    if (trystack->ga_len > 0)
                        trycmd = ((trycmd_T *)trystack->ga_data)
                                                        + trystack->ga_len - 1;
                    if (trycmd != NULL
!                                && trycmd->tcd_frame_idx == ectx.ec_frame_idx)
                    {
                        // jump to ":finally" or ":endtry"
                        if (trycmd->tcd_finally_idx != 0)
!                           ectx.ec_iidx = trycmd->tcd_finally_idx;
                        else
!                           ectx.ec_iidx = trycmd->tcd_endtry_idx;
                        trycmd->tcd_return = TRUE;
                    }
                    else
--- 2553,2572 ----
  
            case ISN_RETURN:
                {
!                   garray_T    *trystack = &ectx->ec_trystack;
                    trycmd_T    *trycmd = NULL;
  
                    if (trystack->ga_len > 0)
                        trycmd = ((trycmd_T *)trystack->ga_data)
                                                        + trystack->ga_len - 1;
                    if (trycmd != NULL
!                                && trycmd->tcd_frame_idx == ectx->ec_frame_idx)
                    {
                        // jump to ":finally" or ":endtry"
                        if (trycmd->tcd_finally_idx != 0)
!                           ectx->ec_iidx = trycmd->tcd_finally_idx;
                        else
!                           ectx->ec_iidx = trycmd->tcd_endtry_idx;
                        trycmd->tcd_return = TRUE;
                    }
                    else
***************
*** 2769,2786 ****
                                               + iptr->isn_arg.funcref.fr_func;
  
                    if (pt == NULL)
!                       goto failed;
!                   if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
                    {
                        vim_free(pt);
!                       goto failed;
                    }
                    if (fill_partial_and_closure(pt, pt_dfunc->df_ufunc,
!                                                               &ectx) == FAIL)
!                       goto failed;
  
                    tv = STACK_TV_BOT(0);
!                   ++ectx.ec_stack.ga_len;
                    tv->vval.v_partial = pt;
                    tv->v_type = VAR_PARTIAL;
                    tv->v_lock = 0;
--- 2582,2599 ----
                                               + iptr->isn_arg.funcref.fr_func;
  
                    if (pt == NULL)
!                       return FAIL;
!                   if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
                    {
                        vim_free(pt);
!                       return FAIL;
                    }
                    if (fill_partial_and_closure(pt, pt_dfunc->df_ufunc,
!                                                                ectx) == FAIL)
!                       return FAIL;
  
                    tv = STACK_TV_BOT(0);
!                   ++ectx->ec_stack.ga_len;
                    tv->vval.v_partial = pt;
                    tv->v_type = VAR_PARTIAL;
                    tv->v_lock = 0;
***************
*** 2793,2800 ****
                    newfunc_T   *newfunc = &iptr->isn_arg.newfunc;
  
                    if (copy_func(newfunc->nf_lambda, newfunc->nf_global,
!                                                               &ectx) == FAIL)
!                       goto failed;
                }
                break;
  
--- 2606,2613 ----
                    newfunc_T   *newfunc = &iptr->isn_arg.newfunc;
  
                    if (copy_func(newfunc->nf_lambda, newfunc->nf_global,
!                                                                ectx) == FAIL)
!                       return FAIL;
                }
                break;
  
***************
*** 2841,2851 ****
                        {
                            // drop the value from the stack
                            clear_tv(tv);
!                           --ectx.ec_stack.ga_len;
                        }
                    }
                    if (jump)
!                       ectx.ec_iidx = iptr->isn_arg.jump.jump_where;
                }
                break;
  
--- 2654,2664 ----
                        {
                            // drop the value from the stack
                            clear_tv(tv);
!                           --ectx->ec_stack.ga_len;
                        }
                    }
                    if (jump)
!                       ectx->ec_iidx = iptr->isn_arg.jump.jump_where;
                }
                break;
  
***************
*** 2856,2862 ****
                if (tv->v_type != VAR_UNKNOWN
                        && !(tv->v_type == VAR_SPECIAL
                                            && tv->vval.v_number == VVAL_NONE))
!                   ectx.ec_iidx = iptr->isn_arg.jumparg.jump_where;
                break;
  
            // top of a for loop
--- 2669,2675 ----
                if (tv->v_type != VAR_UNKNOWN
                        && !(tv->v_type == VAR_SPECIAL
                                            && tv->vval.v_number == VVAL_NONE))
!                   ectx->ec_iidx = iptr->isn_arg.jumparg.jump_where;
                break;
  
            // top of a for loop
***************
*** 2866,2873 ****
                    typval_T    *idxtv =
                                   STACK_TV_VAR(iptr->isn_arg.forloop.for_idx);
  
!                   if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
!                       goto failed;
                    if (ltv->v_type == VAR_LIST)
                    {
                        list_T *list = ltv->vval.v_list;
--- 2679,2686 ----
                    typval_T    *idxtv =
                                   STACK_TV_VAR(iptr->isn_arg.forloop.for_idx);
  
!                   if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
!                       return FAIL;
                    if (ltv->v_type == VAR_LIST)
                    {
                        list_T *list = ltv->vval.v_list;
***************
*** 2878,2885 ****
                                       || idxtv->vval.v_number >= list->lv_len)
                        {
                            // past the end of the list, jump to "endfor"
!                           ectx.ec_iidx = iptr->isn_arg.forloop.for_end;
!                           may_restore_cmdmod(&funclocal);
                        }
                        else if (list->lv_first == &range_list_item)
                        {
--- 2691,2698 ----
                                       || idxtv->vval.v_number >= list->lv_len)
                        {
                            // past the end of the list, jump to "endfor"
!                           ectx->ec_iidx = iptr->isn_arg.forloop.for_end;
!                           may_restore_cmdmod(&ectx->ec_funclocal);
                        }
                        else if (list->lv_first == &range_list_item)
                        {
***************
*** 2889,2895 ****
                            tv->v_lock = 0;
                            tv->vval.v_number = list_find_nr(
                                             list, idxtv->vval.v_number, NULL);
!                           ++ectx.ec_stack.ga_len;
                        }
                        else
                        {
--- 2702,2708 ----
                            tv->v_lock = 0;
                            tv->vval.v_number = list_find_nr(
                                             list, idxtv->vval.v_number, NULL);
!                           ++ectx->ec_stack.ga_len;
                        }
                        else
                        {
***************
*** 2897,2903 ****
                                                         idxtv->vval.v_number);
  
                            copy_tv(&li->li_tv, STACK_TV_BOT(0));
!                           ++ectx.ec_stack.ga_len;
                        }
                    }
                    else if (ltv->v_type == VAR_STRING)
--- 2710,2716 ----
                                                         idxtv->vval.v_number);
  
                            copy_tv(&li->li_tv, STACK_TV_BOT(0));
!                           ++ectx->ec_stack.ga_len;
                        }
                    }
                    else if (ltv->v_type == VAR_STRING)
***************
*** 2910,2917 ****
                        if (str == NULL || str[idxtv->vval.v_number] == NUL)
                        {
                            // past the end of the string, jump to "endfor"
!                           ectx.ec_iidx = iptr->isn_arg.forloop.for_end;
!                           may_restore_cmdmod(&funclocal);
                        }
                        else
                        {
--- 2723,2730 ----
                        if (str == NULL || str[idxtv->vval.v_number] == NUL)
                        {
                            // past the end of the string, jump to "endfor"
!                           ectx->ec_iidx = iptr->isn_arg.forloop.for_end;
!                           may_restore_cmdmod(&ectx->ec_funclocal);
                        }
                        else
                        {
***************
*** 2922,2928 ****
                            tv->v_type = VAR_STRING;
                            tv->vval.v_string = vim_strnsave(
                                             str + idxtv->vval.v_number, clen);
!                           ++ectx.ec_stack.ga_len;
                            idxtv->vval.v_number += clen - 1;
                        }
                    }
--- 2735,2741 ----
                            tv->v_type = VAR_STRING;
                            tv->vval.v_string = vim_strnsave(
                                             str + idxtv->vval.v_number, clen);
!                           ++ectx->ec_stack.ga_len;
                            idxtv->vval.v_number += clen - 1;
                        }
                    }
***************
*** 2946,2953 ****
                                     || idxtv->vval.v_number >= blob_len(blob))
                        {
                            // past the end of the blob, jump to "endfor"
!                           ectx.ec_iidx = iptr->isn_arg.forloop.for_end;
!                           may_restore_cmdmod(&funclocal);
                        }
                        else
                        {
--- 2759,2766 ----
                                     || idxtv->vval.v_number >= blob_len(blob))
                        {
                            // past the end of the blob, jump to "endfor"
!                           ectx->ec_iidx = iptr->isn_arg.forloop.for_end;
!                           may_restore_cmdmod(&ectx->ec_funclocal);
                        }
                        else
                        {
***************
*** 2956,2969 ****
                            tv->v_type = VAR_NUMBER;
                            tv->vval.v_number = blob_get(blob,
                                                         idxtv->vval.v_number);
!                           ++ectx.ec_stack.ga_len;
                        }
                    }
                    else
                    {
                        semsg(_(e_for_loop_on_str_not_supported),
                                                    vartype_name(ltv->v_type));
!                       goto failed;
                    }
                }
                break;
--- 2769,2782 ----
                            tv->v_type = VAR_NUMBER;
                            tv->vval.v_number = blob_get(blob,
                                                         idxtv->vval.v_number);
!                           ++ectx->ec_stack.ga_len;
                        }
                    }
                    else
                    {
                        semsg(_(e_for_loop_on_str_not_supported),
                                                    vartype_name(ltv->v_type));
!                       return FAIL;
                    }
                }
                break;
***************
*** 2973,2987 ****
                {
                    trycmd_T    *trycmd = NULL;
  
!                   if (GA_GROW(&ectx.ec_trystack, 1) == FAIL)
!                       goto failed;
!                   trycmd = ((trycmd_T *)ectx.ec_trystack.ga_data)
!                                                    + ectx.ec_trystack.ga_len;
!                   ++ectx.ec_trystack.ga_len;
                    ++trylevel;
                    CLEAR_POINTER(trycmd);
!                   trycmd->tcd_frame_idx = ectx.ec_frame_idx;
!                   trycmd->tcd_stack_len = ectx.ec_stack.ga_len;
                    trycmd->tcd_catch_idx =
                                          iptr->isn_arg.try.try_ref->try_catch;
                    trycmd->tcd_finally_idx =
--- 2786,2800 ----
                {
                    trycmd_T    *trycmd = NULL;
  
!                   if (GA_GROW(&ectx->ec_trystack, 1) == FAIL)
!                       return FAIL;
!                   trycmd = ((trycmd_T *)ectx->ec_trystack.ga_data)
!                                                    + ectx->ec_trystack.ga_len;
!                   ++ectx->ec_trystack.ga_len;
                    ++trylevel;
                    CLEAR_POINTER(trycmd);
!                   trycmd->tcd_frame_idx = ectx->ec_frame_idx;
!                   trycmd->tcd_stack_len = ectx->ec_stack.ga_len;
                    trycmd->tcd_catch_idx =
                                          iptr->isn_arg.try.try_ref->try_catch;
                    trycmd->tcd_finally_idx =
***************
*** 2996,3007 ****
                {
                    SOURCING_LNUM = iptr->isn_lnum;
                    iemsg("Evaluating catch while current_exception is NULL");
!                   goto failed;
                }
!               if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
!                   goto failed;
                tv = STACK_TV_BOT(0);
!               ++ectx.ec_stack.ga_len;
                tv->v_type = VAR_STRING;
                tv->v_lock = 0;
                tv->vval.v_string = vim_strsave(
--- 2809,2820 ----
                {
                    SOURCING_LNUM = iptr->isn_lnum;
                    iemsg("Evaluating catch while current_exception is NULL");
!                   return FAIL;
                }
!               if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
!                   return FAIL;
                tv = STACK_TV_BOT(0);
!               ++ectx->ec_stack.ga_len;
                tv->v_type = VAR_STRING;
                tv->v_lock = 0;
                tv->vval.v_string = vim_strsave(
***************
*** 3010,3018 ****
  
            case ISN_CATCH:
                {
!                   garray_T    *trystack = &ectx.ec_trystack;
  
!                   may_restore_cmdmod(&funclocal);
                    if (trystack->ga_len > 0)
                    {
                        trycmd_T    *trycmd = ((trycmd_T *)trystack->ga_data)
--- 2823,2831 ----
  
            case ISN_CATCH:
                {
!                   garray_T    *trystack = &ectx->ec_trystack;
  
!                   may_restore_cmdmod(&ectx->ec_funclocal);
                    if (trystack->ga_len > 0)
                    {
                        trycmd_T    *trycmd = ((trycmd_T *)trystack->ga_data)
***************
*** 3027,3033 ****
  
            case ISN_TRYCONT:
                {
!                   garray_T    *trystack = &ectx.ec_trystack;
                    trycont_T   *trycont = &iptr->isn_arg.trycont;
                    int         i;
                    trycmd_T    *trycmd;
--- 2840,2846 ----
  
            case ISN_TRYCONT:
                {
!                   garray_T    *trystack = &ectx->ec_trystack;
                    trycont_T   *trycont = &iptr->isn_arg.trycont;
                    int         i;
                    trycmd_T    *trycmd;
***************
*** 3037,3043 ****
                    {
                        siemsg("TRYCONT: expected %d levels, found %d",
                                        trycont->tct_levels, trystack->ga_len);
!                       goto failed;
                    }
                    // Make :endtry jump to any outer try block and the last
                    // :endtry inside the loop to the loop start.
--- 2850,2856 ----
                    {
                        siemsg("TRYCONT: expected %d levels, found %d",
                                        trycont->tct_levels, trystack->ga_len);
!                       return FAIL;
                    }
                    // Make :endtry jump to any outer try block and the last
                    // :endtry inside the loop to the loop start.
***************
*** 3052,3064 ****
                            ? trycmd->tcd_endtry_idx : trycmd->tcd_finally_idx;
                    }
                    // jump to :finally or :endtry of current try statement
!                   ectx.ec_iidx = iidx;
                }
                break;
  
            case ISN_FINALLY:
                {
!                   garray_T    *trystack = &ectx.ec_trystack;
                    trycmd_T    *trycmd = ((trycmd_T *)trystack->ga_data)
                                                        + trystack->ga_len - 1;
  
--- 2865,2877 ----
                            ? trycmd->tcd_endtry_idx : trycmd->tcd_finally_idx;
                    }
                    // jump to :finally or :endtry of current try statement
!                   ectx->ec_iidx = iidx;
                }
                break;
  
            case ISN_FINALLY:
                {
!                   garray_T    *trystack = &ectx->ec_trystack;
                    trycmd_T    *trycmd = ((trycmd_T *)trystack->ga_data)
                                                        + trystack->ga_len - 1;
  
***************
*** 3071,3077 ****
            // end of ":try" block
            case ISN_ENDTRY:
                {
!                   garray_T    *trystack = &ectx.ec_trystack;
  
                    if (trystack->ga_len > 0)
                    {
--- 2884,2890 ----
            // end of ":try" block
            case ISN_ENDTRY:
                {
!                   garray_T    *trystack = &ectx->ec_trystack;
  
                    if (trystack->ga_len > 0)
                    {
***************
*** 3079,3085 ****
  
                        --trystack->ga_len;
                        --trylevel;
!                       ectx.ec_in_catch = FALSE;
                        trycmd = ((trycmd_T *)trystack->ga_data)
                                                            + trystack->ga_len;
                        if (trycmd->tcd_caught && current_exception != NULL)
--- 2892,2898 ----
  
                        --trystack->ga_len;
                        --trylevel;
!                       ectx->ec_in_catch = FALSE;
                        trycmd = ((trycmd_T *)trystack->ga_data)
                                                            + trystack->ga_len;
                        if (trycmd->tcd_caught && current_exception != NULL)
***************
*** 3093,3114 ****
                        if (trycmd->tcd_return)
                            goto func_return;
  
!                       while (ectx.ec_stack.ga_len > trycmd->tcd_stack_len)
                        {
!                           --ectx.ec_stack.ga_len;
                            clear_tv(STACK_TV_BOT(0));
                        }
                        if (trycmd->tcd_cont != 0)
                            // handling :continue: jump to outer try block or
                            // start of the loop
!                           ectx.ec_iidx = trycmd->tcd_cont - 1;
                    }
                }
                break;
  
            case ISN_THROW:
                {
!                   garray_T    *trystack = &ectx.ec_trystack;
  
                    if (trystack->ga_len == 0 && trylevel == 0 && emsg_silent)
                    {
--- 2906,2927 ----
                        if (trycmd->tcd_return)
                            goto func_return;
  
!                       while (ectx->ec_stack.ga_len > trycmd->tcd_stack_len)
                        {
!                           --ectx->ec_stack.ga_len;
                            clear_tv(STACK_TV_BOT(0));
                        }
                        if (trycmd->tcd_cont != 0)
                            // handling :continue: jump to outer try block or
                            // start of the loop
!                           ectx->ec_iidx = trycmd->tcd_cont - 1;
                    }
                }
                break;
  
            case ISN_THROW:
                {
!                   garray_T    *trystack = &ectx->ec_trystack;
  
                    if (trystack->ga_len == 0 && trylevel == 0 && emsg_silent)
                    {
***************
*** 3120,3126 ****
                        tv->vval.v_number = 0;
                        goto done;
                    }
!                   --ectx.ec_stack.ga_len;
                    tv = STACK_TV_BOT(0);
                    if (tv->vval.v_string == NULL
                                       || *skipwhite(tv->vval.v_string) == NUL)
--- 2933,2939 ----
                        tv->vval.v_number = 0;
                        goto done;
                    }
!                   --ectx->ec_stack.ga_len;
                    tv = STACK_TV_BOT(0);
                    if (tv->vval.v_string == NULL
                                       || *skipwhite(tv->vval.v_string) == NUL)
***************
*** 3128,3134 ****
                        vim_free(tv->vval.v_string);
                        SOURCING_LNUM = iptr->isn_lnum;
                        emsg(_(e_throw_with_empty_string));
!                       goto failed;
                    }
  
                    // Inside a "catch" we need to first discard the caught
--- 2941,2947 ----
                        vim_free(tv->vval.v_string);
                        SOURCING_LNUM = iptr->isn_lnum;
                        emsg(_(e_throw_with_empty_string));
!                       return FAIL;
                    }
  
                    // Inside a "catch" we need to first discard the caught
***************
*** 3151,3157 ****
                                                                       == FAIL)
                    {
                        vim_free(tv->vval.v_string);
!                       goto failed;
                    }
                    did_throw = TRUE;
                }
--- 2964,2970 ----
                                                                       == FAIL)
                    {
                        vim_free(tv->vval.v_string);
!                       return FAIL;
                    }
                    did_throw = TRUE;
                }
***************
*** 3174,3180 ****
                        default: res = 0; break;
                    }
  
!                   --ectx.ec_stack.ga_len;
                    tv1->v_type = VAR_BOOL;
                    tv1->vval.v_number = res ? VVAL_TRUE : VVAL_FALSE;
                }
--- 2987,2993 ----
                        default: res = 0; break;
                    }
  
!                   --ectx->ec_stack.ga_len;
                    tv1->v_type = VAR_BOOL;
                    tv1->vval.v_number = res ? VVAL_TRUE : VVAL_FALSE;
                }
***************
*** 3207,3213 ****
                        default: res = 0; break;
                    }
  
!                   --ectx.ec_stack.ga_len;
                    if (iptr->isn_type == ISN_COMPARENR)
                    {
                        tv1->v_type = VAR_BOOL;
--- 3020,3026 ----
                        default: res = 0; break;
                    }
  
!                   --ectx->ec_stack.ga_len;
                    if (iptr->isn_type == ISN_COMPARENR)
                    {
                        tv1->v_type = VAR_BOOL;
***************
*** 3245,3251 ****
                        case EXPR_SEQUAL: cmp = arg1 <= arg2; break;
                        default: cmp = 0; break;
                    }
!                   --ectx.ec_stack.ga_len;
                    if (iptr->isn_type == ISN_COMPAREFLOAT)
                    {
                        tv1->v_type = VAR_BOOL;
--- 3058,3064 ----
                        case EXPR_SEQUAL: cmp = arg1 <= arg2; break;
                        default: cmp = 0; break;
                    }
!                   --ectx->ec_stack.ga_len;
                    if (iptr->isn_type == ISN_COMPAREFLOAT)
                    {
                        tv1->v_type = VAR_BOOL;
***************
*** 3276,3282 ****
                        case EXPR_ISNOT: cmp = arg1 != arg2; break;
                        default: cmp = 0; break;
                    }
!                   --ectx.ec_stack.ga_len;
                    clear_tv(tv1);
                    clear_tv(tv2);
                    tv1->v_type = VAR_BOOL;
--- 3089,3095 ----
                        case EXPR_ISNOT: cmp = arg1 != arg2; break;
                        default: cmp = 0; break;
                    }
!                   --ectx->ec_stack.ga_len;
                    clear_tv(tv1);
                    clear_tv(tv2);
                    tv1->v_type = VAR_BOOL;
***************
*** 3300,3306 ****
                        case EXPR_ISNOT: cmp = arg1 != arg2; break;
                        default: cmp = 0; break;
                    }
!                   --ectx.ec_stack.ga_len;
                    clear_tv(tv1);
                    clear_tv(tv2);
                    tv1->v_type = VAR_BOOL;
--- 3113,3119 ----
                        case EXPR_ISNOT: cmp = arg1 != arg2; break;
                        default: cmp = 0; break;
                    }
!                   --ectx->ec_stack.ga_len;
                    clear_tv(tv1);
                    clear_tv(tv2);
                    tv1->v_type = VAR_BOOL;
***************
*** 3322,3328 ****
                    SOURCING_LNUM = iptr->isn_lnum;
                    typval_compare(tv1, tv2, exprtype, ic);
                    clear_tv(tv2);
!                   --ectx.ec_stack.ga_len;
                }
                break;
  
--- 3135,3141 ----
                    SOURCING_LNUM = iptr->isn_lnum;
                    typval_compare(tv1, tv2, exprtype, ic);
                    clear_tv(tv2);
!                   --ectx->ec_stack.ga_len;
                }
                break;
  
***************
*** 3338,3344 ****
                    else
                        eval_addblob(tv1, tv2);
                    clear_tv(tv2);
!                   --ectx.ec_stack.ga_len;
                }
                break;
  
--- 3151,3157 ----
                    else
                        eval_addblob(tv1, tv2);
                    clear_tv(tv2);
!                   --ectx->ec_stack.ga_len;
                }
                break;
  
***************
*** 3356,3364 ****
                        goto on_error;
                    }
                    if (list_append_tv(l, tv2) == FAIL)
!                       goto failed;
                    clear_tv(tv2);
!                   --ectx.ec_stack.ga_len;
                }
                break;
  
--- 3169,3177 ----
                        goto on_error;
                    }
                    if (list_append_tv(l, tv2) == FAIL)
!                       return FAIL;
                    clear_tv(tv2);
!                   --ectx->ec_stack.ga_len;
                }
                break;
  
***************
*** 3381,3387 ****
                    if (error)
                        goto on_error;
                    ga_append(&b->bv_ga, (int)n);
!                   --ectx.ec_stack.ga_len;
                }
                break;
  
--- 3194,3200 ----
                    if (error)
                        goto on_error;
                    ga_append(&b->bv_ga, (int)n);
!                   --ectx->ec_stack.ga_len;
                }
                break;
  
***************
*** 3402,3408 ****
                        {
                            eval_addlist(tv1, tv2);
                            clear_tv(tv2);
!                           --ectx.ec_stack.ga_len;
                            break;
                        }
                        else if (tv1->v_type == VAR_BLOB
--- 3215,3221 ----
                        {
                            eval_addlist(tv1, tv2);
                            clear_tv(tv2);
!                           --ectx->ec_stack.ga_len;
                            break;
                        }
                        else if (tv1->v_type == VAR_BLOB
***************
*** 3410,3416 ****
                        {
                            eval_addblob(tv1, tv2);
                            clear_tv(tv2);
!                           --ectx.ec_stack.ga_len;
                            break;
                        }
                    }
--- 3223,3229 ----
                        {
                            eval_addblob(tv1, tv2);
                            clear_tv(tv2);
!                           --ectx->ec_stack.ga_len;
                            break;
                        }
                    }
***************
*** 3467,3473 ****
                        clear_tv(tv2);
                        tv1->v_type = VAR_FLOAT;
                        tv1->vval.v_float = f1;
!                       --ectx.ec_stack.ga_len;
                    }
                    else
  #endif
--- 3280,3286 ----
                        clear_tv(tv2);
                        tv1->v_type = VAR_FLOAT;
                        tv1->vval.v_float = f1;
!                       --ectx->ec_stack.ga_len;
                    }
                    else
  #endif
***************
*** 3492,3498 ****
                        clear_tv(tv2);
                        tv1->v_type = VAR_NUMBER;
                        tv1->vval.v_number = n1;
!                       --ectx.ec_stack.ga_len;
                    }
                }
                break;
--- 3305,3311 ----
                        clear_tv(tv2);
                        tv1->v_type = VAR_NUMBER;
                        tv1->vval.v_number = n1;
!                       --ectx->ec_stack.ga_len;
                    }
                }
                break;
***************
*** 3506,3512 ****
                    res = concat_str(str1, str2);
                    clear_tv(STACK_TV_BOT(-2));
                    clear_tv(STACK_TV_BOT(-1));
!                   --ectx.ec_stack.ga_len;
                    STACK_TV_BOT(-1)->vval.v_string = res;
                }
                break;
--- 3319,3325 ----
                    res = concat_str(str1, str2);
                    clear_tv(STACK_TV_BOT(-2));
                    clear_tv(STACK_TV_BOT(-1));
!                   --ectx->ec_stack.ga_len;
                    STACK_TV_BOT(-1)->vval.v_string = res;
                }
                break;
***************
*** 3530,3536 ****
                    tv = STACK_TV_BOT(-1);
                    n2 = tv->vval.v_number;
  
!                   ectx.ec_stack.ga_len -= is_slice ? 2 : 1;
                    tv = STACK_TV_BOT(-1);
                    if (is_slice)
                        // Slice: Select the characters from the string
--- 3343,3349 ----
                    tv = STACK_TV_BOT(-1);
                    n2 = tv->vval.v_number;
  
!                   ectx->ec_stack.ga_len -= is_slice ? 2 : 1;
                    tv = STACK_TV_BOT(-1);
                    if (is_slice)
                        // Slice: Select the characters from the string
***************
*** 3575,3581 ****
                        clear_tv(tv);
                    }
  
!                   ectx.ec_stack.ga_len -= is_slice ? 2 : 1;
                    tv = STACK_TV_BOT(-1);
                    SOURCING_LNUM = iptr->isn_lnum;
                    if (is_blob)
--- 3388,3394 ----
                        clear_tv(tv);
                    }
  
!                   ectx->ec_stack.ga_len -= is_slice ? 2 : 1;
                    tv = STACK_TV_BOT(-1);
                    SOURCING_LNUM = iptr->isn_lnum;
                    if (is_blob)
***************
*** 3614,3620 ****
                    clear_tv(var1);
                    if (is_slice)
                        clear_tv(var2);
!                   ectx.ec_stack.ga_len -= is_slice ? 2 : 1;
                    if (res == FAIL)
                        goto on_error;
                }
--- 3427,3433 ----
                    clear_tv(var1);
                    if (is_slice)
                        clear_tv(var2);
!                   ectx->ec_stack.ga_len -= is_slice ? 2 : 1;
                    if (res == FAIL)
                        goto on_error;
                }
***************
*** 3655,3669 ****
                    tv = STACK_TV_BOT(-1);
                    li = list_find(tv->vval.v_list, index);
  
!                   if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
!                       goto failed;
!                   ++ectx.ec_stack.ga_len;
                    copy_tv(&li->li_tv, STACK_TV_BOT(-1));
  
                    // Useful when used in unpack assignment.  Reset at
                    // ISN_DROP.
!                   where.wt_index = index + 1;
!                   where.wt_variable = TRUE;
                }
                break;
  
--- 3468,3482 ----
                    tv = STACK_TV_BOT(-1);
                    li = list_find(tv->vval.v_list, index);
  
!                   if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
!                       return FAIL;
!                   ++ectx->ec_stack.ga_len;
                    copy_tv(&li->li_tv, STACK_TV_BOT(-1));
  
                    // Useful when used in unpack assignment.  Reset at
                    // ISN_DROP.
!                   ectx->ec_where.wt_index = index + 1;
!                   ectx->ec_where.wt_variable = TRUE;
                }
                break;
  
***************
*** 3693,3699 ****
                        // If :silent! is used we will continue, make sure the
                        // stack contents makes sense.
                        clear_tv(tv);
!                       --ectx.ec_stack.ga_len;
                        tv = STACK_TV_BOT(-1);
                        clear_tv(tv);
                        tv->v_type = VAR_NUMBER;
--- 3506,3512 ----
                        // If :silent! is used we will continue, make sure the
                        // stack contents makes sense.
                        clear_tv(tv);
!                       --ectx->ec_stack.ga_len;
                        tv = STACK_TV_BOT(-1);
                        clear_tv(tv);
                        tv->v_type = VAR_NUMBER;
***************
*** 3701,3707 ****
                        goto on_fatal_error;
                    }
                    clear_tv(tv);
!                   --ectx.ec_stack.ga_len;
                    // Clear the dict only after getting the item, to avoid
                    // that it makes the item invalid.
                    tv = STACK_TV_BOT(-1);
--- 3514,3520 ----
                        goto on_fatal_error;
                    }
                    clear_tv(tv);
!                   --ectx->ec_stack.ga_len;
                    // Clear the dict only after getting the item, to avoid
                    // that it makes the item invalid.
                    tv = STACK_TV_BOT(-1);
***************
*** 3782,3793 ****
  
                    tv = STACK_TV_BOT((int)ct->ct_off);
                    SOURCING_LNUM = iptr->isn_lnum;
!                   if (!where.wt_variable)
!                       where.wt_index = ct->ct_arg_idx;
!                   if (check_typval_type(ct->ct_type, tv, where) == FAIL)
                        goto on_error;
!                   if (!where.wt_variable)
!                       where.wt_index = 0;
  
                    // number 0 is FALSE, number 1 is TRUE
                    if (tv->v_type == VAR_NUMBER
--- 3595,3607 ----
  
                    tv = STACK_TV_BOT((int)ct->ct_off);
                    SOURCING_LNUM = iptr->isn_lnum;
!                   if (!ectx->ec_where.wt_variable)
!                       ectx->ec_where.wt_index = ct->ct_arg_idx;
!                   if (check_typval_type(ct->ct_type, tv, ectx->ec_where)
!                                                                      == FAIL)
                        goto on_error;
!                   if (!ectx->ec_where.wt_variable)
!                       ectx->ec_where.wt_index = 0;
  
                    // number 0 is FALSE, number 1 is TRUE
                    if (tv->v_type == VAR_NUMBER
***************
*** 3887,3895 ****
                    if (parse_cmd_address(&ea, &errormsg, FALSE) == FAIL)
                        goto on_error;
  
!                   if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
!                       goto failed;
!                   ++ectx.ec_stack.ga_len;
                    tv = STACK_TV_BOT(-1);
                    tv->v_type = VAR_NUMBER;
                    tv->v_lock = 0;
--- 3701,3709 ----
                    if (parse_cmd_address(&ea, &errormsg, FALSE) == FAIL)
                        goto on_error;
  
!                   if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
!                       return FAIL;
!                   ++ectx->ec_stack.ga_len;
                    tv = STACK_TV_BOT(-1);
                    tv->v_type = VAR_NUMBER;
                    tv->v_lock = 0;
***************
*** 3914,3920 ****
                        curwin->w_cursor.lnum = tv->vval.v_number;
                        if (lnum == LNUM_VARIABLE_RANGE_ABOVE)
                            dir = BACKWARD;
!                       --ectx.ec_stack.ga_len;
                    }
                    else if (lnum == -2)
                        // :put! above cursor
--- 3728,3734 ----
                        curwin->w_cursor.lnum = tv->vval.v_number;
                        if (lnum == LNUM_VARIABLE_RANGE_ABOVE)
                            dir = BACKWARD;
!                       --ectx->ec_stack.ga_len;
                    }
                    else if (lnum == -2)
                        // :put! above cursor
***************
*** 3932,3938 ****
                            expr = typval2string(tv, TRUE); // allocates value
                            clear_tv(tv);
                        }
!                       --ectx.ec_stack.ga_len;
                    }
                    check_cursor();
                    do_put(regname, expr, dir, 1L, PUT_LINE|PUT_CURSLINE);
--- 3746,3752 ----
                            expr = typval2string(tv, TRUE); // allocates value
                            clear_tv(tv);
                        }
!                       --ectx->ec_stack.ga_len;
                    }
                    check_cursor();
                    do_put(regname, expr, dir, 1L, PUT_LINE|PUT_CURSLINE);
***************
*** 3941,3949 ****
                break;
  
            case ISN_CMDMOD:
!               funclocal.floc_save_cmdmod = cmdmod;
!               funclocal.floc_restore_cmdmod = TRUE;
!               funclocal.floc_restore_cmdmod_stacklen = ectx.ec_stack.ga_len;
                cmdmod = *iptr->isn_arg.cmdmod.cf_cmdmod;
                apply_cmdmod(&cmdmod);
                break;
--- 3755,3764 ----
                break;
  
            case ISN_CMDMOD:
!               ectx->ec_funclocal.floc_save_cmdmod = cmdmod;
!               ectx->ec_funclocal.floc_restore_cmdmod = TRUE;
!               ectx->ec_funclocal.floc_restore_cmdmod_stacklen =
!                                                        ectx->ec_stack.ga_len;
                cmdmod = *iptr->isn_arg.cmdmod.cf_cmdmod;
                apply_cmdmod(&cmdmod);
                break;
***************
*** 3952,3959 ****
                // filter regprog is owned by the instruction, don't free it
                cmdmod.cmod_filter_regmatch.regprog = NULL;
                undo_cmdmod(&cmdmod);
!               cmdmod = funclocal.floc_save_cmdmod;
!               funclocal.floc_restore_cmdmod = FALSE;
                break;
  
            case ISN_UNPACK:
--- 3767,3774 ----
                // filter regprog is owned by the instruction, don't free it
                cmdmod.cmod_filter_regmatch.regprog = NULL;
                undo_cmdmod(&cmdmod);
!               cmdmod = ectx->ec_funclocal.floc_save_cmdmod;
!               ectx->ec_funclocal.floc_restore_cmdmod = FALSE;
                break;
  
            case ISN_UNPACK:
***************
*** 3988,3996 ****
                    }
  
                    CHECK_LIST_MATERIALIZE(l);
!                   if (GA_GROW(&ectx.ec_stack, count - 1) == FAIL)
!                       goto failed;
!                   ectx.ec_stack.ga_len += count - 1;
  
                    // Variable after semicolon gets a list with the remaining
                    // items.
--- 3803,3811 ----
                    }
  
                    CHECK_LIST_MATERIALIZE(l);
!                   if (GA_GROW(&ectx->ec_stack, count - 1) == FAIL)
!                       return FAIL;
!                   ectx->ec_stack.ga_len += count - 1;
  
                    // Variable after semicolon gets a list with the remaining
                    // items.
***************
*** 4000,4006 ****
                                  list_alloc_with_items(l->lv_len - count + 1);
  
                        if (rem_list == NULL)
!                           goto failed;
                        tv = STACK_TV_BOT(-count);
                        tv->vval.v_list = rem_list;
                        ++rem_list->lv_refcount;
--- 3815,3821 ----
                                  list_alloc_with_items(l->lv_len - count + 1);
  
                        if (rem_list == NULL)
!                           return FAIL;
                        tv = STACK_TV_BOT(-count);
                        tv->vval.v_list = rem_list;
                        ++rem_list->lv_refcount;
***************
*** 4036,4042 ****
                    funccall_T cookie;
                    ufunc_T         *cur_ufunc =
                                    (((dfunc_T *)def_functions.ga_data)
!                                                + ectx.ec_dfunc_idx)->df_ufunc;
  
                    cookie.func = cur_ufunc;
                    if (iptr->isn_type == ISN_PROF_START)
--- 3851,3857 ----
                    funccall_T cookie;
                    ufunc_T         *cur_ufunc =
                                    (((dfunc_T *)def_functions.ga_data)
!                                                + 
ectx->ec_dfunc_idx)->df_ufunc;
  
                    cookie.func = cur_ufunc;
                    if (iptr->isn_type == ISN_PROF_START)
***************
*** 4068,4077 ****
                break;
  
            case ISN_DROP:
!               --ectx.ec_stack.ga_len;
                clear_tv(STACK_TV_BOT(0));
!               where.wt_index = 0;
!               where.wt_variable = FALSE;
                break;
        }
        continue;
--- 3883,3892 ----
                break;
  
            case ISN_DROP:
!               --ectx->ec_stack.ga_len;
                clear_tv(STACK_TV_BOT(0));
!               ectx->ec_where.wt_index = 0;
!               ectx->ec_where.wt_variable = FALSE;
                break;
        }
        continue;
***************
*** 4079,4140 ****
  func_return:
        // Restore previous function. If the frame pointer is where we started
        // then there is none and we are done.
!       if (ectx.ec_frame_idx == initial_frame_idx)
            goto done;
  
!       if (func_return(&funclocal, &ectx) == FAIL)
            // only fails when out of memory
!           goto failed;
        continue;
  
  on_error:
        // Jump here for an error that does not require aborting execution.
        // If "emsg_silent" is set then ignore the error, unless it was set
        // when calling the function.
!       if (did_emsg_cumul + did_emsg == did_emsg_before
                                           && emsg_silent && did_emsg_def == 0)
        {
            // If a sequence of instructions causes an error while ":silent!"
            // was used, restore the stack length and jump ahead to restoring
            // the cmdmod.
!           if (funclocal.floc_restore_cmdmod)
            {
!               while (ectx.ec_stack.ga_len
!                                     > funclocal.floc_restore_cmdmod_stacklen)
                {
!                   --ectx.ec_stack.ga_len;
                    clear_tv(STACK_TV_BOT(0));
                }
!               while (ectx.ec_instr[ectx.ec_iidx].isn_type != ISN_CMDMOD_REV)
!                   ++ectx.ec_iidx;
            }
            continue;
        }
  on_fatal_error:
        // Jump here for an error that messes up the stack.
        // If we are not inside a try-catch started here, abort execution.
!       if (trylevel <= trylevel_at_start)
!           goto failed;
      }
  
  done:
!     // function finished, get result from the stack.
!     if (ufunc->uf_ret_type == &t_void)
      {
!       rettv->v_type = VAR_VOID;
      }
      else
      {
        tv = STACK_TV_BOT(-1);
!       *rettv = *tv;
!       tv->v_type = VAR_UNKNOWN;
      }
-     ret = OK;
  
- failed:
      // When failed need to unwind the call stack.
!     while (ectx.ec_frame_idx != initial_frame_idx)
!       func_return(&funclocal, &ectx);
  
      // Deal with any remaining closures, they may be in use somewhere.
      if (ectx.ec_funcrefs.ga_len > 0)
--- 3894,4238 ----
  func_return:
        // Restore previous function. If the frame pointer is where we started
        // then there is none and we are done.
!       if (ectx->ec_frame_idx == ectx->ec_initial_frame_idx)
            goto done;
  
!       if (func_return(ectx) == FAIL)
            // only fails when out of memory
!           return FAIL;
        continue;
  
  on_error:
        // Jump here for an error that does not require aborting execution.
        // If "emsg_silent" is set then ignore the error, unless it was set
        // when calling the function.
!       if (did_emsg_cumul + did_emsg == ectx->ec_did_emsg_before
                                           && emsg_silent && did_emsg_def == 0)
        {
            // If a sequence of instructions causes an error while ":silent!"
            // was used, restore the stack length and jump ahead to restoring
            // the cmdmod.
!           if (ectx->ec_funclocal.floc_restore_cmdmod)
            {
!               while (ectx->ec_stack.ga_len
!                            > ectx->ec_funclocal.floc_restore_cmdmod_stacklen)
                {
!                   --ectx->ec_stack.ga_len;
                    clear_tv(STACK_TV_BOT(0));
                }
!               while (ectx->ec_instr[ectx->ec_iidx].isn_type != ISN_CMDMOD_REV)
!                   ++ectx->ec_iidx;
            }
            continue;
        }
  on_fatal_error:
        // Jump here for an error that messes up the stack.
        // If we are not inside a try-catch started here, abort execution.
!       if (trylevel <= ectx->ec_trylevel_at_start)
!           return FAIL;
      }
  
  done:
!     return OK;
! }
! 
! /*
!  * Execute the instructions from an ISN_SUBSTITUTE command, which are in
!  * "substitute_instr".
!  */
!     char_u *
! exe_substitute_instr(void)
! {
!     ectx_T    *ectx = substitute_instr->subs_ectx;
!     isn_T     *save_instr = ectx->ec_instr;
!     int               save_iidx = ectx->ec_iidx;
!     char_u    *res;
! 
!     ectx->ec_instr = substitute_instr->subs_instr;
!     if (exec_instructions(ectx) == OK)
      {
!       typval_T *tv = STACK_TV_BOT(-1);
! 
!       res = vim_strsave(tv_get_string(tv));
!       --ectx->ec_stack.ga_len;
!       clear_tv(tv);
      }
      else
      {
+       substitute_instr->subs_status = FAIL;
+       res = vim_strsave((char_u *)"");
+     }
+ 
+     ectx->ec_instr = save_instr;
+     ectx->ec_iidx = save_iidx;
+ 
+     return res;
+ }
+ 
+ /*
+  * Call a "def" function from old Vim script.
+  * Return OK or FAIL.
+  */
+     int
+ call_def_function(
+     ufunc_T   *ufunc,
+     int               argc_arg,       // nr of arguments
+     typval_T  *argv,          // arguments
+     partial_T *partial,       // optional partial for context
+     typval_T  *rettv)         // return value
+ {
+     ectx_T    ectx;           // execution context
+     int               argc = argc_arg;
+     typval_T  *tv;
+     int               idx;
+     int               ret = FAIL;
+     int               defcount = ufunc->uf_args.ga_len - argc;
+     sctx_T    save_current_sctx = current_sctx;
+     int               did_emsg_before = did_emsg_cumul + did_emsg;
+     int               save_suppress_errthrow = suppress_errthrow;
+     msglist_T **saved_msg_list = NULL;
+     msglist_T *private_msg_list = NULL;
+     int               save_emsg_silent_def = emsg_silent_def;
+     int               save_did_emsg_def = did_emsg_def;
+     int               orig_funcdepth;
+ 
+ // Get pointer to item in the stack.
+ #undef STACK_TV
+ #define STACK_TV(idx) (((typval_T *)ectx.ec_stack.ga_data) + idx)
+ 
+ // Get pointer to item at the bottom of the stack, -1 is the bottom.
+ #undef STACK_TV_BOT
+ #define STACK_TV_BOT(idx) (((typval_T *)ectx.ec_stack.ga_data) + 
ectx.ec_stack.ga_len + idx)
+ 
+ // Get pointer to a local variable on the stack.  Negative for arguments.
+ #undef STACK_TV_VAR
+ #define STACK_TV_VAR(idx) (((typval_T *)ectx.ec_stack.ga_data) + 
ectx.ec_frame_idx + STACK_FRAME_SIZE + idx)
+ 
+     if (ufunc->uf_def_status == UF_NOT_COMPILED
+           || ufunc->uf_def_status == UF_COMPILE_ERROR
+           || (func_needs_compiling(ufunc, PROFILING(ufunc))
+               && compile_def_function(ufunc, FALSE, PROFILING(ufunc), NULL)
+                                                                     == FAIL))
+     {
+       if (did_emsg_cumul + did_emsg == did_emsg_before)
+           semsg(_(e_function_is_not_compiled_str),
+                                                  printable_func_name(ufunc));
+       return FAIL;
+     }
+ 
+     {
+       // Check the function was really compiled.
+       dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+                                                        + ufunc->uf_dfunc_idx;
+       if (INSTRUCTIONS(dfunc) == NULL)
+       {
+           iemsg("using call_def_function() on not compiled function");
+           return FAIL;
+       }
+     }
+ 
+     // If depth of calling is getting too high, don't execute the function.
+     orig_funcdepth = funcdepth_get();
+     if (funcdepth_increment() == FAIL)
+       return FAIL;
+ 
+     CLEAR_FIELD(ectx);
+     ectx.ec_dfunc_idx = ufunc->uf_dfunc_idx;
+     ga_init2(&ectx.ec_stack, sizeof(typval_T), 500);
+     if (ga_grow(&ectx.ec_stack, 20) == FAIL)
+     {
+       funcdepth_decrement();
+       return FAIL;
+     }
+     ga_init2(&ectx.ec_trystack, sizeof(trycmd_T), 10);
+     ga_init2(&ectx.ec_funcrefs, sizeof(partial_T *), 10);
+     ectx.ec_did_emsg_before = did_emsg_before;
+     ectx.ec_trylevel_at_start = trylevel;
+ 
+     idx = argc - ufunc->uf_args.ga_len;
+     if (idx > 0 && ufunc->uf_va_name == NULL)
+     {
+       if (idx == 1)
+           emsg(_(e_one_argument_too_many));
+       else
+           semsg(_(e_nr_arguments_too_many), idx);
+       goto failed_early;
+     }
+ 
+     // Put arguments on the stack, but no more than what the function expects.
+     // A lambda can be called with more arguments than it uses.
+     for (idx = 0; idx < argc
+           && (ufunc->uf_va_name != NULL || idx < ufunc->uf_args.ga_len);
+                                                                        ++idx)
+     {
+       if (idx >= ufunc->uf_args.ga_len - ufunc->uf_def_args.ga_len
+               && argv[idx].v_type == VAR_SPECIAL
+               && argv[idx].vval.v_number == VVAL_NONE)
+       {
+           // Use the default value.
+           STACK_TV_BOT(0)->v_type = VAR_UNKNOWN;
+       }
+       else
+       {
+           if (ufunc->uf_arg_types != NULL && idx < ufunc->uf_args.ga_len
+                   && check_typval_arg_type(
+                       ufunc->uf_arg_types[idx], &argv[idx], idx + 1) == FAIL)
+               goto failed_early;
+           copy_tv(&argv[idx], STACK_TV_BOT(0));
+       }
+       ++ectx.ec_stack.ga_len;
+     }
+ 
+     // Turn varargs into a list.  Empty list if no args.
+     if (ufunc->uf_va_name != NULL)
+     {
+       int vararg_count = argc - ufunc->uf_args.ga_len;
+ 
+       if (vararg_count < 0)
+           vararg_count = 0;
+       else
+           argc -= vararg_count;
+       if (exe_newlist(vararg_count, &ectx) == FAIL)
+           goto failed_early;
+ 
+       // Check the type of the list items.
        tv = STACK_TV_BOT(-1);
!       if (ufunc->uf_va_type != NULL
!               && ufunc->uf_va_type != &t_list_any
!               && ufunc->uf_va_type->tt_member != &t_any
!               && tv->vval.v_list != NULL)
!       {
!           type_T      *expected = ufunc->uf_va_type->tt_member;
!           listitem_T  *li = tv->vval.v_list->lv_first;
! 
!           for (idx = 0; idx < vararg_count; ++idx)
!           {
!               if (check_typval_arg_type(expected, &li->li_tv,
!                                                      argc + idx + 1) == FAIL)
!                   goto failed_early;
!               li = li->li_next;
!           }
!       }
! 
!       if (defcount > 0)
!           // Move varargs list to below missing default arguments.
!           *STACK_TV_BOT(defcount - 1) = *STACK_TV_BOT(-1);
!       --ectx.ec_stack.ga_len;
!     }
! 
!     // Make space for omitted arguments, will store default value below.
!     // Any varargs list goes after them.
!     if (defcount > 0)
!       for (idx = 0; idx < defcount; ++idx)
!       {
!           STACK_TV_BOT(0)->v_type = VAR_UNKNOWN;
!           ++ectx.ec_stack.ga_len;
!       }
!     if (ufunc->uf_va_name != NULL)
!       ++ectx.ec_stack.ga_len;
! 
!     // Frame pointer points to just after arguments.
!     ectx.ec_frame_idx = ectx.ec_stack.ga_len;
!     ectx.ec_initial_frame_idx = ectx.ec_frame_idx;
! 
!     {
!       dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
!                                                        + ufunc->uf_dfunc_idx;
!       ufunc_T *base_ufunc = dfunc->df_ufunc;
! 
!       // "uf_partial" is on the ufunc that "df_ufunc" points to, as is done
!       // by copy_func().
!       if (partial != NULL || base_ufunc->uf_partial != NULL)
!       {
!           ectx.ec_outer = ALLOC_CLEAR_ONE(outer_T);
!           if (ectx.ec_outer == NULL)
!               goto failed_early;
!           if (partial != NULL)
!           {
!               if (partial->pt_outer.out_stack == NULL && current_ectx != NULL)
!               {
!                   if (current_ectx->ec_outer != NULL)
!                       *ectx.ec_outer = *current_ectx->ec_outer;
!               }
!               else
!                   *ectx.ec_outer = partial->pt_outer;
!           }
!           else
!               *ectx.ec_outer = base_ufunc->uf_partial->pt_outer;
!           ectx.ec_outer->out_up_is_copy = TRUE;
!       }
!     }
! 
!     // dummy frame entries
!     for (idx = 0; idx < STACK_FRAME_SIZE; ++idx)
!     {
!       STACK_TV(ectx.ec_stack.ga_len)->v_type = VAR_UNKNOWN;
!       ++ectx.ec_stack.ga_len;
!     }
! 
!     {
!       // Reserve space for local variables and any closure reference count.
!       dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
!                                                        + ufunc->uf_dfunc_idx;
! 
!       for (idx = 0; idx < dfunc->df_varcount; ++idx)
!           STACK_TV_VAR(idx)->v_type = VAR_UNKNOWN;
!       ectx.ec_stack.ga_len += dfunc->df_varcount;
!       if (dfunc->df_has_closure)
!       {
!           STACK_TV_VAR(idx)->v_type = VAR_NUMBER;
!           STACK_TV_VAR(idx)->vval.v_number = 0;
!           ++ectx.ec_stack.ga_len;
!       }
! 
!       ectx.ec_instr = INSTRUCTIONS(dfunc);
!     }
! 
!     // Following errors are in the function, not the caller.
!     // Commands behave like vim9script.
!     estack_push_ufunc(ufunc, 1);
!     current_sctx = ufunc->uf_script_ctx;
!     current_sctx.sc_version = SCRIPT_VERSION_VIM9;
! 
!     // Use a specific location for storing error messages to be converted to 
an
!     // exception.
!     saved_msg_list = msg_list;
!     msg_list = &private_msg_list;
! 
!     // Do turn errors into exceptions.
!     suppress_errthrow = FALSE;
! 
!     // Do not delete the function while executing it.
!     ++ufunc->uf_calls;
! 
!     // When ":silent!" was used before calling then we still abort the
!     // function.  If ":silent!" is used in the function then we don't.
!     emsg_silent_def = emsg_silent;
!     did_emsg_def = 0;
! 
!     ectx.ec_where.wt_index = 0;
!     ectx.ec_where.wt_variable = FALSE;
! 
!     // Execute the instructions until done.
!     ret = exec_instructions(&ectx);
!     if (ret == OK)
!     {
!       // function finished, get result from the stack.
!       if (ufunc->uf_ret_type == &t_void)
!       {
!           rettv->v_type = VAR_VOID;
!       }
!       else
!       {
!           tv = STACK_TV_BOT(-1);
!           *rettv = *tv;
!           tv->v_type = VAR_UNKNOWN;
!       }
      }
  
      // When failed need to unwind the call stack.
!     while (ectx.ec_frame_idx != ectx.ec_initial_frame_idx)
!       func_return(&ectx);
  
      // Deal with any remaining closures, they may be in use somewhere.
      if (ectx.ec_funcrefs.ga_len > 0)
***************
*** 4162,4172 ****
      }
      msg_list = saved_msg_list;
  
!     if (funclocal.floc_restore_cmdmod)
      {
        cmdmod.cmod_filter_regmatch.regprog = NULL;
        undo_cmdmod(&cmdmod);
!       cmdmod = funclocal.floc_save_cmdmod;
      }
      emsg_silent_def = save_emsg_silent_def;
      did_emsg_def += save_did_emsg_def;
--- 4260,4270 ----
      }
      msg_list = saved_msg_list;
  
!     if (ectx.ec_funclocal.floc_restore_cmdmod)
      {
        cmdmod.cmod_filter_regmatch.regprog = NULL;
        undo_cmdmod(&cmdmod);
!       cmdmod = ectx.ec_funclocal.floc_save_cmdmod;
      }
      emsg_silent_def = save_emsg_silent_def;
      did_emsg_def += save_did_emsg_def;
***************
*** 4199,4347 ****
  }
  
  /*
!  * ":disassemble".
!  * We don't really need this at runtime, but we do have tests that require it,
!  * so always include this.
   */
!     void
! ex_disassemble(exarg_T *eap)
  {
-     char_u    *arg = eap->arg;
-     char_u    *fname;
-     ufunc_T   *ufunc;
-     dfunc_T   *dfunc;
-     isn_T     *instr;
-     int               instr_count;
-     int               current;
      int               line_idx = 0;
      int               prev_current = 0;
!     int               is_global = FALSE;
! 
!     if (STRNCMP(arg, "<lambda>", 8) == 0)
!     {
!       arg += 8;
!       (void)getdigits(&arg);
!       fname = vim_strnsave(eap->arg, arg - eap->arg);
!     }
!     else
!       fname = trans_function_name(&arg, &is_global, FALSE,
!                     TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD, NULL, NULL, NULL);
!     if (fname == NULL)
!     {
!       semsg(_(e_invarg2), eap->arg);
!       return;
!     }
! 
!     ufunc = find_func(fname, is_global, NULL);
!     if (ufunc == NULL)
!     {
!       char_u *p = untrans_function_name(fname);
! 
!       if (p != NULL)
!           // Try again without making it script-local.
!           ufunc = find_func(p, FALSE, NULL);
!     }
!     vim_free(fname);
!     if (ufunc == NULL)
!     {
!       semsg(_(e_cannot_find_function_str), eap->arg);
!       return;
!     }
!     if (func_needs_compiling(ufunc, eap->forceit)
!           && compile_def_function(ufunc, FALSE, eap->forceit, NULL) == FAIL)
!       return;
!     if (ufunc->uf_def_status != UF_COMPILED)
!     {
!       semsg(_(e_function_is_not_compiled_str), eap->arg);
!       return;
!     }
!     if (ufunc->uf_name_exp != NULL)
!       msg((char *)ufunc->uf_name_exp);
!     else
!       msg((char *)ufunc->uf_name);
  
-     dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx;
- #ifdef FEAT_PROFILE
-     instr = eap->forceit ? dfunc->df_instr_prof : dfunc->df_instr;
-     instr_count = eap->forceit ? dfunc->df_instr_prof_count
-                                                      : dfunc->df_instr_count;
- #else
-     instr = dfunc->df_instr;
-     instr_count = dfunc->df_instr_count;
- #endif
      for (current = 0; current < instr_count; ++current)
      {
        isn_T       *iptr = &instr[current];
        char        *line;
  
!       while (line_idx < iptr->isn_lnum && line_idx < ufunc->uf_lines.ga_len)
!       {
!           if (current > prev_current)
            {
!               msg_puts("\n\n");
!               prev_current = current;
            }
-           line = ((char **)ufunc->uf_lines.ga_data)[line_idx++];
-           if (line != NULL)
-               msg(line);
-       }
  
        switch (iptr->isn_type)
        {
            case ISN_EXEC:
!               smsg("%4d EXEC %s", current, iptr->isn_arg.string);
                break;
            case ISN_EXECCONCAT:
!               smsg("%4d EXECCONCAT %lld", current,
                                              
(varnumber_T)iptr->isn_arg.number);
                break;
            case ISN_ECHO:
                {
                    echo_T *echo = &iptr->isn_arg.echo;
  
!                   smsg("%4d %s %d", current,
                            echo->echo_with_white ? "ECHO" : "ECHON",
                            echo->echo_count);
                }
                break;
            case ISN_EXECUTE:
!               smsg("%4d EXECUTE %lld", current,
                                            
(varnumber_T)(iptr->isn_arg.number));
                break;
            case ISN_ECHOMSG:
!               smsg("%4d ECHOMSG %lld", current,
                                            
(varnumber_T)(iptr->isn_arg.number));
                break;
            case ISN_ECHOERR:
!               smsg("%4d ECHOERR %lld", current,
                                            
(varnumber_T)(iptr->isn_arg.number));
                break;
            case ISN_LOAD:
                {
                    if (iptr->isn_arg.number < 0)
!                       smsg("%4d LOAD arg[%lld]", current,
                                (varnumber_T)(iptr->isn_arg.number
                                                          + STACK_FRAME_SIZE));
                    else
!                       smsg("%4d LOAD $%lld", current,
                                          (varnumber_T)(iptr->isn_arg.number));
                }
                break;
            case ISN_LOADOUTER:
                {
                    if (iptr->isn_arg.number < 0)
!                       smsg("%4d LOADOUTER level %d arg[%d]", current,
                                iptr->isn_arg.outer.outer_depth,
                                iptr->isn_arg.outer.outer_idx
                                                          + STACK_FRAME_SIZE);
                    else
!                       smsg("%4d LOADOUTER level %d $%d", current,
                                              iptr->isn_arg.outer.outer_depth,
                                              iptr->isn_arg.outer.outer_idx);
                }
                break;
            case ISN_LOADV:
!               smsg("%4d LOADV v:%s", current,
                                       get_vim_var_name(iptr->isn_arg.number));
                break;
            case ISN_LOADSCRIPT:
--- 4297,4397 ----
  }
  
  /*
!  * List instructions "instr" up to "instr_count" or until ISN_FINISH.
!  * "ufunc" has the source lines, NULL for the instructions of ISN_SUBSTITUTE.
!  * "pfx" is prefixed to every line.
   */
!     static void
! list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
  {
      int               line_idx = 0;
      int               prev_current = 0;
!     int               current;
  
      for (current = 0; current < instr_count; ++current)
      {
        isn_T       *iptr = &instr[current];
        char        *line;
  
!       if (ufunc != NULL)
!           while (line_idx < iptr->isn_lnum
!                                         && line_idx < ufunc->uf_lines.ga_len)
            {
!               if (current > prev_current)
!               {
!                   msg_puts("\n\n");
!                   prev_current = current;
!               }
!               line = ((char **)ufunc->uf_lines.ga_data)[line_idx++];
!               if (line != NULL)
!                   msg(line);
            }
  
        switch (iptr->isn_type)
        {
            case ISN_EXEC:
!               smsg("%s%4d EXEC %s", pfx, current, iptr->isn_arg.string);
!               break;
!           case ISN_SUBSTITUTE:
!               {
!                   subs_T *subs = &iptr->isn_arg.subs;
! 
!                   smsg("%s%4d SUBSTITUTE %s", pfx, current, subs->subs_cmd);
!                   list_instructions("    ", subs->subs_instr, INT_MAX, NULL);
!                   msg("     -------------");
!               }
                break;
            case ISN_EXECCONCAT:
!               smsg("%s%4d EXECCONCAT %lld", pfx, current,
                                              
(varnumber_T)iptr->isn_arg.number);
                break;
            case ISN_ECHO:
                {
                    echo_T *echo = &iptr->isn_arg.echo;
  
!                   smsg("%s%4d %s %d", pfx, current,
                            echo->echo_with_white ? "ECHO" : "ECHON",
                            echo->echo_count);
                }
                break;
            case ISN_EXECUTE:
!               smsg("%s%4d EXECUTE %lld", pfx, current,
                                            
(varnumber_T)(iptr->isn_arg.number));
                break;
            case ISN_ECHOMSG:
!               smsg("%s%4d ECHOMSG %lld", pfx, current,
                                            
(varnumber_T)(iptr->isn_arg.number));
                break;
            case ISN_ECHOERR:
!               smsg("%s%4d ECHOERR %lld", pfx, current,
                                            
(varnumber_T)(iptr->isn_arg.number));
                break;
            case ISN_LOAD:
                {
                    if (iptr->isn_arg.number < 0)
!                       smsg("%s%4d LOAD arg[%lld]", pfx, current,
                                (varnumber_T)(iptr->isn_arg.number
                                                          + STACK_FRAME_SIZE));
                    else
!                       smsg("%s%4d LOAD $%lld", pfx, current,
                                          (varnumber_T)(iptr->isn_arg.number));
                }
                break;
            case ISN_LOADOUTER:
                {
                    if (iptr->isn_arg.number < 0)
!                       smsg("%s%4d LOADOUTER level %d arg[%d]", pfx, current,
                                iptr->isn_arg.outer.outer_depth,
                                iptr->isn_arg.outer.outer_idx
                                                          + STACK_FRAME_SIZE);
                    else
!                       smsg("%s%4d LOADOUTER level %d $%d", pfx, current,
                                              iptr->isn_arg.outer.outer_depth,
                                              iptr->isn_arg.outer.outer_idx);
                }
                break;
            case ISN_LOADV:
!               smsg("%s%4d LOADV v:%s", pfx, current,
                                       get_vim_var_name(iptr->isn_arg.number));
                break;
            case ISN_LOADSCRIPT:
***************
*** 4351,4357 ****
                    svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data)
                                                              + sref->sref_idx;
  
!                   smsg("%4d LOADSCRIPT %s-%d from %s", current,
                                            sv->sv_name,
                                            sref->sref_idx,
                                            si->sn_name);
--- 4401,4407 ----
                    svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data)
                                                              + sref->sref_idx;
  
!                   smsg("%s%4d LOADSCRIPT %s-%d from %s", pfx, current,
                                            sv->sv_name,
                                            sref->sref_idx,
                                            si->sn_name);
***************
*** 4362,4452 ****
                    scriptitem_T *si = SCRIPT_ITEM(
                                               iptr->isn_arg.loadstore.ls_sid);
  
!                   smsg("%4d LOADS s:%s from %s", current,
                                 iptr->isn_arg.loadstore.ls_name, si->sn_name);
                }
                break;
            case ISN_LOADAUTO:
!               smsg("%4d LOADAUTO %s", current, iptr->isn_arg.string);
                break;
            case ISN_LOADG:
!               smsg("%4d LOADG g:%s", current, iptr->isn_arg.string);
                break;
            case ISN_LOADB:
!               smsg("%4d LOADB b:%s", current, iptr->isn_arg.string);
                break;
            case ISN_LOADW:
!               smsg("%4d LOADW w:%s", current, iptr->isn_arg.string);
                break;
            case ISN_LOADT:
!               smsg("%4d LOADT t:%s", current, iptr->isn_arg.string);
                break;
            case ISN_LOADGDICT:
!               smsg("%4d LOAD g:", current);
                break;
            case ISN_LOADBDICT:
!               smsg("%4d LOAD b:", current);
                break;
            case ISN_LOADWDICT:
!               smsg("%4d LOAD w:", current);
                break;
            case ISN_LOADTDICT:
!               smsg("%4d LOAD t:", current);
                break;
            case ISN_LOADOPT:
!               smsg("%4d LOADOPT %s", current, iptr->isn_arg.string);
                break;
            case ISN_LOADENV:
!               smsg("%4d LOADENV %s", current, iptr->isn_arg.string);
                break;
            case ISN_LOADREG:
!               smsg("%4d LOADREG @%c", current, (int)(iptr->isn_arg.number));
                break;
  
            case ISN_STORE:
                if (iptr->isn_arg.number < 0)
!                   smsg("%4d STORE arg[%lld]", current,
                                      iptr->isn_arg.number + STACK_FRAME_SIZE);
                else
!                   smsg("%4d STORE $%lld", current, iptr->isn_arg.number);
                break;
            case ISN_STOREOUTER:
                {
                if (iptr->isn_arg.number < 0)
!                   smsg("%4d STOREOUTEr level %d arg[%d]", current,
                            iptr->isn_arg.outer.outer_depth,
                            iptr->isn_arg.outer.outer_idx + STACK_FRAME_SIZE);
                else
!                   smsg("%4d STOREOUTER level %d $%d", current,
                            iptr->isn_arg.outer.outer_depth,
                            iptr->isn_arg.outer.outer_idx);
                }
                break;
            case ISN_STOREV:
!               smsg("%4d STOREV v:%s", current,
                                       get_vim_var_name(iptr->isn_arg.number));
                break;
            case ISN_STOREAUTO:
!               smsg("%4d STOREAUTO %s", current, iptr->isn_arg.string);
                break;
            case ISN_STOREG:
!               smsg("%4d STOREG %s", current, iptr->isn_arg.string);
                break;
            case ISN_STOREB:
!               smsg("%4d STOREB %s", current, iptr->isn_arg.string);
                break;
            case ISN_STOREW:
!               smsg("%4d STOREW %s", current, iptr->isn_arg.string);
                break;
            case ISN_STORET:
!               smsg("%4d STORET %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 in %s", current,
                                 iptr->isn_arg.loadstore.ls_name, si->sn_name);
                }
                break;
--- 4412,4502 ----
                    scriptitem_T *si = SCRIPT_ITEM(
                                               iptr->isn_arg.loadstore.ls_sid);
  
!                   smsg("%s%4d LOADS s:%s from %s", pfx, current,
                                 iptr->isn_arg.loadstore.ls_name, si->sn_name);
                }
                break;
            case ISN_LOADAUTO:
!               smsg("%s%4d LOADAUTO %s", pfx, current, iptr->isn_arg.string);
                break;
            case ISN_LOADG:
!               smsg("%s%4d LOADG g:%s", pfx, current, iptr->isn_arg.string);
                break;
            case ISN_LOADB:
!               smsg("%s%4d LOADB b:%s", pfx, current, iptr->isn_arg.string);
                break;
            case ISN_LOADW:
!               smsg("%s%4d LOADW w:%s", pfx, current, iptr->isn_arg.string);
                break;
            case ISN_LOADT:
!               smsg("%s%4d LOADT t:%s", pfx, current, iptr->isn_arg.string);
                break;
            case ISN_LOADGDICT:
!               smsg("%s%4d LOAD g:", pfx, current);
                break;
            case ISN_LOADBDICT:
!               smsg("%s%4d LOAD b:", pfx, current);
                break;
            case ISN_LOADWDICT:
!               smsg("%s%4d LOAD w:", pfx, current);
                break;
            case ISN_LOADTDICT:
!               smsg("%s%4d LOAD t:", pfx, current);
                break;
            case ISN_LOADOPT:
!               smsg("%s%4d LOADOPT %s", pfx, current, iptr->isn_arg.string);
                break;
            case ISN_LOADENV:
!               smsg("%s%4d LOADENV %s", pfx, current, iptr->isn_arg.string);
                break;
            case ISN_LOADREG:
!               smsg("%s%4d LOADREG @%c", pfx, current, 
(int)(iptr->isn_arg.number));
                break;
  
            case ISN_STORE:
                if (iptr->isn_arg.number < 0)
!                   smsg("%s%4d STORE arg[%lld]", pfx, current,
                                      iptr->isn_arg.number + STACK_FRAME_SIZE);
                else
!                   smsg("%s%4d STORE $%lld", pfx, current, 
iptr->isn_arg.number);
                break;
            case ISN_STOREOUTER:
                {
                if (iptr->isn_arg.number < 0)
!                   smsg("%s%4d STOREOUTEr level %d arg[%d]", pfx, current,
                            iptr->isn_arg.outer.outer_depth,
                            iptr->isn_arg.outer.outer_idx + STACK_FRAME_SIZE);
                else
!                   smsg("%s%4d STOREOUTER level %d $%d", pfx, current,
                            iptr->isn_arg.outer.outer_depth,
                            iptr->isn_arg.outer.outer_idx);
                }
                break;
            case ISN_STOREV:
!               smsg("%s%4d STOREV v:%s", pfx, current,
                                       get_vim_var_name(iptr->isn_arg.number));
                break;
            case ISN_STOREAUTO:
!               smsg("%s%4d STOREAUTO %s", pfx, current, iptr->isn_arg.string);
                break;
            case ISN_STOREG:
!               smsg("%s%4d STOREG %s", pfx, current, iptr->isn_arg.string);
                break;
            case ISN_STOREB:
!               smsg("%s%4d STOREB %s", pfx, current, iptr->isn_arg.string);
                break;
            case ISN_STOREW:
!               smsg("%s%4d STOREW %s", pfx, current, iptr->isn_arg.string);
                break;
            case ISN_STORET:
!               smsg("%s%4d STORET %s", pfx, current, iptr->isn_arg.string);
                break;
            case ISN_STORES:
                {
                    scriptitem_T *si = SCRIPT_ITEM(
                                               iptr->isn_arg.loadstore.ls_sid);
  
!                   smsg("%s%4d STORES %s in %s", pfx, current,
                                 iptr->isn_arg.loadstore.ls_name, si->sn_name);
                }
                break;
***************
*** 4457,4510 ****
                    svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data)
                                                              + sref->sref_idx;
  
!                   smsg("%4d STORESCRIPT %s-%d in %s", current,
                                             sv->sv_name,
                                             sref->sref_idx,
                                             si->sn_name);
                }
                break;
            case ISN_STOREOPT:
!               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, (int)iptr->isn_arg.number);
                break;
            case ISN_STORENR:
!               smsg("%4d STORE %lld in $%d", current,
                                iptr->isn_arg.storenr.stnr_val,
                                iptr->isn_arg.storenr.stnr_idx);
                break;
  
            case ISN_STOREINDEX:
!               smsg("%4d STOREINDEX %s", current,
                                          vartype_name(iptr->isn_arg.vartype));
                break;
  
            case ISN_STORERANGE:
!               smsg("%4d STORERANGE", current);
                break;
  
            // constants
            case ISN_PUSHNR:
!               smsg("%4d PUSHNR %lld", current,
                                            
(varnumber_T)(iptr->isn_arg.number));
                break;
            case ISN_PUSHBOOL:
            case ISN_PUSHSPEC:
!               smsg("%4d PUSH %s", current,
                                   get_var_special_name(iptr->isn_arg.number));
                break;
            case ISN_PUSHF:
  #ifdef FEAT_FLOAT
!               smsg("%4d PUSHF %g", current, iptr->isn_arg.fnumber);
  #endif
                break;
            case ISN_PUSHS:
!               smsg("%4d PUSHS \"%s\"", current, iptr->isn_arg.string);
                break;
            case ISN_PUSHBLOB:
                {
--- 4507,4560 ----
                    svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data)
                                                              + sref->sref_idx;
  
!                   smsg("%s%4d STORESCRIPT %s-%d in %s", pfx, current,
                                             sv->sv_name,
                                             sref->sref_idx,
                                             si->sn_name);
                }
                break;
            case ISN_STOREOPT:
!               smsg("%s%4d STOREOPT &%s", pfx, current,
                                               iptr->isn_arg.storeopt.so_name);
                break;
            case ISN_STOREENV:
!               smsg("%s%4d STOREENV $%s", pfx, current, iptr->isn_arg.string);
                break;
            case ISN_STOREREG:
!               smsg("%s%4d STOREREG @%c", pfx, current, 
(int)iptr->isn_arg.number);
                break;
            case ISN_STORENR:
!               smsg("%s%4d STORE %lld in $%d", pfx, current,
                                iptr->isn_arg.storenr.stnr_val,
                                iptr->isn_arg.storenr.stnr_idx);
                break;
  
            case ISN_STOREINDEX:
!               smsg("%s%4d STOREINDEX %s", pfx, current,
                                          vartype_name(iptr->isn_arg.vartype));
                break;
  
            case ISN_STORERANGE:
!               smsg("%s%4d STORERANGE", pfx, current);
                break;
  
            // constants
            case ISN_PUSHNR:
!               smsg("%s%4d PUSHNR %lld", pfx, current,
                                            
(varnumber_T)(iptr->isn_arg.number));
                break;
            case ISN_PUSHBOOL:
            case ISN_PUSHSPEC:
!               smsg("%s%4d PUSH %s", pfx, current,
                                   get_var_special_name(iptr->isn_arg.number));
                break;
            case ISN_PUSHF:
  #ifdef FEAT_FLOAT
!               smsg("%s%4d PUSHF %g", pfx, current, iptr->isn_arg.fnumber);
  #endif
                break;
            case ISN_PUSHS:
!               smsg("%s%4d PUSHS \"%s\"", pfx, current, iptr->isn_arg.string);
                break;
            case ISN_PUSHBLOB:
                {
***************
*** 4513,4519 ****
                    char_u      *tofree;
  
                    r = blob2string(iptr->isn_arg.blob, &tofree, numbuf);
!                   smsg("%4d PUSHBLOB %s", current, r);
                    vim_free(tofree);
                }
                break;
--- 4563,4569 ----
                    char_u      *tofree;
  
                    r = blob2string(iptr->isn_arg.blob, &tofree, numbuf);
!                   smsg("%s%4d PUSHBLOB %s", pfx, current, r);
                    vim_free(tofree);
                }
                break;
***************
*** 4521,4527 ****
                {
                    char *name = (char *)iptr->isn_arg.string;
  
!                   smsg("%4d PUSHFUNC \"%s\"", current,
                                               name == NULL ? "[none]" : name);
                }
                break;
--- 4571,4577 ----
                {
                    char *name = (char *)iptr->isn_arg.string;
  
!                   smsg("%s%4d PUSHFUNC \"%s\"", pfx, current,
                                               name == NULL ? "[none]" : name);
                }
                break;
***************
*** 4530,4536 ****
                {
                    channel_T *channel = iptr->isn_arg.channel;
  
!                   smsg("%4d PUSHCHANNEL %d", current,
                                         channel == NULL ? 0 : channel->ch_id);
                }
  #endif
--- 4580,4586 ----
                {
                    channel_T *channel = iptr->isn_arg.channel;
  
!                   smsg("%s%4d PUSHCHANNEL %d", pfx, current,
                                         channel == NULL ? 0 : channel->ch_id);
                }
  #endif
***************
*** 4544,4581 ****
                    tv.v_type = VAR_JOB;
                    tv.vval.v_job = iptr->isn_arg.job;
                    name = tv_get_string(&tv);
!                   smsg("%4d PUSHJOB \"%s\"", current, name);
                }
  #endif
                break;
            case ISN_PUSHEXC:
!               smsg("%4d PUSH v:exception", current);
                break;
            case ISN_UNLET:
!               smsg("%4d UNLET%s %s", current,
                        iptr->isn_arg.unlet.ul_forceit ? "!" : "",
                        iptr->isn_arg.unlet.ul_name);
                break;
            case ISN_UNLETENV:
!               smsg("%4d UNLETENV%s $%s", current,
                        iptr->isn_arg.unlet.ul_forceit ? "!" : "",
                        iptr->isn_arg.unlet.ul_name);
                break;
            case ISN_UNLETINDEX:
!               smsg("%4d UNLETINDEX", current);
                break;
            case ISN_UNLETRANGE:
!               smsg("%4d UNLETRANGE", current);
                break;
            case ISN_LOCKCONST:
!               smsg("%4d LOCKCONST", current);
                break;
            case ISN_NEWLIST:
!               smsg("%4d NEWLIST size %lld", current,
                                            
(varnumber_T)(iptr->isn_arg.number));
                break;
            case ISN_NEWDICT:
!               smsg("%4d NEWDICT size %lld", current,
                                            
(varnumber_T)(iptr->isn_arg.number));
                break;
  
--- 4594,4631 ----
                    tv.v_type = VAR_JOB;
                    tv.vval.v_job = iptr->isn_arg.job;
                    name = tv_get_string(&tv);
!                   smsg("%s%4d PUSHJOB \"%s\"", pfx, current, name);
                }
  #endif
                break;
            case ISN_PUSHEXC:
!               smsg("%s%4d PUSH v:exception", pfx, current);
                break;
            case ISN_UNLET:
!               smsg("%s%4d UNLET%s %s", pfx, current,
                        iptr->isn_arg.unlet.ul_forceit ? "!" : "",
                        iptr->isn_arg.unlet.ul_name);
                break;
            case ISN_UNLETENV:
!               smsg("%s%4d UNLETENV%s $%s", pfx, current,
                        iptr->isn_arg.unlet.ul_forceit ? "!" : "",
                        iptr->isn_arg.unlet.ul_name);
                break;
            case ISN_UNLETINDEX:
!               smsg("%s%4d UNLETINDEX", pfx, current);
                break;
            case ISN_UNLETRANGE:
!               smsg("%s%4d UNLETRANGE", pfx, current);
                break;
            case ISN_LOCKCONST:
!               smsg("%s%4d LOCKCONST", pfx, current);
                break;
            case ISN_NEWLIST:
!               smsg("%s%4d NEWLIST size %lld", pfx, current,
                                            
(varnumber_T)(iptr->isn_arg.number));
                break;
            case ISN_NEWDICT:
!               smsg("%s%4d NEWDICT size %lld", pfx, current,
                                            
(varnumber_T)(iptr->isn_arg.number));
                break;
  
***************
*** 4584,4590 ****
                {
                    cbfunc_T    *cbfunc = &iptr->isn_arg.bfunc;
  
!                   smsg("%4d BCALL %s(argc %d)", current,
                            internal_func_name(cbfunc->cbf_idx),
                            cbfunc->cbf_argcount);
                }
--- 4634,4640 ----
                {
                    cbfunc_T    *cbfunc = &iptr->isn_arg.bfunc;
  
!                   smsg("%s%4d BCALL %s(argc %d)", pfx, current,
                            internal_func_name(cbfunc->cbf_idx),
                            cbfunc->cbf_argcount);
                }
***************
*** 4595,4601 ****
                    dfunc_T     *df = ((dfunc_T *)def_functions.ga_data)
                                                             + cdfunc->cdf_idx;
  
!                   smsg("%4d DCALL %s(argc %d)", current,
                            df->df_ufunc->uf_name_exp != NULL
                                ? df->df_ufunc->uf_name_exp
                                : df->df_ufunc->uf_name, cdfunc->cdf_argcount);
--- 4645,4651 ----
                    dfunc_T     *df = ((dfunc_T *)def_functions.ga_data)
                                                             + cdfunc->cdf_idx;
  
!                   smsg("%s%4d DCALL %s(argc %d)", pfx, current,
                            df->df_ufunc->uf_name_exp != NULL
                                ? df->df_ufunc->uf_name_exp
                                : df->df_ufunc->uf_name, cdfunc->cdf_argcount);
***************
*** 4605,4611 ****
                {
                    cufunc_T    *cufunc = &iptr->isn_arg.ufunc;
  
!                   smsg("%4d UCALL %s(argc %d)", current,
                                       cufunc->cuf_name, cufunc->cuf_argcount);
                }
                break;
--- 4655,4661 ----
                {
                    cufunc_T    *cufunc = &iptr->isn_arg.ufunc;
  
!                   smsg("%s%4d UCALL %s(argc %d)", pfx, current,
                                       cufunc->cuf_name, cufunc->cuf_argcount);
                }
                break;
***************
*** 4613,4630 ****
                {
                    cpfunc_T    *cpfunc = &iptr->isn_arg.pfunc;
  
!                   smsg("%4d PCALL%s (argc %d)", current,
                           cpfunc->cpf_top ? " top" : "", cpfunc->cpf_argcount);
                }
                break;
            case ISN_PCALL_END:
!               smsg("%4d PCALL end", current);
                break;
            case ISN_RETURN:
!               smsg("%4d RETURN", current);
                break;
            case ISN_RETURN_ZERO:
!               smsg("%4d RETURN 0", current);
                break;
            case ISN_FUNCREF:
                {
--- 4663,4680 ----
                {
                    cpfunc_T    *cpfunc = &iptr->isn_arg.pfunc;
  
!                   smsg("%s%4d PCALL%s (argc %d)", pfx, current,
                           cpfunc->cpf_top ? " top" : "", cpfunc->cpf_argcount);
                }
                break;
            case ISN_PCALL_END:
!               smsg("%s%4d PCALL end", pfx, current);
                break;
            case ISN_RETURN:
!               smsg("%s%4d RETURN", pfx, current);
                break;
            case ISN_RETURN_ZERO:
!               smsg("%s%4d RETURN 0", pfx, current);
                break;
            case ISN_FUNCREF:
                {
***************
*** 4632,4638 ****
                    dfunc_T     *df = ((dfunc_T *)def_functions.ga_data)
                                                            + funcref->fr_func;
  
!                   smsg("%4d FUNCREF %s", current, df->df_ufunc->uf_name);
                }
                break;
  
--- 4682,4688 ----
                    dfunc_T     *df = ((dfunc_T *)def_functions.ga_data)
                                                            + funcref->fr_func;
  
!                   smsg("%s%4d FUNCREF %s", pfx, current, 
df->df_ufunc->uf_name);
                }
                break;
  
***************
*** 4640,4646 ****
                {
                    newfunc_T   *newfunc = &iptr->isn_arg.newfunc;
  
!                   smsg("%4d NEWFUNC %s %s", current,
                                       newfunc->nf_lambda, newfunc->nf_global);
                }
                break;
--- 4690,4696 ----
                {
                    newfunc_T   *newfunc = &iptr->isn_arg.newfunc;
  
!                   smsg("%s%4d NEWFUNC %s %s", pfx, current,
                                       newfunc->nf_lambda, newfunc->nf_global);
                }
                break;
***************
*** 4649,4655 ****
                {
                    char_u *name = iptr->isn_arg.string;
  
!                   smsg("%4d DEF %s", current,
                                           name == NULL ? (char_u *)"" : name);
                }
                break;
--- 4699,4705 ----
                {
                    char_u *name = iptr->isn_arg.string;
  
!                   smsg("%s%4d DEF %s", pfx, current,
                                           name == NULL ? (char_u *)"" : name);
                }
                break;
***************
*** 4679,4691 ****
                            when = "JUMP_IF_COND_TRUE";
                            break;
                    }
!                   smsg("%4d %s -> %d", current, when,
                                                iptr->isn_arg.jump.jump_where);
                }
                break;
  
            case ISN_JUMP_IF_ARG_SET:
!               smsg("%4d JUMP_IF_ARG_SET arg[%d] -> %d", current,
                         iptr->isn_arg.jumparg.jump_arg_off + STACK_FRAME_SIZE,
                                                iptr->isn_arg.jump.jump_where);
                break;
--- 4729,4741 ----
                            when = "JUMP_IF_COND_TRUE";
                            break;
                    }
!                   smsg("%s%4d %s -> %d", pfx, current, when,
                                                iptr->isn_arg.jump.jump_where);
                }
                break;
  
            case ISN_JUMP_IF_ARG_SET:
!               smsg("%s%4d JUMP_IF_ARG_SET arg[%d] -> %d", pfx, current,
                         iptr->isn_arg.jumparg.jump_arg_off + STACK_FRAME_SIZE,
                                                iptr->isn_arg.jump.jump_where);
                break;
***************
*** 4694,4700 ****
                {
                    forloop_T *forloop = &iptr->isn_arg.forloop;
  
!                   smsg("%4d FOR $%d -> %d", current,
                                           forloop->for_idx, forloop->for_end);
                }
                break;
--- 4744,4750 ----
                {
                    forloop_T *forloop = &iptr->isn_arg.forloop;
  
!                   smsg("%s%4d FOR $%d -> %d", pfx, current,
                                           forloop->for_idx, forloop->for_end);
                }
                break;
***************
*** 4704,4716 ****
                    try_T *try = &iptr->isn_arg.try;
  
                    if (try->try_ref->try_finally == 0)
!                       smsg("%4d TRY catch -> %d, endtry -> %d",
!                               current,
                                try->try_ref->try_catch,
                                try->try_ref->try_endtry);
                    else
!                       smsg("%4d TRY catch -> %d, finally -> %d, endtry -> %d",
!                               current,
                                try->try_ref->try_catch,
                                try->try_ref->try_finally,
                                try->try_ref->try_endtry);
--- 4754,4766 ----
                    try_T *try = &iptr->isn_arg.try;
  
                    if (try->try_ref->try_finally == 0)
!                       smsg("%s%4d TRY catch -> %d, endtry -> %d",
!                               pfx, current,
                                try->try_ref->try_catch,
                                try->try_ref->try_endtry);
                    else
!                       smsg("%s%4d TRY catch -> %d, finally -> %d, endtry -> 
%d",
!                               pfx, current,
                                try->try_ref->try_catch,
                                try->try_ref->try_finally,
                                try->try_ref->try_endtry);
***************
*** 4718,4743 ****
                break;
            case ISN_CATCH:
                // TODO
!               smsg("%4d CATCH", current);
                break;
            case ISN_TRYCONT:
                {
                    trycont_T *trycont = &iptr->isn_arg.trycont;
  
!                   smsg("%4d TRY-CONTINUE %d level%s -> %d", current,
                                      trycont->tct_levels,
                                      trycont->tct_levels == 1 ? "" : "s",
                                      trycont->tct_where);
                }
                break;
            case ISN_FINALLY:
!               smsg("%4d FINALLY", current);
                break;
            case ISN_ENDTRY:
!               smsg("%4d ENDTRY", current);
                break;
            case ISN_THROW:
!               smsg("%4d THROW", current);
                break;
  
            // expression operations on number
--- 4768,4793 ----
                break;
            case ISN_CATCH:
                // TODO
!               smsg("%s%4d CATCH", pfx, current);
                break;
            case ISN_TRYCONT:
                {
                    trycont_T *trycont = &iptr->isn_arg.trycont;
  
!                   smsg("%s%4d TRY-CONTINUE %d level%s -> %d", pfx, current,
                                      trycont->tct_levels,
                                      trycont->tct_levels == 1 ? "" : "s",
                                      trycont->tct_where);
                }
                break;
            case ISN_FINALLY:
!               smsg("%s%4d FINALLY", pfx, current);
                break;
            case ISN_ENDTRY:
!               smsg("%s%4d ENDTRY", pfx, current);
                break;
            case ISN_THROW:
!               smsg("%s%4d THROW", pfx, current);
                break;
  
            // expression operations on number
***************
*** 4764,4770 ****
                        case ISN_OPANY: ins = "OPANY"; break;
                        default: ins = "???"; break;
                    }
!                   smsg("%4d %s %s", current, ins, what);
                }
                break;
  
--- 4814,4820 ----
                        case ISN_OPANY: ins = "OPANY"; break;
                        default: ins = "???"; break;
                    }
!                   smsg("%s%4d %s %s", pfx, current, ins, what);
                }
                break;
  
***************
*** 4817,4869 ****
                           default: type = "???"; break;
                       }
  
!                      smsg("%4d %s %s", current, type, buf);
                   }
                   break;
  
!           case ISN_ADDLIST: smsg("%4d ADDLIST", current); break;
!           case ISN_ADDBLOB: smsg("%4d ADDBLOB", current); break;
  
            // expression operations
!           case ISN_CONCAT: smsg("%4d CONCAT", current); break;
!           case ISN_STRINDEX: smsg("%4d STRINDEX", current); break;
!           case ISN_STRSLICE: smsg("%4d STRSLICE", current); break;
!           case ISN_BLOBINDEX: smsg("%4d BLOBINDEX", current); break;
!           case ISN_BLOBSLICE: smsg("%4d BLOBSLICE", current); break;
!           case ISN_LISTAPPEND: smsg("%4d LISTAPPEND", current); break;
!           case ISN_BLOBAPPEND: smsg("%4d BLOBAPPEND", current); break;
!           case ISN_LISTINDEX: smsg("%4d LISTINDEX", current); break;
!           case ISN_LISTSLICE: smsg("%4d LISTSLICE", current); break;
!           case ISN_ANYINDEX: smsg("%4d ANYINDEX", current); break;
!           case ISN_ANYSLICE: smsg("%4d ANYSLICE", current); break;
!           case ISN_SLICE: smsg("%4d SLICE %lld",
!                                        current, iptr->isn_arg.number); break;
!           case ISN_GETITEM: smsg("%4d ITEM %lld",
!                                        current, iptr->isn_arg.number); break;
!           case ISN_MEMBER: smsg("%4d MEMBER", current); break;
!           case ISN_STRINGMEMBER: smsg("%4d MEMBER %s", current,
                                                  iptr->isn_arg.string); break;
!           case ISN_NEGATENR: smsg("%4d NEGATENR", current); break;
  
!           case ISN_CHECKNR: smsg("%4d CHECKNR", current); break;
            case ISN_CHECKTYPE:
                  {
                      checktype_T *ct = &iptr->isn_arg.type;
                      char *tofree;
  
                      if (ct->ct_arg_idx == 0)
!                         smsg("%4d CHECKTYPE %s stack[%d]", current,
                                          type_name(ct->ct_type, &tofree),
                                          (int)ct->ct_off);
                      else
!                         smsg("%4d CHECKTYPE %s stack[%d] arg %d", current,
                                          type_name(ct->ct_type, &tofree),
                                          (int)ct->ct_off,
                                          (int)ct->ct_arg_idx);
                      vim_free(tofree);
                      break;
                  }
!           case ISN_CHECKLEN: smsg("%4d CHECKLEN %s%d", current,
                                iptr->isn_arg.checklen.cl_more_OK ? ">= " : "",
                                iptr->isn_arg.checklen.cl_min_len);
                               break;
--- 4867,4919 ----
                           default: type = "???"; break;
                       }
  
!                      smsg("%s%4d %s %s", pfx, current, type, buf);
                   }
                   break;
  
!           case ISN_ADDLIST: smsg("%s%4d ADDLIST", pfx, current); break;
!           case ISN_ADDBLOB: smsg("%s%4d ADDBLOB", pfx, current); break;
  
            // expression operations
!           case ISN_CONCAT: smsg("%s%4d CONCAT", pfx, current); break;
!           case ISN_STRINDEX: smsg("%s%4d STRINDEX", pfx, current); break;
!           case ISN_STRSLICE: smsg("%s%4d STRSLICE", pfx, current); break;
!           case ISN_BLOBINDEX: smsg("%s%4d BLOBINDEX", pfx, current); break;
!           case ISN_BLOBSLICE: smsg("%s%4d BLOBSLICE", pfx, current); break;
!           case ISN_LISTAPPEND: smsg("%s%4d LISTAPPEND", pfx, current); break;
!           case ISN_BLOBAPPEND: smsg("%s%4d BLOBAPPEND", pfx, current); break;
!           case ISN_LISTINDEX: smsg("%s%4d LISTINDEX", pfx, current); break;
!           case ISN_LISTSLICE: smsg("%s%4d LISTSLICE", pfx, current); break;
!           case ISN_ANYINDEX: smsg("%s%4d ANYINDEX", pfx, current); break;
!           case ISN_ANYSLICE: smsg("%s%4d ANYSLICE", pfx, current); break;
!           case ISN_SLICE: smsg("%s%4d SLICE %lld",
!                                        pfx, current, iptr->isn_arg.number); 
break;
!           case ISN_GETITEM: smsg("%s%4d ITEM %lld",
!                                        pfx, current, iptr->isn_arg.number); 
break;
!           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_NEGATENR: smsg("%s%4d NEGATENR", pfx, current); break;
  
!           case ISN_CHECKNR: smsg("%s%4d CHECKNR", pfx, current); break;
            case ISN_CHECKTYPE:
                  {
                      checktype_T *ct = &iptr->isn_arg.type;
                      char *tofree;
  
                      if (ct->ct_arg_idx == 0)
!                         smsg("%s%4d CHECKTYPE %s stack[%d]", pfx, current,
                                          type_name(ct->ct_type, &tofree),
                                          (int)ct->ct_off);
                      else
!                         smsg("%s%4d CHECKTYPE %s stack[%d] arg %d", pfx, 
current,
                                          type_name(ct->ct_type, &tofree),
                                          (int)ct->ct_off,
                                          (int)ct->ct_arg_idx);
                      vim_free(tofree);
                      break;
                  }
!           case ISN_CHECKLEN: smsg("%s%4d CHECKLEN %s%d", pfx, current,
                                iptr->isn_arg.checklen.cl_more_OK ? ">= " : "",
                                iptr->isn_arg.checklen.cl_min_len);
                               break;
***************
*** 4871,4904 ****
                  {
                      char *tofree;
  
!                     smsg("%4d SETTYPE %s", current,
                              type_name(iptr->isn_arg.type.ct_type, &tofree));
                      vim_free(tofree);
                      break;
                  }
!           case ISN_COND2BOOL: smsg("%4d COND2BOOL", current); break;
            case ISN_2BOOL: if (iptr->isn_arg.number)
!                               smsg("%4d INVERT (!val)", current);
                            else
!                               smsg("%4d 2BOOL (!!val)", current);
                            break;
!           case ISN_2STRING: smsg("%4d 2STRING stack[%lld]", current,
                                         (varnumber_T)(iptr->isn_arg.number));
                              break;
!           case ISN_2STRING_ANY: smsg("%4d 2STRING_ANY stack[%lld]", current,
                                         (varnumber_T)(iptr->isn_arg.number));
                              break;
!           case ISN_RANGE: smsg("%4d RANGE %s", current, iptr->isn_arg.string);
                            break;
            case ISN_PUT:
                if (iptr->isn_arg.put.put_lnum == LNUM_VARIABLE_RANGE_ABOVE)
!                   smsg("%4d PUT %c above range",
!                                      current, iptr->isn_arg.put.put_regname);
                else if (iptr->isn_arg.put.put_lnum == LNUM_VARIABLE_RANGE)
!                   smsg("%4d PUT %c range",
!                                      current, iptr->isn_arg.put.put_regname);
                else
!                   smsg("%4d PUT %c %ld", current,
                                                 iptr->isn_arg.put.put_regname,
                                             (long)iptr->isn_arg.put.put_lnum);
                break;
--- 4921,4954 ----
                  {
                      char *tofree;
  
!                     smsg("%s%4d SETTYPE %s", pfx, current,
                              type_name(iptr->isn_arg.type.ct_type, &tofree));
                      vim_free(tofree);
                      break;
                  }
!           case ISN_COND2BOOL: smsg("%s%4d COND2BOOL", pfx, current); break;
            case ISN_2BOOL: if (iptr->isn_arg.number)
!                               smsg("%s%4d INVERT (!val)", pfx, current);
                            else
!                               smsg("%s%4d 2BOOL (!!val)", pfx, current);
                            break;
!           case ISN_2STRING: smsg("%s%4d 2STRING stack[%lld]", pfx, current,
                                         (varnumber_T)(iptr->isn_arg.number));
                              break;
!           case ISN_2STRING_ANY: smsg("%s%4d 2STRING_ANY stack[%lld]", pfx, 
current,
                                         (varnumber_T)(iptr->isn_arg.number));
                              break;
!           case ISN_RANGE: smsg("%s%4d RANGE %s", pfx, current, 
iptr->isn_arg.string);
                            break;
            case ISN_PUT:
                if (iptr->isn_arg.put.put_lnum == LNUM_VARIABLE_RANGE_ABOVE)
!                   smsg("%s%4d PUT %c above range",
!                                      pfx, current, 
iptr->isn_arg.put.put_regname);
                else if (iptr->isn_arg.put.put_lnum == LNUM_VARIABLE_RANGE)
!                   smsg("%s%4d PUT %c range",
!                                      pfx, current, 
iptr->isn_arg.put.put_regname);
                else
!                   smsg("%s%4d PUT %c %ld", pfx, current,
                                                 iptr->isn_arg.put.put_regname,
                                             (long)iptr->isn_arg.put.put_lnum);
                break;
***************
*** 4915,4944 ****
                    {
                        (void)produce_cmdmods(
                                   buf, iptr->isn_arg.cmdmod.cf_cmdmod, FALSE);
!                       smsg("%4d CMDMOD %s", current, buf);
                        vim_free(buf);
                    }
                    break;
                }
!           case ISN_CMDMOD_REV: smsg("%4d CMDMOD_REV", current); break;
  
            case ISN_PROF_START:
!                smsg("%4d PROFILE START line %d", current, iptr->isn_lnum);
                 break;
  
            case ISN_PROF_END:
!               smsg("%4d PROFILE END", current);
                break;
  
!           case ISN_UNPACK: smsg("%4d UNPACK %d%s", current,
                        iptr->isn_arg.unpack.unp_count,
                        iptr->isn_arg.unpack.unp_semicolon ? " semicolon" : "");
                              break;
!           case ISN_SHUFFLE: smsg("%4d SHUFFLE %d up %d", current,
                                         iptr->isn_arg.shuffle.shfl_item,
                                         iptr->isn_arg.shuffle.shfl_up);
                              break;
!           case ISN_DROP: smsg("%4d DROP", current); break;
        }
  
        out_flush();        // output one line at a time
--- 4965,4997 ----
                    {
                        (void)produce_cmdmods(
                                   buf, iptr->isn_arg.cmdmod.cf_cmdmod, FALSE);
!                       smsg("%s%4d CMDMOD %s", pfx, current, buf);
                        vim_free(buf);
                    }
                    break;
                }
!           case ISN_CMDMOD_REV: smsg("%s%4d CMDMOD_REV", pfx, current); break;
  
            case ISN_PROF_START:
!                smsg("%s%4d PROFILE START line %d", pfx, current, 
iptr->isn_lnum);
                 break;
  
            case ISN_PROF_END:
!               smsg("%s%4d PROFILE END", pfx, current);
                break;
  
!           case ISN_UNPACK: smsg("%s%4d UNPACK %d%s", pfx, current,
                        iptr->isn_arg.unpack.unp_count,
                        iptr->isn_arg.unpack.unp_semicolon ? " semicolon" : "");
                              break;
!           case ISN_SHUFFLE: smsg("%s%4d SHUFFLE %d up %d", pfx, current,
                                         iptr->isn_arg.shuffle.shfl_item,
                                         iptr->isn_arg.shuffle.shfl_up);
                              break;
!           case ISN_DROP: smsg("%s%4d DROP", pfx, current); break;
! 
!           case ISN_FINISH: // End of list of instructions for ISN_SUBSTITUTE.
!                          return;
        }
  
        out_flush();        // output one line at a time
***************
*** 4949,4954 ****
--- 5002,5079 ----
  }
  
  /*
+  * ":disassemble".
+  * We don't really need this at runtime, but we do have tests that require it,
+  * so always include this.
+  */
+     void
+ ex_disassemble(exarg_T *eap)
+ {
+     char_u    *arg = eap->arg;
+     char_u    *fname;
+     ufunc_T   *ufunc;
+     dfunc_T   *dfunc;
+     isn_T     *instr;
+     int               instr_count;
+     int               is_global = FALSE;
+ 
+     if (STRNCMP(arg, "<lambda>", 8) == 0)
+     {
+       arg += 8;
+       (void)getdigits(&arg);
+       fname = vim_strnsave(eap->arg, arg - eap->arg);
+     }
+     else
+       fname = trans_function_name(&arg, &is_global, FALSE,
+                     TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD, NULL, NULL, NULL);
+     if (fname == NULL)
+     {
+       semsg(_(e_invarg2), eap->arg);
+       return;
+     }
+ 
+     ufunc = find_func(fname, is_global, NULL);
+     if (ufunc == NULL)
+     {
+       char_u *p = untrans_function_name(fname);
+ 
+       if (p != NULL)
+           // Try again without making it script-local.
+           ufunc = find_func(p, FALSE, NULL);
+     }
+     vim_free(fname);
+     if (ufunc == NULL)
+     {
+       semsg(_(e_cannot_find_function_str), eap->arg);
+       return;
+     }
+     if (func_needs_compiling(ufunc, eap->forceit)
+           && compile_def_function(ufunc, FALSE, eap->forceit, NULL) == FAIL)
+       return;
+     if (ufunc->uf_def_status != UF_COMPILED)
+     {
+       semsg(_(e_function_is_not_compiled_str), eap->arg);
+       return;
+     }
+     if (ufunc->uf_name_exp != NULL)
+       msg((char *)ufunc->uf_name_exp);
+     else
+       msg((char *)ufunc->uf_name);
+ 
+     dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx;
+ #ifdef FEAT_PROFILE
+     instr = eap->forceit ? dfunc->df_instr_prof : dfunc->df_instr;
+     instr_count = eap->forceit ? dfunc->df_instr_prof_count
+                                                      : dfunc->df_instr_count;
+ #else
+     instr = dfunc->df_instr;
+     instr_count = dfunc->df_instr_count;
+ #endif
+ 
+     list_instructions("", instr, instr_count, ufunc);
+ }
+ 
+ /*
   * Return TRUE when "tv" is not falsy: non-zero, non-empty string, non-empty
   * list, etc.  Mostly like what JavaScript does, except that empty list and
   * empty dictionary are FALSE.
*** ../vim-8.2.2783/src/proto/vim9execute.pro   2021-01-13 21:46:53.832589880 
+0100
--- src/proto/vim9execute.pro   2021-04-18 22:21:32.721663293 +0200
***************
*** 4,9 ****
--- 4,10 ----
  char_u *char_from_string(char_u *str, varnumber_T index);
  char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last, int 
exclusive);
  int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx);
+ char_u *exe_substitute_instr(void);
  int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, partial_T 
*partial, typval_T *rettv);
  void ex_disassemble(exarg_T *eap);
  int tv2bool(typval_T *tv);
*** ../vim-8.2.2783/src/regexp.c        2021-01-04 12:41:49.507891351 +0100
--- src/regexp.c        2021-04-19 15:07:45.706435900 +0200
***************
*** 2069,2074 ****
--- 2069,2077 ----
                }
                clear_tv(&rettv);
            }
+           else if (substitute_instr != NULL)
+               // Execute instructions from ISN_SUBSTITUTE.
+               eval_result = exe_substitute_instr();
            else
                eval_result = eval_to_string(source + 2, TRUE);
  
*** ../vim-8.2.2783/src/ex_cmds.c       2021-04-07 19:42:53.966200626 +0200
--- src/ex_cmds.c       2021-04-19 15:38:30.213278930 +0200
***************
*** 3604,3609 ****
--- 3604,3632 ----
  } subflags_T;
  
  /*
+  * Skip over the "sub" part in :s/pat/sub/ where "delimiter" is the separating
+  * character.
+  */
+     char_u *
+ skip_substitute(char_u *start, int delimiter)
+ {
+     char_u *p = start;
+ 
+     while (p[0])
+     {
+       if (p[0] == delimiter)          // end delimiter found
+       {
+           *p++ = NUL;                 // replace it with a NUL
+           break;
+       }
+       if (p[0] == '\\' && p[1] != 0)  // skip escaped characters
+           ++p;
+       MB_PTR_ADV(p);
+     }
+     return p;
+ }
+ 
+ /*
   * Perform a substitution from line eap->line1 to line eap->line2 using the
   * command pointed to by eap->arg which should be of the form:
   *
***************
*** 3704,3721 ****
         * Vim we want to use '\n' to find/substitute a NUL.
         */
        sub = cmd;          // remember the start of the substitution
! 
!       while (cmd[0])
!       {
!           if (cmd[0] == delimiter)            // end delimiter found
!           {
!               *cmd++ = NUL;                   // replace it with a NUL
!               break;
!           }
!           if (cmd[0] == '\\' && cmd[1] != 0)  // skip escaped characters
!               ++cmd;
!           MB_PTR_ADV(cmd);
!       }
  
        if (!eap->skip)
        {
--- 3727,3733 ----
         * Vim we want to use '\n' to find/substitute a NUL.
         */
        sub = cmd;          // remember the start of the substitution
!       cmd = skip_substitute(cmd, delimiter);
  
        if (!eap->skip)
        {
*** ../vim-8.2.2783/src/proto/ex_cmds.pro       2020-08-12 21:58:08.996049839 
+0200
--- src/proto/ex_cmds.pro       2021-04-19 15:39:01.617181578 +0200
***************
*** 27,32 ****
--- 27,33 ----
  void ex_z(exarg_T *eap);
  int check_restricted(void);
  int check_secure(void);
+ char_u *skip_substitute(char_u *start, int delimiter);
  void ex_substitute(exarg_T *eap);
  int do_sub_msg(int count_only);
  void ex_global(exarg_T *eap);
*** ../vim-8.2.2783/src/globals.h       2021-04-10 14:03:40.312675756 +0200
--- src/globals.h       2021-04-18 22:19:44.650129976 +0200
***************
*** 1379,1384 ****
--- 1379,1387 ----
  EXTERN long   sub_nsubs;      // total number of substitutions
  EXTERN linenr_T       sub_nlines;     // total number of lines changed
  
+ // Used when a compiled :substitute has an expression.
+ EXTERN struct subs_expr_S     *substitute_instr INIT(= NULL);
+ 
  // table to store parsed 'wildmode'
  EXTERN char_u wim_flags[4];
  
*** ../vim-8.2.2783/src/testdir/test_vim9_cmd.vim       2021-03-28 
20:38:30.540591499 +0200
--- src/testdir/test_vim9_cmd.vim       2021-04-19 16:39:30.449204527 +0200
***************
*** 1172,1176 ****
--- 1172,1198 ----
    CheckDefFailure(lines, 'E1178', 2)
  enddef
  
+ def Test_substitute_expr()
+   var to = 'repl'
+   new
+   setline(1, 'one from two')
+   s/from/\=to
+   assert_equal('one repl two', getline(1))
+ 
+   setline(1, 'one from two')
+   s/from/\=to .. '_x'
+   assert_equal('one repl_x two', getline(1))
+ 
+   setline(1, 'one from two from three')
+   var also = 'also'
+   s/from/\=to .. '_' .. also/g#e
+   assert_equal('one repl_also two repl_also three', getline(1))
+ 
+   CheckDefFailure(['s/from/\="x")/'], 'E488:')
+   CheckDefFailure(['s/from/\="x"/9'], 'E488:')
+ 
+   bwipe!
+ enddef
+ 
  
  " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
*** ../vim-8.2.2783/src/testdir/test_vim9_disassemble.vim       2021-04-17 
20:44:52.442520718 +0200
--- src/testdir/test_vim9_disassemble.vim       2021-04-19 16:46:31.231554443 
+0200
***************
*** 121,126 ****
--- 121,145 ----
          res)
  enddef
  
+ def s:Substitute()
+   var expr = "abc"
+   :%s/a/\=expr/&g#c
+ enddef
+ 
+ def Test_disassemble_substitute()
+   var res = execute('disass s:Substitute')
+   assert_match('<SNR>\d*_Substitute.*' ..
+         ' var expr = "abc"\_s*' ..
+         '\d PUSHS "abc"\_s*' ..
+         '\d STORE $0\_s*' ..
+         ' :%s/a/\\=expr/&g#c\_s*' ..
+         '\d SUBSTITUTE   :%s/a/\\=expr/&g#c\_s*' ..
+         '    0 LOAD $0\_s*' ..
+         '    -------------\_s*' ..
+         '\d RETURN 0',
+         res)
+ enddef
+ 
  def s:YankRange()
    norm! m[jjm]
    :'[,']yank
*** ../vim-8.2.2783/src/version.c       2021-04-18 16:08:49.416235259 +0200
--- src/version.c       2021-04-18 17:17:53.278274458 +0200
***************
*** 752,753 ****
--- 752,755 ----
  {   /* Add new patch number below this line */
+ /**/
+     2784,
  /**/

-- 
I have a watch cat! Just break in and she'll watch.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/202104191449.13JEnIhM594229%40masaka.moolenaar.net.

Raspunde prin e-mail lui