# HG changeset patch # User Bram Moolenaar # Date 1597592722 -7200 # Node ID e3f9528bddda314f114f46f2209678d1bf18ad24 # Parent 6bc6328af5f05c084c2cdc780cafa2d6a6af0abc patch 8.2.1466: Vim9: cannot index or slice a variable with type "any" Commit: https://github.com/vim/vim/commit/cc673e746ab98566556ff964d7a76f2fb46d7f84 Author: Bram Moolenaar Date: Sun Aug 16 17:33:35 2020 +0200 patch 8.2.1466: Vim9: cannot index or slice a variable with type "any" Problem: Vim9: cannot index or slice a variable with type "any". Solution: Add runtime index and slice. diff --git a/src/errors.h b/src/errors.h --- a/src/errors.h +++ b/src/errors.h @@ -21,6 +21,10 @@ EXTERN char e_invalid_command[] #ifdef FEAT_EVAL EXTERN char e_invalid_command_str[] INIT(= N_("E476: Invalid command: %s")); +EXTERN char e_cannot_slice_dictionary[] + INIT(= N_("E719: cannot slice a Dictionary")); +EXTERN char e_cannot_index_special_variable[] + INIT(= N_("E909: Cannot index a special variable")); EXTERN char e_missing_let_str[] INIT(= N_("E1100: Missing :let: %s")); EXTERN char e_variable_not_found_str[] @@ -69,9 +73,9 @@ EXTERN char e_const_requires_a_value[] INIT(= N_("E1021: const requires a value")); EXTERN char e_type_or_initialization_required[] INIT(= N_("E1022: type or initialization required")); -EXTERN char e_cannot_slice_dictionary[] - INIT(= N_("E1023: cannot slice a dictionary")); -// E1024 unused +// E1023 unused +EXTERN char e_using_number_as_string[] + INIT(= N_("E1024: Using a Number as a String")); EXTERN char e_using_rcurly_outside_if_block_scope[] INIT(= N_("E1025: using } outside of a block scope")); EXTERN char e_missing_rcurly[] @@ -146,7 +150,8 @@ EXTERN char e_expected_dot_after_name_st INIT(= N_("E1060: expected dot after name: %s")); EXTERN char e_cannot_find_function_str[] INIT(= N_("E1061: Cannot find function %s")); -// E1062 unused +EXTERN char e_cannot_index_number[] + INIT(= N_("E1062: Cannot index a Number")); EXTERN char e_type_mismatch_for_v_variable[] INIT(= N_("E1063: type mismatch for v: variable")); // E1064 unused diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -20,8 +20,6 @@ # include #endif -static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary"); - #define NAMESPACE_CHAR (char_u *)"abglstvw" /* @@ -928,7 +926,7 @@ get_lval( if (lp->ll_tv->v_type == VAR_DICT) { if (!quiet) - emsg(_(e_dictrange)); + emsg(_(e_cannot_slice_dictionary)); clear_tv(&var1); return NULL; } @@ -3549,47 +3547,12 @@ eval_index( && (evalarg->eval_flags & EVAL_EVALUATE); int empty1 = FALSE, empty2 = FALSE; typval_T var1, var2; - long i; - long n1, n2 = 0; - long len = -1; int range = FALSE; - char_u *s; char_u *key = NULL; - - switch (rettv->v_type) - { - case VAR_FUNC: - case VAR_PARTIAL: - if (verbose) - emsg(_("E695: Cannot index a Funcref")); - return FAIL; - case VAR_FLOAT: -#ifdef FEAT_FLOAT - if (verbose) - emsg(_(e_float_as_string)); - return FAIL; -#endif - case VAR_BOOL: - case VAR_SPECIAL: - case VAR_JOB: - case VAR_CHANNEL: - if (verbose) - emsg(_("E909: Cannot index a special variable")); - return FAIL; - case VAR_UNKNOWN: - case VAR_ANY: - case VAR_VOID: - if (evaluate) - return FAIL; - // FALLTHROUGH - - case VAR_STRING: - case VAR_NUMBER: - case VAR_LIST: - case VAR_DICT: - case VAR_BLOB: - break; - } + int keylen = -1; + + if (check_can_index(rettv, evaluate, verbose) == FAIL) + return FAIL; init_tv(&var1); init_tv(&var2); @@ -3599,11 +3562,11 @@ eval_index( * dict.name */ key = *arg + 1; - for (len = 0; eval_isdictc(key[len]); ++len) + for (keylen = 0; eval_isdictc(key[keylen]); ++keylen) ; - if (len == 0) + if (keylen == 0) return FAIL; - *arg = skipwhite(key + len); + *arg = skipwhite(key + keylen); } else { @@ -3666,49 +3629,132 @@ eval_index( if (evaluate) { - n1 = 0; - if (!empty1 && rettv->v_type != VAR_DICT) - { - n1 = tv_get_number(&var1); + int res = eval_index_inner(rettv, range, + empty1 ? NULL : &var1, empty2 ? NULL : &var2, + key, keylen, verbose); + if (!empty1) clear_tv(&var1); - } if (range) + clear_tv(&var2); + return res; + } + return OK; +} + +/* + * Check if "rettv" can have an [index] or [sli:ce] + */ + int +check_can_index(typval_T *rettv, int evaluate, int verbose) +{ + switch (rettv->v_type) + { + case VAR_FUNC: + case VAR_PARTIAL: + if (verbose) + emsg(_("E695: Cannot index a Funcref")); + return FAIL; + case VAR_FLOAT: +#ifdef FEAT_FLOAT + if (verbose) + emsg(_(e_float_as_string)); + return FAIL; +#endif + case VAR_BOOL: + case VAR_SPECIAL: + case VAR_JOB: + case VAR_CHANNEL: + if (verbose) + emsg(_(e_cannot_index_special_variable)); + return FAIL; + case VAR_UNKNOWN: + case VAR_ANY: + case VAR_VOID: + if (evaluate) + { + emsg(_(e_cannot_index_special_variable)); + return FAIL; + } + // FALLTHROUGH + + case VAR_STRING: + case VAR_LIST: + case VAR_DICT: + case VAR_BLOB: + break; + case VAR_NUMBER: + if (in_vim9script()) + emsg(_(e_cannot_index_number)); + break; + } + return OK; +} + +/* + * Apply index or range to "rettv". + * "var1" is the first index, NULL for [:expr]. + * "var2" is the second index, NULL for [expr] and [expr: ] + * Alternatively, "key" is not NULL, then key[keylen] is the dict index. + */ + int +eval_index_inner( + typval_T *rettv, + int is_range, + typval_T *var1, + typval_T *var2, + char_u *key, + int keylen, + int verbose) +{ + long n1, n2 = 0; + long len; + + n1 = 0; + if (var1 != NULL && rettv->v_type != VAR_DICT) + n1 = tv_get_number(var1); + + if (is_range) + { + if (rettv->v_type == VAR_DICT) { - if (empty2) - n2 = -1; - else - { - n2 = tv_get_number(&var2); - clear_tv(&var2); - } + if (verbose) + emsg(_(e_cannot_slice_dictionary)); + return FAIL; } - - switch (rettv->v_type) - { - case VAR_UNKNOWN: - case VAR_ANY: - case VAR_VOID: - case VAR_FUNC: - case VAR_PARTIAL: - case VAR_FLOAT: - case VAR_BOOL: - case VAR_SPECIAL: - case VAR_JOB: - case VAR_CHANNEL: - break; // not evaluating, skipping over subscript - - case VAR_NUMBER: - case VAR_STRING: - s = tv_get_string(rettv); + if (var2 == NULL) + n2 = -1; + else + n2 = tv_get_number(var2); + } + + switch (rettv->v_type) + { + case VAR_UNKNOWN: + case VAR_ANY: + case VAR_VOID: + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_FLOAT: + case VAR_BOOL: + case VAR_SPECIAL: + case VAR_JOB: + case VAR_CHANNEL: + break; // not evaluating, skipping over subscript + + case VAR_NUMBER: + case VAR_STRING: + { + char_u *s = tv_get_string(rettv); + len = (long)STRLEN(s); if (in_vim9script()) { - if (range) + if (is_range) s = string_slice(s, n1, n2); else s = char_from_string(s, n1); } - else if (range) + else if (is_range) { // The resulting variable is a substring. If the indexes // are out of range the result is empty. @@ -3740,119 +3786,107 @@ eval_index( clear_tv(rettv); rettv->v_type = VAR_STRING; rettv->vval.v_string = s; - break; - - case VAR_BLOB: - len = blob_len(rettv->vval.v_blob); - if (range) + } + break; + + case VAR_BLOB: + len = blob_len(rettv->vval.v_blob); + if (is_range) + { + // The resulting variable is a sub-blob. If the indexes + // are out of range the result is empty. + if (n1 < 0) { - // The resulting variable is a sub-blob. If the indexes - // are out of range the result is empty. + n1 = len + n1; if (n1 < 0) - { - n1 = len + n1; - if (n1 < 0) - n1 = 0; - } - if (n2 < 0) - n2 = len + n2; - else if (n2 >= len) - n2 = len - 1; - if (n1 >= len || n2 < 0 || n1 > n2) - { - clear_tv(rettv); - rettv->v_type = VAR_BLOB; - rettv->vval.v_blob = NULL; - } - else - { - blob_T *blob = blob_alloc(); - - if (blob != NULL) - { - if (ga_grow(&blob->bv_ga, n2 - n1 + 1) == FAIL) - { - blob_free(blob); - return FAIL; - } - blob->bv_ga.ga_len = n2 - n1 + 1; - for (i = n1; i <= n2; i++) - blob_set(blob, i - n1, - blob_get(rettv->vval.v_blob, i)); - - clear_tv(rettv); - rettv_blob_set(rettv, blob); - } - } + n1 = 0; + } + if (n2 < 0) + n2 = len + n2; + else if (n2 >= len) + n2 = len - 1; + if (n1 >= len || n2 < 0 || n1 > n2) + { + clear_tv(rettv); + rettv->v_type = VAR_BLOB; + rettv->vval.v_blob = NULL; } else { - // The resulting variable is a byte value. - // If the index is too big or negative that is an error. - if (n1 < 0) - n1 = len + n1; - if (n1 < len && n1 >= 0) + blob_T *blob = blob_alloc(); + long i; + + if (blob != NULL) { - int v = blob_get(rettv->vval.v_blob, n1); + if (ga_grow(&blob->bv_ga, n2 - n1 + 1) == FAIL) + { + blob_free(blob); + return FAIL; + } + blob->bv_ga.ga_len = n2 - n1 + 1; + for (i = n1; i <= n2; i++) + blob_set(blob, i - n1, + blob_get(rettv->vval.v_blob, i)); clear_tv(rettv); - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = v; + rettv_blob_set(rettv, blob); } - else - semsg(_(e_blobidx), n1); } - break; - - case VAR_LIST: - if (empty1) - n1 = 0; - if (empty2) - n2 = -1; - if (list_slice_or_index(rettv->vval.v_list, - range, n1, n2, rettv, verbose) == FAIL) - return FAIL; - break; - - case VAR_DICT: - if (range) - { - if (verbose) - emsg(_(e_dictrange)); - if (len == -1) - clear_tv(&var1); - return FAIL; - } + } + else + { + // The resulting variable is a byte value. + // If the index is too big or negative that is an error. + if (n1 < 0) + n1 = len + n1; + if (n1 < len && n1 >= 0) { - dictitem_T *item; - - if (len == -1) - { - key = tv_get_string_chk(&var1); - if (key == NULL) - { - clear_tv(&var1); - return FAIL; - } - } - - item = dict_find(rettv->vval.v_dict, key, (int)len); - - if (item == NULL && verbose) - semsg(_(e_dictkey), key); - if (len == -1) - clear_tv(&var1); - if (item == NULL) + int v = blob_get(rettv->vval.v_blob, n1); + + clear_tv(rettv); + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = v; + } + else + semsg(_(e_blobidx), n1); + } + break; + + case VAR_LIST: + if (var1 == NULL) + n1 = 0; + if (var2 == NULL) + n2 = -1; + if (list_slice_or_index(rettv->vval.v_list, + is_range, n1, n2, rettv, verbose) == FAIL) + return FAIL; + break; + + case VAR_DICT: + { + dictitem_T *item; + typval_T tmp; + + if (key == NULL) + { + key = tv_get_string_chk(var1); + if (key == NULL) return FAIL; - - copy_tv(&item->di_tv, &var1); - clear_tv(rettv); - *rettv = var1; } - break; - } + + item = dict_find(rettv->vval.v_dict, key, (int)keylen); + + if (item == NULL && verbose) + semsg(_(e_dictkey), key); + if (item == NULL) + return FAIL; + + copy_tv(&item->di_tv, &tmp); + clear_tv(rettv); + *rettv = tmp; + } + break; } - return OK; } @@ -5292,9 +5326,9 @@ char_from_string(char_u *str, varnumber_ * "str_len". * If going over the end return "str_len". * If "idx" is negative count from the end, -1 is the last character. - * When going over the start return zero. + * When going over the start return -1. */ - static size_t + static long char_idx2byte(char_u *str, size_t str_len, varnumber_T idx) { varnumber_T nchar = idx; @@ -5317,8 +5351,10 @@ char_idx2byte(char_u *str, size_t str_le nbyte -= mb_head_off(str, str + nbyte); ++nchar; } + if (nchar < 0) + return -1; } - return nbyte; + return (long)nbyte; } /* @@ -5328,24 +5364,26 @@ char_idx2byte(char_u *str, size_t str_le char_u * string_slice(char_u *str, varnumber_T first, varnumber_T last) { - size_t start_byte, end_byte; - size_t slen; + long start_byte, end_byte; + size_t slen; if (str == NULL) return NULL; slen = STRLEN(str); start_byte = char_idx2byte(str, slen, first); + if (start_byte < 0) + start_byte = 0; // first index very negative: use zero if (last == -1) end_byte = slen; else { end_byte = char_idx2byte(str, slen, last); - if (end_byte < slen) + if (end_byte >= 0 && end_byte < (long)slen) // end index is inclusive end_byte += MB_CPTR2LEN(str + end_byte); } - if (start_byte >= slen || end_byte <= start_byte) + if (start_byte >= (long)slen || end_byte <= start_byte) return NULL; return vim_strnsave(str + start_byte, end_byte - start_byte); } diff --git a/src/list.c b/src/list.c --- a/src/list.c +++ b/src/list.c @@ -914,7 +914,7 @@ list_slice_or_index( semsg(_(e_listidx), n1); return FAIL; } - n1 = len; + n1 = n1 < 0 ? 0 : len; } if (range) { diff --git a/src/proto/eval.pro b/src/proto/eval.pro --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -37,6 +37,8 @@ int eval0(char_u *arg, typval_T *rettv, int eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg); void eval_addblob(typval_T *tv1, typval_T *tv2); int eval_addlist(typval_T *tv1, typval_T *tv2); +int check_can_index(typval_T *rettv, int evaluate, int verbose); +int eval_index_inner(typval_T *rettv, int is_range, typval_T *var1, typval_T *var2, char_u *key, int keylen, int verbose); char_u *partial_name(partial_T *pt); void partial_unref(partial_T *pt); int get_copyID(void); diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -1091,6 +1091,50 @@ def Test_disassemble_dict_member() call assert_equal(1, DictMember()) enddef +let somelist = [1, 2, 3, 4, 5] +def AnyIndex(): number + let res = g:somelist[2] + return res +enddef + +def Test_disassemble_any_index() + let instr = execute('disassemble AnyIndex') + assert_match('AnyIndex\_s*' .. + 'let res = g:somelist\[2\]\_s*' .. + '\d LOADG g:somelist\_s*' .. + '\d PUSHNR 2\_s*' .. + '\d ANYINDEX\_s*' .. + '\d STORE $0\_s*' .. + 'return res\_s*' .. + '\d LOAD $0\_s*' .. + '\d CHECKTYPE number stack\[-1\]\_s*' .. + '\d RETURN', + instr) + assert_equal(3, AnyIndex()) +enddef + +def AnySlice(): list + let res = g:somelist[1:3] + return res +enddef + +def Test_disassemble_any_slice() + let instr = execute('disassemble AnySlice') + assert_match('AnySlice\_s*' .. + 'let res = g:somelist\[1:3\]\_s*' .. + '\d LOADG g:somelist\_s*' .. + '\d PUSHNR 1\_s*' .. + '\d PUSHNR 3\_s*' .. + '\d ANYSLICE\_s*' .. + '\d STORE $0\_s*' .. + 'return res\_s*' .. + '\d LOAD $0\_s*' .. + '\d CHECKTYPE list stack\[-1\]\_s*' .. + '\d RETURN', + instr) + assert_equal([2, 3, 4], AnySlice()) +enddef + def NegateNumber(): number let nr = 9 let plus = +nr diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -1457,7 +1457,7 @@ def Test_expr7_list() 4] call CheckDefFailure(["let x = 1234[3]"], 'E1107:') - call CheckDefExecFailure(["let x = g:anint[3]"], 'E1029:') + call CheckDefExecFailure(["let x = g:anint[3]"], 'E1062:') call CheckDefFailure(["let x = g:list_mixed[xxx]"], 'E1001:') @@ -1768,9 +1768,91 @@ def Test_expr_member() call CheckDefExecFailure(["let d: dict", "d = g:list_empty"], 'E1029: Expected dict but got list') enddef -def Test_expr_index() - # getting the one member should clear the list only after getting the item - assert_equal('bbb', ['aaa', 'bbb', 'ccc'][1]) +def Test_expr7_any_index_slice() + let lines =<< trim END + # getting the one member should clear the list only after getting the item + assert_equal('bbb', ['aaa', 'bbb', 'ccc'][1]) + + # string is permissive, index out of range accepted + g:teststring = 'abcdef' + assert_equal('b', g:teststring[1]) + assert_equal('', g:teststring[-1]) + assert_equal('', g:teststring[99]) + + assert_equal('b', g:teststring[1:1]) + assert_equal('bcdef', g:teststring[1:]) + assert_equal('abcd', g:teststring[:3]) + assert_equal('cdef', g:teststring[-4:]) + assert_equal('abcdef', g:teststring[-9:]) + assert_equal('abcd', g:teststring[:-3]) + assert_equal('', g:teststring[:-9]) + + # blob index cannot be out of range + g:testblob = 0z01ab + assert_equal(0x01, g:testblob[0]) + assert_equal(0xab, g:testblob[1]) + assert_equal(0xab, g:testblob[-1]) + assert_equal(0x01, g:testblob[-2]) + + # blob slice accepts out of range + assert_equal(0z01ab, g:testblob[0:1]) + assert_equal(0z01, g:testblob[0:0]) + assert_equal(0z01, g:testblob[-2:-2]) + assert_equal(0zab, g:testblob[1:1]) + assert_equal(0zab, g:testblob[-1:-1]) + assert_equal(0z, g:testblob[2:2]) + assert_equal(0z, g:testblob[0:-3]) + + # list index cannot be out of range + g:testlist = [0, 1, 2, 3] + assert_equal(0, g:testlist[0]) + assert_equal(1, g:testlist[1]) + assert_equal(3, g:testlist[3]) + assert_equal(3, g:testlist[-1]) + assert_equal(0, g:testlist[-4]) + assert_equal(1, g:testlist[g:theone]) + + # list slice accepts out of range + assert_equal([0], g:testlist[0:0]) + assert_equal([3], g:testlist[3:3]) + assert_equal([0, 1], g:testlist[0:1]) + assert_equal([0, 1, 2, 3], g:testlist[0:3]) + assert_equal([0, 1, 2, 3], g:testlist[0:9]) + assert_equal([], g:testlist[-1:1]) + assert_equal([1], g:testlist[-3:1]) + assert_equal([0, 1], g:testlist[-4:1]) + assert_equal([0, 1], g:testlist[-9:1]) + assert_equal([1, 2, 3], g:testlist[1:-1]) + assert_equal([1], g:testlist[1:-3]) + assert_equal([], g:testlist[1:-4]) + assert_equal([], g:testlist[1:-9]) + + g:testdict = #{a: 1, b: 2} + assert_equal(1, g:testdict['a']) + assert_equal(2, g:testdict['b']) + END + + CheckDefSuccess(lines) + CheckScriptSuccess(['vim9script'] + lines) + + CheckDefExecFailure(['echo g:testblob[2]'], 'E979:') + CheckScriptFailure(['vim9script', 'echo g:testblob[2]'], 'E979:') + CheckDefExecFailure(['echo g:testblob[-3]'], 'E979:') + CheckScriptFailure(['vim9script', 'echo g:testblob[-3]'], 'E979:') + + CheckDefExecFailure(['echo g:testlist[4]'], 'E684:') + CheckScriptFailure(['vim9script', 'echo g:testlist[4]'], 'E684:') + CheckDefExecFailure(['echo g:testlist[-5]'], 'E684:') + CheckScriptFailure(['vim9script', 'echo g:testlist[-5]'], 'E684:') + + CheckDefExecFailure(['echo g:testdict["a":"b"]'], 'E719:') + CheckScriptFailure(['vim9script', 'echo g:testdict["a":"b"]'], 'E719:') + CheckDefExecFailure(['echo g:testdict[1]'], 'E716:') + CheckScriptFailure(['vim9script', 'echo g:testdict[1]'], 'E716:') + + unlet g:teststring + unlet g:testblob + unlet g:testlist enddef def Test_expr_member_vim9script() diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -793,8 +793,8 @@ def Test_try_catch() endtry assert_equal(99, n) - # TODO: this will change when index on "any" works try + # string slice returns a string, not a number n = g:astring[3] catch /E1029:/ n = 77 diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -755,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1466, +/**/ 1465, /**/ 1464, diff --git a/src/vim9.h b/src/vim9.h --- a/src/vim9.h +++ b/src/vim9.h @@ -120,6 +120,8 @@ typedef enum { ISN_STRSLICE, // [expr:expr] string slice ISN_LISTINDEX, // [expr] list index ISN_LISTSLICE, // [expr:expr] list slice + ISN_ANYINDEX, // [expr] runtime index + ISN_ANYSLICE, // [expr:expr] runtime slice ISN_SLICE, // drop isn_arg.number items from start of list ISN_GETITEM, // push list item, isn_arg.number is the index ISN_MEMBER, // dict[member] diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -3179,20 +3179,20 @@ compile_subscript( } else if (vtype == VAR_LIST || *typep == &t_any) { - // TODO: any requires runtime code - if (*typep == &t_any && need_type(*typep, &t_list_any, - is_slice ? -3 : -2, cctx, FALSE) == FAIL) - return FAIL; if (is_slice) { - if (generate_instr_drop(cctx, ISN_LISTSLICE, 2) == FAIL) + if (generate_instr_drop(cctx, + vtype == VAR_LIST ? ISN_LISTSLICE : ISN_ANYSLICE, + 2) == FAIL) return FAIL; } else { if ((*typep)->tt_type == VAR_LIST) *typep = (*typep)->tt_member; - if (generate_instr_drop(cctx, ISN_LISTINDEX, 1) == FAIL) + if (generate_instr_drop(cctx, + vtype == VAR_LIST ? ISN_LISTINDEX : ISN_ANYINDEX, + 1) == FAIL) return FAIL; } } @@ -7085,11 +7085,13 @@ delete_instr(isn_T *isn) case ISN_2STRING_ANY: case ISN_ADDBLOB: case ISN_ADDLIST: + case ISN_ANYINDEX: + case ISN_ANYSLICE: case ISN_BCALL: case ISN_CATCH: + case ISN_CHECKLEN: case ISN_CHECKNR: case ISN_CHECKTYPE: - case ISN_CHECKLEN: case ISN_COMPAREANY: case ISN_COMPAREBLOB: case ISN_COMPAREBOOL: @@ -7102,7 +7104,6 @@ delete_instr(isn_T *isn) case ISN_COMPARESTRING: case ISN_CONCAT: case ISN_DCALL: - case ISN_SHUFFLE: case ISN_DROP: case ISN_ECHO: case ISN_ECHOERR: @@ -7111,14 +7112,10 @@ delete_instr(isn_T *isn) case ISN_EXECCONCAT: case ISN_EXECUTE: case ISN_FOR: + case ISN_GETITEM: + case ISN_JUMP: case ISN_LISTINDEX: case ISN_LISTSLICE: - case ISN_STRINDEX: - case ISN_STRSLICE: - case ISN_GETITEM: - case ISN_SLICE: - case ISN_MEMBER: - case ISN_JUMP: case ISN_LOAD: case ISN_LOADBDICT: case ISN_LOADGDICT: @@ -7128,27 +7125,32 @@ delete_instr(isn_T *isn) case ISN_LOADTDICT: case ISN_LOADV: case ISN_LOADWDICT: + case ISN_MEMBER: case ISN_NEGATENR: case ISN_NEWDICT: case ISN_NEWLIST: - case ISN_OPNR: + case ISN_OPANY: case ISN_OPFLOAT: - case ISN_OPANY: + case ISN_OPNR: case ISN_PCALL: case ISN_PCALL_END: + case ISN_PUSHBOOL: case ISN_PUSHF: case ISN_PUSHNR: - case ISN_PUSHBOOL: case ISN_PUSHSPEC: case ISN_RETURN: + case ISN_SHUFFLE: + case ISN_SLICE: case ISN_STORE: + case ISN_STOREDICT: + case ISN_STORELIST: + case ISN_STORENR: case ISN_STOREOUTER: - case ISN_STOREV: - case ISN_STORENR: case ISN_STOREREG: case ISN_STORESCRIPT: - case ISN_STOREDICT: - case ISN_STORELIST: + case ISN_STOREV: + case ISN_STRINDEX: + case ISN_STRSLICE: case ISN_THROW: case ISN_TRY: // nothing allocated diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -2297,6 +2297,32 @@ call_def_function( } break; + case ISN_ANYINDEX: + case ISN_ANYSLICE: + { + int is_slice = iptr->isn_type == ISN_ANYSLICE; + typval_T *var1, *var2; + int res; + + // index: composite is at stack-2, index at stack-1 + // slice: composite is at stack-3, indexes at stack-2 and + // stack-1 + tv = is_slice ? STACK_TV_BOT(-3) : STACK_TV_BOT(-2); + if (check_can_index(tv, TRUE, TRUE) == FAIL) + goto on_error; + var1 = is_slice ? STACK_TV_BOT(-2) : STACK_TV_BOT(-1); + var2 = is_slice ? STACK_TV_BOT(-1) : NULL; + res = eval_index_inner(tv, is_slice, + var1, var2, NULL, -1, TRUE); + clear_tv(var1); + if (is_slice) + clear_tv(var2); + ectx.ec_stack.ga_len -= is_slice ? 2 : 1; + if (res == FAIL) + goto on_error; + } + break; + case ISN_SLICE: { list_T *list; @@ -3133,6 +3159,8 @@ ex_disassemble(exarg_T *eap) case ISN_STRSLICE: smsg("%4d STRSLICE", current); break; case ISN_LISTINDEX: smsg("%4d LISTINDEX", current); break; case ISN_LISTSLICE: smsg("%4d LISTSLICE", current); break; + case ISN_ANYINDEX: smsg("%4d ANYINDEX", current); break; + case ISN_ANYSLICE: smsg("%4d ANYSLICE", current); break; case ISN_SLICE: smsg("%4d SLICE %lld", current, iptr->isn_arg.number); break; case ISN_GETITEM: smsg("%4d ITEM %lld",