diff options
author | Bram Moolenaar <Bram@vim.org> | 2021-06-21 19:44:11 +0200 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2021-06-21 19:44:11 +0200 |
commit | 035bd1c99f2a8eda5ee886adde4f97ea71fb167f (patch) | |
tree | 8f666e256d60b0d5d490ada1074743ff975fa75a | |
parent | f1e7449d567c630601aa0cec6c663b791785a668 (diff) | |
download | vim-git-035bd1c99f2a8eda5ee886adde4f97ea71fb167f.tar.gz |
patch 8.2.3029: Vim9: crash when using operator and list unpack assignmentv8.2.3029
Problem: Vim9: crash when using operator and list unpack assignment.
(Naohiro Ono)
Solution: Get variable value before operation. (closes #8416)
-rw-r--r-- | src/ex_docmd.c | 13 | ||||
-rw-r--r-- | src/testdir/test_vim9_assign.vim | 23 | ||||
-rw-r--r-- | src/testdir/test_vim9_disassemble.vim | 31 | ||||
-rw-r--r-- | src/version.c | 2 | ||||
-rw-r--r-- | src/vim9.h | 7 | ||||
-rw-r--r-- | src/vim9compile.c | 26 | ||||
-rw-r--r-- | src/vim9execute.c | 14 |
7 files changed, 96 insertions, 20 deletions
diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 2a9983f8a..027d139da 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -3485,6 +3485,8 @@ find_ex_command( // can't be an assignment. if (*eap->cmd == '[') { + char_u *eq; + p = to_name_const_end(eap->cmd); if (p == eap->cmd && *p == '[') { @@ -3493,12 +3495,19 @@ find_ex_command( 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; return eap->cmd; } - if (p > eap->cmd && *skipwhite(p) == '=') + if (p > eap->cmd && *eq == '=') { eap->cmdidx = CMD_var; return eap->cmd; diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim index 1cbdcfb46..1d3c20e9f 100644 --- a/src/testdir/test_vim9_assign.vim +++ b/src/testdir/test_vim9_assign.vim @@ -283,6 +283,29 @@ def Test_assign_unpack() [v1, v2; _] = [1, 2, 3, 4, 5] assert_equal(1, v1) 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 CheckDefAndScriptSuccess(lines) diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim index 9d2f697d8..7938d91f8 100644 --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -452,6 +452,37 @@ def Test_disassemble_list_assign() res) 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() var l: list<number> = [] add(l, 123) diff --git a/src/version.c b/src/version.c index db2433db9..096235fc9 100644 --- a/src/version.c +++ b/src/version.c @@ -756,6 +756,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 3029, +/**/ 3028, /**/ 3027, diff --git a/src/vim9.h b/src/vim9.h index 97e2132a3..58d451cd1 100644 --- a/src/vim9.h +++ b/src/vim9.h @@ -209,6 +209,12 @@ typedef struct { int cuf_argcount; // number of arguments on top of stack } cufunc_T; +// arguments to ISN_GETITEM +typedef struct { + varnumber_T gi_index; + int gi_with_op; +} getitem_T; + typedef enum { JUMP_ALWAYS, JUMP_IF_FALSE, // pop and jump if false @@ -432,6 +438,7 @@ struct isn_S { isn_T *instr; tostring_T tostring; tobool_T tobool; + getitem_T getitem; } isn_arg; }; diff --git a/src/vim9compile.c b/src/vim9compile.c index e4656c99a..c5a2c2dcf 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -1240,13 +1240,16 @@ generate_PUSHFUNC(cctx_T *cctx, char_u *name, type_T *type) /* * 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 -generate_GETITEM(cctx_T *cctx, int index) +generate_GETITEM(cctx_T *cctx, int index, int with_op) { isn_T *isn; 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; RETURN_OK_IF_SKIP(cctx); @@ -1260,7 +1263,8 @@ generate_GETITEM(cctx_T *cctx, int index) item_type = type->tt_member; if ((isn = generate_instr(cctx, ISN_GETITEM)) == NULL) 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 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; 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. if (var_count == 0) { 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. instr_count = instr->ga_len; 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 // list. - if (generate_GETITEM(cctx, var_idx) == FAIL) + if (generate_GETITEM(cctx, var_idx, *op != '=') == FAIL) goto theend; } diff --git a/src/vim9execute.c b/src/vim9execute.c index dfceb27d0..6d0da6179 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -3832,12 +3832,12 @@ exec_instructions(ectx_T *ectx) case ISN_GETITEM: { 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. // List type and length is checked for when compiling. - tv = STACK_TV_BOT(-1); - li = list_find(tv->vval.v_list, index); + tv = STACK_TV_BOT(-1 - gi->gi_with_op); + li = list_find(tv->vval.v_list, gi->gi_index); if (GA_GROW(&ectx->ec_stack, 1) == FAIL) goto theend; @@ -3846,7 +3846,7 @@ exec_instructions(ectx_T *ectx) // Useful when used in unpack assignment. Reset at // ISN_DROP. - ectx->ec_where.wt_index = index + 1; + ectx->ec_where.wt_index = gi->gi_index + 1; ectx->ec_where.wt_variable = TRUE; } 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_SLICE: smsg("%s%4d SLICE %lld", pfx, current, iptr->isn_arg.number); break; - case ISN_GETITEM: smsg("%s%4d ITEM %lld", - pfx, current, iptr->isn_arg.number); break; + case ISN_GETITEM: smsg("%s%4d ITEM %lld%s", pfx, current, + 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_STRINGMEMBER: smsg("%s%4d MEMBER %s", pfx, current, iptr->isn_arg.string); break; |