1
0
forked from aniani/vim

patch 8.2.2650: Vim9: command modifiers not handled in nested function

Problem:    Vim9: command modifiers not handled in nested function.
Solution:   Keep function-local info in a structure and save it on the stack.
This commit is contained in:
Bram Moolenaar
2021-03-24 22:00:56 +01:00
parent 1ff89deeaa
commit 2fecb53115
4 changed files with 127 additions and 45 deletions

View File

@@ -2363,6 +2363,29 @@ def Test_cmdmod_silent_restored()
delete(fname) delete(fname)
enddef 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() def Test_dict_member_with_silent()
var lines =<< trim END var lines =<< trim END
vim9script vim9script

View File

@@ -750,6 +750,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
2650,
/**/ /**/
2649, 2649,
/**/ /**/

View File

@@ -402,12 +402,14 @@ struct dfunc_S {
// - ec_dfunc_idx: function index // - ec_dfunc_idx: function index
// - ec_iidx: instruction index // - ec_iidx: instruction index
// - ec_outer: stack used for closures // - ec_outer: stack used for closures
// - funclocal: function-local data
// - ec_frame_idx: previous frame index // - ec_frame_idx: previous frame index
#define STACK_FRAME_FUNC_OFF 0 #define STACK_FRAME_FUNC_OFF 0
#define STACK_FRAME_IIDX_OFF 1 #define STACK_FRAME_IIDX_OFF 1
#define STACK_FRAME_OUTER_OFF 2 #define STACK_FRAME_OUTER_OFF 2
#define STACK_FRAME_IDX_OFF 3 #define STACK_FRAME_FUNCLOCAL_OFF 3
#define STACK_FRAME_SIZE 4 #define STACK_FRAME_IDX_OFF 4
#define STACK_FRAME_SIZE 5
#ifdef DEFINE_VIM9_GLOBALS #ifdef DEFINE_VIM9_GLOBALS

View File

@@ -154,6 +154,15 @@ exe_newlist(int count, ectx_T *ectx)
return OK; 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. * Call compiled function "cdf_idx" from compiled code.
* This adds a stack frame and sets the instruction pointer to the start of the * This adds a stack frame and sets the instruction pointer to the start of the
@@ -170,16 +179,22 @@ exe_newlist(int count, ectx_T *ectx)
* - reserved space for local variables * - reserved space for local variables
*/ */
static int static int
call_dfunc(int cdf_idx, partial_T *pt, int argcount_arg, ectx_T *ectx) call_dfunc(
int cdf_idx,
partial_T *pt,
int argcount_arg,
funclocal_T *funclocal,
ectx_T *ectx)
{ {
int argcount = argcount_arg; int argcount = argcount_arg;
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + cdf_idx; dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + cdf_idx;
ufunc_T *ufunc = dfunc->df_ufunc; ufunc_T *ufunc = dfunc->df_ufunc;
int arg_to_add; int arg_to_add;
int vararg_count = 0; int vararg_count = 0;
int varcount; int varcount;
int idx; int idx;
estack_T *entry; estack_T *entry;
funclocal_T *floc = NULL;
if (dfunc->df_deleted) if (dfunc->df_deleted)
{ {
@@ -267,6 +282,16 @@ call_dfunc(int cdf_idx, partial_T *pt, int argcount_arg, ectx_T *ectx)
if (funcdepth_increment() == FAIL) if (funcdepth_increment() == FAIL)
return 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. // Move the vararg-list to below the missing optional arguments.
if (vararg_count > 0 && arg_to_add > 0) if (vararg_count > 0 && arg_to_add > 0)
*STACK_TV_BOT(arg_to_add - 1) = *STACK_TV_BOT(-1); *STACK_TV_BOT(arg_to_add - 1) = *STACK_TV_BOT(-1);
@@ -280,6 +305,7 @@ call_dfunc(int cdf_idx, partial_T *pt, int argcount_arg, ectx_T *ectx)
STACK_TV_BOT(STACK_FRAME_FUNC_OFF)->vval.v_number = ectx->ec_dfunc_idx; 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_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_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; STACK_TV_BOT(STACK_FRAME_IDX_OFF)->vval.v_number = ectx->ec_frame_idx;
ectx->ec_frame_idx = ectx->ec_stack.ga_len; ectx->ec_frame_idx = ectx->ec_stack.ga_len;
@@ -530,7 +556,7 @@ funcstack_check_refcount(funcstack_T *funcstack)
* Return from the current function. * Return from the current function.
*/ */
static int static int
func_return(ectx_T *ectx) func_return(funclocal_T *funclocal, ectx_T *ectx)
{ {
int idx; int idx;
int ret_idx; int ret_idx;
@@ -543,6 +569,7 @@ func_return(ectx_T *ectx)
+ STACK_FRAME_FUNC_OFF)->vval.v_number; + STACK_FRAME_FUNC_OFF)->vval.v_number;
dfunc_T *prev_dfunc = ((dfunc_T *)def_functions.ga_data) dfunc_T *prev_dfunc = ((dfunc_T *)def_functions.ga_data)
+ prev_dfunc_idx; + prev_dfunc_idx;
funclocal_T *floc;
#ifdef FEAT_PROFILE #ifdef FEAT_PROFILE
if (do_profiling == PROF_YES) if (do_profiling == PROF_YES)
@@ -592,11 +619,21 @@ func_return(ectx_T *ectx)
+ STACK_FRAME_IIDX_OFF)->vval.v_number; + STACK_FRAME_IIDX_OFF)->vval.v_number;
ectx->ec_outer = (void *)STACK_TV(ectx->ec_frame_idx ectx->ec_outer = (void *)STACK_TV(ectx->ec_frame_idx
+ STACK_FRAME_OUTER_OFF)->vval.v_string; + 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 // restoring ec_frame_idx must be last
ectx->ec_frame_idx = STACK_TV(ectx->ec_frame_idx ectx->ec_frame_idx = STACK_TV(ectx->ec_frame_idx
+ STACK_FRAME_IDX_OFF)->vval.v_number; + STACK_FRAME_IDX_OFF)->vval.v_number;
ectx->ec_instr = INSTRUCTIONS(prev_dfunc); ectx->ec_instr = INSTRUCTIONS(prev_dfunc);
if (floc == NULL)
funclocal->floc_restore_cmdmod = FALSE;
else
{
*funclocal = *floc;
vim_free(floc);
}
if (ret_idx > 0) if (ret_idx > 0)
{ {
// Reset the stack to the position before the call, with a spot for the // Reset the stack to the position before the call, with a spot for the
@@ -690,6 +727,7 @@ call_ufunc(
ufunc_T *ufunc, ufunc_T *ufunc,
partial_T *pt, partial_T *pt,
int argcount, int argcount,
funclocal_T *funclocal,
ectx_T *ectx, ectx_T *ectx,
isn_T *iptr) isn_T *iptr)
{ {
@@ -729,7 +767,7 @@ call_ufunc(
iptr->isn_arg.dfunc.cdf_idx = ufunc->uf_dfunc_idx; iptr->isn_arg.dfunc.cdf_idx = ufunc->uf_dfunc_idx;
iptr->isn_arg.dfunc.cdf_argcount = argcount; iptr->isn_arg.dfunc.cdf_argcount = argcount;
} }
return call_dfunc(ufunc->uf_dfunc_idx, pt, argcount, ectx); return call_dfunc(ufunc->uf_dfunc_idx, pt, argcount, funclocal, ectx);
} }
if (call_prepare(argcount, argvars, ectx) == FAIL) if (call_prepare(argcount, argvars, ectx) == FAIL)
@@ -773,7 +811,12 @@ vim9_aborting(int prev_called_emsg)
* Returns FAIL if not found without an error message. * Returns FAIL if not found without an error message.
*/ */
static int static int
call_by_name(char_u *name, int argcount, ectx_T *ectx, isn_T *iptr) call_by_name(
char_u *name,
int argcount,
funclocal_T *funclocal,
ectx_T *ectx,
isn_T *iptr)
{ {
ufunc_T *ufunc; ufunc_T *ufunc;
@@ -824,14 +867,18 @@ call_by_name(char_u *name, int argcount, ectx_T *ectx, isn_T *iptr)
} }
} }
return call_ufunc(ufunc, NULL, argcount, ectx, iptr); return call_ufunc(ufunc, NULL, argcount, funclocal, ectx, iptr);
} }
return FAIL; return FAIL;
} }
static int static int
call_partial(typval_T *tv, int argcount_arg, ectx_T *ectx) call_partial(
typval_T *tv,
int argcount_arg,
funclocal_T *funclocal,
ectx_T *ectx)
{ {
int argcount = argcount_arg; int argcount = argcount_arg;
char_u *name = NULL; char_u *name = NULL;
@@ -860,7 +907,7 @@ call_partial(typval_T *tv, int argcount_arg, ectx_T *ectx)
} }
if (pt->pt_func != NULL) if (pt->pt_func != NULL)
return call_ufunc(pt->pt_func, pt, argcount, ectx, NULL); return call_ufunc(pt->pt_func, pt, argcount, funclocal, ectx, NULL);
name = pt->pt_name; name = pt->pt_name;
} }
@@ -878,7 +925,7 @@ call_partial(typval_T *tv, int argcount_arg, ectx_T *ectx)
if (error != FCERR_NONE) if (error != FCERR_NONE)
res = FAIL; res = FAIL;
else else
res = call_by_name(fname, argcount, ectx, NULL); res = call_by_name(fname, argcount, funclocal, ectx, NULL);
vim_free(tofree); vim_free(tofree);
} }
@@ -1125,12 +1172,17 @@ get_script_svar(scriptref_T *sref, ectx_T *ectx)
* "iptr" can be used to replace the instruction with a more efficient one. * "iptr" can be used to replace the instruction with a more efficient one.
*/ */
static int static int
call_eval_func(char_u *name, int argcount, ectx_T *ectx, isn_T *iptr) call_eval_func(
char_u *name,
int argcount,
funclocal_T *funclocal,
ectx_T *ectx,
isn_T *iptr)
{ {
int called_emsg_before = called_emsg; int called_emsg_before = called_emsg;
int res; int res;
res = call_by_name(name, argcount, ectx, iptr); res = call_by_name(name, argcount, funclocal, ectx, iptr);
if (res == FAIL && called_emsg == called_emsg_before) if (res == FAIL && called_emsg == called_emsg_before)
{ {
dictitem_T *v; dictitem_T *v;
@@ -1146,7 +1198,7 @@ call_eval_func(char_u *name, int argcount, ectx_T *ectx, isn_T *iptr)
semsg(_(e_unknownfunc), name); semsg(_(e_unknownfunc), name);
return FAIL; return FAIL;
} }
return call_partial(&v->di_tv, argcount, ectx); return call_partial(&v->di_tv, argcount, funclocal, ectx);
} }
return res; return res;
} }
@@ -1222,9 +1274,7 @@ call_def_function(
int save_suppress_errthrow = suppress_errthrow; int save_suppress_errthrow = suppress_errthrow;
msglist_T **saved_msg_list = NULL; msglist_T **saved_msg_list = NULL;
msglist_T *private_msg_list = NULL; msglist_T *private_msg_list = NULL;
cmdmod_T save_cmdmod; funclocal_T funclocal;
int restore_cmdmod = FALSE;
int restore_cmdmod_stacklen = 0;
int save_emsg_silent_def = emsg_silent_def; int save_emsg_silent_def = emsg_silent_def;
int save_did_emsg_def = did_emsg_def; int save_did_emsg_def = did_emsg_def;
int trylevel_at_start = trylevel; int trylevel_at_start = trylevel;
@@ -1268,6 +1318,7 @@ call_def_function(
if (funcdepth_increment() == FAIL) if (funcdepth_increment() == FAIL)
return FAIL; return FAIL;
CLEAR_FIELD(funclocal);
CLEAR_FIELD(ectx); CLEAR_FIELD(ectx);
ectx.ec_dfunc_idx = ufunc->uf_dfunc_idx; ectx.ec_dfunc_idx = ufunc->uf_dfunc_idx;
ga_init2(&ectx.ec_stack, sizeof(typval_T), 500); ga_init2(&ectx.ec_stack, sizeof(typval_T), 500);
@@ -1488,7 +1539,7 @@ call_def_function(
goto done; goto done;
} }
if (func_return(&ectx) == FAIL) if (func_return(&funclocal, &ectx) == FAIL)
goto failed; goto failed;
} }
continue; continue;
@@ -2467,9 +2518,11 @@ call_def_function(
// call a :def function // call a :def function
case ISN_DCALL: case ISN_DCALL:
SOURCING_LNUM = iptr->isn_lnum; SOURCING_LNUM = iptr->isn_lnum;
if (call_dfunc(iptr->isn_arg.dfunc.cdf_idx, NULL, if (call_dfunc(iptr->isn_arg.dfunc.cdf_idx,
iptr->isn_arg.dfunc.cdf_argcount, NULL,
&ectx) == FAIL) iptr->isn_arg.dfunc.cdf_argcount,
&funclocal,
&ectx) == FAIL)
goto on_error; goto on_error;
break; break;
@@ -2502,7 +2555,8 @@ call_def_function(
partial_tv = *STACK_TV_BOT(0); partial_tv = *STACK_TV_BOT(0);
tv = &partial_tv; tv = &partial_tv;
} }
r = call_partial(tv, pfunc->cpf_argcount, &ectx); r = call_partial(tv, pfunc->cpf_argcount,
&funclocal, &ectx);
if (tv == &partial_tv) if (tv == &partial_tv)
clear_tv(&partial_tv); clear_tv(&partial_tv);
if (r == FAIL) if (r == FAIL)
@@ -2525,8 +2579,8 @@ call_def_function(
cufunc_T *cufunc = &iptr->isn_arg.ufunc; cufunc_T *cufunc = &iptr->isn_arg.ufunc;
SOURCING_LNUM = iptr->isn_lnum; SOURCING_LNUM = iptr->isn_lnum;
if (call_eval_func(cufunc->cuf_name, if (call_eval_func(cufunc->cuf_name, cufunc->cuf_argcount,
cufunc->cuf_argcount, &ectx, iptr) == FAIL) &funclocal, &ectx, iptr) == FAIL)
goto on_error; goto on_error;
} }
break; break;
@@ -2728,12 +2782,12 @@ call_def_function(
{ {
garray_T *trystack = &ectx.ec_trystack; garray_T *trystack = &ectx.ec_trystack;
if (restore_cmdmod) if (funclocal.floc_restore_cmdmod)
{ {
cmdmod.cmod_filter_regmatch.regprog = NULL; cmdmod.cmod_filter_regmatch.regprog = NULL;
undo_cmdmod(&cmdmod); undo_cmdmod(&cmdmod);
cmdmod = save_cmdmod; cmdmod = funclocal.floc_save_cmdmod;
restore_cmdmod = FALSE; funclocal.floc_restore_cmdmod = FALSE;
} }
if (trystack->ga_len > 0) if (trystack->ga_len > 0)
{ {
@@ -3649,9 +3703,9 @@ call_def_function(
break; break;
case ISN_CMDMOD: case ISN_CMDMOD:
save_cmdmod = cmdmod; funclocal.floc_save_cmdmod = cmdmod;
restore_cmdmod = TRUE; funclocal.floc_restore_cmdmod = TRUE;
restore_cmdmod_stacklen = ectx.ec_stack.ga_len; funclocal.floc_restore_cmdmod_stacklen = ectx.ec_stack.ga_len;
cmdmod = *iptr->isn_arg.cmdmod.cf_cmdmod; cmdmod = *iptr->isn_arg.cmdmod.cf_cmdmod;
apply_cmdmod(&cmdmod); apply_cmdmod(&cmdmod);
break; break;
@@ -3660,8 +3714,8 @@ call_def_function(
// filter regprog is owned by the instruction, don't free it // filter regprog is owned by the instruction, don't free it
cmdmod.cmod_filter_regmatch.regprog = NULL; cmdmod.cmod_filter_regmatch.regprog = NULL;
undo_cmdmod(&cmdmod); undo_cmdmod(&cmdmod);
cmdmod = save_cmdmod; cmdmod = funclocal.floc_save_cmdmod;
restore_cmdmod = FALSE; funclocal.floc_restore_cmdmod = FALSE;
break; break;
case ISN_UNPACK: case ISN_UNPACK:
@@ -3790,7 +3844,7 @@ func_return:
if (ectx.ec_frame_idx == initial_frame_idx) if (ectx.ec_frame_idx == initial_frame_idx)
goto done; goto done;
if (func_return(&ectx) == FAIL) if (func_return(&funclocal, &ectx) == FAIL)
// only fails when out of memory // only fails when out of memory
goto failed; goto failed;
continue; continue;
@@ -3805,9 +3859,10 @@ on_error:
// If a sequence of instructions causes an error while ":silent!" // If a sequence of instructions causes an error while ":silent!"
// was used, restore the stack length and jump ahead to restoring // was used, restore the stack length and jump ahead to restoring
// the cmdmod. // the cmdmod.
if (restore_cmdmod) if (funclocal.floc_restore_cmdmod)
{ {
while (ectx.ec_stack.ga_len > restore_cmdmod_stacklen) while (ectx.ec_stack.ga_len
> funclocal.floc_restore_cmdmod_stacklen)
{ {
--ectx.ec_stack.ga_len; --ectx.ec_stack.ga_len;
clear_tv(STACK_TV_BOT(0)); clear_tv(STACK_TV_BOT(0));
@@ -3834,7 +3889,7 @@ done:
failed: failed:
// When failed need to unwind the call stack. // When failed need to unwind the call stack.
while (ectx.ec_frame_idx != initial_frame_idx) while (ectx.ec_frame_idx != initial_frame_idx)
func_return(&ectx); func_return(&funclocal, &ectx);
// Deal with any remaining closures, they may be in use somewhere. // Deal with any remaining closures, they may be in use somewhere.
if (ectx.ec_funcrefs.ga_len > 0) if (ectx.ec_funcrefs.ga_len > 0)
@@ -3862,11 +3917,11 @@ failed:
} }
msg_list = saved_msg_list; msg_list = saved_msg_list;
if (restore_cmdmod) if (funclocal.floc_restore_cmdmod)
{ {
cmdmod.cmod_filter_regmatch.regprog = NULL; cmdmod.cmod_filter_regmatch.regprog = NULL;
undo_cmdmod(&cmdmod); undo_cmdmod(&cmdmod);
cmdmod = save_cmdmod; cmdmod = funclocal.floc_save_cmdmod;
} }
emsg_silent_def = save_emsg_silent_def; emsg_silent_def = save_emsg_silent_def;
did_emsg_def += save_did_emsg_def; did_emsg_def += save_did_emsg_def;