Skip to content

Commit 035bd1c

Browse files
committed
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)
1 parent f1e7449 commit 035bd1c

7 files changed

Lines changed: 96 additions & 20 deletions

File tree

src/ex_docmd.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3485,6 +3485,8 @@ find_ex_command(
34853485
// can't be an assignment.
34863486
if (*eap->cmd == '[')
34873487
{
3488+
char_u *eq;
3489+
34883490
p = to_name_const_end(eap->cmd);
34893491
if (p == eap->cmd && *p == '[')
34903492
{
@@ -3493,12 +3495,19 @@ find_ex_command(
34933495

34943496
p = skip_var_list(eap->cmd, TRUE, &count, &semicolon, TRUE);
34953497
}
3496-
if (p == NULL || p == eap->cmd || *skipwhite(p) != '=')
3498+
eq = p;
3499+
if (eq != NULL)
3500+
{
3501+
eq = skipwhite(eq);
3502+
if (vim_strchr((char_u *)"+-*/%", *eq) != NULL)
3503+
++eq;
3504+
}
3505+
if (p == NULL || p == eap->cmd || *eq != '=')
34973506
{
34983507
eap->cmdidx = CMD_eval;
34993508
return eap->cmd;
35003509
}
3501-
if (p > eap->cmd && *skipwhite(p) == '=')
3510+
if (p > eap->cmd && *eq == '=')
35023511
{
35033512
eap->cmdidx = CMD_var;
35043513
return eap->cmd;

src/testdir/test_vim9_assign.vim

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,29 @@ def Test_assign_unpack()
283283
[v1, v2; _] = [1, 2, 3, 4, 5]
284284
assert_equal(1, v1)
285285
assert_equal(2, v2)
286+
287+
var a = 1
288+
var b = 3
289+
[a, b] += [2, 4]
290+
assert_equal(3, a)
291+
assert_equal(7, b)
292+
293+
[a, b] -= [1, 2]
294+
assert_equal(2, a)
295+
assert_equal(5, b)
296+
297+
[a, b] *= [3, 2]
298+
assert_equal(6, a)
299+
assert_equal(10, b)
300+
301+
[a, b] /= [2, 4]
302+
assert_equal(3, a)
303+
assert_equal(2, b)
304+
305+
[a, b] = [17, 15]
306+
[a, b] %= [5, 3]
307+
assert_equal(2, a)
308+
assert_equal(0, b)
286309
END
287310
CheckDefAndScriptSuccess(lines)
288311

src/testdir/test_vim9_disassemble.vim

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,37 @@ def Test_disassemble_list_assign()
452452
res)
453453
enddef
454454

455+
def s:ListAssignWithOp()
456+
var a = 2
457+
var b = 3
458+
[a, b] += [4, 5]
459+
enddef
460+
461+
def Test_disassemble_list_assign_with_op()
462+
var res = execute('disass s:ListAssignWithOp')
463+
assert_match('<SNR>\d*_ListAssignWithOp\_s*' ..
464+
'var a = 2\_s*' ..
465+
'\d STORE 2 in $0\_s*' ..
466+
'var b = 3\_s*' ..
467+
'\d STORE 3 in $1\_s*' ..
468+
'\[a, b\] += \[4, 5\]\_s*' ..
469+
'\d\+ PUSHNR 4\_s*' ..
470+
'\d\+ PUSHNR 5\_s*' ..
471+
'\d\+ NEWLIST size 2\_s*' ..
472+
'\d\+ CHECKLEN 2\_s*' ..
473+
'\d\+ LOAD $0\_s*' ..
474+
'\d\+ ITEM 0 with op\_s*' ..
475+
'\d\+ OPNR +\_s*' ..
476+
'\d\+ STORE $0\_s*' ..
477+
'\d\+ LOAD $1\_s*' ..
478+
'\d\+ ITEM 1 with op\_s*' ..
479+
'\d\+ OPNR +\_s*' ..
480+
'\d\+ STORE $1\_s*' ..
481+
'\d\+ DROP\_s*' ..
482+
'\d\+ RETURN void',
483+
res)
484+
enddef
485+
455486
def s:ListAdd()
456487
var l: list<number> = []
457488
add(l, 123)

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,8 @@ static char *(features[]) =
755755

756756
static int included_patches[] =
757757
{ /* Add new patch number below this line */
758+
/**/
759+
3029,
758760
/**/
759761
3028,
760762
/**/

src/vim9.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,12 @@ typedef struct {
209209
int cuf_argcount; // number of arguments on top of stack
210210
} cufunc_T;
211211

212+
// arguments to ISN_GETITEM
213+
typedef struct {
214+
varnumber_T gi_index;
215+
int gi_with_op;
216+
} getitem_T;
217+
212218
typedef enum {
213219
JUMP_ALWAYS,
214220
JUMP_IF_FALSE, // pop and jump if false
@@ -432,6 +438,7 @@ struct isn_S {
432438
isn_T *instr;
433439
tostring_T tostring;
434440
tobool_T tobool;
441+
getitem_T getitem;
435442
} isn_arg;
436443
};
437444

src/vim9compile.c

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1240,13 +1240,16 @@ generate_PUSHFUNC(cctx_T *cctx, char_u *name, type_T *type)
12401240

12411241
/*
12421242
* Generate an ISN_GETITEM instruction with "index".
1243+
* "with_op" is TRUE for "+=" and other operators, the stack has the current
1244+
* value below the list with values.
12431245
*/
12441246
static int
1245-
generate_GETITEM(cctx_T *cctx, int index)
1247+
generate_GETITEM(cctx_T *cctx, int index, int with_op)
12461248
{
12471249
isn_T *isn;
12481250
garray_T *stack = &cctx->ctx_type_stack;
1249-
type_T *type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
1251+
type_T *type = ((type_T **)stack->ga_data)[stack->ga_len
1252+
- (with_op ? 2 : 1)];
12501253
type_T *item_type = &t_any;
12511254

12521255
RETURN_OK_IF_SKIP(cctx);
@@ -1260,7 +1263,8 @@ generate_GETITEM(cctx_T *cctx, int index)
12601263
item_type = type->tt_member;
12611264
if ((isn = generate_instr(cctx, ISN_GETITEM)) == NULL)
12621265
return FAIL;
1263-
isn->isn_arg.number = index;
1266+
isn->isn_arg.getitem.gi_index = index;
1267+
isn->isn_arg.getitem.gi_with_op = with_op;
12641268

12651269
// add the item type to the type stack
12661270
if (ga_grow(stack, 1) == FAIL)
@@ -6746,19 +6750,17 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
67466750
int is_const = FALSE;
67476751
char_u *wp;
67486752

6753+
// for "+=", "*=", "..=" etc. first load the current value
6754+
if (*op != '='
6755+
&& compile_load_lhs_with_index(&lhs, var_start,
6756+
cctx) == FAIL)
6757+
goto theend;
6758+
67496759
// For "var = expr" evaluate the expression.
67506760
if (var_count == 0)
67516761
{
67526762
int r;
67536763

6754-
// for "+=", "*=", "..=" etc. first load the current value
6755-
if (*op != '=')
6756-
{
6757-
if (compile_load_lhs_with_index(&lhs, var_start,
6758-
cctx) == FAIL)
6759-
goto theend;
6760-
}
6761-
67626764
// Compile the expression.
67636765
instr_count = instr->ga_len;
67646766
if (incdec)
@@ -6795,7 +6797,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
67956797
{
67966798
// For "[var, var] = expr" get the "var_idx" item from the
67976799
// list.
6798-
if (generate_GETITEM(cctx, var_idx) == FAIL)
6800+
if (generate_GETITEM(cctx, var_idx, *op != '=') == FAIL)
67996801
goto theend;
68006802
}
68016803

src/vim9execute.c

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3832,12 +3832,12 @@ exec_instructions(ectx_T *ectx)
38323832
case ISN_GETITEM:
38333833
{
38343834
listitem_T *li;
3835-
int index = iptr->isn_arg.number;
3835+
getitem_T *gi = &iptr->isn_arg.getitem;
38363836

38373837
// Get list item: list is at stack-1, push item.
38383838
// List type and length is checked for when compiling.
3839-
tv = STACK_TV_BOT(-1);
3840-
li = list_find(tv->vval.v_list, index);
3839+
tv = STACK_TV_BOT(-1 - gi->gi_with_op);
3840+
li = list_find(tv->vval.v_list, gi->gi_index);
38413841

38423842
if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
38433843
goto theend;
@@ -3846,7 +3846,7 @@ exec_instructions(ectx_T *ectx)
38463846

38473847
// Useful when used in unpack assignment. Reset at
38483848
// ISN_DROP.
3849-
ectx->ec_where.wt_index = index + 1;
3849+
ectx->ec_where.wt_index = gi->gi_index + 1;
38503850
ectx->ec_where.wt_variable = TRUE;
38513851
}
38523852
break;
@@ -5376,8 +5376,10 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
53765376
case ISN_ANYSLICE: smsg("%s%4d ANYSLICE", pfx, current); break;
53775377
case ISN_SLICE: smsg("%s%4d SLICE %lld",
53785378
pfx, current, iptr->isn_arg.number); break;
5379-
case ISN_GETITEM: smsg("%s%4d ITEM %lld",
5380-
pfx, current, iptr->isn_arg.number); break;
5379+
case ISN_GETITEM: smsg("%s%4d ITEM %lld%s", pfx, current,
5380+
iptr->isn_arg.getitem.gi_index,
5381+
iptr->isn_arg.getitem.gi_with_op ?
5382+
" with op" : ""); break;
53815383
case ISN_MEMBER: smsg("%s%4d MEMBER", pfx, current); break;
53825384
case ISN_STRINGMEMBER: smsg("%s%4d MEMBER %s", pfx, current,
53835385
iptr->isn_arg.string); break;

0 commit comments

Comments
 (0)