forked from aniani/vim
patch 8.2.2325: Vim9: crash if map() changes the item type
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.
This commit is contained in:
@@ -1929,6 +1929,15 @@ internal_func_ret_type(int idx, int argcount, type_T **argtypes)
|
|||||||
return global_functions[idx].f_retfunc(argcount, argtypes);
|
return global_functions[idx].f_retfunc(argcount, 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".
|
* 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
|
* Returns -1 for failure, 0 if no method base accepted, 1 if method base is
|
||||||
|
11
src/list.c
11
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;
|
int stride = l->lv_u.nonmat.lv_stride;
|
||||||
|
|
||||||
// List from range(): loop over the numbers
|
// List from range(): loop over the numbers
|
||||||
l->lv_first = NULL;
|
if (filtermap != FILTERMAP_MAPNEW)
|
||||||
l->lv_u.mat.lv_last = NULL;
|
{
|
||||||
l->lv_len = 0;
|
l->lv_first = NULL;
|
||||||
l->lv_u.mat.lv_idx_item = 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)
|
for (idx = 0; idx < len; ++idx)
|
||||||
{
|
{
|
||||||
|
@@ -6,6 +6,7 @@ int has_internal_func(char_u *name);
|
|||||||
char *internal_func_name(int idx);
|
char *internal_func_name(int idx);
|
||||||
int internal_func_check_arg_types(type_T **types, int idx, int argcount);
|
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);
|
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 check_internal_func(int idx, int argcount);
|
||||||
int call_internal_func(char_u *name, int argcount, typval_T *argvars, typval_T *rettv);
|
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);
|
void call_internal_func_by_idx(int idx, typval_T *argvars, typval_T *rettv);
|
||||||
|
@@ -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))
|
assert_equal({a: 1, b: 2}, extend({a: 1, b: 2}, {b: 4}, s:string_keep))
|
||||||
|
|
||||||
var res: list<dict<any>>
|
var res: list<dict<any>>
|
||||||
extend(res, map([1, 2], (_, v) => ({})))
|
extend(res, mapnew([1, 2], (_, v) => ({})))
|
||||||
assert_equal([{}, {}], res)
|
assert_equal([{}, {}], res)
|
||||||
|
|
||||||
CheckDefFailure(['extend([1, 2], 3)'], 'E1013: Argument 2: type mismatch, expected list<number> but got number')
|
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)
|
CheckDefAndScriptSuccess(lines)
|
||||||
enddef
|
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()
|
def Test_filereadable()
|
||||||
assert_false(filereadable(""))
|
assert_false(filereadable(""))
|
||||||
assert_false(filereadable(test_null_string()))
|
assert_false(filereadable(test_null_string()))
|
||||||
@@ -728,7 +737,7 @@ enddef
|
|||||||
|
|
||||||
def Test_submatch()
|
def Test_submatch()
|
||||||
var pat = 'A\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)'
|
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 actual = substitute('A123456789', pat, Rep, '')
|
||||||
var expected = "[['A123456789'], ['1'], ['2'], ['3'], ['4'], ['5'], ['6'], ['7'], ['8'], ['9']]"
|
var expected = "[['A123456789'], ['1'], ['2'], ['3'], ['4'], ['5'], ['6'], ['7'], ['8'], ['9']]"
|
||||||
actual->assert_equal(expected)
|
actual->assert_equal(expected)
|
||||||
|
@@ -1859,10 +1859,10 @@ def Test_expr7_lambda()
|
|||||||
|
|
||||||
# line continuation inside lambda with "cond ? expr : expr" works
|
# line continuation inside lambda with "cond ? expr : expr" works
|
||||||
var ll = range(3)
|
var ll = range(3)
|
||||||
map(ll, (k, v) => v % 2 ? {
|
var dll = mapnew(ll, (k, v) => v % 2 ? {
|
||||||
['111']: 111 } : {}
|
['111']: 111 } : {}
|
||||||
)
|
)
|
||||||
assert_equal([{}, {111: 111}, {}], ll)
|
assert_equal([{}, {111: 111}, {}], dll)
|
||||||
|
|
||||||
ll = range(3)
|
ll = range(3)
|
||||||
map(ll, (k, v) => v == 8 || v
|
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
|
# line continuation inside lambda with "cond ? expr : expr" works
|
||||||
var ll = range(3)
|
var ll = range(3)
|
||||||
map(ll, (k, v) => v % 2 ? {
|
var dll = mapnew(ll, (k, v) => v % 2 ? {
|
||||||
['111']: 111 } : {}
|
['111']: 111 } : {}
|
||||||
)
|
)
|
||||||
assert_equal([{}, {111: 111}, {}], ll)
|
assert_equal([{}, {111: 111}, {}], dll)
|
||||||
|
|
||||||
ll = range(3)
|
ll = range(3)
|
||||||
map(ll, (k, v) => v == 8 || v
|
map(ll, (k, v) => v == 8 || v
|
||||||
@@ -2964,25 +2964,25 @@ def Test_expr7_subscript_linebreak()
|
|||||||
var range = range(
|
var range = range(
|
||||||
3)
|
3)
|
||||||
var l = range
|
var l = range
|
||||||
->map('string(v:key)')
|
->mapnew('string(v:key)')
|
||||||
assert_equal(['0', '1', '2'], l)
|
assert_equal(['0', '1', '2'], l)
|
||||||
|
|
||||||
l = range
|
l = range
|
||||||
->map('string(v:key)')
|
->mapnew('string(v:key)')
|
||||||
assert_equal(['0', '1', '2'], l)
|
assert_equal(['0', '1', '2'], l)
|
||||||
|
|
||||||
l = range # comment
|
l = range # comment
|
||||||
->map('string(v:key)')
|
->mapnew('string(v:key)')
|
||||||
assert_equal(['0', '1', '2'], l)
|
assert_equal(['0', '1', '2'], l)
|
||||||
|
|
||||||
l = range
|
l = range
|
||||||
|
|
||||||
->map('string(v:key)')
|
->mapnew('string(v:key)')
|
||||||
assert_equal(['0', '1', '2'], l)
|
assert_equal(['0', '1', '2'], l)
|
||||||
|
|
||||||
l = range
|
l = range
|
||||||
# comment
|
# comment
|
||||||
->map('string(v:key)')
|
->mapnew('string(v:key)')
|
||||||
assert_equal(['0', '1', '2'], l)
|
assert_equal(['0', '1', '2'], l)
|
||||||
|
|
||||||
assert_equal('1', l[
|
assert_equal('1', l[
|
||||||
|
@@ -1763,7 +1763,7 @@ enddef
|
|||||||
|
|
||||||
def Shadowed(): list<number>
|
def Shadowed(): list<number>
|
||||||
var FuncList: list<func: number> = [() => 42]
|
var FuncList: list<func: number> = [() => 42]
|
||||||
return FuncList->map((_, Shadowed) => Shadowed())
|
return FuncList->mapnew((_, Shadowed) => Shadowed())
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
def Test_lambda_arg_shadows_func()
|
def Test_lambda_arg_shadows_func()
|
||||||
@@ -1792,7 +1792,7 @@ enddef
|
|||||||
|
|
||||||
def Line_continuation_in_lambda(): list<string>
|
def Line_continuation_in_lambda(): list<string>
|
||||||
var x = range(97, 100)
|
var x = range(97, 100)
|
||||||
->map((_, v) => nr2char(v)
|
->mapnew((_, v) => nr2char(v)
|
||||||
->toupper())
|
->toupper())
|
||||||
->reverse()
|
->reverse()
|
||||||
return x
|
return x
|
||||||
@@ -1908,7 +1908,7 @@ def Test_recursive_call()
|
|||||||
enddef
|
enddef
|
||||||
|
|
||||||
def TreeWalk(dir: string): list<any>
|
def TreeWalk(dir: string): list<any>
|
||||||
return readdir(dir)->map((_, val) =>
|
return readdir(dir)->mapnew((_, val) =>
|
||||||
fnamemodify(dir .. '/' .. val, ':p')->isdirectory()
|
fnamemodify(dir .. '/' .. val, ':p')->isdirectory()
|
||||||
? {[val]: TreeWalk(dir .. '/' .. val)}
|
? {[val]: TreeWalk(dir .. '/' .. val)}
|
||||||
: val
|
: val
|
||||||
|
@@ -750,6 +750,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
2325,
|
||||||
/**/
|
/**/
|
||||||
2324,
|
2324,
|
||||||
/**/
|
/**/
|
||||||
|
@@ -1592,6 +1592,7 @@ generate_BCALL(cctx_T *cctx, int func_idx, int argcount, int method_call)
|
|||||||
garray_T *stack = &cctx->ctx_type_stack;
|
garray_T *stack = &cctx->ctx_type_stack;
|
||||||
int argoff;
|
int argoff;
|
||||||
type_T **argtypes = NULL;
|
type_T **argtypes = NULL;
|
||||||
|
type_T *maptype = NULL;
|
||||||
|
|
||||||
RETURN_OK_IF_SKIP(cctx);
|
RETURN_OK_IF_SKIP(cctx);
|
||||||
argoff = check_internal_func(func_idx, argcount);
|
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;
|
argtypes = ((type_T **)stack->ga_data) + stack->ga_len - argcount;
|
||||||
if (internal_func_check_arg_types(argtypes, func_idx, argcount) == FAIL)
|
if (internal_func_check_arg_types(argtypes, func_idx, argcount) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
if (internal_func_is_map(func_idx))
|
||||||
|
maptype = *argtypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((isn = generate_instr(cctx, ISN_BCALL)) == NULL)
|
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);
|
internal_func_ret_type(func_idx, argcount, argtypes);
|
||||||
++stack->ga_len;
|
++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;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user