1
0
forked from aniani/vim

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:
Bram Moolenaar
2022-09-06 18:31:14 +01:00
parent 2834ebdee4
commit 58779858fb
9 changed files with 123 additions and 19 deletions

View File

@@ -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.