Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add stack traceback to Lua error message. #140

Merged
merged 2 commits into from
Oct 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Include/RmlUi/Lua/Interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,16 @@ namespace Interpreter {
@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
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.
@param[in] code String to execute
@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
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] 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
this if you used lua_ref instead of luaL_ref
Expand Down
7 changes: 0 additions & 7 deletions Include/RmlUi/Lua/Utilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,6 @@ namespace Lua {
@relates LuaType */
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
// to fill the Elements.As table
template<typename ToType>
Expand Down
23 changes: 10 additions & 13 deletions Source/Lua/Elements/LuaDataSource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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

int res = lua_gettop(L);
if(lua_type(L,res) == LUA_TTABLE)
if(lua_type(L,-1) == LUA_TTABLE)
{
lua_pushnil(L);
while(lua_next(L,res) != 0)
while (lua_next(L, -2))
{
//key at -2, value at -1
row.push_back(luaL_checkstring(L,-1));
lua_pop(L,1); //pops value, leaves key for next iteration
}
lua_pop(L,1); //pop key
}
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.
Expand All @@ -89,17 +86,17 @@ int LuaDataSource::GetNumRows(const String& table)
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

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
{
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
Expand Down
113 changes: 52 additions & 61 deletions Source/Lua/Interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
#include "LuaPlugin.h"
#include "LuaDocumentElementInstancer.h"
#include "LuaEventListenerInstancer.h"
#include <RmlUi/Lua/Utilities.h>
#include <RmlUi/Core/Core.h>
#include <RmlUi/Core/Log.h>
#include <RmlUi/Core/FileInterface.h>
Expand All @@ -39,65 +38,87 @@
namespace Rml {
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()
{
return LuaPlugin::GetLuaState();
}

void Interpreter::LoadFile(const String& file)
bool Interpreter::LoadFile(const String& file)
{
lua_State* L = GetLuaState();

//use the file interface to get the contents of the script
FileInterface* file_interface = GetFileInterface();
FileHandle handle = file_interface->Open(file);
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);
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);

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();

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();

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;
}


Expand All @@ -113,43 +134,13 @@ void Interpreter::BeginCall(int funRef)
bool Interpreter::ExecuteCall(int params, int res)
{
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)
{
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
Expand Down
12 changes: 2 additions & 10 deletions Source/Lua/LuaEventListener.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,19 +57,11 @@ LuaEventListener::LuaEventListener(const String& code, Element* element) : Event
int tbl = lua_gettop(L);

//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;
}
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
lua_pop(L,1); //pop the EVENTLISTENERFUNCTIONS table

Expand Down
9 changes: 3 additions & 6 deletions Source/Lua/LuaType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ int LuaTypeImpl::index(lua_State* L, const char* class_name)
if (lua_type(L, -1) == LUA_TFUNCTION) //[-1 = 5]
{
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
{
Expand All @@ -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, 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))
lua_getfield(L, -1, key); //shorthand version of above -> [7] = return value
Expand Down Expand Up @@ -109,8 +107,7 @@ int LuaTypeImpl::newindex(lua_State* L, const char* class_name)
{
lua_pushvalue(L, 1); //userdata at [7]
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
lua_pop(L, 1); //not a setter function.
Expand Down
17 changes: 0 additions & 17 deletions Source/Lua/Utilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 Rml