patch 9.0.0397: :defer not tested with exceptions and ":qa!"
Problem: :defer not tested with exceptions and ":qa!".
Solution: Test :defer works when exceptions are thrown and when ":qa!" is
used. Invoke the deferred calls on exit.
This commit is contained in:
@ -263,8 +263,9 @@ eval_expr_typval(typval_T *expr, typval_T *argv, int argc, typval_T *rettv)
|
||||
if (partial->pt_func != NULL
|
||||
&& partial->pt_func->uf_def_status != UF_NOT_COMPILED)
|
||||
{
|
||||
// FIXME: should create a funccal and link it in current_funccal.
|
||||
if (call_def_function(partial->pt_func, argc, argv,
|
||||
partial, rettv) == FAIL)
|
||||
partial, NULL, rettv) == FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
else
|
||||
|
||||
@ -1583,6 +1583,11 @@ getout(int exitval)
|
||||
if (!is_not_a_term_or_gui())
|
||||
windgoto((int)Rows - 1, 0);
|
||||
|
||||
#ifdef FEAT_EVAL
|
||||
// Invoked all deferred functions in the function stack.
|
||||
invoke_all_defer();
|
||||
#endif
|
||||
|
||||
#if defined(FEAT_EVAL) || defined(FEAT_SYN_HL)
|
||||
// Optionally print hashtable efficiency.
|
||||
hash_debug_results();
|
||||
|
||||
@ -59,7 +59,7 @@ void func_ref(char_u *name);
|
||||
void func_ptr_ref(ufunc_T *fp);
|
||||
void ex_return(exarg_T *eap);
|
||||
int add_defer(char_u *name, int argcount_arg, typval_T *argvars);
|
||||
void handle_defer(void);
|
||||
void invoke_all_defer(void);
|
||||
void ex_call(exarg_T *eap);
|
||||
int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv);
|
||||
void discard_pending_return(void *rettv);
|
||||
|
||||
@ -13,7 +13,9 @@ typval_T *lookup_debug_var(char_u *name);
|
||||
int may_break_in_function(ufunc_T *ufunc);
|
||||
int exe_typval_instr(typval_T *tv, typval_T *rettv);
|
||||
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);
|
||||
int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, partial_T *partial, funccall_T *funccal, typval_T *rettv);
|
||||
void unwind_def_callstack(ectx_T *ectx);
|
||||
void may_invoke_defer_funcs(ectx_T *ectx);
|
||||
void set_context_in_disassemble_cmd(expand_T *xp, char_u *arg);
|
||||
char_u *get_disassemble_argument(expand_T *xp, int idx);
|
||||
void ex_disassemble(exarg_T *eap);
|
||||
|
||||
@ -1753,7 +1753,11 @@ struct funccall_S
|
||||
linenr_T breakpoint; // next line with breakpoint or zero
|
||||
int dbg_tick; // debug_tick when breakpoint was set
|
||||
int level; // top nesting level of executed function
|
||||
|
||||
garray_T fc_defer; // functions to be called on return
|
||||
ectx_T *fc_ectx; // execution context for :def function, NULL
|
||||
// otherwise
|
||||
|
||||
#ifdef FEAT_PROFILE
|
||||
proftime_T prof_child; // time spent in a child
|
||||
#endif
|
||||
|
||||
@ -581,5 +581,49 @@ func Test_defer()
|
||||
call assert_fails('defer Part("arg2")', 'E1300:')
|
||||
endfunc
|
||||
|
||||
func DeferLevelTwo()
|
||||
call writefile(['text'], 'XDeleteTwo', 'D')
|
||||
throw 'someerror'
|
||||
endfunc
|
||||
|
||||
def DeferLevelOne()
|
||||
call writefile(['text'], 'XDeleteOne', 'D')
|
||||
call g:DeferLevelTwo()
|
||||
enddef
|
||||
|
||||
func Test_defer_throw()
|
||||
let caught = 'no'
|
||||
try
|
||||
call DeferLevelOne()
|
||||
catch /someerror/
|
||||
let caught = 'yes'
|
||||
endtry
|
||||
call assert_equal('yes', caught)
|
||||
call assert_false(filereadable('XDeleteOne'))
|
||||
call assert_false(filereadable('XDeleteTwo'))
|
||||
endfunc
|
||||
|
||||
func Test_defer_quitall()
|
||||
let lines =<< trim END
|
||||
vim9script
|
||||
func DeferLevelTwo()
|
||||
call writefile(['text'], 'XQuitallTwo', 'D')
|
||||
qa!
|
||||
endfunc
|
||||
|
||||
def DeferLevelOne()
|
||||
call writefile(['text'], 'XQuitallOne', 'D')
|
||||
call DeferLevelTwo()
|
||||
enddef
|
||||
|
||||
DeferLevelOne()
|
||||
END
|
||||
call writefile(lines, 'XdeferQuitall', 'D')
|
||||
let res = system(GetVimCommandClean() .. ' -X -S XdeferQuitall')
|
||||
call assert_equal(0, v:shell_error)
|
||||
call assert_false(filereadable('XQuitallOne'))
|
||||
call assert_false(filereadable('XQuitallTwo'))
|
||||
endfunc
|
||||
|
||||
|
||||
" vim: shiftwidth=2 sts=2 expandtab
|
||||
|
||||
@ -33,6 +33,7 @@ static void funccal_unref(funccall_T *fc, ufunc_T *fp, int force);
|
||||
static void func_clear(ufunc_T *fp, int force);
|
||||
static int func_free(ufunc_T *fp, int force);
|
||||
static char_u *untrans_function_name(char_u *name);
|
||||
static void handle_defer_one(funccall_T *funccal);
|
||||
|
||||
void
|
||||
func_init()
|
||||
@ -2651,7 +2652,8 @@ call_user_func(
|
||||
profile_may_start_func(&profile_info, fp, caller);
|
||||
#endif
|
||||
sticky_cmdmod_flags = 0;
|
||||
call_def_function(fp, argcount, argvars, funcexe->fe_partial, rettv);
|
||||
call_def_function(fp, argcount, argvars, funcexe->fe_partial,
|
||||
fc, rettv);
|
||||
funcdepth_decrement();
|
||||
#ifdef FEAT_PROFILE
|
||||
if (do_profiling == PROF_YES && (fp->uf_profiling
|
||||
@ -2906,7 +2908,7 @@ call_user_func(
|
||||
DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
|
||||
|
||||
// Invoke functions added with ":defer".
|
||||
handle_defer();
|
||||
handle_defer_one(current_funccal);
|
||||
|
||||
--RedrawingDisabled;
|
||||
|
||||
@ -5660,16 +5662,16 @@ theend:
|
||||
/*
|
||||
* Invoked after a functions has finished: invoke ":defer" functions.
|
||||
*/
|
||||
void
|
||||
handle_defer(void)
|
||||
static void
|
||||
handle_defer_one(funccall_T *funccal)
|
||||
{
|
||||
int idx;
|
||||
|
||||
for (idx = current_funccal->fc_defer.ga_len - 1; idx >= 0; --idx)
|
||||
for (idx = funccal->fc_defer.ga_len - 1; idx >= 0; --idx)
|
||||
{
|
||||
funcexe_T funcexe;
|
||||
typval_T rettv;
|
||||
defer_T *dr = ((defer_T *)current_funccal->fc_defer.ga_data) + idx;
|
||||
defer_T *dr = ((defer_T *)funccal->fc_defer.ga_data) + idx;
|
||||
int i;
|
||||
|
||||
CLEAR_FIELD(funcexe);
|
||||
@ -5683,7 +5685,29 @@ handle_defer(void)
|
||||
for (i = dr->dr_argcount - 1; i >= 0; --i)
|
||||
clear_tv(&dr->dr_argvars[i]);
|
||||
}
|
||||
ga_clear(¤t_funccal->fc_defer);
|
||||
ga_clear(&funccal->fc_defer);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when exiting: call all defer functions.
|
||||
*/
|
||||
void
|
||||
invoke_all_defer(void)
|
||||
{
|
||||
funccall_T *funccal;
|
||||
|
||||
for (funccal = current_funccal; funccal != NULL; funccal = funccal->caller)
|
||||
if (funccal->fc_ectx != NULL)
|
||||
{
|
||||
// :def function
|
||||
unwind_def_callstack(funccal->fc_ectx);
|
||||
may_invoke_defer_funcs(funccal->fc_ectx);
|
||||
}
|
||||
else
|
||||
{
|
||||
// legacy function
|
||||
handle_defer_one(funccal);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@ -703,6 +703,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
397,
|
||||
/**/
|
||||
396,
|
||||
/**/
|
||||
|
||||
@ -5171,13 +5171,7 @@ on_fatal_error:
|
||||
done:
|
||||
ret = OK;
|
||||
theend:
|
||||
{
|
||||
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
|
||||
+ ectx->ec_dfunc_idx;
|
||||
|
||||
if (dfunc->df_defer_var_idx > 0)
|
||||
invoke_defer_funcs(ectx);
|
||||
}
|
||||
may_invoke_defer_funcs(ectx);
|
||||
|
||||
dict_stack_clear(dict_stack_len_at_start);
|
||||
ectx->ec_trylevel_at_start = save_trylevel_at_start;
|
||||
@ -5258,6 +5252,7 @@ call_def_function(
|
||||
int argc_arg, // nr of arguments
|
||||
typval_T *argv, // arguments
|
||||
partial_T *partial, // optional partial for context
|
||||
funccall_T *funccal,
|
||||
typval_T *rettv) // return value
|
||||
{
|
||||
ectx_T ectx; // execution context
|
||||
@ -5494,6 +5489,10 @@ call_def_function(
|
||||
ectx.ec_instr = INSTRUCTIONS(dfunc);
|
||||
}
|
||||
|
||||
// Store the execution context in funccal, used by invoke_all_defer().
|
||||
if (funccal != NULL)
|
||||
funccal->fc_ectx = &ectx;
|
||||
|
||||
// Following errors are in the function, not the caller.
|
||||
// Commands behave like vim9script.
|
||||
estack_push_ufunc(ufunc, 1);
|
||||
@ -5537,8 +5536,7 @@ call_def_function(
|
||||
}
|
||||
|
||||
// When failed need to unwind the call stack.
|
||||
while (ectx.ec_frame_idx != ectx.ec_initial_frame_idx)
|
||||
func_return(&ectx);
|
||||
unwind_def_callstack(&ectx);
|
||||
|
||||
// Deal with any remaining closures, they may be in use somewhere.
|
||||
if (ectx.ec_funcrefs.ga_len > 0)
|
||||
@ -5603,6 +5601,30 @@ failed_early:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when a def function has finished (possibly failed).
|
||||
* Invoke all the function returns to clean up and invoke deferred functions,
|
||||
* except the toplevel one.
|
||||
*/
|
||||
void
|
||||
unwind_def_callstack(ectx_T *ectx)
|
||||
{
|
||||
while (ectx->ec_frame_idx != ectx->ec_initial_frame_idx)
|
||||
func_return(ectx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Invoke any deffered functions for the top function in "ectx".
|
||||
*/
|
||||
void
|
||||
may_invoke_defer_funcs(ectx_T *ectx)
|
||||
{
|
||||
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + ectx->ec_dfunc_idx;
|
||||
|
||||
if (dfunc->df_defer_var_idx > 0)
|
||||
invoke_defer_funcs(ectx);
|
||||
}
|
||||
|
||||
/*
|
||||
* List instructions "instr" up to "instr_count" or until ISN_FINISH.
|
||||
* "ufunc" has the source lines, NULL for the instructions of ISN_SUBSTITUTE.
|
||||
|
||||
Reference in New Issue
Block a user