forked from aniani/vim
patch 8.2.3029: Vim9: crash when using operator and list unpack assignment
Problem: Vim9: crash when using operator and list unpack assignment. (Naohiro Ono) Solution: Get variable value before operation. (closes #8416)
This commit is contained in:
@@ -3485,6 +3485,8 @@ find_ex_command(
|
|||||||
// can't be an assignment.
|
// can't be an assignment.
|
||||||
if (*eap->cmd == '[')
|
if (*eap->cmd == '[')
|
||||||
{
|
{
|
||||||
|
char_u *eq;
|
||||||
|
|
||||||
p = to_name_const_end(eap->cmd);
|
p = to_name_const_end(eap->cmd);
|
||||||
if (p == eap->cmd && *p == '[')
|
if (p == eap->cmd && *p == '[')
|
||||||
{
|
{
|
||||||
@@ -3493,12 +3495,19 @@ find_ex_command(
|
|||||||
|
|
||||||
p = skip_var_list(eap->cmd, TRUE, &count, &semicolon, TRUE);
|
p = skip_var_list(eap->cmd, TRUE, &count, &semicolon, TRUE);
|
||||||
}
|
}
|
||||||
if (p == NULL || p == eap->cmd || *skipwhite(p) != '=')
|
eq = p;
|
||||||
|
if (eq != NULL)
|
||||||
|
{
|
||||||
|
eq = skipwhite(eq);
|
||||||
|
if (vim_strchr((char_u *)"+-*/%", *eq) != NULL)
|
||||||
|
++eq;
|
||||||
|
}
|
||||||
|
if (p == NULL || p == eap->cmd || *eq != '=')
|
||||||
{
|
{
|
||||||
eap->cmdidx = CMD_eval;
|
eap->cmdidx = CMD_eval;
|
||||||
return eap->cmd;
|
return eap->cmd;
|
||||||
}
|
}
|
||||||
if (p > eap->cmd && *skipwhite(p) == '=')
|
if (p > eap->cmd && *eq == '=')
|
||||||
{
|
{
|
||||||
eap->cmdidx = CMD_var;
|
eap->cmdidx = CMD_var;
|
||||||
return eap->cmd;
|
return eap->cmd;
|
||||||
|
@@ -283,6 +283,29 @@ def Test_assign_unpack()
|
|||||||
[v1, v2; _] = [1, 2, 3, 4, 5]
|
[v1, v2; _] = [1, 2, 3, 4, 5]
|
||||||
assert_equal(1, v1)
|
assert_equal(1, v1)
|
||||||
assert_equal(2, v2)
|
assert_equal(2, v2)
|
||||||
|
|
||||||
|
var a = 1
|
||||||
|
var b = 3
|
||||||
|
[a, b] += [2, 4]
|
||||||
|
assert_equal(3, a)
|
||||||
|
assert_equal(7, b)
|
||||||
|
|
||||||
|
[a, b] -= [1, 2]
|
||||||
|
assert_equal(2, a)
|
||||||
|
assert_equal(5, b)
|
||||||
|
|
||||||
|
[a, b] *= [3, 2]
|
||||||
|
assert_equal(6, a)
|
||||||
|
assert_equal(10, b)
|
||||||
|
|
||||||
|
[a, b] /= [2, 4]
|
||||||
|
assert_equal(3, a)
|
||||||
|
assert_equal(2, b)
|
||||||
|
|
||||||
|
[a, b] = [17, 15]
|
||||||
|
[a, b] %= [5, 3]
|
||||||
|
assert_equal(2, a)
|
||||||
|
assert_equal(0, b)
|
||||||
END
|
END
|
||||||
CheckDefAndScriptSuccess(lines)
|
CheckDefAndScriptSuccess(lines)
|
||||||
|
|
||||||
|
@@ -452,6 +452,37 @@ def Test_disassemble_list_assign()
|
|||||||
res)
|
res)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
def s:ListAssignWithOp()
|
||||||
|
var a = 2
|
||||||
|
var b = 3
|
||||||
|
[a, b] += [4, 5]
|
||||||
|
enddef
|
||||||
|
|
||||||
|
def Test_disassemble_list_assign_with_op()
|
||||||
|
var res = execute('disass s:ListAssignWithOp')
|
||||||
|
assert_match('<SNR>\d*_ListAssignWithOp\_s*' ..
|
||||||
|
'var a = 2\_s*' ..
|
||||||
|
'\d STORE 2 in $0\_s*' ..
|
||||||
|
'var b = 3\_s*' ..
|
||||||
|
'\d STORE 3 in $1\_s*' ..
|
||||||
|
'\[a, b\] += \[4, 5\]\_s*' ..
|
||||||
|
'\d\+ PUSHNR 4\_s*' ..
|
||||||
|
'\d\+ PUSHNR 5\_s*' ..
|
||||||
|
'\d\+ NEWLIST size 2\_s*' ..
|
||||||
|
'\d\+ CHECKLEN 2\_s*' ..
|
||||||
|
'\d\+ LOAD $0\_s*' ..
|
||||||
|
'\d\+ ITEM 0 with op\_s*' ..
|
||||||
|
'\d\+ OPNR +\_s*' ..
|
||||||
|
'\d\+ STORE $0\_s*' ..
|
||||||
|
'\d\+ LOAD $1\_s*' ..
|
||||||
|
'\d\+ ITEM 1 with op\_s*' ..
|
||||||
|
'\d\+ OPNR +\_s*' ..
|
||||||
|
'\d\+ STORE $1\_s*' ..
|
||||||
|
'\d\+ DROP\_s*' ..
|
||||||
|
'\d\+ RETURN void',
|
||||||
|
res)
|
||||||
|
enddef
|
||||||
|
|
||||||
def s:ListAdd()
|
def s:ListAdd()
|
||||||
var l: list<number> = []
|
var l: list<number> = []
|
||||||
add(l, 123)
|
add(l, 123)
|
||||||
|
@@ -755,6 +755,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 */
|
||||||
|
/**/
|
||||||
|
3029,
|
||||||
/**/
|
/**/
|
||||||
3028,
|
3028,
|
||||||
/**/
|
/**/
|
||||||
|
@@ -209,6 +209,12 @@ typedef struct {
|
|||||||
int cuf_argcount; // number of arguments on top of stack
|
int cuf_argcount; // number of arguments on top of stack
|
||||||
} cufunc_T;
|
} cufunc_T;
|
||||||
|
|
||||||
|
// arguments to ISN_GETITEM
|
||||||
|
typedef struct {
|
||||||
|
varnumber_T gi_index;
|
||||||
|
int gi_with_op;
|
||||||
|
} getitem_T;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
JUMP_ALWAYS,
|
JUMP_ALWAYS,
|
||||||
JUMP_IF_FALSE, // pop and jump if false
|
JUMP_IF_FALSE, // pop and jump if false
|
||||||
@@ -432,6 +438,7 @@ struct isn_S {
|
|||||||
isn_T *instr;
|
isn_T *instr;
|
||||||
tostring_T tostring;
|
tostring_T tostring;
|
||||||
tobool_T tobool;
|
tobool_T tobool;
|
||||||
|
getitem_T getitem;
|
||||||
} isn_arg;
|
} isn_arg;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1240,13 +1240,16 @@ generate_PUSHFUNC(cctx_T *cctx, char_u *name, type_T *type)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Generate an ISN_GETITEM instruction with "index".
|
* Generate an ISN_GETITEM instruction with "index".
|
||||||
|
* "with_op" is TRUE for "+=" and other operators, the stack has the current
|
||||||
|
* value below the list with values.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
generate_GETITEM(cctx_T *cctx, int index)
|
generate_GETITEM(cctx_T *cctx, int index, int with_op)
|
||||||
{
|
{
|
||||||
isn_T *isn;
|
isn_T *isn;
|
||||||
garray_T *stack = &cctx->ctx_type_stack;
|
garray_T *stack = &cctx->ctx_type_stack;
|
||||||
type_T *type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
|
type_T *type = ((type_T **)stack->ga_data)[stack->ga_len
|
||||||
|
- (with_op ? 2 : 1)];
|
||||||
type_T *item_type = &t_any;
|
type_T *item_type = &t_any;
|
||||||
|
|
||||||
RETURN_OK_IF_SKIP(cctx);
|
RETURN_OK_IF_SKIP(cctx);
|
||||||
@@ -1260,7 +1263,8 @@ generate_GETITEM(cctx_T *cctx, int index)
|
|||||||
item_type = type->tt_member;
|
item_type = type->tt_member;
|
||||||
if ((isn = generate_instr(cctx, ISN_GETITEM)) == NULL)
|
if ((isn = generate_instr(cctx, ISN_GETITEM)) == NULL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
isn->isn_arg.number = index;
|
isn->isn_arg.getitem.gi_index = index;
|
||||||
|
isn->isn_arg.getitem.gi_with_op = with_op;
|
||||||
|
|
||||||
// add the item type to the type stack
|
// add the item type to the type stack
|
||||||
if (ga_grow(stack, 1) == FAIL)
|
if (ga_grow(stack, 1) == FAIL)
|
||||||
@@ -6746,19 +6750,17 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
|
|||||||
int is_const = FALSE;
|
int is_const = FALSE;
|
||||||
char_u *wp;
|
char_u *wp;
|
||||||
|
|
||||||
|
// for "+=", "*=", "..=" etc. first load the current value
|
||||||
|
if (*op != '='
|
||||||
|
&& compile_load_lhs_with_index(&lhs, var_start,
|
||||||
|
cctx) == FAIL)
|
||||||
|
goto theend;
|
||||||
|
|
||||||
// For "var = expr" evaluate the expression.
|
// For "var = expr" evaluate the expression.
|
||||||
if (var_count == 0)
|
if (var_count == 0)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
// for "+=", "*=", "..=" etc. first load the current value
|
|
||||||
if (*op != '=')
|
|
||||||
{
|
|
||||||
if (compile_load_lhs_with_index(&lhs, var_start,
|
|
||||||
cctx) == FAIL)
|
|
||||||
goto theend;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compile the expression.
|
// Compile the expression.
|
||||||
instr_count = instr->ga_len;
|
instr_count = instr->ga_len;
|
||||||
if (incdec)
|
if (incdec)
|
||||||
@@ -6795,7 +6797,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
|
|||||||
{
|
{
|
||||||
// For "[var, var] = expr" get the "var_idx" item from the
|
// For "[var, var] = expr" get the "var_idx" item from the
|
||||||
// list.
|
// list.
|
||||||
if (generate_GETITEM(cctx, var_idx) == FAIL)
|
if (generate_GETITEM(cctx, var_idx, *op != '=') == FAIL)
|
||||||
goto theend;
|
goto theend;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -3832,12 +3832,12 @@ exec_instructions(ectx_T *ectx)
|
|||||||
case ISN_GETITEM:
|
case ISN_GETITEM:
|
||||||
{
|
{
|
||||||
listitem_T *li;
|
listitem_T *li;
|
||||||
int index = iptr->isn_arg.number;
|
getitem_T *gi = &iptr->isn_arg.getitem;
|
||||||
|
|
||||||
// Get list item: list is at stack-1, push item.
|
// Get list item: list is at stack-1, push item.
|
||||||
// List type and length is checked for when compiling.
|
// List type and length is checked for when compiling.
|
||||||
tv = STACK_TV_BOT(-1);
|
tv = STACK_TV_BOT(-1 - gi->gi_with_op);
|
||||||
li = list_find(tv->vval.v_list, index);
|
li = list_find(tv->vval.v_list, gi->gi_index);
|
||||||
|
|
||||||
if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
|
if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
|
||||||
goto theend;
|
goto theend;
|
||||||
@@ -3846,7 +3846,7 @@ exec_instructions(ectx_T *ectx)
|
|||||||
|
|
||||||
// Useful when used in unpack assignment. Reset at
|
// Useful when used in unpack assignment. Reset at
|
||||||
// ISN_DROP.
|
// ISN_DROP.
|
||||||
ectx->ec_where.wt_index = index + 1;
|
ectx->ec_where.wt_index = gi->gi_index + 1;
|
||||||
ectx->ec_where.wt_variable = TRUE;
|
ectx->ec_where.wt_variable = TRUE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -5376,8 +5376,10 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
|
|||||||
case ISN_ANYSLICE: smsg("%s%4d ANYSLICE", pfx, current); break;
|
case ISN_ANYSLICE: smsg("%s%4d ANYSLICE", pfx, current); break;
|
||||||
case ISN_SLICE: smsg("%s%4d SLICE %lld",
|
case ISN_SLICE: smsg("%s%4d SLICE %lld",
|
||||||
pfx, current, iptr->isn_arg.number); break;
|
pfx, current, iptr->isn_arg.number); break;
|
||||||
case ISN_GETITEM: smsg("%s%4d ITEM %lld",
|
case ISN_GETITEM: smsg("%s%4d ITEM %lld%s", pfx, current,
|
||||||
pfx, current, iptr->isn_arg.number); break;
|
iptr->isn_arg.getitem.gi_index,
|
||||||
|
iptr->isn_arg.getitem.gi_with_op ?
|
||||||
|
" with op" : ""); break;
|
||||||
case ISN_MEMBER: smsg("%s%4d MEMBER", pfx, current); break;
|
case ISN_MEMBER: smsg("%s%4d MEMBER", pfx, current); break;
|
||||||
case ISN_STRINGMEMBER: smsg("%s%4d MEMBER %s", pfx, current,
|
case ISN_STRINGMEMBER: smsg("%s%4d MEMBER %s", pfx, current,
|
||||||
iptr->isn_arg.string); break;
|
iptr->isn_arg.string); break;
|
||||||
|
Reference in New Issue
Block a user