To: vim_dev@googlegroups.com Subject: Patch 9.0.0949 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.0949 Problem: Crash when unletting a variable while listing variables. Solution: Disallow changing a hashtable while going over the entries. (closes #11435) Files: src/structs.h, src/buffer.c, src/dict.c, src/proto/dict.pro, src/errors.h, src/evalvars.c, src/hashtab.c, src/proto/hashtab.pro, src/if_lua.c, src/if_ruby.c, src/if_py_both.h, src/spellfile.c, src/sign.c, src/syntax.c, src/terminal.c, src/textprop.c, src/vim9execute.c, src/vim9script.c, src/userfunc.c, src/testdir/test_autocmd.vim *** ../vim-9.0.0948/src/structs.h 2022-11-24 13:27:32.389881082 +0000 --- src/structs.h 2022-11-25 15:33:50.290581762 +0000 *************** *** 1313,1318 **** --- 1313,1324 ---- // This allows for storing 10 items (2/3 of 16) before a resize is needed. #define HT_INIT_SIZE 16 + // flags used for ht_flags + #define HTFLAGS_ERROR 0x01 // Set when growing failed, can't add more + // items before growing works. + #define HTFLAGS_FROZEN 0x02 // Trying to add or remove an item will result + // in an error message. + typedef struct hashtable_S { long_u ht_mask; // mask used for hash value (nr of items in *************** *** 1321,1328 **** long_u ht_filled; // number of items used + removed int ht_changed; // incremented when adding or removing an item int ht_locked; // counter for hash_lock() ! int ht_error; // when set growing failed, can't add more ! // items before growing works hashitem_T *ht_array; // points to the array, allocated when it's // not "ht_smallarray" hashitem_T ht_smallarray[HT_INIT_SIZE]; // initial array --- 1327,1333 ---- long_u ht_filled; // number of items used + removed int ht_changed; // incremented when adding or removing an item int ht_locked; // counter for hash_lock() ! int ht_flags; // HTFLAGS_ values hashitem_T *ht_array; // points to the array, allocated when it's // not "ht_smallarray" hashitem_T ht_smallarray[HT_INIT_SIZE]; // initial array *** ../vim-9.0.0948/src/buffer.c 2022-11-07 12:16:46.393761740 +0000 --- src/buffer.c 2022-11-25 15:51:53.973205560 +0000 *************** *** 434,440 **** buf_hashtab_add(buf_T *buf) { sprintf((char *)buf->b_key, "%x", buf->b_fnum); ! if (hash_add(&buf_hashtab, buf->b_key) == FAIL) emsg(_(e_buffer_cannot_be_registered)); } --- 434,440 ---- buf_hashtab_add(buf_T *buf) { sprintf((char *)buf->b_key, "%x", buf->b_fnum); ! if (hash_add(&buf_hashtab, buf->b_key, "create buffer") == FAIL) emsg(_(e_buffer_cannot_be_registered)); } *************** *** 444,450 **** hashitem_T *hi = hash_find(&buf_hashtab, buf->b_key); if (!HASHITEM_EMPTY(hi)) ! hash_remove(&buf_hashtab, hi); } /* --- 444,450 ---- hashitem_T *hi = hash_find(&buf_hashtab, buf->b_key); if (!HASHITEM_EMPTY(hi)) ! hash_remove(&buf_hashtab, hi, "close buffer"); } /* *************** *** 925,931 **** free_buffer_stuff(buf, TRUE); #ifdef FEAT_EVAL // b:changedtick uses an item in buf_T, remove it now ! dictitem_remove(buf->b_vars, (dictitem_T *)&buf->b_ct_di); unref_var_dict(buf->b_vars); remove_listeners(buf); #endif --- 925,931 ---- free_buffer_stuff(buf, TRUE); #ifdef FEAT_EVAL // b:changedtick uses an item in buf_T, remove it now ! dictitem_remove(buf->b_vars, (dictitem_T *)&buf->b_ct_di, "free buffer"); unref_var_dict(buf->b_vars); remove_listeners(buf); #endif *** ../vim-9.0.0948/src/dict.c 2022-11-23 11:33:57.294027200 +0000 --- src/dict.c 2022-11-25 16:15:43.015168141 +0000 *************** *** 122,127 **** --- 122,130 ---- hashitem_T *hi; dictitem_T *di; + if (check_hashtab_frozen(ht, "clear dict")) + return; + // Lock the hashtab, we don't want it to resize while freeing items. hash_lock(ht); todo = (int)ht->ht_used; *************** *** 132,138 **** // Remove the item before deleting it, just in case there is // something recursive causing trouble. di = HI2DI(hi); ! hash_remove(ht, hi); dictitem_free(di); --todo; } --- 135,141 ---- // Remove the item before deleting it, just in case there is // something recursive causing trouble. di = HI2DI(hi); ! hash_remove(ht, hi, "clear dict"); dictitem_free(di); --todo; } *************** *** 256,264 **** /* * Remove item "item" from Dictionary "dict" and free it. */ void ! dictitem_remove(dict_T *dict, dictitem_T *item) { hashitem_T *hi; --- 259,268 ---- /* * Remove item "item" from Dictionary "dict" and free it. + * "command" is used for the error message when the hashtab if frozen. */ void ! dictitem_remove(dict_T *dict, dictitem_T *item, char *command) { hashitem_T *hi; *************** *** 266,272 **** if (HASHITEM_EMPTY(hi)) internal_error("dictitem_remove()"); else ! hash_remove(&dict->dv_hashtab, hi); dictitem_free(item); } --- 270,276 ---- if (HASHITEM_EMPTY(hi)) internal_error("dictitem_remove()"); else ! hash_remove(&dict->dv_hashtab, hi, command); dictitem_free(item); } *************** *** 375,381 **** { if (dict_wrong_func_name(d, &item->di_tv, item->di_key)) return FAIL; ! return hash_add(&d->dv_hashtab, item->di_key); } /* --- 379,385 ---- { if (dict_wrong_func_name(d, &item->di_tv, item->di_key)) return FAIL; ! return hash_add(&d->dv_hashtab, item->di_key, "add to dictionary"); } /* *************** *** 1094,1107 **** char_u *arg_errmsg = (char_u *)N_("extend() argument"); type_T *type; if (d1->dv_type != NULL && d1->dv_type->tt_member != NULL) type = d1->dv_type->tt_member; else type = NULL; - if (*action == 'm') - hash_lock(&d2->dv_hashtab); // don't rehash on hash_remove() - todo = (int)d2->dv_hashtab.ht_used; for (hashitem_T *hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2) { --- 1098,1118 ---- char_u *arg_errmsg = (char_u *)N_("extend() argument"); type_T *type; + if (check_hashtab_frozen(&d1->dv_hashtab, "extend")) + return; + + if (*action == 'm') + { + if (check_hashtab_frozen(&d2->dv_hashtab, "extend")) + return; + hash_lock(&d2->dv_hashtab); // don't rehash on hash_remove() + } + if (d1->dv_type != NULL && d1->dv_type->tt_member != NULL) type = d1->dv_type->tt_member; else type = NULL; todo = (int)d2->dv_hashtab.ht_used; for (hashitem_T *hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2) { *************** *** 1126,1132 **** // If dict_add() fails then "d2" won't be empty. di1 = HI2DI(hi2); if (dict_add(d1, di1) == OK) ! hash_remove(&d2->dv_hashtab, hi2); } else { --- 1137,1143 ---- // If dict_add() fails then "d2" won't be empty. di1 = HI2DI(hi2); if (dict_add(d1, di1) == OK) ! hash_remove(&d2->dv_hashtab, hi2, "extend"); } else { *************** *** 1406,1412 **** if (var_check_fixed(di->di_flags, arg_errmsg, TRUE) || var_check_ro(di->di_flags, arg_errmsg, TRUE)) break; ! dictitem_remove(d, di); } } } --- 1417,1423 ---- if (var_check_fixed(di->di_flags, arg_errmsg, TRUE) || var_check_ro(di->di_flags, arg_errmsg, TRUE)) break; ! dictitem_remove(d, di, "filter"); } } } *************** *** 1453,1459 **** *rettv = di->di_tv; init_tv(&di->di_tv); ! dictitem_remove(d, di); } typedef enum { --- 1464,1470 ---- *rettv = di->di_tv; init_tv(&di->di_tv); ! dictitem_remove(d, di, "remove()"); } typedef enum { *** ../vim-9.0.0948/src/proto/dict.pro 2022-07-23 09:52:00.333814262 +0100 --- src/proto/dict.pro 2022-11-25 16:14:45.703129007 +0000 *************** *** 10,16 **** int dict_free_nonref(int copyID); void dict_free_items(int copyID); dictitem_T *dictitem_alloc(char_u *key); ! void dictitem_remove(dict_T *dict, dictitem_T *item); void dictitem_free(dictitem_T *item); dict_T *dict_copy(dict_T *orig, int deep, int top, int copyID); int dict_wrong_func_name(dict_T *d, typval_T *tv, char_u *name); --- 10,16 ---- int dict_free_nonref(int copyID); void dict_free_items(int copyID); dictitem_T *dictitem_alloc(char_u *key); ! void dictitem_remove(dict_T *dict, dictitem_T *item, char *command); void dictitem_free(dictitem_T *item); dict_T *dict_copy(dict_T *orig, int deep, int top, int copyID); int dict_wrong_func_name(dict_T *d, typval_T *tv, char_u *name); *** ../vim-9.0.0948/src/errors.h 2022-11-19 11:41:26.341764390 +0000 --- src/errors.h 2022-11-25 15:43:23.962714750 +0000 *************** *** 3343,3345 **** --- 3343,3347 ---- INIT(= N_("E1311: Cannot change user commands while listing")); EXTERN char e_not_allowed_to_change_window_layout_in_this_autocmd[] INIT(= N_("E1312: Not allowed to change the window layout in this autocmd")); + EXTERN char e_not_allowed_to_add_or_remove_entries_str[] + INIT(= N_("E1313: Not allowed to add or remove entries (%s)")); *** ../vim-9.0.0948/src/evalvars.c 2022-11-16 20:33:17.088528372 +0000 --- src/evalvars.c 2022-11-25 16:22:31.953763428 +0000 *************** *** 217,226 **** // add to v: scope dict, unless the value is not always available if (p->vv_tv_type != VAR_UNKNOWN) ! hash_add(&vimvarht, p->vv_di.di_key); if (p->vv_flags & VV_COMPAT) // add to compat scope dict ! hash_add(&compat_hashtab, p->vv_di.di_key); } set_vim_var_nr(VV_VERSION, VIM_VERSION_100); set_vim_var_nr(VV_VERSIONLONG, VIM_VERSION_100 * 10000 + highest_patch()); --- 217,226 ---- // add to v: scope dict, unless the value is not always available if (p->vv_tv_type != VAR_UNKNOWN) ! hash_add(&vimvarht, p->vv_di.di_key, "initialization"); if (p->vv_flags & VV_COMPAT) // add to compat scope dict ! hash_add(&compat_hashtab, p->vv_di.di_key, "initialization"); } set_vim_var_nr(VV_VERSION, VIM_VERSION_100); set_vim_var_nr(VV_VERSIONLONG, VIM_VERSION_100 * 10000 + highest_patch()); *************** *** 562,568 **** *save_tv = vimvars[idx].vv_tv; vimvars[idx].vv_str = NULL; // don't free it now if (vimvars[idx].vv_tv_type == VAR_UNKNOWN) ! hash_add(&vimvarht, vimvars[idx].vv_di.di_key); } /* --- 562,568 ---- *save_tv = vimvars[idx].vv_tv; vimvars[idx].vv_str = NULL; // don't free it now if (vimvars[idx].vv_tv_type == VAR_UNKNOWN) ! hash_add(&vimvarht, vimvars[idx].vv_di.di_key, "prepare vimvar"); } /* *************** *** 582,588 **** if (HASHITEM_EMPTY(hi)) internal_error("restore_vimvar()"); else ! hash_remove(&vimvarht, hi); } } --- 582,588 ---- if (HASHITEM_EMPTY(hi)) internal_error("restore_vimvar()"); else ! hash_remove(&vimvarht, hi, "restore vimvar"); } } *************** *** 1380,1385 **** --- 1380,1388 ---- int todo; char_u buf[IOSIZE]; + int save_ht_flags = ht->ht_flags; + ht->ht_flags |= HTFLAGS_FROZEN; + todo = (int)ht->ht_used; for (hi = ht->ht_array; todo > 0 && !got_int; ++hi) { *************** *** 1399,1404 **** --- 1402,1409 ---- list_one_var(di, prefix, first); } } + + ht->ht_flags = save_ht_flags; } /* *************** *** 2008,2014 **** listitem_remove(lp->ll_list, lp->ll_li); else // unlet a Dictionary item. ! dictitem_remove(lp->ll_dict, lp->ll_di); return ret; } --- 2013,2019 ---- listitem_remove(lp->ll_list, lp->ll_li); else // unlet a Dictionary item. ! dictitem_remove(lp->ll_dict, lp->ll_di, "unlet"); return ret; } *************** *** 2095,2101 **** di = HI2DI(hi); if (var_check_fixed(di->di_flags, name, FALSE) || var_check_ro(di->di_flags, name, FALSE) ! || value_check_lock(d->dv_lock, name, FALSE)) return FAIL; delete_var(ht, hi); --- 2100,2107 ---- di = HI2DI(hi); if (var_check_fixed(di->di_flags, name, FALSE) || var_check_ro(di->di_flags, name, FALSE) ! || value_check_lock(d->dv_lock, name, FALSE) ! || check_hashtab_frozen(ht, "unlet")) return FAIL; delete_var(ht, hi); *************** *** 3554,3562 **** { dictitem_T *di = HI2DI(hi); ! hash_remove(ht, hi); ! clear_tv(&di->di_tv); ! vim_free(di); } /* --- 3560,3570 ---- { dictitem_T *di = HI2DI(hi); ! if (hash_remove(ht, hi, "delete variable") == OK) ! { ! clear_tv(&di->di_tv); ! vim_free(di); ! } } /* *************** *** 3895,3900 **** --- 3903,3911 ---- goto failed; } + if (check_hashtab_frozen(ht, "add variable")) + goto failed; + // Can't add "v:" or "a:" variable. if (ht == &vimvarht || ht == get_funccal_args_ht()) { *************** *** 3913,3919 **** if (di == NULL) goto failed; STRCPY(di->di_key, varname); ! if (hash_add(ht, DI2HIKEY(di)) == FAIL) { vim_free(di); goto failed; --- 3924,3930 ---- if (di == NULL) goto failed; STRCPY(di->di_key, varname); ! if (hash_add(ht, DI2HIKEY(di), "add variable") == FAIL) { vim_free(di); goto failed; *** ../vim-9.0.0948/src/hashtab.c 2022-02-03 13:02:32.000000000 +0000 --- src/hashtab.c 2022-11-25 16:22:18.337806451 +0000 *************** *** 71,76 **** --- 71,90 ---- } /* + * If "ht->ht_flags" has HTFLAGS_FROZEN then give an error message using + * "command" and return TRUE. + */ + int + check_hashtab_frozen(hashtab_T *ht, char *command) + { + if ((ht->ht_flags & HTFLAGS_FROZEN) == 0) + return FALSE; + + semsg(_(e_not_allowed_to_add_or_remove_entries_str), command); + return TRUE; + } + + /* * Free the array of a hash table. Does not free the items it contains! * If "ht" is not freed then you should call hash_init() next! */ *************** *** 201,214 **** /* * Add item with key "key" to hashtable "ht". * Returns FAIL when out of memory or the key is already present. */ int ! hash_add(hashtab_T *ht, char_u *key) { hash_T hash = hash_hash(key); hashitem_T *hi; hi = hash_lookup(ht, key, hash); if (!HASHITEM_EMPTY(hi)) { --- 215,231 ---- /* * Add item with key "key" to hashtable "ht". + * "command" is used for the error message when the hashtab if frozen. * Returns FAIL when out of memory or the key is already present. */ int ! hash_add(hashtab_T *ht, char_u *key, char *command) { hash_T hash = hash_hash(key); hashitem_T *hi; + if (check_hashtab_frozen(ht, command)) + return FAIL; hi = hash_lookup(ht, key, hash); if (!HASHITEM_EMPTY(hi)) { *************** *** 232,238 **** hash_T hash) { // If resizing failed before and it fails again we can't add an item. ! if (ht->ht_error && hash_may_resize(ht, 0) == FAIL) return FAIL; ++ht->ht_used; --- 249,255 ---- hash_T hash) { // If resizing failed before and it fails again we can't add an item. ! if ((ht->ht_flags & HTFLAGS_ERROR) && hash_may_resize(ht, 0) == FAIL) return FAIL; ++ht->ht_used; *************** *** 266,280 **** /* * Remove item "hi" from hashtable "ht". "hi" must have been obtained with * hash_lookup(). * The caller must take care of freeing the item itself. */ ! void ! hash_remove(hashtab_T *ht, hashitem_T *hi) { --ht->ht_used; ++ht->ht_changed; hi->hi_key = HI_KEY_REMOVED; hash_may_resize(ht, 0); } /* --- 283,301 ---- /* * Remove item "hi" from hashtable "ht". "hi" must have been obtained with * hash_lookup(). + * "command" is used for the error message when the hashtab if frozen. * The caller must take care of freeing the item itself. */ ! int ! hash_remove(hashtab_T *ht, hashitem_T *hi, char *command) { + if (check_hashtab_frozen(ht, command)) + return FAIL; --ht->ht_used; ++ht->ht_changed; hi->hi_key = HI_KEY_REMOVED; hash_may_resize(ht, 0); + return OK; } /* *************** *** 407,417 **** if (newarray == NULL) { // Out of memory. When there are NULL items still return OK. ! // Otherwise set ht_error, because lookup may result in a hang if ! // we add another item. if (ht->ht_filled < ht->ht_mask) return OK; ! ht->ht_error = TRUE; return FAIL; } oldarray = ht->ht_array; --- 428,438 ---- if (newarray == NULL) { // Out of memory. When there are NULL items still return OK. ! // Otherwise set ht_flags to HTFLAGS_ERROR, because lookup may ! // result in a hang if we add another item. if (ht->ht_filled < ht->ht_mask) return OK; ! ht->ht_flags |= HTFLAGS_ERROR; return FAIL; } oldarray = ht->ht_array; *************** *** 453,459 **** ht->ht_mask = newmask; ht->ht_filled = ht->ht_used; ++ht->ht_changed; ! ht->ht_error = FALSE; return OK; } --- 474,480 ---- ht->ht_mask = newmask; ht->ht_filled = ht->ht_used; ++ht->ht_changed; ! ht->ht_flags &= ~HTFLAGS_ERROR; return OK; } *** ../vim-9.0.0948/src/proto/hashtab.pro 2022-06-27 23:15:08.000000000 +0100 --- src/proto/hashtab.pro 2022-11-25 16:22:46.221719381 +0000 *************** *** 1,13 **** /* hashtab.c */ void hash_init(hashtab_T *ht); void hash_clear(hashtab_T *ht); void hash_clear_all(hashtab_T *ht, int off); hashitem_T *hash_find(hashtab_T *ht, char_u *key); hashitem_T *hash_lookup(hashtab_T *ht, char_u *key, hash_T hash); void hash_debug_results(void); ! int hash_add(hashtab_T *ht, char_u *key); int hash_add_item(hashtab_T *ht, hashitem_T *hi, char_u *key, hash_T hash); ! void hash_remove(hashtab_T *ht, hashitem_T *hi); void hash_lock(hashtab_T *ht); void hash_lock_size(hashtab_T *ht, int size); void hash_unlock(hashtab_T *ht); --- 1,14 ---- /* hashtab.c */ void hash_init(hashtab_T *ht); + int check_hashtab_frozen(hashtab_T *ht, char *command); void hash_clear(hashtab_T *ht); void hash_clear_all(hashtab_T *ht, int off); hashitem_T *hash_find(hashtab_T *ht, char_u *key); hashitem_T *hash_lookup(hashtab_T *ht, char_u *key, hash_T hash); void hash_debug_results(void); ! int hash_add(hashtab_T *ht, char_u *key, char *command); int hash_add_item(hashtab_T *ht, hashitem_T *hi, char_u *key, hash_T hash); ! int hash_remove(hashtab_T *ht, hashitem_T *hi, char *command); void hash_lock(hashtab_T *ht); void hash_lock_size(hashtab_T *ht, int size); void hash_unlock(hashtab_T *ht); *** ../vim-9.0.0948/src/if_lua.c 2022-09-17 21:07:52.099993159 +0100 --- src/if_lua.c 2022-11-25 16:16:29.083198869 +0000 *************** *** 1150,1156 **** if (lua_isnil(L, 3)) // remove? { hashitem_T *hi = hash_find(&d->dv_hashtab, di->di_key); ! hash_remove(&d->dv_hashtab, hi); dictitem_free(di); } else --- 1150,1156 ---- if (lua_isnil(L, 3)) // remove? { hashitem_T *hi = hash_find(&d->dv_hashtab, di->di_key); ! hash_remove(&d->dv_hashtab, hi, "Lua new index"); dictitem_free(di); } else *************** *** 1838,1846 **** if (di == NULL) // Doesn't exist, nothing to do return 0; ! else ! // Delete the entry ! dictitem_remove(dict, di); } else { --- 1838,1845 ---- if (di == NULL) // Doesn't exist, nothing to do return 0; ! // Delete the entry ! dictitem_remove(dict, di, "Lua delete variable"); } else { *** ../vim-9.0.0948/src/if_ruby.c 2022-09-22 16:11:47.281127583 +0100 --- src/if_ruby.c 2022-11-25 15:36:19.674531651 +0000 *************** *** 1799,1805 **** if (di == NULL || ruby_convert_to_vim_value(val, &di->di_tv) != OK || dict_add(d, di) != OK) { ! d->dv_hashtab.ht_error = TRUE; return ST_STOP; } return ST_CONTINUE; --- 1799,1805 ---- if (di == NULL || ruby_convert_to_vim_value(val, &di->di_tv) != OK || dict_add(d, di) != OK) { ! d->dv_hashtab.ht_flags |= HTFLAGS_ERROR; return ST_STOP; } return ST_CONTINUE; *************** *** 1879,1885 **** return FAIL; rb_hash_foreach(val, convert_hash2dict, (VALUE)d); ! if (d->dv_hashtab.ht_error) { dict_unref(d); return FAIL; --- 1879,1885 ---- return FAIL; rb_hash_foreach(val, convert_hash2dict, (VALUE)d); ! if (d->dv_hashtab.ht_flags & HTFLAGS_ERROR) { dict_unref(d); return FAIL; *** ../vim-9.0.0948/src/if_py_both.h 2022-09-17 21:07:52.103993150 +0100 --- src/if_py_both.h 2022-11-25 16:14:12.627105898 +0000 *************** *** 1768,1774 **** return NULL; } ! hash_remove(&dict->dv_hashtab, hi); dictitem_free(di); } --- 1768,1774 ---- return NULL; } ! hash_remove(&dict->dv_hashtab, hi, "Python remove variable"); dictitem_free(di); } *************** *** 1893,1899 **** return -1; } hi = hash_find(&dict->dv_hashtab, di->di_key); ! hash_remove(&dict->dv_hashtab, hi); dictitem_free(di); Py_XDECREF(todecref); return 0; --- 1893,1899 ---- return -1; } hi = hash_find(&dict->dv_hashtab, di->di_key); ! hash_remove(&dict->dv_hashtab, hi, "Python remove item"); dictitem_free(di); Py_XDECREF(todecref); return 0; *************** *** 2194,2200 **** return NULL; } ! hash_remove(&self->dict->dv_hashtab, hi); dictitem_free(di); return ret; --- 2194,2200 ---- return NULL; } ! hash_remove(&self->dict->dv_hashtab, hi, "Python pop item"); dictitem_free(di); return ret; *** ../vim-9.0.0948/src/spellfile.c 2022-11-02 13:30:37.538314551 +0000 --- src/spellfile.c 2022-11-25 15:58:10.438078816 +0000 *************** *** 2643,2649 **** smsg(_("Affix also used for BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST in %s line %d: %s"), fname, lnum, items[1]); STRCPY(cur_aff->ah_key, items[1]); ! hash_add(tp, cur_aff->ah_key); cur_aff->ah_combine = (*items[2] == 'Y'); } --- 2643,2649 ---- smsg(_("Affix also used for BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST in %s line %d: %s"), fname, lnum, items[1]); STRCPY(cur_aff->ah_key, items[1]); ! hash_add(tp, cur_aff->ah_key, "spelling"); cur_aff->ah_combine = (*items[2] == 'Y'); } *************** *** 2994,3000 **** p = vim_strsave(items[i]); if (p == NULL) break; ! hash_add(&spin->si_commonwords, p); } } } --- 2994,3000 ---- p = vim_strsave(items[i]); if (p == NULL) break; ! hash_add(&spin->si_commonwords, p, "spelling"); } } } *************** *** 3312,3318 **** id = spin->si_newcompID--; } while (vim_strchr((char_u *)"/?*+[]\\-^", id) != NULL); ci->ci_newID = id; ! hash_add(&aff->af_comp, ci->ci_key); } *tp++ = id; } --- 3312,3318 ---- id = spin->si_newcompID--; } while (vim_strchr((char_u *)"/?*+[]\\-^", id) != NULL); ci->ci_newID = id; ! hash_add(&aff->af_comp, ci->ci_key, "spelling"); } *tp++ = id; } *** ../vim-9.0.0948/src/sign.c 2022-09-02 15:15:11.063569185 +0100 --- src/sign.c 2022-11-25 16:11:32.590988048 +0000 *************** *** 126,132 **** if (group->sg_refcount == 0) { // All the signs in this group are removed ! hash_remove(&sg_table, hi); vim_free(group); } } --- 126,132 ---- if (group->sg_refcount == 0) { // All the signs in this group are removed ! hash_remove(&sg_table, hi, "sign remove"); vim_free(group); } } *** ../vim-9.0.0948/src/syntax.c 2022-10-14 17:04:05.891675444 +0100 --- src/syntax.c 2022-11-25 16:11:54.767005038 +0000 *************** *** 4339,4345 **** if (kp_prev == NULL) { if (kp_next == NULL) ! hash_remove(ht, hi); else hi->hi_key = KE2HIKEY(kp_next); } --- 4339,4345 ---- if (kp_prev == NULL) { if (kp_next == NULL) ! hash_remove(ht, hi, "syntax clear keyword"); else hi->hi_key = KE2HIKEY(kp_next); } *** ../vim-9.0.0948/src/terminal.c 2022-11-24 14:05:15.526236452 +0000 --- src/terminal.c 2022-11-25 15:58:31.802115403 +0000 *************** *** 1020,1026 **** char *hash_key = alloc(NUMBUFLEN); vim_snprintf(hash_key, NUMBUFLEN, "%d", bufnr); ! hash_add(terminal_bufs, (char_u *)hash_key); } return put_eol(fd); --- 1020,1026 ---- char *hash_key = alloc(NUMBUFLEN); vim_snprintf(hash_key, NUMBUFLEN, "%d", bufnr); ! hash_add(terminal_bufs, (char_u *)hash_key, "terminal session"); } return put_eol(fd); *** ../vim-9.0.0948/src/textprop.c 2022-11-02 13:30:37.542314565 +0000 --- src/textprop.c 2022-11-25 16:12:27.035029368 +0000 *************** *** 1789,1795 **** } hash_init(*htp); } ! hash_add(*htp, PT2HIKEY(prop)); } else { --- 1789,1795 ---- } hash_init(*htp); } ! hash_add(*htp, PT2HIKEY(prop), "prop type"); } else { *************** *** 1924,1930 **** ht = buf->b_proptypes; VIM_CLEAR(buf->b_proparray); } ! hash_remove(ht, hi); vim_free(prop); } } --- 1924,1930 ---- ht = buf->b_proptypes; VIM_CLEAR(buf->b_proparray); } ! hash_remove(ht, hi, "prop type delete"); vim_free(prop); } } *** ../vim-9.0.0948/src/vim9execute.c 2022-11-02 13:30:37.542314565 +0000 --- src/vim9execute.c 2022-11-25 16:16:58.735168936 +0000 *************** *** 2366,2372 **** NULL, FALSE)) status = FAIL; else ! dictitem_remove(d, di); } } } --- 2366,2372 ---- NULL, FALSE)) status = FAIL; else ! dictitem_remove(d, di, "unlet"); } } } *** ../vim-9.0.0948/src/vim9script.c 2022-10-07 17:26:19.019293893 +0100 --- src/vim9script.c 2022-11-25 16:12:57.267051748 +0000 *************** *** 942,948 **** if (HASHITEM_EMPTY(hi)) // new variable name ! hash_add(&si->sn_all_vars.dv_hashtab, newsav->sav_key); else if (sav != NULL) // existing name in a new block, append to the list sav->sav_next = newsav; --- 942,949 ---- if (HASHITEM_EMPTY(hi)) // new variable name ! hash_add(&si->sn_all_vars.dv_hashtab, newsav->sav_key, ! "add variable"); else if (sav != NULL) // existing name in a new block, append to the list sav->sav_next = newsav; *************** *** 1033,1039 **** else { if (sav_prev == NULL) ! hash_remove(all_ht, all_hi); else sav_prev->sav_next = sav->sav_next; sv->sv_name = NULL; --- 1034,1040 ---- else { if (sav_prev == NULL) ! hash_remove(all_ht, all_hi, "hide variable"); else sav_prev->sav_next = sav->sav_next; sv->sv_name = NULL; *** ../vim-9.0.0948/src/userfunc.c 2022-11-13 22:13:29.848975595 +0000 --- src/userfunc.c 2022-11-25 16:17:37.018962640 +0000 *************** *** 585,591 **** fp->uf_cb_state = state; set_ufunc_name(fp, name); ! hash_add(&func_hashtab, UF2HIKEY(fp)); return name; } --- 585,591 ---- fp->uf_cb_state = state; set_ufunc_name(fp, name); ! hash_add(&func_hashtab, UF2HIKEY(fp), "add C function"); return name; } *************** *** 1278,1284 **** if (ufunc == NULL) goto erret; set_ufunc_name(ufunc, name); ! if (hash_add(&func_hashtab, UF2HIKEY(ufunc)) == FAIL) goto erret; ufunc->uf_flags = FC_LAMBDA; ufunc->uf_refcount = 1; --- 1278,1284 ---- if (ufunc == NULL) goto erret; set_ufunc_name(ufunc, name); ! if (hash_add(&func_hashtab, UF2HIKEY(ufunc), "add function") == FAIL) goto erret; ufunc->uf_flags = FC_LAMBDA; ufunc->uf_refcount = 1; *************** *** 1572,1578 **** rettv->vval.v_partial = pt; rettv->v_type = VAR_PARTIAL; ! hash_add(&func_hashtab, UF2HIKEY(fp)); } theend: --- 1572,1578 ---- rettv->vval.v_partial = pt; rettv->v_type = VAR_PARTIAL; ! hash_add(&func_hashtab, UF2HIKEY(fp), "add lambda"); } theend: *************** *** 2128,2134 **** { STRCPY(v->di_key, name); v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; ! hash_add(&dp->dv_hashtab, DI2HIKEY(v)); v->di_tv.v_type = VAR_NUMBER; v->di_tv.v_lock = VAR_FIXED; v->di_tv.vval.v_number = nr; --- 2128,2134 ---- { STRCPY(v->di_key, name); v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; ! hash_add(&dp->dv_hashtab, DI2HIKEY(v), "add variable"); v->di_tv.v_type = VAR_NUMBER; v->di_tv.v_lock = VAR_FIXED; v->di_tv.vval.v_number = nr; *************** *** 2348,2354 **** fp->uf_flags |= FC_DEAD; return FALSE; } ! hash_remove(&func_hashtab, hi); fp->uf_flags |= FC_DELETED; return TRUE; } --- 2348,2354 ---- fp->uf_flags |= FC_DEAD; return FALSE; } ! hash_remove(&func_hashtab, hi, "remove function"); fp->uf_flags |= FC_DELETED; return TRUE; } *************** *** 2510,2516 **** fp->uf_refcount = 1; STRCPY(fp->uf_name, global); ! hash_add(&func_hashtab, UF2HIKEY(fp)); // the referenced dfunc_T is now used one more time link_def_function(fp); --- 2510,2516 ---- fp->uf_refcount = 1; STRCPY(fp->uf_name, global); ! hash_add(&func_hashtab, UF2HIKEY(fp), "copy lambda"); // the referenced dfunc_T is now used one more time link_def_function(fp); *************** *** 2718,2724 **** name = v->di_key; STRCPY(name, "self"); v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; ! hash_add(&fc->fc_l_vars.dv_hashtab, DI2HIKEY(v)); v->di_tv.v_type = VAR_DICT; v->di_tv.v_lock = 0; v->di_tv.vval.v_dict = selfdict; --- 2718,2724 ---- name = v->di_key; STRCPY(name, "self"); v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; ! hash_add(&fc->fc_l_vars.dv_hashtab, DI2HIKEY(v), "set self dictionary"); v->di_tv.v_type = VAR_DICT; v->di_tv.v_lock = 0; v->di_tv.vval.v_dict = selfdict; *************** *** 2744,2750 **** name = v->di_key; STRCPY(name, "000"); v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; ! hash_add(&fc->fc_l_avars.dv_hashtab, DI2HIKEY(v)); v->di_tv.v_type = VAR_LIST; v->di_tv.v_lock = VAR_FIXED; v->di_tv.vval.v_list = &fc->fc_l_varlist; --- 2744,2750 ---- name = v->di_key; STRCPY(name, "000"); v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; ! hash_add(&fc->fc_l_avars.dv_hashtab, DI2HIKEY(v), "function argument"); v->di_tv.v_type = VAR_LIST; v->di_tv.v_lock = VAR_FIXED; v->di_tv.vval.v_list = &fc->fc_l_varlist; *************** *** 2838,2847 **** // Named arguments should be accessed without the "a:" prefix in // lambda expressions. Add to the l: dict. copy_tv(&v->di_tv, &v->di_tv); ! hash_add(&fc->fc_l_vars.dv_hashtab, DI2HIKEY(v)); } else ! hash_add(&fc->fc_l_avars.dv_hashtab, DI2HIKEY(v)); if (ai >= 0 && ai < MAX_FUNC_ARGS) { --- 2838,2847 ---- // Named arguments should be accessed without the "a:" prefix in // lambda expressions. Add to the l: dict. copy_tv(&v->di_tv, &v->di_tv); ! hash_add(&fc->fc_l_vars.dv_hashtab, DI2HIKEY(v), "local variable"); } else ! hash_add(&fc->fc_l_avars.dv_hashtab, DI2HIKEY(v), "add variable"); if (ai >= 0 && ai < MAX_FUNC_ARGS) { *************** *** 5060,5066 **** hi = hash_find(&func_hashtab, name); hi->hi_key = UF2HIKEY(fp); } ! else if (hash_add(&func_hashtab, UF2HIKEY(fp)) == FAIL) { free_fp = TRUE; goto erret; --- 5060,5066 ---- hi = hash_find(&func_hashtab, name); hi->hi_key = UF2HIKEY(fp); } ! else if (hash_add(&func_hashtab, UF2HIKEY(fp), "add function") == FAIL) { free_fp = TRUE; goto erret; *************** *** 5462,5468 **** { // Delete the dict item that refers to the function, it will // invoke func_unref() and possibly delete the function. ! dictitem_remove(fudi.fd_dict, fudi.fd_di); } else { --- 5462,5468 ---- { // Delete the dict item that refers to the function, it will // invoke func_unref() and possibly delete the function. ! dictitem_remove(fudi.fd_dict, fudi.fd_di, "delfunction"); } else { *** ../vim-9.0.0948/src/testdir/test_autocmd.vim 2022-11-22 12:40:44.066427878 +0000 --- src/testdir/test_autocmd.vim 2022-11-25 16:26:19.629169272 +0000 *************** *** 2326,2331 **** --- 2326,2353 ---- call StopVimInTerminal(buf) endfunc + func Test_autocmd_CmdlineLeave_unlet() + CheckRunVimInTerminal + + let lines =<< trim END + for i in range(1, 999) + exe 'let g:var' .. i '=' i + endfor + au CmdlineLeave : call timer_start(0, {-> execute('unlet g:var990')}) + END + call writefile(lines, 'XleaveUnlet', 'D') + let buf = RunVimInTerminal('-S XleaveUnlet', {'rows': 10}) + + " this was using freed memory + call term_sendkeys(buf, ":let g:\") + call TermWait(buf, 50) + call term_sendkeys(buf, "G") + call TermWait(buf, 50) + call term_sendkeys(buf, "\") " for the hit-enter prompt + + call StopVimInTerminal(buf) + endfunc + function s:Before_test_dirchanged() augroup test_dirchanged autocmd! *** ../vim-9.0.0948/src/version.c 2022-11-25 15:09:30.710402884 +0000 --- src/version.c 2022-11-25 15:29:26.846659184 +0000 *************** *** 697,698 **** --- 697,700 ---- { /* Add new patch number below this line */ + /**/ + 949, /**/ -- hundred-and-one symptoms of being an internet addict: 143. You dream in pallettes of 216 websafe colors. /// 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 ///