Browse Source

Add stack traceback to Lua error message. (#140)

* Add stack traceback to Lua error message.

* Stack balance.
actboy168 5 years ago
parent
commit
79f779c13b

+ 3 - 3
Include/RmlUi/Lua/Interpreter.h

@@ -50,16 +50,16 @@ namespace Interpreter {
     @param[in] file Fully qualified file name to execute.
     @param[in] file Fully qualified file name to execute.
     @remark Somewhat misleading name if you are used to the Lua meaning of "load file". It behaves
     @remark Somewhat misleading name if you are used to the Lua meaning of "load file". It behaves
     exactly as luaL_dofile does.            */
     exactly as luaL_dofile does.            */
-    RMLUILUA_API void LoadFile(const String& file);
+    RMLUILUA_API bool LoadFile(const String& file);
     /** Calls lua_dostring and reports the errors.
     /** Calls lua_dostring and reports the errors.
     @param[in] code String to execute
     @param[in] code String to execute
     @param[in] name Name for the code that will show up in the Log  */
     @param[in] name Name for the code that will show up in the Log  */
-    RMLUILUA_API void DoString(const String& code, const String& name = "");
+    RMLUILUA_API bool DoString(const String& code, const String& name = "");
     /** Same as DoString, except does NOT call pcall on it. It will leave the compiled (but not executed) string
     /** Same as DoString, except does NOT call pcall on it. It will leave the compiled (but not executed) string
     on top of the stack. It behaves exactly like luaL_loadstring, but you get to specify the name
     on top of the stack. It behaves exactly like luaL_loadstring, but you get to specify the name
     @param[in] code String to compile
     @param[in] code String to compile
     @param[in] name Name for the code that will show up in the Log    */
     @param[in] name Name for the code that will show up in the Log    */
-    RMLUILUA_API void LoadString(const String& code, const String& name = "");
+    RMLUILUA_API bool LoadString(const String& code, const String& name = "");
 
 
     /** Clears all of the items on the stack, and pushes the function from funRef on top of the stack. Only use
     /** Clears all of the items on the stack, and pushes the function from funRef on top of the stack. Only use
     this if you used lua_ref instead of luaL_ref
     this if you used lua_ref instead of luaL_ref

+ 0 - 7
Include/RmlUi/Lua/Utilities.h

@@ -43,13 +43,6 @@ namespace Lua {
 @relates LuaType */
 @relates LuaType */
 void RMLUILUA_API PushVariant(lua_State* L, const Variant* var);
 void RMLUILUA_API PushVariant(lua_State* L, const Variant* var);
 
 
-/** If there are errors on the top of the stack, this will print those out to the log.
-@param L A Lua state, and if not passed in, will use the Interpreter's state
-@param place A string that will be printed to the log right before the error message, seperated by a space. Set
-this when you would get no information about where the error happens.
-@relates Interpreter   */
-void RMLUILUA_API Report(lua_State* L = nullptr, const String& place = "");
-
 //Helper function, so that the types don't have to define individual functions themselves
 //Helper function, so that the types don't have to define individual functions themselves
 // to fill the Elements.As table
 // to fill the Elements.As table
 template<typename ToType>
 template<typename ToType>

+ 10 - 13
Source/Lua/Elements/LuaDataSource.cpp

@@ -59,22 +59,19 @@ void LuaDataSource::GetRow(StringList& row, const String& table, int row_index,
     }
     }
     Interpreter::ExecuteCall(3,1); //3 parameters, 1 return. After here, the top of the stack contains the return value
     Interpreter::ExecuteCall(3,1); //3 parameters, 1 return. After here, the top of the stack contains the return value
 
 
-    int res = lua_gettop(L);
-    if(lua_type(L,res) == LUA_TTABLE)
+    if(lua_type(L,-1) == LUA_TTABLE)
     {
     {
         lua_pushnil(L);
         lua_pushnil(L);
-        while(lua_next(L,res) != 0)
+        while (lua_next(L, -2))
         {
         {
             //key at -2, value at -1
             //key at -2, value at -1
             row.push_back(luaL_checkstring(L,-1));
             row.push_back(luaL_checkstring(L,-1));
             lua_pop(L,1); //pops value, leaves key for next iteration
             lua_pop(L,1); //pops value, leaves key for next iteration
         }
         }
-        lua_pop(L,1); //pop key
     }
     }
     else
     else
-        Log::Message(Log::LT_WARNING, "Lua: DataSource.GetRow must return a table, the function it called returned a %s", lua_typename(L,res));
-
-    Interpreter::EndCall(1);
+        Log::Message(Log::LT_WARNING, "Lua: DataSource.GetRow must return a table, the function it called returned a %s", lua_typename(L,-1));
+    lua_pop(L,1);
 }
 }
 
 
 /// Fetches the number of rows within one of this data source's tables.
 /// Fetches the number of rows within one of this data source's tables.
@@ -89,17 +86,17 @@ int LuaDataSource::GetNumRows(const String& table)
     lua_pushstring(L,table.c_str());
     lua_pushstring(L,table.c_str());
     Interpreter::ExecuteCall(1,1); //1 parameter, 1 return. After this, the top of the stack contains the return value
     Interpreter::ExecuteCall(1,1); //1 parameter, 1 return. After this, the top of the stack contains the return value
 
 
-    int res = lua_gettop(L);
-    if(lua_type(L,res) == LUA_TNUMBER)
+    int res = -1;
+    if(lua_type(L, -1) == LUA_TNUMBER)
     {
     {
-        return (int)luaL_checkinteger(L,res);
+        res = (int)luaL_checkinteger(L,res);
     }
     }
     else
     else
     {
     {
-        Log::Message(Log::LT_WARNING, "Lua: DataSource.GetNumRows must return an integer, the function it called returned a %s", lua_typename(L,res));
-        return -1;
+        Log::Message(Log::LT_WARNING, "Lua: DataSource.GetNumRows must return an integer, the function it called returned a %s", lua_typename(L,-1));
     }
     }
-
+    lua_pop(L, 1);
+    return res;
 }
 }
 
 
 } // namespace Lua
 } // namespace Lua

+ 52 - 61
Source/Lua/Interpreter.cpp

@@ -30,7 +30,6 @@
 #include "LuaPlugin.h"
 #include "LuaPlugin.h"
 #include "LuaDocumentElementInstancer.h"
 #include "LuaDocumentElementInstancer.h"
 #include "LuaEventListenerInstancer.h"
 #include "LuaEventListenerInstancer.h"
-#include <RmlUi/Lua/Utilities.h>
 #include <RmlUi/Core/Core.h>
 #include <RmlUi/Core/Core.h>
 #include <RmlUi/Core/Log.h>
 #include <RmlUi/Core/Log.h>
 #include <RmlUi/Core/FileInterface.h>
 #include <RmlUi/Core/FileInterface.h>
@@ -39,12 +38,41 @@
 namespace Rml {
 namespace Rml {
 namespace Lua {
 namespace Lua {
 
 
+static int ErrorHandler(lua_State* L)
+{
+    const char* msg = lua_tostring(L, 1);
+    if (msg == NULL)
+    {
+        if (luaL_callmeta(L, 1, "__tostring") && lua_type(L, -1) == LUA_TSTRING)
+            return 1;
+        else
+            msg = lua_pushfstring(L, "(error object is a %s value)", luaL_typename(L, 1));
+    }
+    luaL_traceback(L, L, msg, 1);
+    return 1;
+}
+
+static bool LuaCall(lua_State* L, int nargs, int nresults)
+{
+    int errfunc = -2 - nargs;
+    lua_pushcfunction(L, ErrorHandler);
+    lua_insert(L, errfunc);
+    if (lua_pcall(L, nargs, nresults, errfunc) != LUA_OK)
+    {
+        Log::Message(Log::LT_WARNING, "%s", lua_tostring(L, -1));
+        lua_pop(L, 2);
+        return false;
+    }
+    lua_remove(L, -1 - nresults);
+    return true;
+}
+
 lua_State* Interpreter::GetLuaState()
 lua_State* Interpreter::GetLuaState()
 {
 {
     return LuaPlugin::GetLuaState();
     return LuaPlugin::GetLuaState();
 }
 }
 
 
-void Interpreter::LoadFile(const String& file)
+bool Interpreter::LoadFile(const String& file)
 {
 {
     lua_State* L = GetLuaState();
     lua_State* L = GetLuaState();
 
 
@@ -52,52 +80,45 @@ void Interpreter::LoadFile(const String& file)
     FileInterface* file_interface = GetFileInterface();
     FileInterface* file_interface = GetFileInterface();
     FileHandle handle = file_interface->Open(file);
     FileHandle handle = file_interface->Open(file);
     if (handle == 0) {
     if (handle == 0) {
-        lua_pushfstring(L, "LoadFile: Unable to open file: %s", file.c_str());
-        Report(L);
-        return;
+        Log::Message(Log::LT_WARNING, "LoadFile: Unable to open file: %s", file.c_str());
+        return false;
     }
     }
 
 
     size_t size = file_interface->Length(handle);
     size_t size = file_interface->Length(handle);
     if (size == 0) {
     if (size == 0) {
-        lua_pushfstring(L, "LoadFile: File is 0 bytes in size: %s", file.c_str());
-        Report(L);
-        return;
+        Log::Message(Log::LT_WARNING, "LoadFile: File is 0 bytes in size: %s", file.c_str());
+        return false;
     }
     }
-    char* file_contents = new char[size];
-    file_interface->Read(file_contents, size, handle);
+    std::unique_ptr<char[]> file_contents(new char[size]);
+    file_interface->Read(file_contents.get(), size, handle);
     file_interface->Close(handle);
     file_interface->Close(handle);
 
 
-    if (luaL_loadbuffer(L, file_contents, size, ("@" + file).c_str()) != 0)
-        Report(L);
-    else //if there were no errors loading, then the compiled function is on the top of the stack
+    if (luaL_loadbuffer(L, file_contents.get(), size, ("@" + file).c_str()) != 0)
     {
     {
-        if (lua_pcall(L, 0, 0, 0) != 0)
-            Report(L);
+        Log::Message(Log::LT_WARNING, "%s", lua_tostring(L, -1));
+        lua_pop(L, 1);
+        return false;
     }
     }
-
-    delete[] file_contents;
+    return LuaCall(L, 0, 0);;
 }
 }
 
 
-
-void Interpreter::DoString(const String& code, const String& name)
+bool Interpreter::DoString(const String& code, const String& name)
 {
 {
     lua_State* L = GetLuaState();
     lua_State* L = GetLuaState();
-
-    if (luaL_loadbuffer(L, code.c_str(), code.length(), name.c_str()) != 0)
-        Report(L);
-    else
-    {
-        if (lua_pcall(L, 0, 0, 0) != 0)
-            Report(L);
-    }
+    return LoadString(code, name) && LuaCall(L, 0, 0);
 }
 }
 
 
-void Interpreter::LoadString(const String& code, const String& name)
+bool Interpreter::LoadString(const String& code, const String& name)
 {
 {
     lua_State* L = GetLuaState();
     lua_State* L = GetLuaState();
 
 
     if (luaL_loadbuffer(L, code.c_str(), code.length(), name.c_str()) != 0)
     if (luaL_loadbuffer(L, code.c_str(), code.length(), name.c_str()) != 0)
-        Report(L);
+    {
+        Log::Message(Log::LT_WARNING, "%s", lua_tostring(L, -1));
+        lua_pop(L, 1);
+        return false;
+    }
+    return true;
 }
 }
 
 
 
 
@@ -113,43 +134,13 @@ void Interpreter::BeginCall(int funRef)
 bool Interpreter::ExecuteCall(int params, int res)
 bool Interpreter::ExecuteCall(int params, int res)
 {
 {
     lua_State* L = GetLuaState();
     lua_State* L = GetLuaState();
-
-    bool ret = true;
-    int top = lua_gettop(L);
-    if (lua_type(L, top - params) != LUA_TFUNCTION)
-    {
-        ret = false;
-        //stack cleanup
-        if (params > 0)
-        {
-            for (int i = top; i >= (top - params); i--)
-            {
-                if (!lua_isnone(L, i))
-                    lua_remove(L, i);
-            }
-        }
-    }
-    else
-    {
-        if (lua_pcall(L, params, res, 0) != 0)
-        {
-            Report(L);
-            ret = false;
-        }
-    }
-    return ret;
+    return LuaCall(L, params, res);
 }
 }
 
 
 void Interpreter::EndCall(int res)
 void Interpreter::EndCall(int res)
 {
 {
     lua_State* L = GetLuaState();
     lua_State* L = GetLuaState();
-
-    //stack cleanup
-    for (int i = res; i > 0; i--)
-    {
-        if (!lua_isnone(L, res))
-            lua_remove(L, res);
-    }
+    lua_pop(L, res);
 }
 }
 
 
 } // namespace Lua
 } // namespace Lua

+ 2 - 10
Source/Lua/LuaEventListener.cpp

@@ -57,19 +57,11 @@ LuaEventListener::LuaEventListener(const String& code, Element* element) : Event
     int tbl = lua_gettop(L);
     int tbl = lua_gettop(L);
 
 
     //compile,execute,and save the function
     //compile,execute,and save the function
-    if(luaL_loadstring(L,function.c_str()) != 0)
+    if (!Interpreter::LoadString(function, code) || !Interpreter::ExecuteCall(0, 1))
     {
     {
-        Report(L);
         return;
         return;
     }
     }
-    else
-    {
-        if(lua_pcall(L,0,1,0) != 0)
-        {
-            Report(L);
-            return;
-        }
-    }
+
     luaFuncRef = luaL_ref(L,tbl); //creates a reference to the item at the top of the stack in to the table we just created
     luaFuncRef = luaL_ref(L,tbl); //creates a reference to the item at the top of the stack in to the table we just created
     lua_pop(L,1); //pop the EVENTLISTENERFUNCTIONS table
     lua_pop(L,1); //pop the EVENTLISTENERFUNCTIONS table
 
 

+ 3 - 6
Source/Lua/LuaType.cpp

@@ -56,8 +56,7 @@ int LuaTypeImpl::index(lua_State* L, const char* class_name)
             if (lua_type(L, -1) == LUA_TFUNCTION) //[-1 = 5]
             if (lua_type(L, -1) == LUA_TFUNCTION) //[-1 = 5]
             {
             {
                 lua_pushvalue(L, 1); //push the userdata to the stack [6]
                 lua_pushvalue(L, 1); //push the userdata to the stack [6]
-                if (lua_pcall(L, 1, 1, 0) != 0) //remove one, result is at [6]
-                    Report(L, String(class_name).append(".__index for ").append(lua_tostring(L, 2)).append(": "));
+                lua_call(L, 1, 1); //remove one, result is at [6]
             }
             }
             else
             else
             {
             {
@@ -70,8 +69,7 @@ int LuaTypeImpl::index(lua_State* L, const char* class_name)
                     {
                     {
                         lua_pushvalue(L, 1); //[1] = object -> [7] = object
                         lua_pushvalue(L, 1); //[1] = object -> [7] = object
                         lua_pushvalue(L, 2); //[2] = key -> [8] = key
                         lua_pushvalue(L, 2); //[2] = key -> [8] = key
-                        if (lua_pcall(L, 2, 1, 0) != 0) //call function at top of stack (__index) -> pop top 2 as args; [7] = return value
-                            Report(L, String(class_name).append(".__index for ").append(lua_tostring(L, 2)).append(": "));
+                        lua_call(L, 2, 1); //call function at top of stack (__index) -> pop top 2 as args; [7] = return value
                     }
                     }
                     else if (lua_istable(L, -1))
                     else if (lua_istable(L, -1))
                         lua_getfield(L, -1, key); //shorthand version of above -> [7] = return value
                         lua_getfield(L, -1, key); //shorthand version of above -> [7] = return value
@@ -109,8 +107,7 @@ int LuaTypeImpl::newindex(lua_State* L, const char* class_name)
     {
     {
         lua_pushvalue(L, 1); //userdata at [7]
         lua_pushvalue(L, 1); //userdata at [7]
         lua_pushvalue(L, 3); //[8] = copy of [3]
         lua_pushvalue(L, 3); //[8] = copy of [3]
-        if (lua_pcall(L, 2, 0, 0) != 0) //call function, pop 2 off push 0 on
-            Report(L, String(class_name).append(".__newindex for ").append(lua_tostring(L, 2)).append(": "));
+        lua_call(L, 2, 0); //call function, pop 2 off push 0 on
     }
     }
     else
     else
         lua_pop(L, 1); //not a setter function.
         lua_pop(L, 1); //not a setter function.

+ 0 - 17
Source/Lua/Utilities.cpp

@@ -72,22 +72,5 @@ void PushVariant(lua_State* L, const Variant* var)
     }
     }
 }
 }
 
 
-
-void Report(lua_State* L, const String& place)
-{
-    const char * msg= lua_tostring(L,-1);
-    String strmsg;
-    while(msg)
-    {
-        lua_pop(L,1);
-        if(place == "")
-            strmsg = msg;
-        else
-            strmsg = String(place).append(" ").append(msg);
-        Log::Message(Log::LT_WARNING, strmsg.c_str());
-        msg=lua_tostring(L,-1);
-    }
-}
-
 } // namespace Lua
 } // namespace Lua
 } // namespace Rml
 } // namespace Rml