aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbfredl <bjorn.linse@gmail.com>2025-03-05 13:30:06 +0100
committerGitHub <noreply@github.com>2025-03-05 13:30:06 +0100
commit84487036624df8243f6dedc9f36dfc10789c5f47 (patch)
tree0e7f7b3027a9755e6c1a019981b13a652494472f
parente4c094a84da8f417687d2730a4793dd4b408739d (diff)
parentebb963a4a08283227233c417fed7d822c86a8807 (diff)
downloadrneovim-84487036624df8243f6dedc9f36dfc10789c5f47.tar.gz
rneovim-84487036624df8243f6dedc9f36dfc10789c5f47.tar.bz2
rneovim-84487036624df8243f6dedc9f36dfc10789c5f47.zip
Merge pull request #12049 from bfredl/luverr
convert non-string errors using tostring()
-rw-r--r--src/nvim/lua/executor.c41
-rw-r--r--test/functional/lua/loop_spec.lua48
2 files changed, 73 insertions, 16 deletions
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index b1766db379..91ff285046 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -125,30 +125,37 @@ lua_State *get_global_lstate(void)
return global_lstate;
}
-/// Convert lua error into a Vim error message
+/// get error on top of stack as a string
///
-/// @param lstate Lua interpreter state.
-/// @param[in] msg Message base, must contain one `%.*s`.
-void nlua_error(lua_State *const lstate, const char *const msg)
- FUNC_ATTR_NONNULL_ALL
+/// 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)
{
- size_t len;
- const char *str = NULL;
-
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);
+ // call __tostring, convert the result and replace error with it
+ lua_replace(lstate, -3);
}
// 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);
- }
+ return lua_tolstring(lstate, -1, len);
+}
+
+/// Convert lua error into a Vim error message
+///
+/// @param lstate Lua interpreter state.
+/// @param[in] msg Message base, must contain one `%.*s`.
+void nlua_error(lua_State *const lstate, const char *const msg)
+ FUNC_ATTR_NONNULL_ALL
+{
+ size_t len;
+ const char *str = nlua_get_error(lstate, &len);
if (in_script) {
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()
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,
error != NULL ? xstrdup(error) : NULL, (void *)(intptr_t)kCallback);
diff --git a/test/functional/lua/loop_spec.lua b/test/functional/lua/loop_spec.lua
index 8df2900368..fbf70d7be7 100644
--- a/test/functional/lua/loop_spec.lua
+++ b/test/functional/lua/loop_spec.lua
@@ -194,4 +194,52 @@ describe('vim.uv', function()
feed('<cr>')
n.assert_alive()
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)