To: vim_dev@googlegroups.com Subject: Patch 8.2.2650 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.2650 Problem: Vim9: command modifiers not handled in nested function. Solution: Keep function-local info in a structure and save it on the stack. Files: src/vim9execute.c, src/vim9.h, src/testdir/test_vim9_func.vim *** ../vim-8.2.2649/src/vim9execute.c 2021-03-17 15:02:52.170478171 +0100 --- src/vim9execute.c 2021-03-24 21:51:06.577057287 +0100 *************** *** 154,159 **** --- 154,168 ---- 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 *************** *** 170,185 **** * - reserved space for local variables */ static int ! call_dfunc(int cdf_idx, partial_T *pt, int argcount_arg, ectx_T *ectx) { ! int argcount = argcount_arg; ! dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + cdf_idx; ! ufunc_T *ufunc = dfunc->df_ufunc; ! int arg_to_add; ! int vararg_count = 0; ! int varcount; ! int idx; ! estack_T *entry; if (dfunc->df_deleted) { --- 179,200 ---- * - reserved space for local variables */ static int ! call_dfunc( ! int cdf_idx, ! partial_T *pt, ! int argcount_arg, ! funclocal_T *funclocal, ! ectx_T *ectx) { ! int argcount = argcount_arg; ! dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + cdf_idx; ! ufunc_T *ufunc = dfunc->df_ufunc; ! int arg_to_add; ! int vararg_count = 0; ! int varcount; ! int idx; ! estack_T *entry; ! funclocal_T *floc = NULL; if (dfunc->df_deleted) { *************** *** 267,272 **** --- 282,297 ---- if (funcdepth_increment() == FAIL) 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. if (vararg_count > 0 && arg_to_add > 0) *STACK_TV_BOT(arg_to_add - 1) = *STACK_TV_BOT(-1); *************** *** 280,285 **** --- 305,311 ---- STACK_TV_BOT(STACK_FRAME_FUNC_OFF)->vval.v_number = ectx->ec_dfunc_idx; STACK_TV_BOT(STACK_FRAME_IIDX_OFF)->vval.v_number = ectx->ec_iidx; STACK_TV_BOT(STACK_FRAME_OUTER_OFF)->vval.v_string = (void *)ectx->ec_outer; + STACK_TV_BOT(STACK_FRAME_FUNCLOCAL_OFF)->vval.v_string = (void *)floc; STACK_TV_BOT(STACK_FRAME_IDX_OFF)->vval.v_number = ectx->ec_frame_idx; ectx->ec_frame_idx = ectx->ec_stack.ga_len; *************** *** 530,536 **** * Return from the current function. */ static int ! func_return(ectx_T *ectx) { int idx; int ret_idx; --- 556,562 ---- * Return from the current function. */ static int ! func_return(funclocal_T *funclocal, ectx_T *ectx) { int idx; int ret_idx; *************** *** 543,548 **** --- 569,575 ---- + STACK_FRAME_FUNC_OFF)->vval.v_number; dfunc_T *prev_dfunc = ((dfunc_T *)def_functions.ga_data) + prev_dfunc_idx; + funclocal_T *floc; #ifdef FEAT_PROFILE if (do_profiling == PROF_YES) *************** *** 592,602 **** --- 619,639 ---- + STACK_FRAME_IIDX_OFF)->vval.v_number; ectx->ec_outer = (void *)STACK_TV(ectx->ec_frame_idx + STACK_FRAME_OUTER_OFF)->vval.v_string; + floc = (void *)STACK_TV(ectx->ec_frame_idx + + STACK_FRAME_FUNCLOCAL_OFF)->vval.v_string; // restoring ec_frame_idx must be last ectx->ec_frame_idx = STACK_TV(ectx->ec_frame_idx + STACK_FRAME_IDX_OFF)->vval.v_number; ectx->ec_instr = INSTRUCTIONS(prev_dfunc); + if (floc == NULL) + funclocal->floc_restore_cmdmod = FALSE; + else + { + *funclocal = *floc; + vim_free(floc); + } + if (ret_idx > 0) { // Reset the stack to the position before the call, with a spot for the *************** *** 690,695 **** --- 727,733 ---- ufunc_T *ufunc, partial_T *pt, int argcount, + funclocal_T *funclocal, ectx_T *ectx, isn_T *iptr) { *************** *** 729,735 **** 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) --- 767,773 ---- 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) *************** *** 773,779 **** * Returns FAIL if not found without an error message. */ static int ! call_by_name(char_u *name, int argcount, ectx_T *ectx, isn_T *iptr) { ufunc_T *ufunc; --- 811,822 ---- * Returns FAIL if not found without an error message. */ static int ! call_by_name( ! char_u *name, ! int argcount, ! funclocal_T *funclocal, ! ectx_T *ectx, ! isn_T *iptr) { ufunc_T *ufunc; *************** *** 824,837 **** } } ! return call_ufunc(ufunc, NULL, argcount, ectx, iptr); } return FAIL; } static int ! call_partial(typval_T *tv, int argcount_arg, ectx_T *ectx) { int argcount = argcount_arg; char_u *name = NULL; --- 867,884 ---- } } ! return call_ufunc(ufunc, NULL, argcount, funclocal, ectx, iptr); } return FAIL; } static int ! call_partial( ! typval_T *tv, ! int argcount_arg, ! funclocal_T *funclocal, ! ectx_T *ectx) { int argcount = argcount_arg; char_u *name = NULL; *************** *** 860,866 **** } if (pt->pt_func != NULL) ! return call_ufunc(pt->pt_func, pt, argcount, ectx, NULL); name = pt->pt_name; } --- 907,913 ---- } if (pt->pt_func != NULL) ! return call_ufunc(pt->pt_func, pt, argcount, funclocal, ectx, NULL); name = pt->pt_name; } *************** *** 878,884 **** if (error != FCERR_NONE) res = FAIL; else ! res = call_by_name(fname, argcount, ectx, NULL); vim_free(tofree); } --- 925,931 ---- if (error != FCERR_NONE) res = FAIL; else ! res = call_by_name(fname, argcount, funclocal, ectx, NULL); vim_free(tofree); } *************** *** 1125,1136 **** * "iptr" can be used to replace the instruction with a more efficient one. */ static int ! 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; --- 1172,1188 ---- * "iptr" can be used to replace the instruction with a more efficient one. */ static int ! 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; *************** *** 1146,1152 **** semsg(_(e_unknownfunc), name); return FAIL; } ! return call_partial(&v->di_tv, argcount, ectx); } return res; } --- 1198,1204 ---- semsg(_(e_unknownfunc), name); return FAIL; } ! return call_partial(&v->di_tv, argcount, funclocal, ectx); } return res; } *************** *** 1222,1230 **** int save_suppress_errthrow = suppress_errthrow; msglist_T **saved_msg_list = NULL; msglist_T *private_msg_list = NULL; ! cmdmod_T save_cmdmod; ! int restore_cmdmod = FALSE; ! int restore_cmdmod_stacklen = 0; int save_emsg_silent_def = emsg_silent_def; int save_did_emsg_def = did_emsg_def; int trylevel_at_start = trylevel; --- 1274,1280 ---- 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; *************** *** 1268,1273 **** --- 1318,1324 ---- 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); *************** *** 1488,1494 **** goto done; } ! if (func_return(&ectx) == FAIL) goto failed; } continue; --- 1539,1545 ---- goto done; } ! if (func_return(&funclocal, &ectx) == FAIL) goto failed; } continue; *************** *** 2467,2475 **** // call a :def function case ISN_DCALL: SOURCING_LNUM = iptr->isn_lnum; ! if (call_dfunc(iptr->isn_arg.dfunc.cdf_idx, NULL, ! iptr->isn_arg.dfunc.cdf_argcount, ! &ectx) == FAIL) goto on_error; break; --- 2518,2528 ---- // call a :def function case ISN_DCALL: SOURCING_LNUM = iptr->isn_lnum; ! if (call_dfunc(iptr->isn_arg.dfunc.cdf_idx, ! NULL, ! iptr->isn_arg.dfunc.cdf_argcount, ! &funclocal, ! &ectx) == FAIL) goto on_error; break; *************** *** 2502,2508 **** 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) --- 2555,2562 ---- 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) *************** *** 2525,2532 **** cufunc_T *cufunc = &iptr->isn_arg.ufunc; SOURCING_LNUM = iptr->isn_lnum; ! if (call_eval_func(cufunc->cuf_name, ! cufunc->cuf_argcount, &ectx, iptr) == FAIL) goto on_error; } break; --- 2579,2586 ---- cufunc_T *cufunc = &iptr->isn_arg.ufunc; SOURCING_LNUM = iptr->isn_lnum; ! if (call_eval_func(cufunc->cuf_name, cufunc->cuf_argcount, ! &funclocal, &ectx, iptr) == FAIL) goto on_error; } break; *************** *** 2728,2739 **** { garray_T *trystack = &ectx.ec_trystack; ! if (restore_cmdmod) { cmdmod.cmod_filter_regmatch.regprog = NULL; undo_cmdmod(&cmdmod); ! cmdmod = save_cmdmod; ! restore_cmdmod = FALSE; } if (trystack->ga_len > 0) { --- 2782,2793 ---- { garray_T *trystack = &ectx.ec_trystack; ! if (funclocal.floc_restore_cmdmod) { cmdmod.cmod_filter_regmatch.regprog = NULL; undo_cmdmod(&cmdmod); ! cmdmod = funclocal.floc_save_cmdmod; ! funclocal.floc_restore_cmdmod = FALSE; } if (trystack->ga_len > 0) { *************** *** 3649,3657 **** break; case ISN_CMDMOD: ! save_cmdmod = cmdmod; ! restore_cmdmod = TRUE; ! restore_cmdmod_stacklen = ectx.ec_stack.ga_len; cmdmod = *iptr->isn_arg.cmdmod.cf_cmdmod; apply_cmdmod(&cmdmod); break; --- 3703,3711 ---- 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; *************** *** 3660,3667 **** // filter regprog is owned by the instruction, don't free it cmdmod.cmod_filter_regmatch.regprog = NULL; undo_cmdmod(&cmdmod); ! cmdmod = save_cmdmod; ! restore_cmdmod = FALSE; break; case ISN_UNPACK: --- 3714,3721 ---- // 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: *************** *** 3790,3796 **** if (ectx.ec_frame_idx == initial_frame_idx) goto done; ! if (func_return(&ectx) == FAIL) // only fails when out of memory goto failed; continue; --- 3844,3850 ---- 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; *************** *** 3805,3813 **** // If a sequence of instructions causes an error while ":silent!" // was used, restore the stack length and jump ahead to restoring // the cmdmod. ! if (restore_cmdmod) { ! while (ectx.ec_stack.ga_len > restore_cmdmod_stacklen) { --ectx.ec_stack.ga_len; clear_tv(STACK_TV_BOT(0)); --- 3859,3868 ---- // 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)); *************** *** 3834,3840 **** failed: // When failed need to unwind the call stack. while (ectx.ec_frame_idx != initial_frame_idx) ! func_return(&ectx); // Deal with any remaining closures, they may be in use somewhere. if (ectx.ec_funcrefs.ga_len > 0) --- 3889,3895 ---- 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) *************** *** 3862,3872 **** } msg_list = saved_msg_list; ! if (restore_cmdmod) { cmdmod.cmod_filter_regmatch.regprog = NULL; undo_cmdmod(&cmdmod); ! cmdmod = save_cmdmod; } emsg_silent_def = save_emsg_silent_def; did_emsg_def += save_did_emsg_def; --- 3917,3927 ---- } 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; *** ../vim-8.2.2649/src/vim9.h 2021-02-21 21:32:38.301201176 +0100 --- src/vim9.h 2021-03-24 21:41:09.265664954 +0100 *************** *** 402,413 **** // - ec_dfunc_idx: function index // - ec_iidx: instruction index // - ec_outer: stack used for closures // - ec_frame_idx: previous frame index #define STACK_FRAME_FUNC_OFF 0 #define STACK_FRAME_IIDX_OFF 1 #define STACK_FRAME_OUTER_OFF 2 ! #define STACK_FRAME_IDX_OFF 3 ! #define STACK_FRAME_SIZE 4 #ifdef DEFINE_VIM9_GLOBALS --- 402,415 ---- // - ec_dfunc_idx: function index // - ec_iidx: instruction index // - ec_outer: stack used for closures + // - funclocal: function-local data // - ec_frame_idx: previous frame index #define STACK_FRAME_FUNC_OFF 0 #define STACK_FRAME_IIDX_OFF 1 #define STACK_FRAME_OUTER_OFF 2 ! #define STACK_FRAME_FUNCLOCAL_OFF 3 ! #define STACK_FRAME_IDX_OFF 4 ! #define STACK_FRAME_SIZE 5 #ifdef DEFINE_VIM9_GLOBALS *** ../vim-8.2.2649/src/testdir/test_vim9_func.vim 2021-03-22 20:48:57.863992154 +0100 --- src/testdir/test_vim9_func.vim 2021-03-24 21:56:22.291344330 +0100 *************** *** 2363,2368 **** --- 2363,2391 ---- delete(fname) enddef + def Test_cmdmod_silent_nested() + var lines =<< trim END + vim9script + var result = '' + + def Error() + result ..= 'Eb' + eval [][0] + result ..= 'Ea' + enddef + + def Crash() + result ..= 'Cb' + sil! Error() + result ..= 'Ca' + enddef + + Crash() + assert_equal('CbEbEaCa', result) + END + CheckScriptSuccess(lines) + enddef + def Test_dict_member_with_silent() var lines =<< trim END vim9script *** ../vim-8.2.2649/src/version.c 2021-03-24 20:08:08.540745895 +0100 --- src/version.c 2021-03-24 21:18:10.087243868 +0100 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 2650, /**/ -- I have a drinking problem -- I don't have a drink! /// 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 ///