To: vim_dev@googlegroups.com Subject: Patch 8.2.4198 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.4198 Problem: Vim9: the switch for executing instructions is too long. Solution: Move some code to separate functions. Files: src/vim9execute.c *** ../vim-8.2.4197/src/vim9execute.c 2022-01-18 17:43:01.061598437 +0000 --- src/vim9execute.c 2022-01-23 19:57:34.023716035 +0000 *************** *** 1694,1699 **** --- 1694,2265 ---- } /* + * 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,1803 **** --- 2364,2375 ---- 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,2818 **** // 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); goto on_error; ! } } 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; ! } break; // load or store variable or argument from outer scope --- 3123,3141 ---- // store value in list or dict variable case ISN_STOREINDEX: { ! 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: ! if (execute_storerange(iptr, ectx) == FAIL) ! goto on_error; break; // load or store variable or argument from outer scope *************** *** 2861,3042 **** // 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; ! } 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; ! } break; // push constant --- 3184,3197 ---- // unlet item in list or dict variable case ISN_UNLETINDEX: ! if (execute_unletindex(iptr, ectx) == FAIL) ! goto on_error; break; // unlet range of items in list variable case ISN_UNLETRANGE: ! if (execute_unletrange(iptr, ectx) == FAIL) ! goto on_error; break; // push constant *************** *** 3424,3534 **** // 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; ! } ! } break; // start of ":try" block --- 3579,3586 ---- // top of a for loop case ISN_FOR: ! if (execute_for(iptr, ectx) == FAIL) ! goto theend; break; // start of ":try" block *** ../vim-8.2.4197/src/version.c 2022-01-23 17:59:01.226173123 +0000 --- src/version.c 2022-01-23 19:50:55.675808046 +0000 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 4198, /**/ -- Q: What kind of stuff do you do? A: I collect hobbies. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///