summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2021-01-10 22:42:50 +0100
committerBram Moolenaar <Bram@vim.org>2021-01-10 22:42:50 +0100
commit75ab91ff3403e725a79ac9c7351b78e9aff71d67 (patch)
tree624dae1f8aaeff2cfcd5aea7ac6aad5573835072
parent6f02b00bb0958f70bc15534e115b4c6dadff0e06 (diff)
downloadvim-git-75ab91ff3403e725a79ac9c7351b78e9aff71d67.tar.gz
patch 8.2.2325: Vim9: crash if map() changes the item typev8.2.2325
Problem: Vim9: crash if map() changes the item type. Solution: Check that the item type is still OK. (closes #7652) Fix problem with mapnew() on range list.
-rw-r--r--src/evalfunc.c9
-rw-r--r--src/list.c11
-rw-r--r--src/proto/evalfunc.pro1
-rw-r--r--src/testdir/test_vim9_builtin.vim13
-rw-r--r--src/testdir/test_vim9_expr.vim18
-rw-r--r--src/testdir/test_vim9_func.vim6
-rw-r--r--src/version.c2
-rw-r--r--src/vim9compile.c8
8 files changed, 50 insertions, 18 deletions
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 85f8befff..1abcd5e11 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -1930,6 +1930,15 @@ internal_func_ret_type(int idx, int argcount, type_T **argtypes)
}
/*
+ * Return TRUE if "idx" is for the map() function.
+ */
+ int
+internal_func_is_map(int idx)
+{
+ return global_functions[idx].f_func == f_map;
+}
+
+/*
* Check the argument count to use for internal function "idx".
* Returns -1 for failure, 0 if no method base accepted, 1 if method base is
* first argument, 2 if method base is second argument, etc. 9 if method base
diff --git a/src/list.c b/src/list.c
index 4531e5885..2b44ebacb 100644
--- a/src/list.c
+++ b/src/list.c
@@ -2188,10 +2188,13 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
int stride = l->lv_u.nonmat.lv_stride;
// List from range(): loop over the numbers
- l->lv_first = NULL;
- l->lv_u.mat.lv_last = NULL;
- l->lv_len = 0;
- l->lv_u.mat.lv_idx_item = NULL;
+ if (filtermap != FILTERMAP_MAPNEW)
+ {
+ l->lv_first = NULL;
+ l->lv_u.mat.lv_last = NULL;
+ l->lv_len = 0;
+ l->lv_u.mat.lv_idx_item = NULL;
+ }
for (idx = 0; idx < len; ++idx)
{
diff --git a/src/proto/evalfunc.pro b/src/proto/evalfunc.pro
index 562a7c83d..ad4b98a29 100644
--- a/src/proto/evalfunc.pro
+++ b/src/proto/evalfunc.pro
@@ -6,6 +6,7 @@ int has_internal_func(char_u *name);
char *internal_func_name(int idx);
int internal_func_check_arg_types(type_T **types, int idx, int argcount);
type_T *internal_func_ret_type(int idx, int argcount, type_T **argtypes);
+int internal_func_is_map(int idx);
int check_internal_func(int idx, int argcount);
int call_internal_func(char_u *name, int argcount, typval_T *argvars, typval_T *rettv);
void call_internal_func_by_idx(int idx, typval_T *argvars, typval_T *rettv);
diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim
index 7aef315dc..673f0c056 100644
--- a/src/testdir/test_vim9_builtin.vim
+++ b/src/testdir/test_vim9_builtin.vim
@@ -231,7 +231,7 @@ def Test_extend_arg_types()
assert_equal({a: 1, b: 2}, extend({a: 1, b: 2}, {b: 4}, s:string_keep))
var res: list<dict<any>>
- extend(res, map([1, 2], (_, v) => ({})))
+ extend(res, mapnew([1, 2], (_, v) => ({})))
assert_equal([{}, {}], res)
CheckDefFailure(['extend([1, 2], 3)'], 'E1013: Argument 2: type mismatch, expected list<number> but got number')
@@ -320,6 +320,15 @@ def Test_map_function_arg()
CheckDefAndScriptSuccess(lines)
enddef
+def Test_map_item_type()
+ var lines =<< trim END
+ var l = ['a', 'b', 'c']
+ map(l, (k, v) => k .. '/' .. v )
+ assert_equal(['0/a', '1/b', '2/c'], l)
+ END
+ CheckDefAndScriptSuccess(lines)
+enddef
+
def Test_filereadable()
assert_false(filereadable(""))
assert_false(filereadable(test_null_string()))
@@ -728,7 +737,7 @@ enddef
def Test_submatch()
var pat = 'A\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)'
- var Rep = () => range(10)->map((_, v) => submatch(v, true))->string()
+ var Rep = () => range(10)->mapnew((_, v) => submatch(v, true))->string()
var actual = substitute('A123456789', pat, Rep, '')
var expected = "[['A123456789'], ['1'], ['2'], ['3'], ['4'], ['5'], ['6'], ['7'], ['8'], ['9']]"
actual->assert_equal(expected)
diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim
index 8955a7ccf..50537c858 100644
--- a/src/testdir/test_vim9_expr.vim
+++ b/src/testdir/test_vim9_expr.vim
@@ -1859,10 +1859,10 @@ def Test_expr7_lambda()
# line continuation inside lambda with "cond ? expr : expr" works
var ll = range(3)
- map(ll, (k, v) => v % 2 ? {
+ var dll = mapnew(ll, (k, v) => v % 2 ? {
['111']: 111 } : {}
)
- assert_equal([{}, {111: 111}, {}], ll)
+ assert_equal([{}, {111: 111}, {}], dll)
ll = range(3)
map(ll, (k, v) => v == 8 || v
@@ -1946,10 +1946,10 @@ def Test_expr7_new_lambda()
# line continuation inside lambda with "cond ? expr : expr" works
var ll = range(3)
- map(ll, (k, v) => v % 2 ? {
+ var dll = mapnew(ll, (k, v) => v % 2 ? {
['111']: 111 } : {}
)
- assert_equal([{}, {111: 111}, {}], ll)
+ assert_equal([{}, {111: 111}, {}], dll)
ll = range(3)
map(ll, (k, v) => v == 8 || v
@@ -2964,25 +2964,25 @@ def Test_expr7_subscript_linebreak()
var range = range(
3)
var l = range
- ->map('string(v:key)')
+ ->mapnew('string(v:key)')
assert_equal(['0', '1', '2'], l)
l = range
- ->map('string(v:key)')
+ ->mapnew('string(v:key)')
assert_equal(['0', '1', '2'], l)
l = range # comment
- ->map('string(v:key)')
+ ->mapnew('string(v:key)')
assert_equal(['0', '1', '2'], l)
l = range
- ->map('string(v:key)')
+ ->mapnew('string(v:key)')
assert_equal(['0', '1', '2'], l)
l = range
# comment
- ->map('string(v:key)')
+ ->mapnew('string(v:key)')
assert_equal(['0', '1', '2'], l)
assert_equal('1', l[
diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim
index fdad359de..e87c33e90 100644
--- a/src/testdir/test_vim9_func.vim
+++ b/src/testdir/test_vim9_func.vim
@@ -1763,7 +1763,7 @@ enddef
def Shadowed(): list<number>
var FuncList: list<func: number> = [() => 42]
- return FuncList->map((_, Shadowed) => Shadowed())
+ return FuncList->mapnew((_, Shadowed) => Shadowed())
enddef
def Test_lambda_arg_shadows_func()
@@ -1792,7 +1792,7 @@ enddef
def Line_continuation_in_lambda(): list<string>
var x = range(97, 100)
- ->map((_, v) => nr2char(v)
+ ->mapnew((_, v) => nr2char(v)
->toupper())
->reverse()
return x
@@ -1908,7 +1908,7 @@ def Test_recursive_call()
enddef
def TreeWalk(dir: string): list<any>
- return readdir(dir)->map((_, val) =>
+ return readdir(dir)->mapnew((_, val) =>
fnamemodify(dir .. '/' .. val, ':p')->isdirectory()
? {[val]: TreeWalk(dir .. '/' .. val)}
: val
diff --git a/src/version.c b/src/version.c
index 07c784098..764d09d3f 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 */
/**/
+ 2325,
+/**/
2324,
/**/
2323,
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 94f30d439..bcbc57dd1 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -1592,6 +1592,7 @@ generate_BCALL(cctx_T *cctx, int func_idx, int argcount, int method_call)
garray_T *stack = &cctx->ctx_type_stack;
int argoff;
type_T **argtypes = NULL;
+ type_T *maptype = NULL;
RETURN_OK_IF_SKIP(cctx);
argoff = check_internal_func(func_idx, argcount);
@@ -1612,6 +1613,8 @@ generate_BCALL(cctx_T *cctx, int func_idx, int argcount, int method_call)
argtypes = ((type_T **)stack->ga_data) + stack->ga_len - argcount;
if (internal_func_check_arg_types(argtypes, func_idx, argcount) == FAIL)
return FAIL;
+ if (internal_func_is_map(func_idx))
+ maptype = *argtypes;
}
if ((isn = generate_instr(cctx, ISN_BCALL)) == NULL)
@@ -1627,6 +1630,11 @@ generate_BCALL(cctx_T *cctx, int func_idx, int argcount, int method_call)
internal_func_ret_type(func_idx, argcount, argtypes);
++stack->ga_len;
+ if (maptype != NULL && maptype->tt_member != NULL
+ && maptype->tt_member != &t_any)
+ // Check that map() didn't change the item types.
+ generate_TYPECHECK(cctx, maptype, -1);
+
return OK;
}