summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2020-11-25 19:15:19 +0100
committerBram Moolenaar <Bram@vim.org>2020-11-25 19:15:19 +0100
commit34c54eb6cbda5dbc14376c8b1c62ad11d4852793 (patch)
treeb77319d19be510ea6923adb5529e052143cd566f
parent6bed0dbc8500be3ea751cc527a6ee89ca073a4d1 (diff)
patch 8.2.2051: Vim9: crash when aborting a user function callv8.2.2051
Problem: Vim9: crash when aborting a user function call. Solution: Do not use the return value when aboring. (closes #7372)
-rw-r--r--src/testdir/test_vim9_func.vim23
-rw-r--r--src/version.c2
-rw-r--r--src/vim9execute.c22
3 files changed, 42 insertions, 5 deletions
diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim
index 80d564e3e2..8cb6ecf33f 100644
--- a/src/testdir/test_vim9_func.vim
+++ b/src/testdir/test_vim9_func.vim
@@ -1468,6 +1468,29 @@ def Test_nested_closure_fails()
CheckScriptFailure(lines, 'E1012:')
enddef
+def Test_failure_in_called_function()
+ # this was using the frame index as the return value
+ var lines =<< trim END
+ vim9script
+ au TerminalWinOpen * eval [][0]
+ def PopupTerm(a: any)
+ # make sure typvals on stack are string
+ ['a', 'b', 'c', 'd', 'e', 'f', 'g']->join()
+ FireEvent()
+ enddef
+ def FireEvent()
+ do TerminalWinOpen
+ enddef
+ # use try/catch to make eval fail
+ try
+ call PopupTerm(0)
+ catch
+ endtry
+ au! TerminalWinOpen
+ END
+ CheckScriptSuccess(lines)
+enddef
+
def Test_nested_lambda()
var lines =<< trim END
vim9script
diff --git a/src/version.c b/src/version.c
index 216f5c9895..4c54fe5806 100644
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 2051,
+/**/
2050,
/**/
2049,
diff --git a/src/vim9execute.c b/src/vim9execute.c
index 323f2926b5..ab2cf385f9 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -467,6 +467,7 @@ funcstack_check_refcount(funcstack_T *funcstack)
func_return(ectx_T *ectx)
{
int idx;
+ int ret_idx;
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ ectx->ec_dfunc_idx;
int argcount = ufunc_argcount(dfunc->df_ufunc);
@@ -490,6 +491,12 @@ func_return(ectx_T *ectx)
idx < ectx->ec_stack.ga_len - 1; ++idx)
clear_tv(STACK_TV(idx));
+ // The return value should be on top of the stack. However, when aborting
+ // it may not be there and ec_frame_idx is the top of the stack.
+ ret_idx = ectx->ec_stack.ga_len - 1;
+ if (ret_idx == ectx->ec_frame_idx + 4)
+ ret_idx = 0;
+
// Restore the previous frame.
ectx->ec_dfunc_idx = STACK_TV(ectx->ec_frame_idx)->vval.v_number;
ectx->ec_iidx = STACK_TV(ectx->ec_frame_idx + 1)->vval.v_number;
@@ -501,11 +508,16 @@ func_return(ectx_T *ectx)
dfunc = ((dfunc_T *)def_functions.ga_data) + ectx->ec_dfunc_idx;
ectx->ec_instr = dfunc->df_instr;
- // Reset the stack to the position before the call, move the return value
- // to the top of the stack.
- idx = ectx->ec_stack.ga_len - 1;
- ectx->ec_stack.ga_len = top + 1;
- *STACK_TV_BOT(-1) = *STACK_TV(idx);
+ if (ret_idx > 0)
+ {
+ // Reset the stack to the position before the call, with a spot for the
+ // return value, moved there from above the frame.
+ ectx->ec_stack.ga_len = top + 1;
+ *STACK_TV_BOT(-1) = *STACK_TV(ret_idx);
+ }
+ else
+ // Reset the stack to the position before the call.
+ ectx->ec_stack.ga_len = top;
funcdepth_decrement();
return OK;