To: vim_dev@googlegroups.com Subject: Patch 8.2.2325 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.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. Files: src/evalfunc.c, src/proto/evalfunc.pro, src/vim9compile.c, src/list.c, src/testdir/test_vim9_builtin.vim, src/testdir/test_vim9_expr.vim, src/testdir/test_vim9_func.vim *** ../vim-8.2.2324/src/evalfunc.c 2021-01-10 20:22:20.763926913 +0100 --- src/evalfunc.c 2021-01-10 21:52:46.893315360 +0100 *************** *** 1930,1935 **** --- 1930,1944 ---- } /* + * 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 *** ../vim-8.2.2324/src/proto/evalfunc.pro 2020-11-08 12:49:43.120372854 +0100 --- src/proto/evalfunc.pro 2021-01-10 21:52:50.661305593 +0100 *************** *** 6,11 **** --- 6,12 ---- 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); *** ../vim-8.2.2324/src/vim9compile.c 2021-01-10 19:23:23.352958354 +0100 --- src/vim9compile.c 2021-01-10 21:53:21.017226887 +0100 *************** *** 1592,1597 **** --- 1592,1598 ---- 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,1617 **** --- 1613,1620 ---- 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,1632 **** --- 1630,1640 ---- 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; } *** ../vim-8.2.2324/src/list.c 2021-01-09 13:20:32.200472552 +0100 --- src/list.c 2021-01-10 22:33:58.382050386 +0100 *************** *** 2188,2197 **** 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; for (idx = 0; idx < len; ++idx) { --- 2188,2200 ---- int stride = l->lv_u.nonmat.lv_stride; // List from range(): loop over the numbers ! 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) { *** ../vim-8.2.2324/src/testdir/test_vim9_builtin.vim 2021-01-07 20:23:29.842515296 +0100 --- src/testdir/test_vim9_builtin.vim 2021-01-10 22:01:52.191433608 +0100 *************** *** 231,237 **** assert_equal({a: 1, b: 2}, extend({a: 1, b: 2}, {b: 4}, s:string_keep)) var res: list> ! extend(res, map([1, 2], (_, v) => ({}))) assert_equal([{}, {}], res) CheckDefFailure(['extend([1, 2], 3)'], 'E1013: Argument 2: type mismatch, expected list but got number') --- 231,237 ---- assert_equal({a: 1, b: 2}, extend({a: 1, b: 2}, {b: 4}, s:string_keep)) var res: list> ! extend(res, mapnew([1, 2], (_, v) => ({}))) assert_equal([{}, {}], res) CheckDefFailure(['extend([1, 2], 3)'], 'E1013: Argument 2: type mismatch, expected list but got number') *************** *** 320,325 **** --- 320,334 ---- 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,734 **** def Test_submatch() var pat = 'A\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)' ! var Rep = () => range(10)->map((_, 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) --- 737,743 ---- def Test_submatch() var pat = 'A\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)' ! 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) *** ../vim-8.2.2324/src/testdir/test_vim9_expr.vim 2021-01-10 19:23:23.352958354 +0100 --- src/testdir/test_vim9_expr.vim 2021-01-10 22:42:25.212899527 +0100 *************** *** 1859,1868 **** # line continuation inside lambda with "cond ? expr : expr" works var ll = range(3) ! map(ll, (k, v) => v % 2 ? { ['111']: 111 } : {} ) ! assert_equal([{}, {111: 111}, {}], ll) ll = range(3) map(ll, (k, v) => v == 8 || v --- 1859,1868 ---- # line continuation inside lambda with "cond ? expr : expr" works var ll = range(3) ! var dll = mapnew(ll, (k, v) => v % 2 ? { ['111']: 111 } : {} ) ! assert_equal([{}, {111: 111}, {}], dll) ll = range(3) map(ll, (k, v) => v == 8 || v *************** *** 1946,1955 **** # line continuation inside lambda with "cond ? expr : expr" works var ll = range(3) ! map(ll, (k, v) => v % 2 ? { ['111']: 111 } : {} ) ! assert_equal([{}, {111: 111}, {}], ll) ll = range(3) map(ll, (k, v) => v == 8 || v --- 1946,1955 ---- # line continuation inside lambda with "cond ? expr : expr" works var ll = range(3) ! var dll = mapnew(ll, (k, v) => v % 2 ? { ['111']: 111 } : {} ) ! assert_equal([{}, {111: 111}, {}], dll) ll = range(3) map(ll, (k, v) => v == 8 || v *************** *** 2964,2988 **** var range = range( 3) var l = range ! ->map('string(v:key)') assert_equal(['0', '1', '2'], l) l = range ! ->map('string(v:key)') assert_equal(['0', '1', '2'], l) l = range # comment ! ->map('string(v:key)') assert_equal(['0', '1', '2'], l) l = range ! ->map('string(v:key)') assert_equal(['0', '1', '2'], l) l = range # comment ! ->map('string(v:key)') assert_equal(['0', '1', '2'], l) assert_equal('1', l[ --- 2964,2988 ---- var range = range( 3) var l = range ! ->mapnew('string(v:key)') assert_equal(['0', '1', '2'], l) l = range ! ->mapnew('string(v:key)') assert_equal(['0', '1', '2'], l) l = range # comment ! ->mapnew('string(v:key)') assert_equal(['0', '1', '2'], l) l = range ! ->mapnew('string(v:key)') assert_equal(['0', '1', '2'], l) l = range # comment ! ->mapnew('string(v:key)') assert_equal(['0', '1', '2'], l) assert_equal('1', l[ *** ../vim-8.2.2324/src/testdir/test_vim9_func.vim 2021-01-10 18:33:08.011683523 +0100 --- src/testdir/test_vim9_func.vim 2021-01-10 22:39:30.681313837 +0100 *************** *** 1763,1769 **** def Shadowed(): list var FuncList: list = [() => 42] ! return FuncList->map((_, Shadowed) => Shadowed()) enddef def Test_lambda_arg_shadows_func() --- 1763,1769 ---- def Shadowed(): list var FuncList: list = [() => 42] ! return FuncList->mapnew((_, Shadowed) => Shadowed()) enddef def Test_lambda_arg_shadows_func() *************** *** 1792,1798 **** def Line_continuation_in_lambda(): list var x = range(97, 100) ! ->map((_, v) => nr2char(v) ->toupper()) ->reverse() return x --- 1792,1798 ---- def Line_continuation_in_lambda(): list var x = range(97, 100) ! ->mapnew((_, v) => nr2char(v) ->toupper()) ->reverse() return x *************** *** 1908,1914 **** enddef def TreeWalk(dir: string): list ! return readdir(dir)->map((_, val) => fnamemodify(dir .. '/' .. val, ':p')->isdirectory() ? {[val]: TreeWalk(dir .. '/' .. val)} : val --- 1908,1914 ---- enddef def TreeWalk(dir: string): list ! return readdir(dir)->mapnew((_, val) => fnamemodify(dir .. '/' .. val, ':p')->isdirectory() ? {[val]: TreeWalk(dir .. '/' .. val)} : val *** ../vim-8.2.2324/src/version.c 2021-01-10 20:22:20.767926906 +0100 --- src/version.c 2021-01-10 21:40:02.739257436 +0100 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 2325, /**/ -- hundred-and-one symptoms of being an internet addict: 122. You ask if the Netaholics Anonymous t-shirt you ordered can be sent to you via e-mail. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///