diff options
author | bfredl <bjorn.linse@gmail.com> | 2025-03-05 13:30:06 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-03-05 13:30:06 +0100 |
commit | 84487036624df8243f6dedc9f36dfc10789c5f47 (patch) | |
tree | 0e7f7b3027a9755e6c1a019981b13a652494472f | |
parent | e4c094a84da8f417687d2730a4793dd4b408739d (diff) | |
parent | ebb963a4a08283227233c417fed7d822c86a8807 (diff) | |
download | rneovim-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.c | 41 | ||||
-rw-r--r-- | test/functional/lua/loop_spec.lua | 48 |
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) |