# HG changeset patch # User Bram Moolenaar # Date 1642968903 -3600 # Node ID 2b3cc874334c7da339b1657d3bdf66484286f8f4 # Parent 9d2ab2f4f7e486a61e53280557ca216b835ee3f6 patch 8.2.4198: Vim9: the switch for executing instructions is too long Commit: https://github.com/vim/vim/commit/058ee7c5699ef551be5aa04c66b3cffc436e9b08 Author: Bram Moolenaar Date: Sun Jan 23 20:00:42 2022 +0000 patch 8.2.4198: Vim9: the switch for executing instructions is too long Problem: Vim9: the switch for executing instructions is too long. Solution: Move some code to separate functions. diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 4198, +/**/ 4197, /**/ 4196, diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -1694,6 +1694,572 @@ handle_debug(isn_T *iptr, ectx_T *ectx) } /* + * Store a value in a list or dict variable. + * Returns OK, FAIL or NOTDONE (uncatchable error). + */ + static int +execute_storeindex(isn_T *iptr, ectx_T *ectx) +{ + vartype_T dest_type = iptr->isn_arg.vartype; + typval_T *tv; + typval_T *tv_idx = STACK_TV_BOT(-2); + typval_T *tv_dest = STACK_TV_BOT(-1); + int status = OK; + + // Stack contains: + // -3 value to be stored + // -2 index + // -1 dict or list + tv = STACK_TV_BOT(-3); + SOURCING_LNUM = iptr->isn_lnum; + if (dest_type == VAR_ANY) + { + dest_type = tv_dest->v_type; + if (dest_type == VAR_DICT) + status = do_2string(tv_idx, TRUE, FALSE); + else if (dest_type == VAR_LIST && tv_idx->v_type != VAR_NUMBER) + { + emsg(_(e_number_expected)); + status = FAIL; + } + } + else if (dest_type != tv_dest->v_type) + { + // just in case, should be OK + semsg(_(e_expected_str_but_got_str), + vartype_name(dest_type), + vartype_name(tv_dest->v_type)); + status = FAIL; + } + + if (status == OK && dest_type == VAR_LIST) + { + long lidx = (long)tv_idx->vval.v_number; + list_T *list = tv_dest->vval.v_list; + + if (list == NULL) + { + emsg(_(e_list_not_set)); + return FAIL; + } + if (lidx < 0 && list->lv_len + lidx >= 0) + // negative index is relative to the end + lidx = list->lv_len + lidx; + if (lidx < 0 || lidx > list->lv_len) + { + semsg(_(e_list_index_out_of_range_nr), lidx); + return FAIL; + } + if (lidx < list->lv_len) + { + listitem_T *li = list_find(list, lidx); + + if (error_if_locked(li->li_tv.v_lock, e_cannot_change_list_item)) + return FAIL; + // overwrite existing list item + clear_tv(&li->li_tv); + li->li_tv = *tv; + } + else + { + if (error_if_locked(list->lv_lock, e_cannot_change_list)) + return FAIL; + // append to list, only fails when out of memory + if (list_append_tv(list, tv) == FAIL) + return NOTDONE; + clear_tv(tv); + } + } + else if (status == OK && dest_type == VAR_DICT) + { + char_u *key = tv_idx->vval.v_string; + dict_T *dict = tv_dest->vval.v_dict; + dictitem_T *di; + + SOURCING_LNUM = iptr->isn_lnum; + if (dict == NULL) + { + emsg(_(e_dictionary_not_set)); + return FAIL; + } + if (key == NULL) + key = (char_u *)""; + di = dict_find(dict, key, -1); + if (di != NULL) + { + if (error_if_locked(di->di_tv.v_lock, e_cannot_change_dict_item)) + return FAIL; + // overwrite existing value + clear_tv(&di->di_tv); + di->di_tv = *tv; + } + else + { + if (error_if_locked(dict->dv_lock, e_cannot_change_dict)) + return FAIL; + // add to dict, only fails when out of memory + if (dict_add_tv(dict, (char *)key, tv) == FAIL) + return NOTDONE; + clear_tv(tv); + } + } + else if (status == OK && dest_type == VAR_BLOB) + { + long lidx = (long)tv_idx->vval.v_number; + blob_T *blob = tv_dest->vval.v_blob; + varnumber_T nr; + int error = FALSE; + int len; + + if (blob == NULL) + { + emsg(_(e_blob_not_set)); + return FAIL; + } + len = blob_len(blob); + if (lidx < 0 && len + lidx >= 0) + // negative index is relative to the end + lidx = len + lidx; + + // Can add one byte at the end. + if (lidx < 0 || lidx > len) + { + semsg(_(e_blob_index_out_of_range_nr), lidx); + return FAIL; + } + if (value_check_lock(blob->bv_lock, (char_u *)"blob", FALSE)) + return FAIL; + nr = tv_get_number_chk(tv, &error); + if (error) + return FAIL; + blob_set_append(blob, lidx, nr); + } + else + { + status = FAIL; + semsg(_(e_cannot_index_str), vartype_name(dest_type)); + } + + clear_tv(tv_idx); + clear_tv(tv_dest); + ectx->ec_stack.ga_len -= 3; + if (status == FAIL) + { + clear_tv(tv); + return FAIL; + } + return OK; +} + +/* + * Store a value in a blob range. + */ + static int +execute_storerange(isn_T *iptr, ectx_T *ectx) +{ + typval_T *tv; + typval_T *tv_idx1 = STACK_TV_BOT(-3); + typval_T *tv_idx2 = STACK_TV_BOT(-2); + typval_T *tv_dest = STACK_TV_BOT(-1); + int status = OK; + + // Stack contains: + // -4 value to be stored + // -3 first index or "none" + // -2 second index or "none" + // -1 destination list or blob + tv = STACK_TV_BOT(-4); + if (tv_dest->v_type == VAR_LIST) + { + long n1; + long n2; + int error = FALSE; + + SOURCING_LNUM = iptr->isn_lnum; + n1 = (long)tv_get_number_chk(tv_idx1, &error); + if (error) + status = FAIL; + else + { + if (tv_idx2->v_type == VAR_SPECIAL + && tv_idx2->vval.v_number == VVAL_NONE) + n2 = list_len(tv_dest->vval.v_list) - 1; + else + n2 = (long)tv_get_number_chk(tv_idx2, &error); + if (error) + status = FAIL; + else + { + listitem_T *li1 = check_range_index_one( + tv_dest->vval.v_list, &n1, FALSE); + + if (li1 == NULL) + status = FAIL; + else + { + status = check_range_index_two( + tv_dest->vval.v_list, + &n1, li1, &n2, FALSE); + if (status != FAIL) + status = list_assign_range( + tv_dest->vval.v_list, + tv->vval.v_list, + n1, + n2, + tv_idx2->v_type == VAR_SPECIAL, + (char_u *)"=", + (char_u *)"[unknown]"); + } + } + } + } + else if (tv_dest->v_type == VAR_BLOB) + { + varnumber_T n1; + varnumber_T n2; + int error = FALSE; + + n1 = tv_get_number_chk(tv_idx1, &error); + if (error) + status = FAIL; + else + { + if (tv_idx2->v_type == VAR_SPECIAL + && tv_idx2->vval.v_number == VVAL_NONE) + n2 = blob_len(tv_dest->vval.v_blob) - 1; + else + n2 = tv_get_number_chk(tv_idx2, &error); + if (error) + status = FAIL; + else + { + long bloblen = blob_len(tv_dest->vval.v_blob); + + if (check_blob_index(bloblen, + n1, FALSE) == FAIL + || check_blob_range(bloblen, + n1, n2, FALSE) == FAIL) + status = FAIL; + else + status = blob_set_range( + tv_dest->vval.v_blob, n1, n2, tv); + } + } + } + else + { + status = FAIL; + emsg(_(e_blob_required)); + } + + clear_tv(tv_idx1); + clear_tv(tv_idx2); + clear_tv(tv_dest); + ectx->ec_stack.ga_len -= 4; + clear_tv(tv); + + return status; +} + +/* + * Unlet item in list or dict variable. + */ + static int +execute_unletindex(isn_T *iptr, ectx_T *ectx) +{ + typval_T *tv_idx = STACK_TV_BOT(-2); + typval_T *tv_dest = STACK_TV_BOT(-1); + int status = OK; + + // Stack contains: + // -2 index + // -1 dict or list + if (tv_dest->v_type == VAR_DICT) + { + // unlet a dict item, index must be a string + if (tv_idx->v_type != VAR_STRING) + { + SOURCING_LNUM = iptr->isn_lnum; + semsg(_(e_expected_str_but_got_str), + vartype_name(VAR_STRING), + vartype_name(tv_idx->v_type)); + status = FAIL; + } + else + { + dict_T *d = tv_dest->vval.v_dict; + char_u *key = tv_idx->vval.v_string; + dictitem_T *di = NULL; + + if (d != NULL && value_check_lock( + d->dv_lock, NULL, FALSE)) + status = FAIL; + else + { + SOURCING_LNUM = iptr->isn_lnum; + if (key == NULL) + key = (char_u *)""; + if (d != NULL) + di = dict_find(d, key, (int)STRLEN(key)); + if (di == NULL) + { + // NULL dict is equivalent to empty dict + semsg(_(e_key_not_present_in_dictionary), + key); + status = FAIL; + } + else if (var_check_fixed(di->di_flags, + NULL, FALSE) + || var_check_ro(di->di_flags, + NULL, FALSE)) + status = FAIL; + else + dictitem_remove(d, di); + } + } + } + else if (tv_dest->v_type == VAR_LIST) + { + // unlet a List item, index must be a number + SOURCING_LNUM = iptr->isn_lnum; + if (check_for_number(tv_idx) == FAIL) + { + status = FAIL; + } + else + { + list_T *l = tv_dest->vval.v_list; + long n = (long)tv_idx->vval.v_number; + + if (l != NULL && value_check_lock( + l->lv_lock, NULL, FALSE)) + status = FAIL; + else + { + listitem_T *li = list_find(l, n); + + if (li == NULL) + { + SOURCING_LNUM = iptr->isn_lnum; + semsg(_(e_list_index_out_of_range_nr), n); + status = FAIL; + } + else if (value_check_lock(li->li_tv.v_lock, + NULL, FALSE)) + status = FAIL; + else + listitem_remove(l, li); + } + } + } + else + { + status = FAIL; + semsg(_(e_cannot_index_str), + vartype_name(tv_dest->v_type)); + } + + clear_tv(tv_idx); + clear_tv(tv_dest); + ectx->ec_stack.ga_len -= 2; + + return status; +} + +/* + * Unlet a range of items in a list variable. + */ + static int +execute_unletrange(isn_T *iptr, ectx_T *ectx) +{ + // Stack contains: + // -3 index1 + // -2 index2 + // -1 dict or list + typval_T *tv_idx1 = STACK_TV_BOT(-3); + typval_T *tv_idx2 = STACK_TV_BOT(-2); + typval_T *tv_dest = STACK_TV_BOT(-1); + int status = OK; + + if (tv_dest->v_type == VAR_LIST) + { + // indexes must be a number + SOURCING_LNUM = iptr->isn_lnum; + if (check_for_number(tv_idx1) == FAIL + || (tv_idx2->v_type != VAR_SPECIAL + && check_for_number(tv_idx2) == FAIL)) + { + status = FAIL; + } + else + { + list_T *l = tv_dest->vval.v_list; + long n1 = (long)tv_idx1->vval.v_number; + long n2 = tv_idx2->v_type == VAR_SPECIAL + ? 0 : (long)tv_idx2->vval.v_number; + listitem_T *li; + + li = list_find_index(l, &n1); + if (li == NULL) + status = FAIL; + else + { + if (n1 < 0) + n1 = list_idx_of_item(l, li); + if (n2 < 0) + { + listitem_T *li2 = list_find(l, n2); + + if (li2 == NULL) + status = FAIL; + else + n2 = list_idx_of_item(l, li2); + } + if (status != FAIL + && tv_idx2->v_type != VAR_SPECIAL + && n2 < n1) + { + semsg(_(e_list_index_out_of_range_nr), n2); + status = FAIL; + } + if (status != FAIL + && list_unlet_range(l, li, NULL, n1, + tv_idx2->v_type != VAR_SPECIAL, n2) + == FAIL) + status = FAIL; + } + } + } + else + { + status = FAIL; + SOURCING_LNUM = iptr->isn_lnum; + semsg(_(e_cannot_index_str), + vartype_name(tv_dest->v_type)); + } + + clear_tv(tv_idx1); + clear_tv(tv_idx2); + clear_tv(tv_dest); + ectx->ec_stack.ga_len -= 3; + + return status; +} + +/* + * Top of a for loop. + */ + static int +execute_for(isn_T *iptr, ectx_T *ectx) +{ + typval_T *tv; + typval_T *ltv = STACK_TV_BOT(-1); + typval_T *idxtv = + STACK_TV_VAR(iptr->isn_arg.forloop.for_idx); + + if (GA_GROW_FAILS(&ectx->ec_stack, 1)) + return FAIL; + if (ltv->v_type == VAR_LIST) + { + list_T *list = ltv->vval.v_list; + + // push the next item from the list + ++idxtv->vval.v_number; + if (list == NULL + || idxtv->vval.v_number >= list->lv_len) + { + // past the end of the list, jump to "endfor" + ectx->ec_iidx = iptr->isn_arg.forloop.for_end; + may_restore_cmdmod(&ectx->ec_funclocal); + } + else if (list->lv_first == &range_list_item) + { + // non-materialized range() list + tv = STACK_TV_BOT(0); + tv->v_type = VAR_NUMBER; + tv->v_lock = 0; + tv->vval.v_number = list_find_nr( + list, idxtv->vval.v_number, NULL); + ++ectx->ec_stack.ga_len; + } + else + { + listitem_T *li = list_find(list, + idxtv->vval.v_number); + + copy_tv(&li->li_tv, STACK_TV_BOT(0)); + ++ectx->ec_stack.ga_len; + } + } + else if (ltv->v_type == VAR_STRING) + { + char_u *str = ltv->vval.v_string; + + // The index is for the last byte of the previous + // character. + ++idxtv->vval.v_number; + if (str == NULL || str[idxtv->vval.v_number] == NUL) + { + // past the end of the string, jump to "endfor" + ectx->ec_iidx = iptr->isn_arg.forloop.for_end; + may_restore_cmdmod(&ectx->ec_funclocal); + } + else + { + int clen = mb_ptr2len(str + idxtv->vval.v_number); + + // Push the next character from the string. + tv = STACK_TV_BOT(0); + tv->v_type = VAR_STRING; + tv->vval.v_string = vim_strnsave( + str + idxtv->vval.v_number, clen); + ++ectx->ec_stack.ga_len; + idxtv->vval.v_number += clen - 1; + } + } + else if (ltv->v_type == VAR_BLOB) + { + blob_T *blob = ltv->vval.v_blob; + + // When we get here the first time make a copy of the + // blob, so that the iteration still works when it is + // changed. + if (idxtv->vval.v_number == -1 && blob != NULL) + { + blob_copy(blob, ltv); + blob_unref(blob); + blob = ltv->vval.v_blob; + } + + // The index is for the previous byte. + ++idxtv->vval.v_number; + if (blob == NULL + || idxtv->vval.v_number >= blob_len(blob)) + { + // past the end of the blob, jump to "endfor" + ectx->ec_iidx = iptr->isn_arg.forloop.for_end; + may_restore_cmdmod(&ectx->ec_funclocal); + } + else + { + // Push the next byte from the blob. + tv = STACK_TV_BOT(0); + tv->v_type = VAR_NUMBER; + tv->vval.v_number = blob_get(blob, + idxtv->vval.v_number); + ++ectx->ec_stack.ga_len; + } + } + else + { + semsg(_(e_for_loop_on_str_not_supported), + vartype_name(ltv->v_type)); + return FAIL; + } + return OK; +} + +/* * Execute instructions in execution context "ectx". * Return OK or FAIL; */ @@ -1798,6 +2364,12 @@ exec_instructions(ectx_T *ectx) continue; } + /* + * Big switch on the instruction. Most compilers will be turning this + * into an efficient lookup table, since the "case" values are an enum + * with sequential numbers. It may look ugly, but it should be the + * most efficient way. + */ iptr = &ectx->ec_instr[ectx->ec_iidx++]; switch (iptr->isn_type) { @@ -2551,268 +3123,19 @@ exec_instructions(ectx_T *ectx) // store value in list or dict variable case ISN_STOREINDEX: { - vartype_T dest_type = iptr->isn_arg.vartype; - typval_T *tv_idx = STACK_TV_BOT(-2); - typval_T *tv_dest = STACK_TV_BOT(-1); - int status = OK; - - // Stack contains: - // -3 value to be stored - // -2 index - // -1 dict or list - tv = STACK_TV_BOT(-3); - SOURCING_LNUM = iptr->isn_lnum; - if (dest_type == VAR_ANY) - { - dest_type = tv_dest->v_type; - if (dest_type == VAR_DICT) - status = do_2string(tv_idx, TRUE, FALSE); - else if (dest_type == VAR_LIST - && tv_idx->v_type != VAR_NUMBER) - { - emsg(_(e_number_expected)); - status = FAIL; - } - } - else if (dest_type != tv_dest->v_type) - { - // just in case, should be OK - semsg(_(e_expected_str_but_got_str), - vartype_name(dest_type), - vartype_name(tv_dest->v_type)); - status = FAIL; - } - - if (status == OK && dest_type == VAR_LIST) - { - long lidx = (long)tv_idx->vval.v_number; - list_T *list = tv_dest->vval.v_list; - - if (list == NULL) - { - emsg(_(e_list_not_set)); - goto on_error; - } - if (lidx < 0 && list->lv_len + lidx >= 0) - // negative index is relative to the end - lidx = list->lv_len + lidx; - if (lidx < 0 || lidx > list->lv_len) - { - semsg(_(e_list_index_out_of_range_nr), lidx); - goto on_error; - } - if (lidx < list->lv_len) - { - listitem_T *li = list_find(list, lidx); - - if (error_if_locked(li->li_tv.v_lock, - e_cannot_change_list_item)) - goto on_error; - // overwrite existing list item - clear_tv(&li->li_tv); - li->li_tv = *tv; - } - else - { - if (error_if_locked(list->lv_lock, - e_cannot_change_list)) - goto on_error; - // append to list, only fails when out of memory - if (list_append_tv(list, tv) == FAIL) - goto theend; - clear_tv(tv); - } - } - else if (status == OK && dest_type == VAR_DICT) - { - char_u *key = tv_idx->vval.v_string; - dict_T *dict = tv_dest->vval.v_dict; - dictitem_T *di; - - SOURCING_LNUM = iptr->isn_lnum; - if (dict == NULL) - { - emsg(_(e_dictionary_not_set)); - goto on_error; - } - if (key == NULL) - key = (char_u *)""; - di = dict_find(dict, key, -1); - if (di != NULL) - { - if (error_if_locked(di->di_tv.v_lock, - e_cannot_change_dict_item)) - goto on_error; - // overwrite existing value - clear_tv(&di->di_tv); - di->di_tv = *tv; - } - else - { - if (error_if_locked(dict->dv_lock, - e_cannot_change_dict)) - goto on_error; - // add to dict, only fails when out of memory - if (dict_add_tv(dict, (char *)key, tv) == FAIL) - goto theend; - clear_tv(tv); - } - } - else if (status == OK && dest_type == VAR_BLOB) - { - long lidx = (long)tv_idx->vval.v_number; - blob_T *blob = tv_dest->vval.v_blob; - varnumber_T nr; - int error = FALSE; - int len; - - if (blob == NULL) - { - emsg(_(e_blob_not_set)); - goto on_error; - } - len = blob_len(blob); - if (lidx < 0 && len + lidx >= 0) - // negative index is relative to the end - lidx = len + lidx; - - // Can add one byte at the end. - if (lidx < 0 || lidx > len) - { - semsg(_(e_blob_index_out_of_range_nr), lidx); - goto on_error; - } - if (value_check_lock(blob->bv_lock, - (char_u *)"blob", FALSE)) - goto on_error; - nr = tv_get_number_chk(tv, &error); - if (error) - goto on_error; - blob_set_append(blob, lidx, nr); - } - else - { - status = FAIL; - semsg(_(e_cannot_index_str), vartype_name(dest_type)); - } - - clear_tv(tv_idx); - clear_tv(tv_dest); - ectx->ec_stack.ga_len -= 3; - if (status == FAIL) - { - clear_tv(tv); + int res = execute_storeindex(iptr, ectx); + + if (res == FAIL) goto on_error; - } + if (res == NOTDONE) + goto theend; } break; // store value in blob range case ISN_STORERANGE: - { - typval_T *tv_idx1 = STACK_TV_BOT(-3); - typval_T *tv_idx2 = STACK_TV_BOT(-2); - typval_T *tv_dest = STACK_TV_BOT(-1); - int status = OK; - - // Stack contains: - // -4 value to be stored - // -3 first index or "none" - // -2 second index or "none" - // -1 destination list or blob - tv = STACK_TV_BOT(-4); - if (tv_dest->v_type == VAR_LIST) - { - long n1; - long n2; - int error = FALSE; - - SOURCING_LNUM = iptr->isn_lnum; - n1 = (long)tv_get_number_chk(tv_idx1, &error); - if (error) - status = FAIL; - else - { - if (tv_idx2->v_type == VAR_SPECIAL - && tv_idx2->vval.v_number == VVAL_NONE) - n2 = list_len(tv_dest->vval.v_list) - 1; - else - n2 = (long)tv_get_number_chk(tv_idx2, &error); - if (error) - status = FAIL; - else - { - listitem_T *li1 = check_range_index_one( - tv_dest->vval.v_list, &n1, FALSE); - - if (li1 == NULL) - status = FAIL; - else - { - status = check_range_index_two( - tv_dest->vval.v_list, - &n1, li1, &n2, FALSE); - if (status != FAIL) - status = list_assign_range( - tv_dest->vval.v_list, - tv->vval.v_list, - n1, - n2, - tv_idx2->v_type == VAR_SPECIAL, - (char_u *)"=", - (char_u *)"[unknown]"); - } - } - } - } - else if (tv_dest->v_type == VAR_BLOB) - { - varnumber_T n1; - varnumber_T n2; - int error = FALSE; - - n1 = tv_get_number_chk(tv_idx1, &error); - if (error) - status = FAIL; - else - { - if (tv_idx2->v_type == VAR_SPECIAL - && tv_idx2->vval.v_number == VVAL_NONE) - n2 = blob_len(tv_dest->vval.v_blob) - 1; - else - n2 = tv_get_number_chk(tv_idx2, &error); - if (error) - status = FAIL; - else - { - long bloblen = blob_len(tv_dest->vval.v_blob); - - if (check_blob_index(bloblen, - n1, FALSE) == FAIL - || check_blob_range(bloblen, - n1, n2, FALSE) == FAIL) - status = FAIL; - else - status = blob_set_range( - tv_dest->vval.v_blob, n1, n2, tv); - } - } - } - else - { - status = FAIL; - emsg(_(e_blob_required)); - } - - clear_tv(tv_idx1); - clear_tv(tv_idx2); - clear_tv(tv_dest); - ectx->ec_stack.ga_len -= 4; - clear_tv(tv); - - if (status == FAIL) - goto on_error; - } + if (execute_storerange(iptr, ectx) == FAIL) + goto on_error; break; // load or store variable or argument from outer scope @@ -2861,182 +3184,14 @@ exec_instructions(ectx_T *ectx) // unlet item in list or dict variable case ISN_UNLETINDEX: - { - typval_T *tv_idx = STACK_TV_BOT(-2); - typval_T *tv_dest = STACK_TV_BOT(-1); - int status = OK; - - // Stack contains: - // -2 index - // -1 dict or list - if (tv_dest->v_type == VAR_DICT) - { - // unlet a dict item, index must be a string - if (tv_idx->v_type != VAR_STRING) - { - SOURCING_LNUM = iptr->isn_lnum; - semsg(_(e_expected_str_but_got_str), - vartype_name(VAR_STRING), - vartype_name(tv_idx->v_type)); - status = FAIL; - } - else - { - dict_T *d = tv_dest->vval.v_dict; - char_u *key = tv_idx->vval.v_string; - dictitem_T *di = NULL; - - if (d != NULL && value_check_lock( - d->dv_lock, NULL, FALSE)) - status = FAIL; - else - { - SOURCING_LNUM = iptr->isn_lnum; - if (key == NULL) - key = (char_u *)""; - if (d != NULL) - di = dict_find(d, key, (int)STRLEN(key)); - if (di == NULL) - { - // NULL dict is equivalent to empty dict - semsg(_(e_key_not_present_in_dictionary), key); - status = FAIL; - } - else if (var_check_fixed(di->di_flags, - NULL, FALSE) - || var_check_ro(di->di_flags, - NULL, FALSE)) - status = FAIL; - else - dictitem_remove(d, di); - } - } - } - else if (tv_dest->v_type == VAR_LIST) - { - // unlet a List item, index must be a number - SOURCING_LNUM = iptr->isn_lnum; - if (check_for_number(tv_idx) == FAIL) - { - status = FAIL; - } - else - { - list_T *l = tv_dest->vval.v_list; - long n = (long)tv_idx->vval.v_number; - - if (l != NULL && value_check_lock( - l->lv_lock, NULL, FALSE)) - status = FAIL; - else - { - listitem_T *li = list_find(l, n); - - if (li == NULL) - { - SOURCING_LNUM = iptr->isn_lnum; - semsg(_(e_list_index_out_of_range_nr), n); - status = FAIL; - } - else if (value_check_lock(li->li_tv.v_lock, - NULL, FALSE)) - status = FAIL; - else - listitem_remove(l, li); - } - } - } - else - { - status = FAIL; - semsg(_(e_cannot_index_str), - vartype_name(tv_dest->v_type)); - } - - clear_tv(tv_idx); - clear_tv(tv_dest); - ectx->ec_stack.ga_len -= 2; - if (status == FAIL) - goto on_error; - } + if (execute_unletindex(iptr, ectx) == FAIL) + goto on_error; break; // unlet range of items in list variable case ISN_UNLETRANGE: - { - // Stack contains: - // -3 index1 - // -2 index2 - // -1 dict or list - typval_T *tv_idx1 = STACK_TV_BOT(-3); - typval_T *tv_idx2 = STACK_TV_BOT(-2); - typval_T *tv_dest = STACK_TV_BOT(-1); - int status = OK; - - if (tv_dest->v_type == VAR_LIST) - { - // indexes must be a number - SOURCING_LNUM = iptr->isn_lnum; - if (check_for_number(tv_idx1) == FAIL - || (tv_idx2->v_type != VAR_SPECIAL - && check_for_number(tv_idx2) == FAIL)) - { - status = FAIL; - } - else - { - list_T *l = tv_dest->vval.v_list; - long n1 = (long)tv_idx1->vval.v_number; - long n2 = tv_idx2->v_type == VAR_SPECIAL - ? 0 : (long)tv_idx2->vval.v_number; - listitem_T *li; - - li = list_find_index(l, &n1); - if (li == NULL) - status = FAIL; - else - { - if (n1 < 0) - n1 = list_idx_of_item(l, li); - if (n2 < 0) - { - listitem_T *li2 = list_find(l, n2); - - if (li2 == NULL) - status = FAIL; - else - n2 = list_idx_of_item(l, li2); - } - if (status != FAIL - && tv_idx2->v_type != VAR_SPECIAL - && n2 < n1) - { - semsg(_(e_list_index_out_of_range_nr), n2); - status = FAIL; - } - if (status != FAIL - && list_unlet_range(l, li, NULL, n1, - tv_idx2->v_type != VAR_SPECIAL, n2) - == FAIL) - status = FAIL; - } - } - } - else - { - status = FAIL; - SOURCING_LNUM = iptr->isn_lnum; - semsg(_(e_cannot_index_str), - vartype_name(tv_dest->v_type)); - } - - clear_tv(tv_idx1); - clear_tv(tv_idx2); - clear_tv(tv_dest); - ectx->ec_stack.ga_len -= 3; - if (status == FAIL) - goto on_error; - } + if (execute_unletrange(iptr, ectx) == FAIL) + goto on_error; break; // push constant @@ -3424,111 +3579,8 @@ exec_instructions(ectx_T *ectx) // top of a for loop case ISN_FOR: - { - typval_T *ltv = STACK_TV_BOT(-1); - typval_T *idxtv = - STACK_TV_VAR(iptr->isn_arg.forloop.for_idx); - - if (GA_GROW_FAILS(&ectx->ec_stack, 1)) - goto theend; - if (ltv->v_type == VAR_LIST) - { - list_T *list = ltv->vval.v_list; - - // push the next item from the list - ++idxtv->vval.v_number; - if (list == NULL - || idxtv->vval.v_number >= list->lv_len) - { - // past the end of the list, jump to "endfor" - ectx->ec_iidx = iptr->isn_arg.forloop.for_end; - may_restore_cmdmod(&ectx->ec_funclocal); - } - else if (list->lv_first == &range_list_item) - { - // non-materialized range() list - tv = STACK_TV_BOT(0); - tv->v_type = VAR_NUMBER; - tv->v_lock = 0; - tv->vval.v_number = list_find_nr( - list, idxtv->vval.v_number, NULL); - ++ectx->ec_stack.ga_len; - } - else - { - listitem_T *li = list_find(list, - idxtv->vval.v_number); - - copy_tv(&li->li_tv, STACK_TV_BOT(0)); - ++ectx->ec_stack.ga_len; - } - } - else if (ltv->v_type == VAR_STRING) - { - char_u *str = ltv->vval.v_string; - - // The index is for the last byte of the previous - // character. - ++idxtv->vval.v_number; - if (str == NULL || str[idxtv->vval.v_number] == NUL) - { - // past the end of the string, jump to "endfor" - ectx->ec_iidx = iptr->isn_arg.forloop.for_end; - may_restore_cmdmod(&ectx->ec_funclocal); - } - else - { - int clen = mb_ptr2len(str + idxtv->vval.v_number); - - // Push the next character from the string. - tv = STACK_TV_BOT(0); - tv->v_type = VAR_STRING; - tv->vval.v_string = vim_strnsave( - str + idxtv->vval.v_number, clen); - ++ectx->ec_stack.ga_len; - idxtv->vval.v_number += clen - 1; - } - } - else if (ltv->v_type == VAR_BLOB) - { - blob_T *blob = ltv->vval.v_blob; - - // When we get here the first time make a copy of the - // blob, so that the iteration still works when it is - // changed. - if (idxtv->vval.v_number == -1 && blob != NULL) - { - blob_copy(blob, ltv); - blob_unref(blob); - blob = ltv->vval.v_blob; - } - - // The index is for the previous byte. - ++idxtv->vval.v_number; - if (blob == NULL - || idxtv->vval.v_number >= blob_len(blob)) - { - // past the end of the blob, jump to "endfor" - ectx->ec_iidx = iptr->isn_arg.forloop.for_end; - may_restore_cmdmod(&ectx->ec_funclocal); - } - else - { - // Push the next byte from the blob. - tv = STACK_TV_BOT(0); - tv->v_type = VAR_NUMBER; - tv->vval.v_number = blob_get(blob, - idxtv->vval.v_number); - ++ectx->ec_stack.ga_len; - } - } - else - { - semsg(_(e_for_loop_on_str_not_supported), - vartype_name(ltv->v_type)); - goto theend; - } - } + if (execute_for(iptr, ectx) == FAIL) + goto theend; break; // start of ":try" block