summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2020-10-01 13:01:34 +0200
committerBram Moolenaar <Bram@vim.org>2020-10-01 13:01:34 +0200
commit5366e1aecfff4546df6af86cf98013f23ed5c3bd (patch)
treebb7d5a1c8452bcc7572828402b25232c3df75b2a
parent55759b522814995af36803823d342d51d68c0b67 (diff)
patch 8.2.1778: Vim9: returning from a partial call clears outer contextv8.2.1778
Problem: Vim9: returning from a partial call clears outer context, causing a crash. Solution: Put the outer context in the stack frame. (closes #7044)
-rw-r--r--src/testdir/test_vim9_func.vim15
-rw-r--r--src/version.c2
-rw-r--r--src/vim9.h10
-rw-r--r--src/vim9execute.c10
4 files changed, 31 insertions, 6 deletions
diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim
index c9f902773b..eec5af62f9 100644
--- a/src/testdir/test_vim9_func.vim
+++ b/src/testdir/test_vim9_func.vim
@@ -1384,6 +1384,21 @@ def Test_nested_closure_fails()
CheckScriptFailure(lines, 'E1012:')
enddef
+def Test_nested_lambda()
+ var lines =<< trim END
+ vim9script
+ def Func()
+ var x = 4
+ var Lambda1 = {-> 7}
+ var Lambda2 = {-> [Lambda1(), x]}
+ var res = Lambda2()
+ assert_equal([7, 4], res)
+ enddef
+ Func()
+ END
+ CheckScriptSuccess(lines)
+enddef
+
def Test_sort_return_type()
var res: list<number>
res = [1, 2, 3]->sort()
diff --git a/src/version.c b/src/version.c
index 446ac55eb5..b92e99be54 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 */
/**/
+ 1778,
+/**/
1777,
/**/
1776,
diff --git a/src/vim9.h b/src/vim9.h
index a39fbbb4e3..663faa6800 100644
--- a/src/vim9.h
+++ b/src/vim9.h
@@ -326,10 +326,12 @@ struct dfunc_S {
};
// Number of entries used by stack frame for a function call.
-// - function index
-// - instruction index
-// - previous frame index
-#define STACK_FRAME_SIZE 3
+// - ec_dfunc_idx: function index
+// - ec_iidx: instruction index
+// - ec_outer_stack: stack used for closures TODO: can we avoid this?
+// - ec_outer_frame: stack frame for closures
+// - ec_frame_idx: previous frame index
+#define STACK_FRAME_SIZE 5
#ifdef DEFINE_VIM9_GLOBALS
diff --git a/src/vim9execute.c b/src/vim9execute.c
index 5a94354295..4013571a0b 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -239,7 +239,9 @@ call_dfunc(int cdf_idx, int argcount_arg, ectx_T *ectx)
// Store current execution state in stack frame for ISN_RETURN.
STACK_TV_BOT(0)->vval.v_number = ectx->ec_dfunc_idx;
STACK_TV_BOT(1)->vval.v_number = ectx->ec_iidx;
- STACK_TV_BOT(2)->vval.v_number = ectx->ec_frame_idx;
+ STACK_TV_BOT(2)->vval.v_string = (void *)ectx->ec_outer_stack;
+ STACK_TV_BOT(3)->vval.v_number = ectx->ec_outer_frame;
+ STACK_TV_BOT(4)->vval.v_number = ectx->ec_frame_idx;
ectx->ec_frame_idx = ectx->ec_stack.ga_len;
// Initialize local variables
@@ -455,7 +457,11 @@ func_return(ectx_T *ectx)
// 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;
- ectx->ec_frame_idx = STACK_TV(ectx->ec_frame_idx + 2)->vval.v_number;
+ ectx->ec_outer_stack =
+ (void *)STACK_TV(ectx->ec_frame_idx + 2)->vval.v_string;
+ ectx->ec_outer_frame = STACK_TV(ectx->ec_frame_idx + 3)->vval.v_number;
+ // restoring ec_frame_idx must be last
+ ectx->ec_frame_idx = STACK_TV(ectx->ec_frame_idx + 4)->vval.v_number;
dfunc = ((dfunc_T *)def_functions.ga_data) + ectx->ec_dfunc_idx;
ectx->ec_instr = dfunc->df_instr;