Patch 8.2.2784

2 views
Skip to first unread message

Bram Moolenaar

unread,
Apr 19, 2021, 10:49:25 AM4/19/21
to vim...@googlegroups.com

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 -- Br...@Moolenaar.net -- http://www.Moolenaar.net \\\
/// \\\
\\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
Reply all
Reply to author
Forward
0 new messages