Test and initial fix for crash with dictwatcherdel

Fixes https://github.com/neovim/neovim/issues/11188.
This commit is contained in:
Daniel Hahler 2019-10-09 19:40:50 +02:00 committed by Jan Edmund Lazo
parent c20ae3aadb
commit 4c76b1e981
No known key found for this signature in database
GPG Key ID: 64915E6E9F735B15
3 changed files with 30 additions and 1 deletions

View File

@ -1109,6 +1109,7 @@ void tv_dict_watcher_add(dict_T *const dict, const char *const key_pattern,
watcher->key_pattern_len = key_pattern_len;
watcher->callback = callback;
watcher->busy = false;
watcher->needs_free = false;
QUEUE_INSERT_TAIL(&dict->watchers, &watcher->node);
}
@ -1197,7 +1198,11 @@ bool tv_dict_watcher_remove(dict_T *const dict, const char *const key_pattern,
}
QUEUE_REMOVE(w);
tv_dict_watcher_free(watcher);
if (watcher->busy) {
watcher->needs_free = true;
} else {
tv_dict_watcher_free(watcher);
}
return true;
}
@ -1268,6 +1273,9 @@ void tv_dict_watcher_notify(dict_T *const dict, const char *const key,
callback_call(&watcher->callback, 3, argv, &rettv);
watcher->busy = false;
tv_clear(&rettv);
if (watcher->needs_free) {
tv_dict_watcher_free(watcher);
}
}
}
tv_dict_unref(dict);

View File

@ -89,6 +89,7 @@ typedef struct dict_watcher {
size_t key_pattern_len;
QUEUE node;
bool busy; // prevent recursion if the dict is changed in the callback
bool needs_free;
} DictWatcher;
/// Bool variable values

View File

@ -371,4 +371,24 @@ describe('VimL dictionary notifications', function()
eq(1, eval('g:called'))
end)
it('does not crash when using dictwatcherdel in callback', function()
source([[
let g:d = {}
function! W(...)
call dictwatcherdel(g:d, '*', function('W'))
try
call dictwatcherdel({}, 'meh', function('tr'))
catch
let g:exc = v:exception
endtry
endfunction
call dictwatcheradd(g:d, '*', function('W'))
let g:d.foo = 23
]])
eq(23, eval('g:d.foo'))
eq("Vim(call):Couldn't find a watcher matching key and callback", eval('g:exc'))
end)
end)