changeset 22460:4097509ecc1e v8.2.1778

patch 8.2.1778: Vim9: returning from a partial call clears outer context Commit: https://github.com/vim/vim/commit/5366e1aecfff4546df6af86cf98013f23ed5c3bd Author: Bram Moolenaar <Bram@vim.org> Date: Thu Oct 1 13:01:34 2020 +0200 patch 8.2.1778: Vim9: returning from a partial call clears outer context Problem: Vim9: returning from a partial call clears outer context, causing a crash. Solution: Put the outer context in the stack frame. (closes #7044)
author Bram Moolenaar <Bram@vim.org>
date Thu, 01 Oct 2020 13:15:03 +0200
parents a2d91a3440d2
children bb43a58b02a5
files src/testdir/test_vim9_func.vim src/version.c src/vim9.h src/vim9execute.c
diffstat 4 files changed, 31 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- 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()
--- 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,
--- 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
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -239,7 +239,9 @@ call_dfunc(int cdf_idx, int argcount_arg
     // 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;