# HG changeset patch # User Bram Moolenaar # Date 1618844404 -7200 # Node ID f293bb501b30533e594698759aaae6a20a292194 # Parent 2bcec3ba6a08c6467f8a1b69723c420bba3bc757 patch 8.2.2784: Vim9: cannot use =expr in :substitute Commit: https://github.com/vim/vim/commit/4c13721482d7786f92f5a56e43b0f5c499264b7e Author: Bram Moolenaar Date: Mon Apr 19 16:48:48 2021 +0200 patch 8.2.2784: Vim9: cannot use \=expr in :substitute Problem: Vim9: cannot use \=expr in :substitute. Solution: Compile the expression into instructions and execute them when invoked. diff --git a/src/ex_cmds.c b/src/ex_cmds.c --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -3604,6 +3604,29 @@ typedef struct { } 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,18 +3727,7 @@ ex_substitute(exarg_T *eap) * 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); - } + cmd = skip_substitute(cmd, delimiter); if (!eap->skip) { diff --git a/src/globals.h b/src/globals.h --- a/src/globals.h +++ b/src/globals.h @@ -1379,6 +1379,9 @@ EXTERN char_u no_lines_msg[] INIT(= N_(" 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]; diff --git a/src/proto/ex_cmds.pro b/src/proto/ex_cmds.pro --- a/src/proto/ex_cmds.pro +++ b/src/proto/ex_cmds.pro @@ -27,6 +27,7 @@ void ex_change(exarg_T *eap); 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); diff --git a/src/proto/vim9execute.pro b/src/proto/vim9execute.pro --- a/src/proto/vim9execute.pro +++ b/src/proto/vim9execute.pro @@ -4,6 +4,7 @@ void funcstack_check_refcount(funcstack_ 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); diff --git a/src/regexp.c b/src/regexp.c --- a/src/regexp.c +++ b/src/regexp.c @@ -2069,6 +2069,9 @@ vim_regsub_both( } 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); diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim --- a/src/testdir/test_vim9_cmd.vim +++ b/src/testdir/test_vim9_cmd.vim @@ -1172,5 +1172,27 @@ def Test_lockvar() 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 diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -121,6 +121,25 @@ def Test_disassemble_exec_expr() 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('\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 diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2784, +/**/ 2783, /**/ 2782, diff --git a/src/vim9.h b/src/vim9.h --- a/src/vim9.h +++ b/src/vim9.h @@ -19,6 +19,7 @@ typedef enum { 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,7 +95,8 @@ typedef enum { // 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 + 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,7 +167,9 @@ typedef enum { 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_DROP, // pop stack and discard value + + ISN_FINISH // end marker in list of instructions } isntype_T; @@ -339,6 +343,12 @@ typedef struct { 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,6 +391,7 @@ struct isn_S { cmod_T cmdmod; unpack_T unpack; isn_outer_T outer; + subs_T subs; } isn_arg; }; diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -2130,6 +2130,33 @@ generate_EXECCONCAT(cctx_T *cctx, int co 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,6 +8493,55 @@ theend: } /* + * :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,6 +9072,16 @@ compile_def_function( 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,6 +9309,11 @@ delete_instr(isn_T *isn) 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,6 +9491,7 @@ delete_instr(isn_T *isn) case ISN_UNLETINDEX: case ISN_UNLETRANGE: case ISN_UNPACK: + case ISN_FINISH: // nothing allocated break; } diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -34,6 +34,14 @@ typedef struct { 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,8 +68,10 @@ typedef struct { 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,6 +81,10 @@ struct ectx_S { 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,15 +139,6 @@ exe_newlist(int count, ectx_T *ectx) 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 @@ -154,7 +159,6 @@ call_dfunc( int cdf_idx, partial_T *pt, int argcount_arg, - funclocal_T *funclocal, ectx_T *ectx) { int argcount = argcount_arg; @@ -254,13 +258,13 @@ call_dfunc( return FAIL; // Only make a copy of funclocal if it contains something to restore. - if (funclocal->floc_restore_cmdmod) + if (ectx->ec_funclocal.floc_restore_cmdmod) { floc = ALLOC_ONE(funclocal_T); if (floc == NULL) return FAIL; - *floc = *funclocal; - funclocal->floc_restore_cmdmod = FALSE; + *floc = ectx->ec_funclocal; + ectx->ec_funclocal.floc_restore_cmdmod = FALSE; } // Move the vararg-list to below the missing optional arguments. @@ -527,7 +531,7 @@ funcstack_check_refcount(funcstack_T *fu * Return from the current function. */ static int -func_return(funclocal_T *funclocal, ectx_T *ectx) +func_return(ectx_T *ectx) { int idx; int ret_idx; @@ -598,10 +602,10 @@ func_return(funclocal_T *funclocal, ectx ectx->ec_instr = INSTRUCTIONS(prev_dfunc); if (floc == NULL) - funclocal->floc_restore_cmdmod = FALSE; + ectx->ec_funclocal.floc_restore_cmdmod = FALSE; else { - *funclocal = *floc; + ectx->ec_funclocal = *floc; vim_free(floc); } @@ -698,7 +702,6 @@ call_ufunc( ufunc_T *ufunc, partial_T *pt, int argcount, - funclocal_T *funclocal, ectx_T *ectx, isn_T *iptr) { @@ -738,7 +741,7 @@ call_ufunc( 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); + return call_dfunc(ufunc->uf_dfunc_idx, pt, argcount, ectx); } if (call_prepare(argcount, argvars, ectx) == FAIL) @@ -800,7 +803,6 @@ vim9_aborting(int prev_called_emsg) call_by_name( char_u *name, int argcount, - funclocal_T *funclocal, ectx_T *ectx, isn_T *iptr) { @@ -853,7 +855,7 @@ call_by_name( } } - return call_ufunc(ufunc, NULL, argcount, funclocal, ectx, iptr); + return call_ufunc(ufunc, NULL, argcount, ectx, iptr); } return FAIL; @@ -863,7 +865,6 @@ call_by_name( call_partial( typval_T *tv, int argcount_arg, - funclocal_T *funclocal, ectx_T *ectx) { int argcount = argcount_arg; @@ -893,7 +894,7 @@ call_partial( } if (pt->pt_func != NULL) - return call_ufunc(pt->pt_func, pt, argcount, funclocal, ectx, NULL); + return call_ufunc(pt->pt_func, pt, argcount, ectx, NULL); name = pt->pt_name; } @@ -911,7 +912,7 @@ call_partial( if (error != FCERR_NONE) res = FAIL; else - res = call_by_name(fname, argcount, funclocal, ectx, NULL); + res = call_by_name(fname, argcount, ectx, NULL); vim_free(tofree); } @@ -1184,14 +1185,13 @@ get_script_svar(scriptref_T *sref, ectx_ 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); + res = call_by_name(name, argcount, ectx, iptr); if (res == FAIL && called_emsg == called_emsg_before) { dictitem_T *v; @@ -1207,7 +1207,7 @@ call_eval_func( semsg(_(e_unknownfunc), name); return FAIL; } - return call_partial(&v->di_tv, argcount, funclocal, ectx); + return call_partial(&v->di_tv, argcount, ectx); } return res; } @@ -1257,255 +1257,35 @@ fill_partial_and_closure(partial_T *pt, 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; +// 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) +#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) +#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; +#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; + ectx->ec_iidx = 0; for (;;) { @@ -1521,7 +1301,7 @@ call_def_function( // Turn CTRL-C into an exception. got_int = FALSE; if (throw_exception("Vim:Interrupt", ET_INTERRUPT, NULL) == FAIL) - goto failed; + return FAIL; did_throw = TRUE; } @@ -1530,52 +1310,52 @@ call_def_function( // Turn an error message into an exception. did_emsg = FALSE; if (throw_exception(*msg_list, ET_ERROR, NULL) == FAIL) - goto failed; + return FAIL; did_throw = TRUE; *msg_list = NULL; } - if (did_throw && !ectx.ec_in_catch) + if (did_throw && !ectx->ec_in_catch) { - garray_T *trystack = &ectx.ec_trystack; + 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) + 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; + 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; + 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 == initial_frame_idx) + ++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) - goto failed; + if (handle_closure_in_use(ectx, FALSE) == FAIL) + return FAIL; goto done; } - if (func_return(&funclocal, &ectx) == FAIL) - goto failed; + if (func_return(ectx) == FAIL) + return FAIL; } continue; } - iptr = &ectx.ec_instr[ectx.ec_iidx++]; + iptr = &ectx->ec_instr[ectx->ec_iidx++]; switch (iptr->isn_type) { // execute Ex command line @@ -1597,6 +1377,38 @@ call_def_function( } 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,7 +1438,7 @@ call_def_function( { cmd = alloc(len + 1); if (cmd == NULL) - goto failed; + return FAIL; len = 0; } } @@ -1643,6 +1455,7 @@ call_def_function( int count = iptr->isn_arg.echo.echo_count; int atstart = TRUE; int needclr = TRUE; + int idx; for (idx = 0; idx < count; ++idx) { @@ -1653,7 +1466,7 @@ call_def_function( } if (needclr) msg_clr_eos(); - ectx.ec_stack.ga_len -= count; + ectx->ec_stack.ga_len -= count; } break; @@ -1670,6 +1483,7 @@ call_def_function( char_u *p; int len; int failed = FALSE; + int idx; ga_init2(&ga, 1, 80); for (idx = 0; idx < count; ++idx) @@ -1702,7 +1516,7 @@ call_def_function( } clear_tv(tv); } - ectx.ec_stack.ga_len -= count; + ectx->ec_stack.ga_len -= count; if (failed) { ga_clear(&ga); @@ -1742,18 +1556,18 @@ call_def_function( // load local variable or argument case ISN_LOAD: - if (GA_GROW(&ectx.ec_stack, 1) == FAIL) - goto failed; + 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; + ++ectx->ec_stack.ga_len; break; // load v: variable case ISN_LOADV: - if (GA_GROW(&ectx.ec_stack, 1) == FAIL) - goto failed; + 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; + ++ectx->ec_stack.ga_len; break; // load s: variable in Vim9 script @@ -1762,14 +1576,14 @@ call_def_function( scriptref_T *sref = iptr->isn_arg.script.scriptref; svar_T *sv; - sv = get_script_svar(sref, &ectx); + sv = get_script_svar(sref, ectx); if (sv == NULL) - goto failed; + return FAIL; allocate_if_null(sv->sv_tv); - if (GA_GROW(&ectx.ec_stack, 1) == FAIL) - goto failed; + if (GA_GROW(&ectx->ec_stack, 1) == FAIL) + return FAIL; copy_tv(sv->sv_tv, STACK_TV_BOT(0)); - ++ectx.ec_stack.ga_len; + ++ectx->ec_stack.ga_len; } break; @@ -1789,10 +1603,10 @@ call_def_function( } else { - if (GA_GROW(&ectx.ec_stack, 1) == FAIL) - goto failed; + if (GA_GROW(&ectx->ec_stack, 1) == FAIL) + return FAIL; copy_tv(&di->di_tv, STACK_TV_BOT(0)); - ++ectx.ec_stack.ga_len; + ++ectx->ec_stack.ga_len; } } break; @@ -1826,7 +1640,7 @@ call_def_function( namespace = 't'; break; default: // Cannot reach here - goto failed; + return FAIL; } di = find_var_in_ht(ht, 0, iptr->isn_arg.string, TRUE); @@ -1839,10 +1653,10 @@ call_def_function( } else { - if (GA_GROW(&ectx.ec_stack, 1) == FAIL) - goto failed; + if (GA_GROW(&ectx->ec_stack, 1) == FAIL) + return FAIL; copy_tv(&di->di_tv, STACK_TV_BOT(0)); - ++ectx.ec_stack.ga_len; + ++ectx->ec_stack.ga_len; } } break; @@ -1852,13 +1666,13 @@ call_def_function( { char_u *name = iptr->isn_arg.string; - if (GA_GROW(&ectx.ec_stack, 1) == FAIL) - goto failed; + 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; + ++ectx->ec_stack.ga_len; } break; @@ -1877,16 +1691,16 @@ call_def_function( case ISN_LOADWDICT: d = curwin->w_vars; break; case ISN_LOADTDICT: d = curtab->tp_vars; break; default: // Cannot reach here - goto failed; + return FAIL; } - if (GA_GROW(&ectx.ec_stack, 1) == FAIL) - goto failed; + 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; + ++ectx->ec_stack.ga_len; } break; @@ -1898,12 +1712,12 @@ call_def_function( // 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 (GA_GROW(&ectx->ec_stack, 1) == FAIL) + return FAIL; if (eval_option(&name, &optval, TRUE) == FAIL) - goto failed; + return FAIL; *STACK_TV_BOT(0) = optval; - ++ectx.ec_stack.ga_len; + ++ectx->ec_stack.ga_len; } break; @@ -1913,19 +1727,19 @@ call_def_function( typval_T optval; char_u *name = iptr->isn_arg.string; - if (GA_GROW(&ectx.ec_stack, 1) == FAIL) - goto failed; + 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; + ++ectx->ec_stack.ga_len; } break; // load @register case ISN_LOADREG: - if (GA_GROW(&ectx.ec_stack, 1) == FAIL) - goto failed; + 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,12 +1747,12 @@ call_def_function( // empty string. tv->vval.v_string = get_reg_contents( iptr->isn_arg.number, GREG_EXPR_SRC); - ++ectx.ec_stack.ga_len; + ++ectx->ec_stack.ga_len; break; // store local variable case ISN_STORE: - --ectx.ec_stack.ga_len; + --ectx->ec_stack.ga_len; tv = STACK_TV_VAR(iptr->isn_arg.number); clear_tv(tv); *tv = *STACK_TV_BOT(0); @@ -1952,7 +1766,7 @@ call_def_function( 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; + --ectx->ec_stack.ga_len; if (di == NULL) store_var(name, STACK_TV_BOT(0)); else @@ -1975,10 +1789,10 @@ call_def_function( scriptref_T *sref = iptr->isn_arg.script.scriptref; svar_T *sv; - sv = get_script_svar(sref, &ectx); + sv = get_script_svar(sref, ectx); if (sv == NULL) - goto failed; - --ectx.ec_stack.ga_len; + return FAIL; + --ectx->ec_stack.ga_len; // "const" and "final" are checked at compile time, locking // the value needs to be checked here. @@ -2001,7 +1815,7 @@ call_def_function( char_u *s = NULL; char *msg; - --ectx.ec_stack.ga_len; + --ectx->ec_stack.ga_len; tv = STACK_TV_BOT(0); if (tv->v_type == VAR_STRING) { @@ -2026,7 +1840,7 @@ call_def_function( // store $ENV case ISN_STOREENV: - --ectx.ec_stack.ga_len; + --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,7 +1851,7 @@ call_def_function( { int reg = iptr->isn_arg.number; - --ectx.ec_stack.ga_len; + --ectx->ec_stack.ga_len; tv = STACK_TV_BOT(0); write_reg_contents(reg == '@' ? '"' : reg, tv_get_string(tv), -1, FALSE); @@ -2047,7 +1861,7 @@ call_def_function( // store v: variable case ISN_STOREV: - --ectx.ec_stack.ga_len; + --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,10 +1893,10 @@ call_def_function( ht = &curtab->tp_vars->dv_hashtab; break; default: // Cannot reach here - goto failed; + return FAIL; } - --ectx.ec_stack.ga_len; + --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,7 +1916,7 @@ call_def_function( 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; + --ectx->ec_stack.ga_len; break; // store number in local variable @@ -2184,7 +1998,7 @@ call_def_function( goto on_error; // append to list, only fails when out of memory if (list_append_tv(list, tv) == FAIL) - goto failed; + return FAIL; clear_tv(tv); } } @@ -2219,7 +2033,7 @@ call_def_function( goto on_error; // add to dict, only fails when out of memory if (dict_add_tv(dict, (char *)key, tv) == FAIL) - goto failed; + return FAIL; clear_tv(tv); } } @@ -2263,7 +2077,7 @@ call_def_function( clear_tv(tv_idx); clear_tv(tv_dest); - ectx.ec_stack.ga_len -= 3; + ectx->ec_stack.ga_len -= 3; if (status == FAIL) { clear_tv(tv); @@ -2328,7 +2142,7 @@ call_def_function( clear_tv(tv_idx1); clear_tv(tv_idx2); clear_tv(tv_dest); - ectx.ec_stack.ga_len -= 4; + ectx->ec_stack.ga_len -= 4; clear_tv(tv); if (status == FAIL) @@ -2341,7 +2155,7 @@ call_def_function( case ISN_STOREOUTER: { int depth = iptr->isn_arg.outer.outer_depth; - outer_T *outer = ectx.ec_outer; + outer_T *outer = ectx->ec_outer; while (depth > 1 && outer != NULL) { @@ -2352,21 +2166,21 @@ call_def_function( { SOURCING_LNUM = iptr->isn_lnum; iemsg("LOADOUTER depth more than scope levels"); - goto failed; + 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) - goto failed; + if (GA_GROW(&ectx->ec_stack, 1) == FAIL) + return FAIL; copy_tv(tv, STACK_TV_BOT(0)); - ++ectx.ec_stack.ga_len; + ++ectx->ec_stack.ga_len; } else { - --ectx.ec_stack.ga_len; + --ectx->ec_stack.ga_len; clear_tv(tv); *tv = *STACK_TV_BOT(0); } @@ -2453,7 +2267,7 @@ call_def_function( clear_tv(tv_idx); clear_tv(tv_dest); - ectx.ec_stack.ga_len -= 2; + ectx->ec_stack.ga_len -= 2; if (status == FAIL) goto on_error; } @@ -2505,7 +2319,7 @@ call_def_function( clear_tv(tv_idx1); clear_tv(tv_idx2); clear_tv(tv_dest); - ectx.ec_stack.ga_len -= 3; + ectx->ec_stack.ga_len -= 3; if (status == FAIL) goto on_error; } @@ -2521,11 +2335,11 @@ call_def_function( case ISN_PUSHFUNC: case ISN_PUSHCHANNEL: case ISN_PUSHJOB: - if (GA_GROW(&ectx.ec_stack, 1) == FAIL) - goto failed; + if (GA_GROW(&ectx->ec_stack, 1) == FAIL) + return FAIL; tv = STACK_TV_BOT(0); tv->v_lock = 0; - ++ectx.ec_stack.ga_len; + ++ectx->ec_stack.ga_len; switch (iptr->isn_type) { case ISN_PUSHNR: @@ -2597,8 +2411,8 @@ call_def_function( // 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; + if (exe_newlist(iptr->isn_arg.number, ectx) == FAIL) + return FAIL; break; // create a dict from items on the stack @@ -2608,9 +2422,10 @@ call_def_function( dict_T *dict = dict_alloc(); dictitem_T *item; char_u *key; + int idx; if (dict == NULL) - goto failed; + return FAIL; for (idx = 0; idx < count; ++idx) { // have already checked key type is VAR_STRING @@ -2631,7 +2446,7 @@ call_def_function( if (item == NULL) { dict_unref(dict); - goto failed; + return FAIL; } item->di_tv = *STACK_TV_BOT(2 * (idx - count) + 1); item->di_tv.v_lock = 0; @@ -2639,16 +2454,16 @@ call_def_function( { // can this ever happen? dict_unref(dict); - goto failed; + return FAIL; } } if (count > 0) - ectx.ec_stack.ga_len -= 2 * count - 1; - else if (GA_GROW(&ectx.ec_stack, 1) == FAIL) - goto failed; + 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; + ++ectx->ec_stack.ga_len; tv = STACK_TV_BOT(-1); tv->v_type = VAR_DICT; tv->v_lock = 0; @@ -2663,8 +2478,7 @@ call_def_function( if (call_dfunc(iptr->isn_arg.dfunc.cdf_idx, NULL, iptr->isn_arg.dfunc.cdf_argcount, - &funclocal, - &ectx) == FAIL) + ectx) == FAIL) goto on_error; break; @@ -2673,7 +2487,7 @@ call_def_function( SOURCING_LNUM = iptr->isn_lnum; if (call_bfunc(iptr->isn_arg.bfunc.cbf_idx, iptr->isn_arg.bfunc.cbf_argcount, - &ectx) == FAIL) + ectx) == FAIL) goto on_error; break; @@ -2693,12 +2507,11 @@ call_def_function( else { // Get the funcref from the stack. - --ectx.ec_stack.ga_len; + --ectx->ec_stack.ga_len; partial_tv = *STACK_TV_BOT(0); tv = &partial_tv; } - r = call_partial(tv, pfunc->cpf_argcount, - &funclocal, &ectx); + r = call_partial(tv, pfunc->cpf_argcount, ectx); if (tv == &partial_tv) clear_tv(&partial_tv); if (r == FAIL) @@ -2710,7 +2523,7 @@ call_def_function( // 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; + --ectx->ec_stack.ga_len; clear_tv(STACK_TV_BOT(-1)); *STACK_TV_BOT(-1) = *STACK_TV_BOT(0); break; @@ -2722,17 +2535,17 @@ call_def_function( SOURCING_LNUM = iptr->isn_lnum; if (call_eval_func(cufunc->cuf_name, cufunc->cuf_argcount, - &funclocal, &ectx, iptr) == FAIL) + 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; + if (GA_GROW(&ectx->ec_stack, 1) == FAIL) + return FAIL; tv = STACK_TV_BOT(0); - ++ectx.ec_stack.ga_len; + ++ectx->ec_stack.ga_len; tv->v_type = VAR_NUMBER; tv->vval.v_number = 0; tv->v_lock = 0; @@ -2740,20 +2553,20 @@ call_def_function( case ISN_RETURN: { - garray_T *trystack = &ectx.ec_trystack; + 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) + && 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; + ectx->ec_iidx = trycmd->tcd_finally_idx; else - ectx.ec_iidx = trycmd->tcd_endtry_idx; + ectx->ec_iidx = trycmd->tcd_endtry_idx; trycmd->tcd_return = TRUE; } else @@ -2769,18 +2582,18 @@ call_def_function( + iptr->isn_arg.funcref.fr_func; if (pt == NULL) - goto failed; - if (GA_GROW(&ectx.ec_stack, 1) == FAIL) + return FAIL; + if (GA_GROW(&ectx->ec_stack, 1) == FAIL) { vim_free(pt); - goto failed; + return FAIL; } if (fill_partial_and_closure(pt, pt_dfunc->df_ufunc, - &ectx) == FAIL) - goto failed; + ectx) == FAIL) + return FAIL; tv = STACK_TV_BOT(0); - ++ectx.ec_stack.ga_len; + ++ectx->ec_stack.ga_len; tv->vval.v_partial = pt; tv->v_type = VAR_PARTIAL; tv->v_lock = 0; @@ -2793,8 +2606,8 @@ call_def_function( newfunc_T *newfunc = &iptr->isn_arg.newfunc; if (copy_func(newfunc->nf_lambda, newfunc->nf_global, - &ectx) == FAIL) - goto failed; + ectx) == FAIL) + return FAIL; } break; @@ -2841,11 +2654,11 @@ call_def_function( { // drop the value from the stack clear_tv(tv); - --ectx.ec_stack.ga_len; + --ectx->ec_stack.ga_len; } } if (jump) - ectx.ec_iidx = iptr->isn_arg.jump.jump_where; + ectx->ec_iidx = iptr->isn_arg.jump.jump_where; } break; @@ -2856,7 +2669,7 @@ call_def_function( 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; + ectx->ec_iidx = iptr->isn_arg.jumparg.jump_where; break; // top of a for loop @@ -2866,8 +2679,8 @@ call_def_function( typval_T *idxtv = STACK_TV_VAR(iptr->isn_arg.forloop.for_idx); - if (GA_GROW(&ectx.ec_stack, 1) == FAIL) - goto failed; + if (GA_GROW(&ectx->ec_stack, 1) == FAIL) + return FAIL; if (ltv->v_type == VAR_LIST) { list_T *list = ltv->vval.v_list; @@ -2878,8 +2691,8 @@ call_def_function( || 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); + ectx->ec_iidx = iptr->isn_arg.forloop.for_end; + may_restore_cmdmod(&ectx->ec_funclocal); } else if (list->lv_first == &range_list_item) { @@ -2889,7 +2702,7 @@ call_def_function( tv->v_lock = 0; tv->vval.v_number = list_find_nr( list, idxtv->vval.v_number, NULL); - ++ectx.ec_stack.ga_len; + ++ectx->ec_stack.ga_len; } else { @@ -2897,7 +2710,7 @@ call_def_function( idxtv->vval.v_number); copy_tv(&li->li_tv, STACK_TV_BOT(0)); - ++ectx.ec_stack.ga_len; + ++ectx->ec_stack.ga_len; } } else if (ltv->v_type == VAR_STRING) @@ -2910,8 +2723,8 @@ call_def_function( 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); + ectx->ec_iidx = iptr->isn_arg.forloop.for_end; + may_restore_cmdmod(&ectx->ec_funclocal); } else { @@ -2922,7 +2735,7 @@ call_def_function( tv->v_type = VAR_STRING; tv->vval.v_string = vim_strnsave( str + idxtv->vval.v_number, clen); - ++ectx.ec_stack.ga_len; + ++ectx->ec_stack.ga_len; idxtv->vval.v_number += clen - 1; } } @@ -2946,8 +2759,8 @@ call_def_function( || 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); + ectx->ec_iidx = iptr->isn_arg.forloop.for_end; + may_restore_cmdmod(&ectx->ec_funclocal); } else { @@ -2956,14 +2769,14 @@ call_def_function( tv->v_type = VAR_NUMBER; tv->vval.v_number = blob_get(blob, idxtv->vval.v_number); - ++ectx.ec_stack.ga_len; + ++ectx->ec_stack.ga_len; } } else { semsg(_(e_for_loop_on_str_not_supported), vartype_name(ltv->v_type)); - goto failed; + return FAIL; } } break; @@ -2973,15 +2786,15 @@ call_def_function( { 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; + 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_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,12 +2809,12 @@ call_def_function( { SOURCING_LNUM = iptr->isn_lnum; iemsg("Evaluating catch while current_exception is NULL"); - goto failed; + return FAIL; } - if (GA_GROW(&ectx.ec_stack, 1) == FAIL) - goto failed; + if (GA_GROW(&ectx->ec_stack, 1) == FAIL) + return FAIL; tv = STACK_TV_BOT(0); - ++ectx.ec_stack.ga_len; + ++ectx->ec_stack.ga_len; tv->v_type = VAR_STRING; tv->v_lock = 0; tv->vval.v_string = vim_strsave( @@ -3010,9 +2823,9 @@ call_def_function( case ISN_CATCH: { - garray_T *trystack = &ectx.ec_trystack; - - may_restore_cmdmod(&funclocal); + 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,7 +2840,7 @@ call_def_function( case ISN_TRYCONT: { - garray_T *trystack = &ectx.ec_trystack; + garray_T *trystack = &ectx->ec_trystack; trycont_T *trycont = &iptr->isn_arg.trycont; int i; trycmd_T *trycmd; @@ -3037,7 +2850,7 @@ call_def_function( { siemsg("TRYCONT: expected %d levels, found %d", trycont->tct_levels, trystack->ga_len); - goto failed; + return FAIL; } // Make :endtry jump to any outer try block and the last // :endtry inside the loop to the loop start. @@ -3052,13 +2865,13 @@ call_def_function( ? trycmd->tcd_endtry_idx : trycmd->tcd_finally_idx; } // jump to :finally or :endtry of current try statement - ectx.ec_iidx = iidx; + ectx->ec_iidx = iidx; } break; case ISN_FINALLY: { - garray_T *trystack = &ectx.ec_trystack; + garray_T *trystack = &ectx->ec_trystack; trycmd_T *trycmd = ((trycmd_T *)trystack->ga_data) + trystack->ga_len - 1; @@ -3071,7 +2884,7 @@ call_def_function( // end of ":try" block case ISN_ENDTRY: { - garray_T *trystack = &ectx.ec_trystack; + garray_T *trystack = &ectx->ec_trystack; if (trystack->ga_len > 0) { @@ -3079,7 +2892,7 @@ call_def_function( --trystack->ga_len; --trylevel; - ectx.ec_in_catch = FALSE; + ectx->ec_in_catch = FALSE; trycmd = ((trycmd_T *)trystack->ga_data) + trystack->ga_len; if (trycmd->tcd_caught && current_exception != NULL) @@ -3093,22 +2906,22 @@ call_def_function( if (trycmd->tcd_return) goto func_return; - while (ectx.ec_stack.ga_len > trycmd->tcd_stack_len) + while (ectx->ec_stack.ga_len > trycmd->tcd_stack_len) { - --ectx.ec_stack.ga_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; + ectx->ec_iidx = trycmd->tcd_cont - 1; } } break; case ISN_THROW: { - garray_T *trystack = &ectx.ec_trystack; + garray_T *trystack = &ectx->ec_trystack; if (trystack->ga_len == 0 && trylevel == 0 && emsg_silent) { @@ -3120,7 +2933,7 @@ call_def_function( tv->vval.v_number = 0; goto done; } - --ectx.ec_stack.ga_len; + --ectx->ec_stack.ga_len; tv = STACK_TV_BOT(0); if (tv->vval.v_string == NULL || *skipwhite(tv->vval.v_string) == NUL) @@ -3128,7 +2941,7 @@ call_def_function( vim_free(tv->vval.v_string); SOURCING_LNUM = iptr->isn_lnum; emsg(_(e_throw_with_empty_string)); - goto failed; + return FAIL; } // Inside a "catch" we need to first discard the caught @@ -3151,7 +2964,7 @@ call_def_function( == FAIL) { vim_free(tv->vval.v_string); - goto failed; + return FAIL; } did_throw = TRUE; } @@ -3174,7 +2987,7 @@ call_def_function( default: res = 0; break; } - --ectx.ec_stack.ga_len; + --ectx->ec_stack.ga_len; tv1->v_type = VAR_BOOL; tv1->vval.v_number = res ? VVAL_TRUE : VVAL_FALSE; } @@ -3207,7 +3020,7 @@ call_def_function( default: res = 0; break; } - --ectx.ec_stack.ga_len; + --ectx->ec_stack.ga_len; if (iptr->isn_type == ISN_COMPARENR) { tv1->v_type = VAR_BOOL; @@ -3245,7 +3058,7 @@ call_def_function( case EXPR_SEQUAL: cmp = arg1 <= arg2; break; default: cmp = 0; break; } - --ectx.ec_stack.ga_len; + --ectx->ec_stack.ga_len; if (iptr->isn_type == ISN_COMPAREFLOAT) { tv1->v_type = VAR_BOOL; @@ -3276,7 +3089,7 @@ call_def_function( case EXPR_ISNOT: cmp = arg1 != arg2; break; default: cmp = 0; break; } - --ectx.ec_stack.ga_len; + --ectx->ec_stack.ga_len; clear_tv(tv1); clear_tv(tv2); tv1->v_type = VAR_BOOL; @@ -3300,7 +3113,7 @@ call_def_function( case EXPR_ISNOT: cmp = arg1 != arg2; break; default: cmp = 0; break; } - --ectx.ec_stack.ga_len; + --ectx->ec_stack.ga_len; clear_tv(tv1); clear_tv(tv2); tv1->v_type = VAR_BOOL; @@ -3322,7 +3135,7 @@ call_def_function( SOURCING_LNUM = iptr->isn_lnum; typval_compare(tv1, tv2, exprtype, ic); clear_tv(tv2); - --ectx.ec_stack.ga_len; + --ectx->ec_stack.ga_len; } break; @@ -3338,7 +3151,7 @@ call_def_function( else eval_addblob(tv1, tv2); clear_tv(tv2); - --ectx.ec_stack.ga_len; + --ectx->ec_stack.ga_len; } break; @@ -3356,9 +3169,9 @@ call_def_function( goto on_error; } if (list_append_tv(l, tv2) == FAIL) - goto failed; + return FAIL; clear_tv(tv2); - --ectx.ec_stack.ga_len; + --ectx->ec_stack.ga_len; } break; @@ -3381,7 +3194,7 @@ call_def_function( if (error) goto on_error; ga_append(&b->bv_ga, (int)n); - --ectx.ec_stack.ga_len; + --ectx->ec_stack.ga_len; } break; @@ -3402,7 +3215,7 @@ call_def_function( { eval_addlist(tv1, tv2); clear_tv(tv2); - --ectx.ec_stack.ga_len; + --ectx->ec_stack.ga_len; break; } else if (tv1->v_type == VAR_BLOB @@ -3410,7 +3223,7 @@ call_def_function( { eval_addblob(tv1, tv2); clear_tv(tv2); - --ectx.ec_stack.ga_len; + --ectx->ec_stack.ga_len; break; } } @@ -3467,7 +3280,7 @@ call_def_function( clear_tv(tv2); tv1->v_type = VAR_FLOAT; tv1->vval.v_float = f1; - --ectx.ec_stack.ga_len; + --ectx->ec_stack.ga_len; } else #endif @@ -3492,7 +3305,7 @@ call_def_function( clear_tv(tv2); tv1->v_type = VAR_NUMBER; tv1->vval.v_number = n1; - --ectx.ec_stack.ga_len; + --ectx->ec_stack.ga_len; } } break; @@ -3506,7 +3319,7 @@ call_def_function( res = concat_str(str1, str2); clear_tv(STACK_TV_BOT(-2)); clear_tv(STACK_TV_BOT(-1)); - --ectx.ec_stack.ga_len; + --ectx->ec_stack.ga_len; STACK_TV_BOT(-1)->vval.v_string = res; } break; @@ -3530,7 +3343,7 @@ call_def_function( tv = STACK_TV_BOT(-1); n2 = tv->vval.v_number; - ectx.ec_stack.ga_len -= is_slice ? 2 : 1; + 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,7 +3388,7 @@ call_def_function( clear_tv(tv); } - ectx.ec_stack.ga_len -= is_slice ? 2 : 1; + ectx->ec_stack.ga_len -= is_slice ? 2 : 1; tv = STACK_TV_BOT(-1); SOURCING_LNUM = iptr->isn_lnum; if (is_blob) @@ -3614,7 +3427,7 @@ call_def_function( clear_tv(var1); if (is_slice) clear_tv(var2); - ectx.ec_stack.ga_len -= is_slice ? 2 : 1; + ectx->ec_stack.ga_len -= is_slice ? 2 : 1; if (res == FAIL) goto on_error; } @@ -3655,15 +3468,15 @@ call_def_function( 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; + 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. - where.wt_index = index + 1; - where.wt_variable = TRUE; + ectx->ec_where.wt_index = index + 1; + ectx->ec_where.wt_variable = TRUE; } break; @@ -3693,7 +3506,7 @@ call_def_function( // If :silent! is used we will continue, make sure the // stack contents makes sense. clear_tv(tv); - --ectx.ec_stack.ga_len; + --ectx->ec_stack.ga_len; tv = STACK_TV_BOT(-1); clear_tv(tv); tv->v_type = VAR_NUMBER; @@ -3701,7 +3514,7 @@ call_def_function( goto on_fatal_error; } clear_tv(tv); - --ectx.ec_stack.ga_len; + --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,12 +3595,13 @@ call_def_function( 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) + 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 (!where.wt_variable) - where.wt_index = 0; + 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,9 +3701,9 @@ call_def_function( 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; + 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,7 +3728,7 @@ call_def_function( curwin->w_cursor.lnum = tv->vval.v_number; if (lnum == LNUM_VARIABLE_RANGE_ABOVE) dir = BACKWARD; - --ectx.ec_stack.ga_len; + --ectx->ec_stack.ga_len; } else if (lnum == -2) // :put! above cursor @@ -3932,7 +3746,7 @@ call_def_function( expr = typval2string(tv, TRUE); // allocates value clear_tv(tv); } - --ectx.ec_stack.ga_len; + --ectx->ec_stack.ga_len; } check_cursor(); do_put(regname, expr, dir, 1L, PUT_LINE|PUT_CURSLINE); @@ -3941,9 +3755,10 @@ call_def_function( break; case ISN_CMDMOD: - funclocal.floc_save_cmdmod = cmdmod; - funclocal.floc_restore_cmdmod = TRUE; - funclocal.floc_restore_cmdmod_stacklen = ectx.ec_stack.ga_len; + 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,8 +3767,8 @@ call_def_function( // 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; + cmdmod = ectx->ec_funclocal.floc_save_cmdmod; + ectx->ec_funclocal.floc_restore_cmdmod = FALSE; break; case ISN_UNPACK: @@ -3988,9 +3803,9 @@ call_def_function( } CHECK_LIST_MATERIALIZE(l); - if (GA_GROW(&ectx.ec_stack, count - 1) == FAIL) - goto failed; - ectx.ec_stack.ga_len += count - 1; + 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,7 +3815,7 @@ call_def_function( list_alloc_with_items(l->lv_len - count + 1); if (rem_list == NULL) - goto failed; + return FAIL; tv = STACK_TV_BOT(-count); tv->vval.v_list = rem_list; ++rem_list->lv_refcount; @@ -4036,7 +3851,7 @@ call_def_function( funccall_T cookie; ufunc_T *cur_ufunc = (((dfunc_T *)def_functions.ga_data) - + ectx.ec_dfunc_idx)->df_ufunc; + + ectx->ec_dfunc_idx)->df_ufunc; cookie.func = cur_ufunc; if (iptr->isn_type == ISN_PROF_START) @@ -4068,10 +3883,10 @@ call_def_function( break; case ISN_DROP: - --ectx.ec_stack.ga_len; + --ectx->ec_stack.ga_len; clear_tv(STACK_TV_BOT(0)); - where.wt_index = 0; - where.wt_variable = FALSE; + ectx->ec_where.wt_index = 0; + ectx->ec_where.wt_variable = FALSE; break; } continue; @@ -4079,62 +3894,345 @@ call_def_function( 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) + if (ectx->ec_frame_idx == ectx->ec_initial_frame_idx) goto done; - if (func_return(&funclocal, &ectx) == FAIL) + if (func_return(ectx) == FAIL) // only fails when out of memory - goto failed; + 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 == did_emsg_before + 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 (funclocal.floc_restore_cmdmod) + if (ectx->ec_funclocal.floc_restore_cmdmod) { - while (ectx.ec_stack.ga_len - > funclocal.floc_restore_cmdmod_stacklen) + while (ectx->ec_stack.ga_len + > ectx->ec_funclocal.floc_restore_cmdmod_stacklen) { - --ectx.ec_stack.ga_len; + --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; + 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; + if (trylevel <= ectx->ec_trylevel_at_start) + return FAIL; } done: - // function finished, get result from the stack. - if (ufunc->uf_ret_type == &t_void) + 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) { - rettv->v_type = VAR_VOID; + typval_T *tv = STACK_TV_BOT(-1); + + res = vim_strsave(tv_get_string(tv)); + --ectx->ec_stack.ga_len; + clear_tv(tv); } else { - tv = STACK_TV_BOT(-1); - *rettv = *tv; - tv->v_type = VAR_UNKNOWN; + 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; } - ret = OK; - -failed: + + // 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 != initial_frame_idx) - func_return(&funclocal, &ectx); + 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,11 +4260,11 @@ failed: } msg_list = saved_msg_list; - if (funclocal.floc_restore_cmdmod) + if (ectx.ec_funclocal.floc_restore_cmdmod) { cmdmod.cmod_filter_regmatch.regprog = NULL; undo_cmdmod(&cmdmod); - cmdmod = funclocal.floc_save_cmdmod; + cmdmod = ectx.ec_funclocal.floc_save_cmdmod; } emsg_silent_def = save_emsg_silent_def; did_emsg_def += save_did_emsg_def; @@ -4199,6 +4297,711 @@ failed_early: } /* + * 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: + { + scriptref_T *sref = iptr->isn_arg.script.scriptref; + scriptitem_T *si = SCRIPT_ITEM(sref->sref_sid); + 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); + } + break; + case ISN_LOADS: + { + 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; + case ISN_STORESCRIPT: + { + scriptref_T *sref = iptr->isn_arg.script.scriptref; + scriptitem_T *si = SCRIPT_ITEM(sref->sref_sid); + 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: + { + char_u *r; + char_u numbuf[NUMBUFLEN]; + char_u *tofree; + + r = blob2string(iptr->isn_arg.blob, &tofree, numbuf); + smsg("%s%4d PUSHBLOB %s", pfx, current, r); + vim_free(tofree); + } + break; + case ISN_PUSHFUNC: + { + char *name = (char *)iptr->isn_arg.string; + + smsg("%s%4d PUSHFUNC \"%s\"", pfx, current, + name == NULL ? "[none]" : name); + } + break; + case ISN_PUSHCHANNEL: +#ifdef FEAT_JOB_CHANNEL + { + channel_T *channel = iptr->isn_arg.channel; + + smsg("%s%4d PUSHCHANNEL %d", pfx, current, + channel == NULL ? 0 : channel->ch_id); + } +#endif + break; + case ISN_PUSHJOB: +#ifdef FEAT_JOB_CHANNEL + { + typval_T tv; + char_u *name; + + 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; + + // function call + case ISN_BCALL: + { + 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); + } + break; + case ISN_DCALL: + { + cdfunc_T *cdfunc = &iptr->isn_arg.dfunc; + 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); + } + break; + case ISN_UCALL: + { + cufunc_T *cufunc = &iptr->isn_arg.ufunc; + + smsg("%s%4d UCALL %s(argc %d)", pfx, current, + cufunc->cuf_name, cufunc->cuf_argcount); + } + break; + case ISN_PCALL: + { + 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: + { + funcref_T *funcref = &iptr->isn_arg.funcref; + 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; + + case ISN_NEWFUNC: + { + newfunc_T *newfunc = &iptr->isn_arg.newfunc; + + smsg("%s%4d NEWFUNC %s %s", pfx, current, + newfunc->nf_lambda, newfunc->nf_global); + } + break; + + case ISN_DEF: + { + char_u *name = iptr->isn_arg.string; + + smsg("%s%4d DEF %s", pfx, current, + name == NULL ? (char_u *)"" : name); + } + break; + + case ISN_JUMP: + { + char *when = "?"; + + switch (iptr->isn_arg.jump.jump_when) + { + case JUMP_ALWAYS: + when = "JUMP"; + break; + case JUMP_AND_KEEP_IF_TRUE: + when = "JUMP_AND_KEEP_IF_TRUE"; + break; + case JUMP_IF_FALSE: + when = "JUMP_IF_FALSE"; + break; + case JUMP_AND_KEEP_IF_FALSE: + when = "JUMP_AND_KEEP_IF_FALSE"; + break; + case JUMP_IF_COND_FALSE: + when = "JUMP_IF_COND_FALSE"; + break; + case JUMP_IF_COND_TRUE: + 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; + + case ISN_FOR: + { + forloop_T *forloop = &iptr->isn_arg.forloop; + + smsg("%s%4d FOR $%d -> %d", pfx, current, + forloop->for_idx, forloop->for_end); + } + break; + + case ISN_TRY: + { + 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); + } + 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 + case ISN_OPNR: + case ISN_OPFLOAT: + case ISN_OPANY: + { + char *what; + char *ins; + + switch (iptr->isn_arg.op.op_type) + { + case EXPR_MULT: what = "*"; break; + case EXPR_DIV: what = "/"; break; + case EXPR_REM: what = "%"; break; + case EXPR_SUB: what = "-"; break; + case EXPR_ADD: what = "+"; break; + default: what = "???"; break; + } + switch (iptr->isn_type) + { + case ISN_OPNR: ins = "OPNR"; break; + case ISN_OPFLOAT: ins = "OPFLOAT"; break; + case ISN_OPANY: ins = "OPANY"; break; + default: ins = "???"; break; + } + smsg("%s%4d %s %s", pfx, current, ins, what); + } + break; + + case ISN_COMPAREBOOL: + case ISN_COMPARESPECIAL: + case ISN_COMPARENR: + case ISN_COMPAREFLOAT: + case ISN_COMPARESTRING: + case ISN_COMPAREBLOB: + case ISN_COMPARELIST: + case ISN_COMPAREDICT: + case ISN_COMPAREFUNC: + case ISN_COMPAREANY: + { + char *p; + char buf[10]; + char *type; + + switch (iptr->isn_arg.op.op_type) + { + case EXPR_EQUAL: p = "=="; break; + case EXPR_NEQUAL: p = "!="; break; + case EXPR_GREATER: p = ">"; break; + case EXPR_GEQUAL: p = ">="; break; + case EXPR_SMALLER: p = "<"; break; + case EXPR_SEQUAL: p = "<="; break; + case EXPR_MATCH: p = "=~"; break; + case EXPR_IS: p = "is"; break; + case EXPR_ISNOT: p = "isnot"; break; + case EXPR_NOMATCH: p = "!~"; break; + default: p = "???"; break; + } + STRCPY(buf, p); + if (iptr->isn_arg.op.op_ic == TRUE) + strcat(buf, "?"); + switch(iptr->isn_type) + { + case ISN_COMPAREBOOL: type = "COMPAREBOOL"; break; + case ISN_COMPARESPECIAL: + type = "COMPARESPECIAL"; break; + case ISN_COMPARENR: type = "COMPARENR"; break; + case ISN_COMPAREFLOAT: type = "COMPAREFLOAT"; break; + case ISN_COMPARESTRING: + type = "COMPARESTRING"; break; + case ISN_COMPAREBLOB: type = "COMPAREBLOB"; break; + case ISN_COMPARELIST: type = "COMPARELIST"; break; + case ISN_COMPAREDICT: type = "COMPAREDICT"; break; + case ISN_COMPAREFUNC: type = "COMPAREFUNC"; break; + case ISN_COMPAREANY: type = "COMPAREANY"; break; + 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; + case ISN_SETTYPE: + { + 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; + + // TODO: summarize modifiers + case ISN_CMDMOD: + { + char_u *buf; + size_t len = produce_cmdmods( + NULL, iptr->isn_arg.cmdmod.cf_cmdmod, FALSE); + + buf = alloc(len + 1); + if (buf != NULL) + { + (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 + ui_breakcheck(); + if (got_int) + break; + } +} + +/* * ":disassemble". * We don't really need this at runtime, but we do have tests that require it, * so always include this. @@ -4212,9 +5015,6 @@ ex_disassemble(exarg_T *eap) 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, "", 8) == 0) @@ -4269,683 +5069,8 @@ ex_disassemble(exarg_T *eap) 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: - { - scriptref_T *sref = iptr->isn_arg.script.scriptref; - scriptitem_T *si = SCRIPT_ITEM(sref->sref_sid); - 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); - } - break; - case ISN_LOADS: - { - 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; - case ISN_STORESCRIPT: - { - scriptref_T *sref = iptr->isn_arg.script.scriptref; - scriptitem_T *si = SCRIPT_ITEM(sref->sref_sid); - 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: - { - char_u *r; - char_u numbuf[NUMBUFLEN]; - char_u *tofree; - - r = blob2string(iptr->isn_arg.blob, &tofree, numbuf); - smsg("%4d PUSHBLOB %s", current, r); - vim_free(tofree); - } - break; - case ISN_PUSHFUNC: - { - char *name = (char *)iptr->isn_arg.string; - - smsg("%4d PUSHFUNC \"%s\"", current, - name == NULL ? "[none]" : name); - } - break; - case ISN_PUSHCHANNEL: -#ifdef FEAT_JOB_CHANNEL - { - channel_T *channel = iptr->isn_arg.channel; - - smsg("%4d PUSHCHANNEL %d", current, - channel == NULL ? 0 : channel->ch_id); - } -#endif - break; - case ISN_PUSHJOB: -#ifdef FEAT_JOB_CHANNEL - { - typval_T tv; - char_u *name; - - 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; - - // function call - case ISN_BCALL: - { - cbfunc_T *cbfunc = &iptr->isn_arg.bfunc; - - smsg("%4d BCALL %s(argc %d)", current, - internal_func_name(cbfunc->cbf_idx), - cbfunc->cbf_argcount); - } - break; - case ISN_DCALL: - { - cdfunc_T *cdfunc = &iptr->isn_arg.dfunc; - 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); - } - break; - case ISN_UCALL: - { - cufunc_T *cufunc = &iptr->isn_arg.ufunc; - - smsg("%4d UCALL %s(argc %d)", current, - cufunc->cuf_name, cufunc->cuf_argcount); - } - break; - case ISN_PCALL: - { - 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: - { - funcref_T *funcref = &iptr->isn_arg.funcref; - dfunc_T *df = ((dfunc_T *)def_functions.ga_data) - + funcref->fr_func; - - smsg("%4d FUNCREF %s", current, df->df_ufunc->uf_name); - } - break; - - case ISN_NEWFUNC: - { - newfunc_T *newfunc = &iptr->isn_arg.newfunc; - - smsg("%4d NEWFUNC %s %s", current, - newfunc->nf_lambda, newfunc->nf_global); - } - break; - - case ISN_DEF: - { - char_u *name = iptr->isn_arg.string; - - smsg("%4d DEF %s", current, - name == NULL ? (char_u *)"" : name); - } - break; - - case ISN_JUMP: - { - char *when = "?"; - - switch (iptr->isn_arg.jump.jump_when) - { - case JUMP_ALWAYS: - when = "JUMP"; - break; - case JUMP_AND_KEEP_IF_TRUE: - when = "JUMP_AND_KEEP_IF_TRUE"; - break; - case JUMP_IF_FALSE: - when = "JUMP_IF_FALSE"; - break; - case JUMP_AND_KEEP_IF_FALSE: - when = "JUMP_AND_KEEP_IF_FALSE"; - break; - case JUMP_IF_COND_FALSE: - when = "JUMP_IF_COND_FALSE"; - break; - case JUMP_IF_COND_TRUE: - 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; - - case ISN_FOR: - { - forloop_T *forloop = &iptr->isn_arg.forloop; - - smsg("%4d FOR $%d -> %d", current, - forloop->for_idx, forloop->for_end); - } - break; - - case ISN_TRY: - { - 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); - } - 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 - case ISN_OPNR: - case ISN_OPFLOAT: - case ISN_OPANY: - { - char *what; - char *ins; - - switch (iptr->isn_arg.op.op_type) - { - case EXPR_MULT: what = "*"; break; - case EXPR_DIV: what = "/"; break; - case EXPR_REM: what = "%"; break; - case EXPR_SUB: what = "-"; break; - case EXPR_ADD: what = "+"; break; - default: what = "???"; break; - } - switch (iptr->isn_type) - { - case ISN_OPNR: ins = "OPNR"; break; - case ISN_OPFLOAT: ins = "OPFLOAT"; break; - case ISN_OPANY: ins = "OPANY"; break; - default: ins = "???"; break; - } - smsg("%4d %s %s", current, ins, what); - } - break; - - case ISN_COMPAREBOOL: - case ISN_COMPARESPECIAL: - case ISN_COMPARENR: - case ISN_COMPAREFLOAT: - case ISN_COMPARESTRING: - case ISN_COMPAREBLOB: - case ISN_COMPARELIST: - case ISN_COMPAREDICT: - case ISN_COMPAREFUNC: - case ISN_COMPAREANY: - { - char *p; - char buf[10]; - char *type; - - switch (iptr->isn_arg.op.op_type) - { - case EXPR_EQUAL: p = "=="; break; - case EXPR_NEQUAL: p = "!="; break; - case EXPR_GREATER: p = ">"; break; - case EXPR_GEQUAL: p = ">="; break; - case EXPR_SMALLER: p = "<"; break; - case EXPR_SEQUAL: p = "<="; break; - case EXPR_MATCH: p = "=~"; break; - case EXPR_IS: p = "is"; break; - case EXPR_ISNOT: p = "isnot"; break; - case EXPR_NOMATCH: p = "!~"; break; - default: p = "???"; break; - } - STRCPY(buf, p); - if (iptr->isn_arg.op.op_ic == TRUE) - strcat(buf, "?"); - switch(iptr->isn_type) - { - case ISN_COMPAREBOOL: type = "COMPAREBOOL"; break; - case ISN_COMPARESPECIAL: - type = "COMPARESPECIAL"; break; - case ISN_COMPARENR: type = "COMPARENR"; break; - case ISN_COMPAREFLOAT: type = "COMPAREFLOAT"; break; - case ISN_COMPARESTRING: - type = "COMPARESTRING"; break; - case ISN_COMPAREBLOB: type = "COMPAREBLOB"; break; - case ISN_COMPARELIST: type = "COMPARELIST"; break; - case ISN_COMPAREDICT: type = "COMPAREDICT"; break; - case ISN_COMPAREFUNC: type = "COMPAREFUNC"; break; - case ISN_COMPAREANY: type = "COMPAREANY"; break; - 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; - case ISN_SETTYPE: - { - 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; - - // TODO: summarize modifiers - case ISN_CMDMOD: - { - char_u *buf; - size_t len = produce_cmdmods( - NULL, iptr->isn_arg.cmdmod.cf_cmdmod, FALSE); - - buf = alloc(len + 1); - if (buf != NULL) - { - (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 - ui_breakcheck(); - if (got_int) - break; - } + + list_instructions("", instr, instr_count, ufunc); } /*