summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2020-07-13 20:41:08 +0200
committerBram Moolenaar <Bram@vim.org>2020-07-13 20:41:08 +0200
commit08f7a41b0a280e5901eb4ee4bbfe682113863492 (patch)
treec5a4c229cde639d8c6518e3dcb79f014ddca90ab
parentf1a2368d81fc3f70dfcf7d577957185da6ccf0b6 (diff)
patch 8.2.1202: Vim9: crash when calling a closure from a builtin functionv8.2.1202
Problem: Vim9: crash when calling a closure from a builtin function. Solution: Use the current execution context. (closes #6441)
-rw-r--r--src/testdir/test_vim9_func.vim19
-rw-r--r--src/version.c2
-rw-r--r--src/vim9execute.c23
3 files changed, 41 insertions, 3 deletions
diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim
index 99b2a113ac..82146756f0 100644
--- a/src/testdir/test_vim9_func.vim
+++ b/src/testdir/test_vim9_func.vim
@@ -1019,5 +1019,24 @@ def Test_recursive_call()
assert_equal(6765, Fibonacci(20))
enddef
+def TreeWalk(dir: string): list<any>
+ return readdir(dir)->map({_, val ->
+ fnamemodify(dir .. '/' .. val, ':p')->isdirectory()
+ ? {val : TreeWalk(dir .. '/' .. val)}
+ : val
+ })
+enddef
+
+def Test_closure_in_map()
+ mkdir('XclosureDir/tdir', 'p')
+ writefile(['111'], 'XclosureDir/file1')
+ writefile(['222'], 'XclosureDir/file2')
+ writefile(['333'], 'XclosureDir/tdir/file3')
+
+ assert_equal(['file1', 'file2', {'tdir': ['file3']}], TreeWalk('XclosureDir'))
+
+ delete('XclosureDir', 'rf')
+enddef
+
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/version.c b/src/version.c
index 2c9c4f07a5..ddc6f2e066 100644
--- a/src/version.c
+++ b/src/version.c
@@ -755,6 +755,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1202,
+/**/
1201,
/**/
1200,
diff --git a/src/vim9execute.c b/src/vim9execute.c
index 5fdbfbf518..eb900c876f 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -461,6 +461,10 @@ call_prepare(int argcount, typval_T *argvars, ectx_T *ectx)
return OK;
}
+// Ugly global to avoid passing the execution context around through many
+// layers.
+static ectx_T *current_ectx = NULL;
+
/*
* Call a builtin function by index.
*/
@@ -470,12 +474,16 @@ call_bfunc(int func_idx, int argcount, ectx_T *ectx)
typval_T argvars[MAX_FUNC_ARGS];
int idx;
int did_emsg_before = did_emsg;
+ ectx_T *prev_ectx = current_ectx;
if (call_prepare(argcount, argvars, ectx) == FAIL)
return FAIL;
- // Call the builtin function.
+ // Call the builtin function. Set "current_ectx" so that when it
+ // recursively invokes call_def_function() a closure context can be set.
+ current_ectx = ectx;
call_internal_func_by_idx(func_idx, argvars, STACK_TV_BOT(-1));
+ current_ectx = prev_ectx;
// Clear the arguments.
for (idx = 0; idx < argcount; ++idx)
@@ -749,8 +757,17 @@ call_def_function(
if (partial != NULL)
{
- ectx.ec_outer_stack = partial->pt_ectx_stack;
- ectx.ec_outer_frame = partial->pt_ectx_frame;
+ if (partial->pt_ectx_stack == NULL && current_ectx != NULL)
+ {
+ // TODO: is this always the right way?
+ ectx.ec_outer_stack = &current_ectx->ec_stack;
+ ectx.ec_outer_frame = current_ectx->ec_frame_idx;
+ }
+ else
+ {
+ ectx.ec_outer_stack = partial->pt_ectx_stack;
+ ectx.ec_outer_frame = partial->pt_ectx_frame;
+ }
}
// dummy frame entries