diff options
author | Bram Moolenaar <Bram@vim.org> | 2021-01-10 22:42:50 +0100 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2021-01-10 22:42:50 +0100 |
commit | 75ab91ff3403e725a79ac9c7351b78e9aff71d67 (patch) | |
tree | 624dae1f8aaeff2cfcd5aea7ac6aad5573835072 | |
parent | 6f02b00bb0958f70bc15534e115b4c6dadff0e06 (diff) | |
download | vim-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.c | 9 | ||||
-rw-r--r-- | src/list.c | 11 | ||||
-rw-r--r-- | src/proto/evalfunc.pro | 1 | ||||
-rw-r--r-- | src/testdir/test_vim9_builtin.vim | 13 | ||||
-rw-r--r-- | src/testdir/test_vim9_expr.vim | 18 | ||||
-rw-r--r-- | src/testdir/test_vim9_func.vim | 6 | ||||
-rw-r--r-- | src/version.c | 2 | ||||
-rw-r--r-- | src/vim9compile.c | 8 |
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; } |