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 -- Br...@Moolenaar.net --
http://www.Moolenaar.net \\\
/// \\\
\\\ sponsor Vim, vote for features --
http://www.Vim.org/sponsor/ ///
\\\ help me help AIDS victims --
http://ICCF-Holland.org ///