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