summaryrefslogtreecommitdiffstats
path: root/src/Bindings/LuaState.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/Bindings/LuaState.cpp317
1 files changed, 274 insertions, 43 deletions
diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp
index cfa3f70ca..2fca7142c 100644
--- a/src/Bindings/LuaState.cpp
+++ b/src/Bindings/LuaState.cpp
@@ -18,7 +18,7 @@ extern "C"
// fwd: SQLite/lsqlite3.c
extern "C"
{
- LUALIB_API int luaopen_lsqlite3(lua_State * L);
+ int luaopen_lsqlite3(lua_State * L);
}
// fwd: LuaExpat/lxplib.c:
@@ -228,11 +228,14 @@ bool cLuaState::PushFunction(const char * a_FunctionName)
return false;
}
+ // Push the error handler for lua_pcall()
+ lua_pushcfunction(m_LuaState, &ReportFnCallErrors);
+
lua_getglobal(m_LuaState, a_FunctionName);
if (!lua_isfunction(m_LuaState, -1))
{
LOGWARNING("Error in %s: Could not find function %s()", m_SubsystemName.c_str(), a_FunctionName);
- lua_pop(m_LuaState, 1);
+ lua_pop(m_LuaState, 2);
return false;
}
m_CurrentFunctionName.assign(a_FunctionName);
@@ -249,10 +252,13 @@ bool cLuaState::PushFunction(int a_FnRef)
ASSERT(IsValid());
ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack
+ // Push the error handler for lua_pcall()
+ lua_pushcfunction(m_LuaState, &ReportFnCallErrors);
+
lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, a_FnRef); // same as lua_getref()
if (!lua_isfunction(m_LuaState, -1))
{
- lua_pop(m_LuaState, 1);
+ lua_pop(m_LuaState, 2);
return false;
}
m_CurrentFunctionName = "<callback>";
@@ -264,27 +270,29 @@ bool cLuaState::PushFunction(int a_FnRef)
-bool cLuaState::PushFunctionFromRefTable(cRef & a_TableRef, const char * a_FnName)
+bool cLuaState::PushFunction(const cTableRef & a_TableRef)
{
ASSERT(IsValid());
ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack
-
- lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, a_TableRef); // Get the table ref
+
+ // Push the error handler for lua_pcall()
+ lua_pushcfunction(m_LuaState, &ReportFnCallErrors);
+
+ lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, a_TableRef.GetTableRef()); // Get the table ref
if (!lua_istable(m_LuaState, -1))
{
// Not a table, bail out
- lua_pop(m_LuaState, 1);
+ lua_pop(m_LuaState, 2);
return false;
}
- lua_getfield(m_LuaState, -1, a_FnName);
+ lua_getfield(m_LuaState, -1, a_TableRef.GetFnName());
if (lua_isnil(m_LuaState, -1) || !lua_isfunction(m_LuaState, -1))
{
// Not a valid function, bail out
lua_pop(m_LuaState, 2);
return false;
}
- lua_remove(m_LuaState, -2); // Remove the table ref from the stack
- m_CurrentFunctionName = "<table_callback>";
+ Printf(m_CurrentFunctionName, "<table-callback %s>", a_TableRef.GetFnName());
m_NumCurrentFunctionArgs = 0;
return true;
}
@@ -297,7 +305,7 @@ void cLuaState::Push(const AString & a_String)
{
ASSERT(IsValid());
- tolua_pushcppstring(m_LuaState, a_String);
+ lua_pushlstring(m_LuaState, a_String.data(), a_String.size());
m_NumCurrentFunctionArgs += 1;
}
@@ -309,7 +317,7 @@ void cLuaState::Push(const AStringVector & a_Vector)
{
ASSERT(IsValid());
- lua_createtable(m_LuaState, a_Vector.size(), 0);
+ lua_createtable(m_LuaState, (int)a_Vector.size(), 0);
int newTable = lua_gettop(m_LuaState);
int index = 1;
for (AStringVector::const_iterator itr = a_Vector.begin(), end = a_Vector.end(); itr != end; ++itr, ++index)
@@ -468,6 +476,18 @@ void cLuaState::Push(cItems * a_Items)
+void cLuaState::Push(const cItems & a_Items)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, (void *)&a_Items, "cItems");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
void cLuaState::Push(cClientHandle * a_Client)
{
ASSERT(IsValid());
@@ -612,18 +632,6 @@ void cLuaState::Push(cTNTEntity * a_TNTEntity)
-void cLuaState::Push(cCreeper * a_Creeper)
-{
- ASSERT(IsValid());
-
- tolua_pushusertype(m_LuaState, a_Creeper, "cCreeper");
- m_NumCurrentFunctionArgs += 1;
-}
-
-
-
-
-
void cLuaState::Push(Vector3i * a_Vector)
{
ASSERT(IsValid());
@@ -720,11 +728,13 @@ void cLuaState::GetReturn(int a_StackPos, double & a_ReturnedVal)
bool cLuaState::CallFunction(int a_NumResults)
{
ASSERT (m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first
- ASSERT(lua_isfunction(m_LuaState, -m_NumCurrentFunctionArgs - 1));
+ ASSERT(lua_isfunction(m_LuaState, -m_NumCurrentFunctionArgs - 1)); // The function to call
+ ASSERT(lua_isfunction(m_LuaState, -m_NumCurrentFunctionArgs - 2)); // The error handler
- int s = lua_pcall(m_LuaState, m_NumCurrentFunctionArgs, a_NumResults, 0);
- if (ReportErrors(s))
+ int s = lua_pcall(m_LuaState, m_NumCurrentFunctionArgs, a_NumResults, -m_NumCurrentFunctionArgs - 2);
+ if (s != 0)
{
+ // The error has already been printed together with the stacktrace
LOGWARNING("Error in %s calling function %s()", m_SubsystemName.c_str(), m_CurrentFunctionName.c_str());
m_NumCurrentFunctionArgs = -1;
m_CurrentFunctionName.clear();
@@ -732,6 +742,10 @@ bool cLuaState::CallFunction(int a_NumResults)
}
m_NumCurrentFunctionArgs = -1;
m_CurrentFunctionName.clear();
+
+ // Remove the error handler from the stack:
+ lua_remove(m_LuaState, -a_NumResults - 1);
+
return true;
}
@@ -904,6 +918,40 @@ bool cLuaState::CheckParamString(int a_StartParam, int a_EndParam)
+bool cLuaState::CheckParamFunction(int a_StartParam, int a_EndParam)
+{
+ ASSERT(IsValid());
+
+ if (a_EndParam < 0)
+ {
+ a_EndParam = a_StartParam;
+ }
+
+ for (int i = a_StartParam; i <= a_EndParam; i++)
+ {
+ if (lua_isfunction(m_LuaState, i))
+ {
+ continue;
+ }
+ // Not the correct parameter
+ lua_Debug entry;
+ VERIFY(lua_getstack(m_LuaState, 0, &entry));
+ VERIFY(lua_getinfo (m_LuaState, "n", &entry));
+ AString ErrMsg = Printf("Error in function '%s' parameter #%d. Function expected, got %s",
+ (entry.name != NULL) ? entry.name : "?", i, GetTypeText(i).c_str()
+ );
+ LogStackTrace();
+ return false;
+ } // for i - Param
+
+ // All params checked ok
+ return true;
+}
+
+
+
+
+
bool cLuaState::CheckParamEnd(int a_Param)
{
tolua_Error tolua_err;
@@ -952,15 +1000,24 @@ bool cLuaState::ReportErrors(lua_State * a_LuaState, int a_Status)
void cLuaState::LogStackTrace(void)
{
+ LogStackTrace(m_LuaState);
+}
+
+
+
+
+
+void cLuaState::LogStackTrace(lua_State * a_LuaState)
+{
LOGWARNING("Stack trace:");
lua_Debug entry;
int depth = 0;
- while (lua_getstack(m_LuaState, depth, &entry))
+ while (lua_getstack(a_LuaState, depth, &entry))
{
- int status = lua_getinfo(m_LuaState, "Sln", &entry);
+ int status = lua_getinfo(a_LuaState, "Sln", &entry);
assert(status);
- LOGWARNING(" %s(%d): %s", entry.short_src, entry.currentline, entry.name ? entry.name : "?");
+ LOGWARNING(" %s(%d): %s", entry.short_src, entry.currentline, entry.name ? entry.name : "(no name)");
depth++;
}
LOGWARNING("Stack trace end");
@@ -972,21 +1029,195 @@ void cLuaState::LogStackTrace(void)
AString cLuaState::GetTypeText(int a_StackPos)
{
- int Type = lua_type(m_LuaState, a_StackPos);
- switch (Type)
+ return lua_typename(m_LuaState, lua_type(m_LuaState, a_StackPos));
+}
+
+
+
+
+
+int cLuaState::CallFunctionWithForeignParams(
+ const AString & a_FunctionName,
+ cLuaState & a_SrcLuaState,
+ int a_SrcParamStart,
+ int a_SrcParamEnd
+)
+{
+ ASSERT(IsValid());
+ ASSERT(a_SrcLuaState.IsValid());
+
+ // Store the stack position before any changes
+ int OldTop = lua_gettop(m_LuaState);
+
+ // Push the function to call, including the error handler:
+ if (!PushFunction(a_FunctionName.c_str()))
+ {
+ LOGWARNING("Function '%s' not found", a_FunctionName.c_str());
+ lua_pop(m_LuaState, 2);
+ return -1;
+ }
+
+ // Copy the function parameters to the target state
+ if (CopyStackFrom(a_SrcLuaState, a_SrcParamStart, a_SrcParamEnd) < 0)
+ {
+ // Something went wrong, fix the stack and exit
+ lua_pop(m_LuaState, 2);
+ return -1;
+ }
+
+ // Call the function, with an error handler:
+ int s = lua_pcall(m_LuaState, a_SrcParamEnd - a_SrcParamStart + 1, LUA_MULTRET, OldTop);
+ if (ReportErrors(s))
+ {
+ LOGWARN("Error while calling function '%s' in '%s'", a_FunctionName.c_str(), m_SubsystemName.c_str());
+ // Fix the stack.
+ // We don't know how many values have been pushed, so just get rid of any that weren't there initially
+ int CurTop = lua_gettop(m_LuaState);
+ if (CurTop > OldTop)
+ {
+ lua_pop(m_LuaState, CurTop - OldTop);
+ }
+ return -1;
+ }
+
+ // Reset the internal checking mechanisms:
+ m_NumCurrentFunctionArgs = -1;
+
+ // Remove the error handler from the stack:
+ lua_remove(m_LuaState, OldTop + 1);
+
+ // Return the number of return values:
+ return lua_gettop(m_LuaState) - OldTop;
+}
+
+
+
+
+
+int cLuaState::CopyStackFrom(cLuaState & a_SrcLuaState, int a_SrcStart, int a_SrcEnd)
+{
+ /*
+ // DEBUG:
+ LOGD("Copying stack values from %d to %d", a_SrcStart, a_SrcEnd);
+ a_SrcLuaState.LogStack("Src stack before copying:");
+ LogStack("Dst stack before copying:");
+ */
+ for (int i = a_SrcStart; i <= a_SrcEnd; ++i)
+ {
+ int t = lua_type(a_SrcLuaState, i);
+ switch (t)
+ {
+ case LUA_TNIL:
+ {
+ lua_pushnil(m_LuaState);
+ break;
+ }
+ case LUA_TSTRING:
+ {
+ AString s;
+ a_SrcLuaState.ToString(i, s);
+ Push(s);
+ break;
+ }
+ case LUA_TBOOLEAN:
+ {
+ bool b = (tolua_toboolean(a_SrcLuaState, i, false) != 0);
+ Push(b);
+ break;
+ }
+ case LUA_TNUMBER:
+ {
+ lua_Number d = tolua_tonumber(a_SrcLuaState, i, 0);
+ Push(d);
+ break;
+ }
+ case LUA_TUSERDATA:
+ {
+ // Get the class name:
+ const char * type = NULL;
+ if (lua_getmetatable(a_SrcLuaState, i) == 0)
+ {
+ LOGWARNING("%s: Unknown class in pos %d, cannot copy.", __FUNCTION__, i);
+ lua_pop(m_LuaState, i - a_SrcStart);
+ return -1;
+ }
+ lua_rawget(a_SrcLuaState, LUA_REGISTRYINDEX); // Stack +1
+ type = lua_tostring(a_SrcLuaState, -1);
+ lua_pop(a_SrcLuaState, 1); // Stack -1
+
+ // Copy the value:
+ void * ud = tolua_touserdata(a_SrcLuaState, i, NULL);
+ tolua_pushusertype(m_LuaState, ud, type);
+ }
+ default:
+ {
+ LOGWARNING("%s: Unsupported value: '%s' at stack position %d. Can only copy numbers, strings, bools and classes!",
+ __FUNCTION__, lua_typename(a_SrcLuaState, t), i
+ );
+ a_SrcLuaState.LogStack("Stack where copying failed:");
+ lua_pop(m_LuaState, i - a_SrcStart);
+ return -1;
+ }
+ }
+ }
+ return a_SrcEnd - a_SrcStart + 1;
+}
+
+
+
+
+
+void cLuaState::ToString(int a_StackPos, AString & a_String)
+{
+ size_t len;
+ const char * s = lua_tolstring(m_LuaState, a_StackPos, &len);
+ if (s != NULL)
{
- case LUA_TNONE: return "TNONE";
- case LUA_TNIL: return "TNIL";
- case LUA_TBOOLEAN: return "TBOOLEAN";
- case LUA_TLIGHTUSERDATA: return "TLIGHTUSERDATA";
- case LUA_TNUMBER: return "TNUMBER";
- case LUA_TSTRING: return "TSTRING";
- case LUA_TTABLE: return "TTABLE";
- case LUA_TFUNCTION: return "TFUNCTION";
- case LUA_TUSERDATA: return "TUSERDATA";
- case LUA_TTHREAD: return "TTHREAD";
+ a_String.assign(s, len);
}
- return Printf("Unknown (%d)", Type);
+}
+
+
+
+
+
+void cLuaState::LogStack(const char * a_Header)
+{
+ LogStack(m_LuaState, a_Header);
+}
+
+
+
+
+
+void cLuaState::LogStack(lua_State * a_LuaState, const char * a_Header)
+{
+ LOGD((a_Header != NULL) ? a_Header : "Lua C API Stack contents:");
+ for (int i = lua_gettop(a_LuaState); i >= 0; i--)
+ {
+ AString Value;
+ int Type = lua_type(a_LuaState, i);
+ switch (Type)
+ {
+ case LUA_TBOOLEAN: Value.assign((lua_toboolean(a_LuaState, i) != 0) ? "true" : "false"); break;
+ case LUA_TLIGHTUSERDATA: Printf(Value, "%p", lua_touserdata(a_LuaState, i)); break;
+ case LUA_TNUMBER: Printf(Value, "%f", (double)lua_tonumber(a_LuaState, i)); break;
+ case LUA_TSTRING: Printf(Value, "%s", lua_tostring(a_LuaState, i)); break;
+ default: break;
+ }
+ LOGD(" Idx %d: type %d (%s) %s", i, Type, lua_typename(a_LuaState, Type), Value.c_str());
+ } // for i - stack idx
+}
+
+
+
+
+
+int cLuaState::ReportFnCallErrors(lua_State * a_LuaState)
+{
+ LOGWARNING("LUA: %s", lua_tostring(a_LuaState, -1));
+ LogStackTrace(a_LuaState);
+ return 1; // We left the error message on the stack as the return value
}