mirror of
https://github.com/neovim/neovim
synced 2025-07-18 02:01:46 +00:00
fix(lua): format errors from luv callbacks using __tostring
This commit is contained in:
@ -125,6 +125,28 @@ lua_State *get_global_lstate(void)
|
|||||||
return global_lstate;
|
return global_lstate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// get error on top of stack as a string
|
||||||
|
///
|
||||||
|
/// Might alter the top value on stack in place (but doesn't change stack height)
|
||||||
|
///
|
||||||
|
/// "error" points to memory on the lua stack, use
|
||||||
|
/// or duplicate the string before using "lstate" again
|
||||||
|
///
|
||||||
|
/// @param[out] len length of error (can be NULL)
|
||||||
|
static const char *nlua_get_error(lua_State *lstate, size_t *len)
|
||||||
|
{
|
||||||
|
if (luaL_getmetafield(lstate, -1, "__tostring")) {
|
||||||
|
if (lua_isfunction(lstate, -1) && luaL_callmeta(lstate, -2, "__tostring")) {
|
||||||
|
// call __tostring, convert the result and replace error with it
|
||||||
|
lua_replace(lstate, -3);
|
||||||
|
}
|
||||||
|
// pop __tostring.
|
||||||
|
lua_pop(lstate, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lua_tolstring(lstate, -1, len);
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert lua error into a Vim error message
|
/// Convert lua error into a Vim error message
|
||||||
///
|
///
|
||||||
/// @param lstate Lua interpreter state.
|
/// @param lstate Lua interpreter state.
|
||||||
@ -133,22 +155,7 @@ void nlua_error(lua_State *const lstate, const char *const msg)
|
|||||||
FUNC_ATTR_NONNULL_ALL
|
FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
size_t len;
|
size_t len;
|
||||||
const char *str = NULL;
|
const char *str = nlua_get_error(lstate, &len);
|
||||||
|
|
||||||
if (luaL_getmetafield(lstate, -1, "__tostring")) {
|
|
||||||
if (lua_isfunction(lstate, -1) && luaL_callmeta(lstate, -2, "__tostring")) {
|
|
||||||
// call __tostring, convert the result and pop result.
|
|
||||||
str = lua_tolstring(lstate, -1, &len);
|
|
||||||
lua_pop(lstate, 1);
|
|
||||||
}
|
|
||||||
// pop __tostring.
|
|
||||||
lua_pop(lstate, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!str) {
|
|
||||||
// defer to lua default conversion, this will render tables as [NULL].
|
|
||||||
str = lua_tolstring(lstate, -1, &len);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in_script) {
|
if (in_script) {
|
||||||
fprintf(stderr, msg, (int)len, str);
|
fprintf(stderr, msg, (int)len, str);
|
||||||
@ -218,7 +225,9 @@ static int nlua_fast_cfpcall(lua_State *lstate, int nargs, int nresult, int flag
|
|||||||
// consider out of memory errors unrecoverable, just like xmalloc()
|
// consider out of memory errors unrecoverable, just like xmalloc()
|
||||||
preserve_exit(e_outofmem);
|
preserve_exit(e_outofmem);
|
||||||
}
|
}
|
||||||
const char *error = lua_tostring(lstate, -1);
|
|
||||||
|
size_t len;
|
||||||
|
const char *error = nlua_get_error(lstate, &len);
|
||||||
|
|
||||||
multiqueue_put(main_loop.events, nlua_luv_error_event,
|
multiqueue_put(main_loop.events, nlua_luv_error_event,
|
||||||
error != NULL ? xstrdup(error) : NULL, (void *)(intptr_t)kCallback);
|
error != NULL ? xstrdup(error) : NULL, (void *)(intptr_t)kCallback);
|
||||||
|
@ -194,4 +194,52 @@ describe('vim.uv', function()
|
|||||||
feed('<cr>')
|
feed('<cr>')
|
||||||
n.assert_alive()
|
n.assert_alive()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it("doesn't crash on async callbacks throwing nil error", function()
|
||||||
|
local screen = Screen.new(50, 4)
|
||||||
|
|
||||||
|
exec_lua(function()
|
||||||
|
_G.idle = vim.uv.new_idle()
|
||||||
|
_G.idle:start(function()
|
||||||
|
_G.idle:stop()
|
||||||
|
error()
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
screen:expect([[
|
||||||
|
{3: }|
|
||||||
|
{9:Error executing callback:} |
|
||||||
|
{9:[NULL]} |
|
||||||
|
{6:Press ENTER or type command to continue}^ |
|
||||||
|
]])
|
||||||
|
feed('<cr>')
|
||||||
|
|
||||||
|
exec_lua(function()
|
||||||
|
_G.idle:close()
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("doesn't crash on async callbacks throwing object as an error", function()
|
||||||
|
local screen = Screen.new(50, 4)
|
||||||
|
|
||||||
|
exec_lua(function()
|
||||||
|
_G.idle = vim.uv.new_idle()
|
||||||
|
_G.idle:start(function()
|
||||||
|
_G.idle:stop()
|
||||||
|
error(_G.idle) -- userdata with __tostring method
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
screen:expect([[
|
||||||
|
{3: }|
|
||||||
|
{9:Error executing callback:} |
|
||||||
|
{9:uv_idle_t: 0x{MATCH:%w+}} |
|
||||||
|
{6:Press ENTER or type command to continue}^ |
|
||||||
|
]])
|
||||||
|
feed('<cr>')
|
||||||
|
|
||||||
|
exec_lua(function()
|
||||||
|
_G.idle:close()
|
||||||
|
end)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
Reference in New Issue
Block a user