vim-patch:8.2.3609: internal error when ModeChanged is triggered recursively

Problem:    Internal error when ModeChanged is triggered when v:event is
            already in use.
Solution:   Save and restore v:event if needed.
3075a45592

In the vim codebase there is no occurrence of get_vim_var_dict(VV_EVENT)
after the above patch, so in order to hold the same invariant in the
neovim codebase we needed to replace more occurrences than the related
vim patch.
This commit is contained in:
Magnus Groß 2021-11-17 17:55:49 +01:00
parent fdfd1eda43
commit 1fb101afe4
No known key found for this signature in database
GPG Key ID: 56A1295EB9CA7359
10 changed files with 67 additions and 20 deletions

View File

@ -8,6 +8,7 @@
#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
#include "nvim/main.h"
#include "nvim/misc1.h"
#include "nvim/os/os.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
@ -25,13 +26,14 @@ void do_autocmd_uienter(uint64_t chanid, bool attached)
}
recursive = true;
dict_T *dict = get_vim_var_dict(VV_EVENT);
save_v_event_T save_v_event;
dict_T *dict = get_v_event(&save_v_event);
assert(chanid < VARNUMBER_MAX);
tv_dict_add_nr(dict, S_LEN("chan"), (varnumber_T)chanid);
tv_dict_set_keys_readonly(dict);
apply_autocmds(attached ? EVENT_UIENTER : EVENT_UILEAVE,
NULL, NULL, false, curbuf);
tv_dict_clear(dict);
restore_v_event(dict, &save_v_event);
recursive = false;
}

View File

@ -1116,6 +1116,12 @@ typedef struct {
pos_T w_cursor_corr; // corrected cursor position
} pos_save_T;
// Struct passed to get_v_event() and restore_v_event().
typedef struct {
bool sve_did_save;
hashtab_T sve_hashtab;
} save_v_event_T;
/// Indices into vimmenu_T->strings[] and vimmenu_T->noremap[] for each mode
/// \addtogroup MENU_INDEX
/// @{

View File

@ -10,6 +10,7 @@
#include "nvim/event/socket.h"
#include "nvim/fileio.h"
#include "nvim/lua/executor.h"
#include "nvim/misc1.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/server.h"
#include "nvim/os/shell.h"
@ -821,7 +822,8 @@ static void set_info_event(void **argv)
Channel *chan = argv[0];
event_T event = (event_T)(ptrdiff_t)argv[1];
dict_T *dict = get_vim_var_dict(VV_EVENT);
save_v_event_T save_v_event;
dict_T *dict = get_v_event(&save_v_event);
Dictionary info = channel_info(chan->id);
typval_T retval;
(void)object_to_vim(DICTIONARY_OBJ(info), &retval, NULL);
@ -829,7 +831,7 @@ static void set_info_event(void **argv)
apply_autocmds(event, NULL, NULL, false, curbuf);
tv_dict_clear(dict);
restore_v_event(dict, &save_v_event);
api_free_dictionary(info);
channel_decref(chan);
}

View File

@ -2719,12 +2719,13 @@ static bool pum_enough_matches(void)
static void trigger_complete_changed_event(int cur)
{
static bool recursive = false;
save_v_event_T save_v_event;
if (recursive) {
return;
}
dict_T *v_event = get_vim_var_dict(VV_EVENT);
dict_T *v_event = get_v_event(&save_v_event);
if (cur < 0) {
tv_dict_add_dict(v_event, S_LEN("completed_item"), tv_dict_alloc());
} else {
@ -2740,7 +2741,7 @@ static void trigger_complete_changed_event(int cur)
textlock--;
recursive = false;
tv_dict_clear(v_event);
restore_v_event(v_event, &save_v_event);
}
/// Show the popup menu for the list of matches.

View File

@ -880,7 +880,8 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
TryState tstate;
Error err = ERROR_INIT;
bool tl_ret = true;
dict_T *dict = get_vim_var_dict(VV_EVENT);
save_v_event_T save_v_event;
dict_T *dict = get_v_event(&save_v_event);
char firstcbuf[2];
firstcbuf[0] = (char)(firstc > 0 ? firstc : '-');
firstcbuf[1] = 0;
@ -894,7 +895,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
apply_autocmds(EVENT_CMDLINEENTER, (char_u *)firstcbuf, (char_u *)firstcbuf,
false, curbuf);
tv_dict_clear(dict);
restore_v_event(dict, &save_v_event);
tl_ret = try_leave(&tstate, &err);
@ -925,7 +926,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
if (tv_dict_get_number(dict, "abort") != 0) {
s->gotesc = 1;
}
tv_dict_clear(dict);
restore_v_event(dict, &save_v_event);
}
cmdmsg_rl = false;
@ -2290,7 +2291,8 @@ static int command_line_changed(CommandLineState *s)
if (has_event(EVENT_CMDLINECHANGED)) {
TryState tstate;
Error err = ERROR_INIT;
dict_T *dict = get_vim_var_dict(VV_EVENT);
save_v_event_T save_v_event;
dict_T *dict = get_v_event(&save_v_event);
char firstcbuf[2];
firstcbuf[0] = (char)(s->firstc > 0 ? s->firstc : '-');
@ -2304,7 +2306,7 @@ static int command_line_changed(CommandLineState *s)
apply_autocmds(EVENT_CMDLINECHANGED, (char_u *)firstcbuf,
(char_u *)firstcbuf, false, curbuf);
tv_dict_clear(dict);
restore_v_event(dict, &save_v_event);
bool tl_ret = try_leave(&tstate, &err);
if (!tl_ret && ERROR_SET(&err)) {

View File

@ -1603,7 +1603,8 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, CdCause cause)
recursive = true;
dict_T *dict = get_vim_var_dict(VV_EVENT);
save_v_event_T save_v_event;
dict_T *dict = get_v_event(&save_v_event);
char buf[8];
switch (scope) {
@ -1648,7 +1649,7 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, CdCause cause)
apply_autocmds(EVENT_DIRCHANGED, (char_u *)buf, (char_u *)new_dir, false,
curbuf);
tv_dict_clear(dict);
restore_v_event(dict, &save_v_event);
recursive = false;
}

View File

@ -1060,6 +1060,31 @@ void add_time(char_u *buf, size_t buflen, time_t tt)
}
}
dict_T *get_v_event(save_v_event_T *sve)
{
dict_T *v_event = get_vim_var_dict(VV_EVENT);
if (v_event->dv_hashtab.ht_used > 0) {
// recursive use of v:event, save, make empty and restore later
sve->sve_did_save = true;
sve->sve_hashtab = v_event->dv_hashtab;
hash_init(&v_event->dv_hashtab);
} else {
sve->sve_did_save = false;
}
return v_event;
}
void restore_v_event(dict_T *v_event, save_v_event_T *sve)
{
tv_dict_free_contents(v_event);
if (sve->sve_did_save) {
v_event->dv_hashtab = sve->sve_hashtab;
} else {
hash_init(&v_event->dv_hashtab);
}
}
/// Fires a ModeChanged autocmd.
void trigger_modechanged(void)
{
@ -1073,7 +1098,8 @@ void trigger_modechanged(void)
return;
}
dict_T *v_event = get_vim_var_dict(VV_EVENT);
save_v_event_T save_v_event;
dict_T *v_event = get_v_event(&save_v_event);
tv_dict_add_str(v_event, S_LEN("new_mode"), mode);
tv_dict_add_str(v_event, S_LEN("old_mode"), last_mode);
@ -1086,6 +1112,5 @@ void trigger_modechanged(void)
last_mode = mode;
xfree(pat);
tv_dict_free_contents(v_event);
hash_init(&v_event->dv_hashtab);
restore_v_event(v_event, &save_v_event);
}

View File

@ -2792,8 +2792,9 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
recursive = true;
save_v_event_T save_v_event;
// Set the v:event dictionary with information about the yank.
dict_T *dict = get_vim_var_dict(VV_EVENT);
dict_T *dict = get_v_event(&save_v_event);
// The yanked text contents.
list_T *const list = tv_list_alloc((ptrdiff_t)reg->y_size);
@ -2830,7 +2831,7 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
textlock++;
apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, false, curbuf);
textlock--;
tv_dict_clear(dict);
restore_v_event(dict, &save_v_event);
recursive = false;
}

View File

@ -324,10 +324,11 @@ void terminal_close(Terminal *term, int status)
}
if (buf && !is_autocmd_blocked()) {
dict_T *dict = get_vim_var_dict(VV_EVENT);
save_v_event_T save_v_event;
dict_T *dict = get_v_event(&save_v_event);
tv_dict_add_nr(dict, S_LEN("status"), status);
apply_autocmds(EVENT_TERMCLOSE, NULL, NULL, false, buf);
tv_dict_clear(dict);
restore_v_event(dict, &save_v_event);
}
}

View File

@ -1721,4 +1721,10 @@ func Test_mode_changes()
unlet! g:i_to_any
endfunc
func Test_recursive_ModeChanged()
au! ModeChanged * norm 0u
sil! norm 
au!
endfunc
" vim: shiftwidth=2 sts=2 expandtab