summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2022-11-25 16:31:51 +0000
committerBram Moolenaar <Bram@vim.org>2022-11-25 16:31:51 +0000
commitef2c325f5e3c437b722bb96bf369ba2a5c541163 (patch)
treedc85f0dc98dce1937b459d8d3882473f25db03c3
parentc1cf4c91072f91b6b8dd636627a4ddf6f4b21f16 (diff)
downloadvim-git-ef2c325f5e3c437b722bb96bf369ba2a5c541163.tar.gz
patch 9.0.0949: crash when unletting a variable while listing variablesv9.0.0949
Problem: Crash when unletting a variable while listing variables. Solution: Disallow changing a hashtable while going over the entries. (closes #11435)
-rw-r--r--src/buffer.c6
-rw-r--r--src/dict.c31
-rw-r--r--src/errors.h2
-rw-r--r--src/evalvars.c31
-rw-r--r--src/hashtab.c37
-rw-r--r--src/if_lua.c7
-rw-r--r--src/if_py_both.h6
-rw-r--r--src/if_ruby.c4
-rw-r--r--src/proto/dict.pro2
-rw-r--r--src/proto/hashtab.pro5
-rw-r--r--src/sign.c2
-rw-r--r--src/spellfile.c6
-rw-r--r--src/structs.h9
-rw-r--r--src/syntax.c2
-rw-r--r--src/terminal.c2
-rw-r--r--src/testdir/test_autocmd.vim22
-rw-r--r--src/textprop.c4
-rw-r--r--src/userfunc.c24
-rw-r--r--src/version.c2
-rw-r--r--src/vim9execute.c2
-rw-r--r--src/vim9script.c5
21 files changed, 143 insertions, 68 deletions
diff --git a/src/buffer.c b/src/buffer.c
index de4c40b58..f99da25e8 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -434,7 +434,7 @@ static hashtab_T buf_hashtab;
buf_hashtab_add(buf_T *buf)
{
sprintf((char *)buf->b_key, "%x", buf->b_fnum);
- if (hash_add(&buf_hashtab, buf->b_key) == FAIL)
+ if (hash_add(&buf_hashtab, buf->b_key, "create buffer") == FAIL)
emsg(_(e_buffer_cannot_be_registered));
}
@@ -444,7 +444,7 @@ buf_hashtab_remove(buf_T *buf)
hashitem_T *hi = hash_find(&buf_hashtab, buf->b_key);
if (!HASHITEM_EMPTY(hi))
- hash_remove(&buf_hashtab, hi);
+ hash_remove(&buf_hashtab, hi, "close buffer");
}
/*
@@ -925,7 +925,7 @@ free_buffer(buf_T *buf)
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);
+ dictitem_remove(buf->b_vars, (dictitem_T *)&buf->b_ct_di, "free buffer");
unref_var_dict(buf->b_vars);
remove_listeners(buf);
#endif
diff --git a/src/dict.c b/src/dict.c
index ffd5d381e..1f34b8a88 100644
--- a/src/dict.c
+++ b/src/dict.c
@@ -122,6 +122,9 @@ hashtab_free_contents(hashtab_T *ht)
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,7 +135,7 @@ hashtab_free_contents(hashtab_T *ht)
// Remove the item before deleting it, just in case there is
// something recursive causing trouble.
di = HI2DI(hi);
- hash_remove(ht, hi);
+ hash_remove(ht, hi, "clear dict");
dictitem_free(di);
--todo;
}
@@ -256,9 +259,10 @@ dictitem_copy(dictitem_T *org)
/*
* 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)
+dictitem_remove(dict_T *dict, dictitem_T *item, char *command)
{
hashitem_T *hi;
@@ -266,7 +270,7 @@ dictitem_remove(dict_T *dict, dictitem_T *item)
if (HASHITEM_EMPTY(hi))
internal_error("dictitem_remove()");
else
- hash_remove(&dict->dv_hashtab, hi);
+ hash_remove(&dict->dv_hashtab, hi, command);
dictitem_free(item);
}
@@ -375,7 +379,7 @@ dict_add(dict_T *d, dictitem_T *item)
{
if (dict_wrong_func_name(d, &item->di_tv, item->di_key))
return FAIL;
- return hash_add(&d->dv_hashtab, item->di_key);
+ return hash_add(&d->dv_hashtab, item->di_key, "add to dictionary");
}
/*
@@ -1094,14 +1098,21 @@ dict_extend(dict_T *d1, dict_T *d2, char_u *action, char *func_name)
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;
- 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)
{
@@ -1126,7 +1137,7 @@ dict_extend(dict_T *d1, dict_T *d2, char_u *action, char *func_name)
// 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);
+ hash_remove(&d2->dv_hashtab, hi2, "extend");
}
else
{
@@ -1406,7 +1417,7 @@ dict_filter_map(
if (var_check_fixed(di->di_flags, arg_errmsg, TRUE)
|| var_check_ro(di->di_flags, arg_errmsg, TRUE))
break;
- dictitem_remove(d, di);
+ dictitem_remove(d, di, "filter");
}
}
}
@@ -1453,7 +1464,7 @@ dict_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
*rettv = di->di_tv;
init_tv(&di->di_tv);
- dictitem_remove(d, di);
+ dictitem_remove(d, di, "remove()");
}
typedef enum {
diff --git a/src/errors.h b/src/errors.h
index 30032c0aa..2fc80c5f1 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -3343,3 +3343,5 @@ EXTERN char e_cannot_change_user_commands_while_listing[]
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)"));
diff --git a/src/evalvars.c b/src/evalvars.c
index 793f5632c..28516c172 100644
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -217,10 +217,10 @@ evalvars_init(void)
// 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);
+ 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);
+ 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,7 +562,7 @@ prepare_vimvar(int idx, typval_T *save_tv)
*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);
+ hash_add(&vimvarht, vimvars[idx].vv_di.di_key, "prepare vimvar");
}
/*
@@ -582,7 +582,7 @@ restore_vimvar(int idx, typval_T *save_tv)
if (HASHITEM_EMPTY(hi))
internal_error("restore_vimvar()");
else
- hash_remove(&vimvarht, hi);
+ hash_remove(&vimvarht, hi, "restore vimvar");
}
}
@@ -1380,6 +1380,9 @@ list_hashtable_vars(
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,6 +1402,8 @@ list_hashtable_vars(
list_one_var(di, prefix, first);
}
}
+
+ ht->ht_flags = save_ht_flags;
}
/*
@@ -2008,7 +2013,7 @@ do_unlet_var(
listitem_remove(lp->ll_list, lp->ll_li);
else
// unlet a Dictionary item.
- dictitem_remove(lp->ll_dict, lp->ll_di);
+ dictitem_remove(lp->ll_dict, lp->ll_di, "unlet");
return ret;
}
@@ -2095,7 +2100,8 @@ do_unlet(char_u *name, int forceit)
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))
+ || value_check_lock(d->dv_lock, name, FALSE)
+ || check_hashtab_frozen(ht, "unlet"))
return FAIL;
delete_var(ht, hi);
@@ -3554,9 +3560,11 @@ delete_var(hashtab_T *ht, hashitem_T *hi)
{
dictitem_T *di = HI2DI(hi);
- hash_remove(ht, hi);
- clear_tv(&di->di_tv);
- vim_free(di);
+ if (hash_remove(ht, hi, "delete variable") == OK)
+ {
+ clear_tv(&di->di_tv);
+ vim_free(di);
+ }
}
/*
@@ -3895,6 +3903,9 @@ set_var_const(
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,7 +3924,7 @@ set_var_const(
if (di == NULL)
goto failed;
STRCPY(di->di_key, varname);
- if (hash_add(ht, DI2HIKEY(di)) == FAIL)
+ if (hash_add(ht, DI2HIKEY(di), "add variable") == FAIL)
{
vim_free(di);
goto failed;
diff --git a/src/hashtab.c b/src/hashtab.c
index a7470ecdc..db76fde35 100644
--- a/src/hashtab.c
+++ b/src/hashtab.c
@@ -71,6 +71,20 @@ hash_init(hashtab_T *ht)
}
/*
+ * 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,14 +215,17 @@ hash_debug_results(void)
/*
* 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)
+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,7 +249,7 @@ hash_add_item(
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)
+ if ((ht->ht_flags & HTFLAGS_ERROR) && hash_may_resize(ht, 0) == FAIL)
return FAIL;
++ht->ht_used;
@@ -266,15 +283,19 @@ hash_set(hashitem_T *hi, char_u *key)
/*
* 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.
*/
- void
-hash_remove(hashtab_T *ht, hashitem_T *hi)
+ 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,11 +428,11 @@ hash_may_resize(
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.
+ // 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_error = TRUE;
+ ht->ht_flags |= HTFLAGS_ERROR;
return FAIL;
}
oldarray = ht->ht_array;
@@ -453,7 +474,7 @@ hash_may_resize(
ht->ht_mask = newmask;
ht->ht_filled = ht->ht_used;
++ht->ht_changed;
- ht->ht_error = FALSE;
+ ht->ht_flags &= ~HTFLAGS_ERROR;
return OK;
}
diff --git a/src/if_lua.c b/src/if_lua.c
index 4900534b4..78bc19095 100644
--- a/src/if_lua.c
+++ b/src/if_lua.c
@@ -1150,7 +1150,7 @@ luaV_dict_newindex(lua_State *L)
if (lua_isnil(L, 3)) // remove?
{
hashitem_T *hi = hash_find(&d->dv_hashtab, di->di_key);
- hash_remove(&d->dv_hashtab, hi);
+ hash_remove(&d->dv_hashtab, hi, "Lua new index");
dictitem_free(di);
}
else
@@ -1838,9 +1838,8 @@ luaV_setvar(lua_State *L)
if (di == NULL)
// Doesn't exist, nothing to do
return 0;
- else
- // Delete the entry
- dictitem_remove(dict, di);
+ // Delete the entry
+ dictitem_remove(dict, di, "Lua delete variable");
}
else
{
diff --git a/src/if_py_both.h b/src/if_py_both.h
index 2857cc705..d6cb13b83 100644
--- a/src/if_py_both.h
+++ b/src/if_py_both.h
@@ -1768,7 +1768,7 @@ _DictionaryItem(DictionaryObject *self, PyObject *args, int flags)
return NULL;
}
- hash_remove(&dict->dv_hashtab, hi);
+ hash_remove(&dict->dv_hashtab, hi, "Python remove variable");
dictitem_free(di);
}
@@ -1893,7 +1893,7 @@ DictionaryAssItem(
return -1;
}
hi = hash_find(&dict->dv_hashtab, di->di_key);
- hash_remove(&dict->dv_hashtab, hi);
+ hash_remove(&dict->dv_hashtab, hi, "Python remove item");
dictitem_free(di);
Py_XDECREF(todecref);
return 0;
@@ -2194,7 +2194,7 @@ DictionaryPopItem(DictionaryObject *self, PyObject *args UNUSED)
return NULL;
}
- hash_remove(&self->dict->dv_hashtab, hi);
+ hash_remove(&self->dict->dv_hashtab, hi, "Python pop item");
dictitem_free(di);
return ret;
diff --git a/src/if_ruby.c b/src/if_ruby.c
index feb1d5009..51cfff13d 100644
--- a/src/if_ruby.c
+++ b/src/if_ruby.c
@@ -1799,7 +1799,7 @@ convert_hash2dict(VALUE key, VALUE val, VALUE arg)
if (di == NULL || ruby_convert_to_vim_value(val, &di->di_tv) != OK
|| dict_add(d, di) != OK)
{
- d->dv_hashtab.ht_error = TRUE;
+ d->dv_hashtab.ht_flags |= HTFLAGS_ERROR;
return ST_STOP;
}
return ST_CONTINUE;
@@ -1879,7 +1879,7 @@ ruby_convert_to_vim_value(VALUE val, typval_T *rettv)
return FAIL;
rb_hash_foreach(val, convert_hash2dict, (VALUE)d);
- if (d->dv_hashtab.ht_error)
+ if (d->dv_hashtab.ht_flags & HTFLAGS_ERROR)
{
dict_unref(d);
return FAIL;
diff --git a/src/proto/dict.pro b/src/proto/dict.pro
index a4442ccb9..fdccca573 100644
--- a/src/proto/dict.pro
+++ b/src/proto/dict.pro
@@ -10,7 +10,7 @@ void dict_unref(dict_T *d);
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_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);
diff --git a/src/proto/hashtab.pro b/src/proto/hashtab.pro
index 21794ed6f..320889edc 100644
--- a/src/proto/hashtab.pro
+++ b/src/proto/hashtab.pro
@@ -1,13 +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);
+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);
-void hash_remove(hashtab_T *ht, hashitem_T *hi);
+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);
diff --git a/src/sign.c b/src/sign.c
index 1073e6529..2a360216f 100644
--- a/src/sign.c
+++ b/src/sign.c
@@ -126,7 +126,7 @@ sign_group_unref(char_u *groupname)
if (group->sg_refcount == 0)
{
// All the signs in this group are removed
- hash_remove(&sg_table, hi);
+ hash_remove(&sg_table, hi, "sign remove");
vim_free(group);
}
}
diff --git a/src/spellfile.c b/src/spellfile.c
index b5db526ed..6247968a8 100644
--- a/src/spellfile.c
+++ b/src/spellfile.c
@@ -2643,7 +2643,7 @@ spell_read_aff(spellinfo_T *spin, char_u *fname)
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);
+ hash_add(tp, cur_aff->ah_key, "spelling");
cur_aff->ah_combine = (*items[2] == 'Y');
}
@@ -2994,7 +2994,7 @@ spell_read_aff(spellinfo_T *spin, char_u *fname)
p = vim_strsave(items[i]);
if (p == NULL)
break;
- hash_add(&spin->si_commonwords, p);
+ hash_add(&spin->si_commonwords, p, "spelling");
}
}
}
@@ -3312,7 +3312,7 @@ process_compflags(
id = spin->si_newcompID--;
} while (vim_strchr((char_u *)"/?*+[]\\-^", id) != NULL);
ci->ci_newID = id;
- hash_add(&aff->af_comp, ci->ci_key);
+ hash_add(&aff->af_comp, ci->ci_key, "spelling");
}
*tp++ = id;
}
diff --git a/src/structs.h b/src/structs.h
index 111872d63..f57fb8561 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -1313,6 +1313,12 @@ typedef struct hashitem_S
// 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,8 +1327,7 @@ typedef struct hashtable_S
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
+ 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
diff --git a/src/syntax.c b/src/syntax.c
index 468dee09a..6570e9e89 100644
--- a/src/syntax.c
+++ b/src/syntax.c
@@ -4339,7 +4339,7 @@ syn_clear_keyword(int id, hashtab_T *ht)
if (kp_prev == NULL)
{
if (kp_next == NULL)
- hash_remove(ht, hi);
+ hash_remove(ht, hi, "syntax clear keyword");
else
hi->hi_key = KE2HIKEY(kp_next);
}
diff --git a/src/terminal.c b/src/terminal.c
index bc363175f..d300c59a2 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -1020,7 +1020,7 @@ term_write_session(FILE *fd, win_T *wp, hashtab_T *terminal_bufs)
char *hash_key = alloc(NUMBUFLEN);
vim_snprintf(hash_key, NUMBUFLEN, "%d", bufnr);
- hash_add(terminal_bufs, (char_u *)hash_key);
+ hash_add(terminal_bufs, (char_u *)hash_key, "terminal session");
}
return put_eol(fd);
diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim
index aa204c4f3..a0d83ecf3 100644
--- a/src/testdir/test_autocmd.vim
+++ b/src/testdir/test_autocmd.vim
@@ -2326,6 +2326,28 @@ func Test_autocmd_user_clear_group()
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!
diff --git a/src/textprop.c b/src/textprop.c
index dee35c3ec..789ab8ffe 100644
--- a/src/textprop.c
+++ b/src/textprop.c
@@ -1789,7 +1789,7 @@ prop_type_set(typval_T *argvars, int add)
}
hash_init(*htp);
}
- hash_add(*htp, PT2HIKEY(prop));
+ hash_add(*htp, PT2HIKEY(prop), "prop type");
}
else
{
@@ -1924,7 +1924,7 @@ f_prop_type_delete(typval_T *argvars, typval_T *rettv UNUSED)
ht = buf->b_proptypes;
VIM_CLEAR(buf->b_proparray);
}
- hash_remove(ht, hi);
+ hash_remove(ht, hi, "prop type delete");
vim_free(prop);
}
}
diff --git a/src/userfunc.c b/src/userfunc.c
index ff4cae732..492c6721b 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -585,7 +585,7 @@ register_cfunc(cfunc_T cb, cfunc_free_T cb_free, void *state)
fp->uf_cb_state = state;
set_ufunc_name(fp, name);
- hash_add(&func_hashtab, UF2HIKEY(fp));
+ hash_add(&func_hashtab, UF2HIKEY(fp), "add C function");
return name;
}
@@ -1278,7 +1278,7 @@ lambda_function_body(
if (ufunc == NULL)
goto erret;
set_ufunc_name(ufunc, name);
- if (hash_add(&func_hashtab, UF2HIKEY(ufunc)) == FAIL)
+ if (hash_add(&func_hashtab, UF2HIKEY(ufunc), "add function") == FAIL)
goto erret;
ufunc->uf_flags = FC_LAMBDA;
ufunc->uf_refcount = 1;
@@ -1572,7 +1572,7 @@ get_lambda_tv(
rettv->vval.v_partial = pt;
rettv->v_type = VAR_PARTIAL;
- hash_add(&func_hashtab, UF2HIKEY(fp));
+ hash_add(&func_hashtab, UF2HIKEY(fp), "add lambda");
}
theend:
@@ -2128,7 +2128,7 @@ add_nr_var(
{
STRCPY(v->di_key, name);
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
- hash_add(&dp->dv_hashtab, DI2HIKEY(v));
+ 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,7 +2348,7 @@ func_remove(ufunc_T *fp)
fp->uf_flags |= FC_DEAD;
return FALSE;
}
- hash_remove(&func_hashtab, hi);
+ hash_remove(&func_hashtab, hi, "remove function");
fp->uf_flags |= FC_DELETED;
return TRUE;
}
@@ -2510,7 +2510,7 @@ copy_lambda_to_global_func(
fp->uf_refcount = 1;
STRCPY(fp->uf_name, global);
- hash_add(&func_hashtab, UF2HIKEY(fp));
+ hash_add(&func_hashtab, UF2HIKEY(fp), "copy lambda");
// the referenced dfunc_T is now used one more time
link_def_function(fp);
@@ -2718,7 +2718,7 @@ call_user_func(
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));
+ 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,7 +2744,7 @@ call_user_func(
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));
+ 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,10 +2838,10 @@ call_user_func(
// 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));
+ hash_add(&fc->fc_l_vars.dv_hashtab, DI2HIKEY(v), "local variable");
}
else
- hash_add(&fc->fc_l_avars.dv_hashtab, DI2HIKEY(v));
+ hash_add(&fc->fc_l_avars.dv_hashtab, DI2HIKEY(v), "add variable");
if (ai >= 0 && ai < MAX_FUNC_ARGS)
{
@@ -5060,7 +5060,7 @@ define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free)
hi = hash_find(&func_hashtab, name);
hi->hi_key = UF2HIKEY(fp);
}
- else if (hash_add(&func_hashtab, UF2HIKEY(fp)) == FAIL)
+ else if (hash_add(&func_hashtab, UF2HIKEY(fp), "add function") == FAIL)
{
free_fp = TRUE;
goto erret;
@@ -5462,7 +5462,7 @@ ex_delfunction(exarg_T *eap)
{
// 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);
+ dictitem_remove(fudi.fd_dict, fudi.fd_di, "delfunction");
}
else
{
diff --git a/src/version.c b/src/version.c
index 8427210fd..e6fc986fd 100644
--- a/src/version.c
+++ b/src/version.c
@@ -696,6 +696,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 949,
+/**/
948,
/**/
947,
diff --git a/src/vim9execute.c b/src/vim9execute.c
index cb52438fd..217147a74 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -2366,7 +2366,7 @@ execute_unletindex(isn_T *iptr, ectx_T *ectx)
NULL, FALSE))
status = FAIL;
else
- dictitem_remove(d, di);
+ dictitem_remove(d, di, "unlet");
}
}
}
diff --git a/src/vim9script.c b/src/vim9script.c
index 557892cf8..1d7eea0d4 100644
--- a/src/vim9script.c
+++ b/src/vim9script.c
@@ -942,7 +942,8 @@ update_vim9_script_var(
if (HASHITEM_EMPTY(hi))
// new variable name
- hash_add(&si->sn_all_vars.dv_hashtab, newsav->sav_key);
+ 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,7 +1034,7 @@ hide_script_var(scriptitem_T *si, int idx, int func_defined)
else
{
if (sav_prev == NULL)
- hash_remove(all_ht, all_hi);
+ hash_remove(all_ht, all_hi, "hide variable");
else
sav_prev->sav_next = sav->sav_next;
sv->sv_name = NULL;