mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 01:34:25 +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