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:\<CR>")
+ call TermWait(buf, 50)
+ call term_sendkeys(buf, "G")
+ call TermWait(buf, 50)
+ call term_sendkeys(buf, "\<CR>") " 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 -- Br...@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 ///