mirror of
https://github.com/neovim/neovim.git
synced 2025-09-06 11:28:22 +00:00
vim-patch:8.2.0908: crash when changing the function table while listing it
Problem: Crash when changing the function table while listing it.
Solution: Bail out when the function table changes. (closes vim/vim#6209)
3fffa97159
Co-authored-by: Bram Moolenaar <Bram@vim.org>
This commit is contained in:
@@ -1877,6 +1877,36 @@ theend:
|
|||||||
|
|
||||||
#define MAX_FUNC_NESTING 50
|
#define MAX_FUNC_NESTING 50
|
||||||
|
|
||||||
|
/// List functions.
|
||||||
|
///
|
||||||
|
/// @param regmatch When NULL, all of them.
|
||||||
|
/// Otherwise functions matching "regmatch".
|
||||||
|
static void list_functions(regmatch_T *regmatch)
|
||||||
|
{
|
||||||
|
const size_t used = func_hashtab.ht_used;
|
||||||
|
size_t todo = used;
|
||||||
|
const hashitem_T *const ht_array = func_hashtab.ht_array;
|
||||||
|
|
||||||
|
for (const hashitem_T *hi = ht_array; todo > 0 && !got_int; hi++) {
|
||||||
|
if (!HASHITEM_EMPTY(hi)) {
|
||||||
|
ufunc_T *fp = HI2UF(hi);
|
||||||
|
todo--;
|
||||||
|
if ((fp->uf_flags & FC_DEAD) == 0
|
||||||
|
&& (regmatch == NULL
|
||||||
|
? (!message_filtered((char *)fp->uf_name)
|
||||||
|
&& !func_name_refcount(fp->uf_name))
|
||||||
|
: (!isdigit(*fp->uf_name)
|
||||||
|
&& vim_regexec(regmatch, (char *)fp->uf_name, 0)))) {
|
||||||
|
list_func_head(fp, false, false);
|
||||||
|
if (used != func_hashtab.ht_used || ht_array != func_hashtab.ht_array) {
|
||||||
|
emsg(_("E454: function list was modified"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// ":function"
|
/// ":function"
|
||||||
void ex_function(exarg_T *eap)
|
void ex_function(exarg_T *eap)
|
||||||
{
|
{
|
||||||
@@ -1903,7 +1933,6 @@ void ex_function(exarg_T *eap)
|
|||||||
static int func_nr = 0; // number for nameless function
|
static int func_nr = 0; // number for nameless function
|
||||||
int paren;
|
int paren;
|
||||||
hashtab_T *ht;
|
hashtab_T *ht;
|
||||||
int todo;
|
|
||||||
hashitem_T *hi;
|
hashitem_T *hi;
|
||||||
linenr_T sourcing_lnum_off;
|
linenr_T sourcing_lnum_off;
|
||||||
linenr_T sourcing_lnum_top;
|
linenr_T sourcing_lnum_top;
|
||||||
@@ -1916,19 +1945,7 @@ void ex_function(exarg_T *eap)
|
|||||||
// ":function" without argument: list functions.
|
// ":function" without argument: list functions.
|
||||||
if (ends_excmd(*eap->arg)) {
|
if (ends_excmd(*eap->arg)) {
|
||||||
if (!eap->skip) {
|
if (!eap->skip) {
|
||||||
todo = (int)func_hashtab.ht_used;
|
list_functions(NULL);
|
||||||
for (hi = func_hashtab.ht_array; todo > 0 && !got_int; hi++) {
|
|
||||||
if (!HASHITEM_EMPTY(hi)) {
|
|
||||||
todo--;
|
|
||||||
fp = HI2UF(hi);
|
|
||||||
if (message_filtered((char *)fp->uf_name)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!func_name_refcount(fp->uf_name)) {
|
|
||||||
list_func_head(fp, false, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
eap->nextcmd = check_nextcmd(eap->arg);
|
eap->nextcmd = check_nextcmd(eap->arg);
|
||||||
return;
|
return;
|
||||||
@@ -1946,18 +1963,7 @@ void ex_function(exarg_T *eap)
|
|||||||
*p = c;
|
*p = c;
|
||||||
if (regmatch.regprog != NULL) {
|
if (regmatch.regprog != NULL) {
|
||||||
regmatch.rm_ic = p_ic;
|
regmatch.rm_ic = p_ic;
|
||||||
|
list_functions(®match);
|
||||||
todo = (int)func_hashtab.ht_used;
|
|
||||||
for (hi = func_hashtab.ht_array; todo > 0 && !got_int; hi++) {
|
|
||||||
if (!HASHITEM_EMPTY(hi)) {
|
|
||||||
todo--;
|
|
||||||
fp = HI2UF(hi);
|
|
||||||
if (!isdigit(*fp->uf_name)
|
|
||||||
&& vim_regexec(®match, (char *)fp->uf_name, 0)) {
|
|
||||||
list_func_head(fp, false, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vim_regfree(regmatch.regprog);
|
vim_regfree(regmatch.regprog);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
source check.vim
|
source check.vim
|
||||||
CheckFeature timers
|
CheckFeature timers
|
||||||
|
|
||||||
|
source screendump.vim
|
||||||
source shared.vim
|
source shared.vim
|
||||||
source term_util.vim
|
source term_util.vim
|
||||||
source load.vim
|
source load.vim
|
||||||
@@ -408,6 +409,30 @@ func Test_timer_invalid_callback()
|
|||||||
call assert_fails('call timer_start(0, "0")', 'E921')
|
call assert_fails('call timer_start(0, "0")', 'E921')
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func Test_timer_changing_function_list()
|
||||||
|
CheckRunVimInTerminal
|
||||||
|
|
||||||
|
" Create a large number of functions. Should get the "more" prompt.
|
||||||
|
" The typing "G" triggers the timer, which changes the function table.
|
||||||
|
let lines =<< trim END
|
||||||
|
for func in map(range(1,99), "'Func' .. v:val")
|
||||||
|
exe "func " .. func .. "()"
|
||||||
|
endfunc
|
||||||
|
endfor
|
||||||
|
au CmdlineLeave : call timer_start(0, {-> 0})
|
||||||
|
END
|
||||||
|
call writefile(lines, 'XTest_timerchange')
|
||||||
|
let buf = RunVimInTerminal('-S XTest_timerchange', #{rows: 10})
|
||||||
|
call term_sendkeys(buf, ":fu\<CR>")
|
||||||
|
call WaitForAssert({-> assert_match('-- More --', term_getline(buf, 10))})
|
||||||
|
call term_sendkeys(buf, "G")
|
||||||
|
call WaitForAssert({-> assert_match('E454', term_getline(buf, 9))})
|
||||||
|
call term_sendkeys(buf, "\<Esc>")
|
||||||
|
|
||||||
|
call StopVimInTerminal(buf)
|
||||||
|
call delete('XTest_timerchange')
|
||||||
|
endfunc
|
||||||
|
|
||||||
func Test_timer_using_win_execute_undo_sync()
|
func Test_timer_using_win_execute_undo_sync()
|
||||||
let bufnr1 = bufnr()
|
let bufnr1 = bufnr()
|
||||||
new
|
new
|
||||||
|
Reference in New Issue
Block a user