1
0
cuberite-2a/src/Bindings/LuaState.cpp
Mattes D 11682d1386 cLuaState: Moved function param counting to PushCallPop() template.
The Push() functions can be used not only for function params, but also returns or temporaries, so it doesn't make sense to count the params there.
2016-08-22 20:10:48 +02:00

2297 lines
45 KiB
C++

// LuaState.cpp
// Implements the cLuaState class representing the wrapper over lua_State *, provides associated helper functions
#include "Globals.h"
#include "LuaState.h"
extern "C"
{
#include "lua/src/lualib.h"
}
#undef TOLUA_TEMPLATE_BIND
#include "tolua++/include/tolua++.h"
#include "Bindings.h"
#include "ManualBindings.h"
#include "DeprecatedBindings.h"
#include "LuaJson.h"
#include "../Entities/Entity.h"
#include "../BlockEntities/BlockEntity.h"
// fwd: "SQLite/lsqlite3.c"
extern "C"
{
int luaopen_lsqlite3(lua_State * L);
}
// fwd: "LuaExpat/lxplib.c":
extern "C"
{
int luaopen_lxp(lua_State * L);
}
const cLuaState::cRet cLuaState::Return = {};
const cLuaState::cNil cLuaState::Nil = {};
/** Each Lua state stores a pointer to its creating cLuaState in Lua globals, under this name.
This way any cLuaState can reference the main cLuaState's TrackedCallbacks, mutex etc. */
static const char * g_CanonLuaStateGlobalName = "_CuberiteInternal_CanonLuaState";
////////////////////////////////////////////////////////////////////////////////
// cLuaStateTracker:
void cLuaStateTracker::Add(cLuaState & a_LuaState)
{
auto & Instance = Get();
cCSLock Lock(Instance.m_CSLuaStates);
Instance.m_LuaStates.push_back(&a_LuaState);
}
void cLuaStateTracker::Del(cLuaState & a_LuaState)
{
auto & Instance = Get();
cCSLock Lock(Instance.m_CSLuaStates);
Instance.m_LuaStates.erase(
std::remove_if(
Instance.m_LuaStates.begin(), Instance.m_LuaStates.end(),
[&a_LuaState](cLuaStatePtr a_StoredLuaState)
{
return (&a_LuaState == a_StoredLuaState);
}
),
Instance.m_LuaStates.end()
);
}
AString cLuaStateTracker::GetStats(void)
{
auto & Instance = Get();
cCSLock Lock(Instance.m_CSLuaStates);
AString res;
int Total = 0;
for (auto state: Instance.m_LuaStates)
{
int Mem = 0;
if (!state->Call("collectgarbage", "count", cLuaState::Return, Mem))
{
res.append(Printf("Cannot query memory for state \"%s\"\n", state->GetSubsystemName().c_str()));
}
else
{
res.append(Printf("State \"%s\" is using %d KiB of memory\n", state->GetSubsystemName().c_str(), Mem));
Total += Mem;
}
}
res.append(Printf("Total memory used by Lua: %d KiB\n", Total));
return res;
}
cLuaStateTracker & cLuaStateTracker::Get(void)
{
static cLuaStateTracker Inst; // The singleton
return Inst;
}
////////////////////////////////////////////////////////////////////////////////
// cLuaState::cTrackedRef:
cLuaState::cTrackedRef::cTrackedRef(void):
m_CS(nullptr)
{
}
bool cLuaState::cTrackedRef::RefStack(cLuaState & a_LuaState, int a_StackPos)
{
// Clear any previous callback:
Clear();
// Add self to LuaState's callback-tracking:
auto canonState = a_LuaState.QueryCanonLuaState();
canonState->TrackRef(*this);
// Store the new callback:
m_CS = &(canonState->m_CS);
m_Ref.RefStack(*canonState, a_StackPos);
return true;
}
void cLuaState::cTrackedRef::Clear(void)
{
// Free the reference:
lua_State * luaState = nullptr;
{
auto cs = m_CS;
if (cs != nullptr)
{
cCSLock Lock(*cs);
if (!m_Ref.IsValid())
{
return;
}
luaState = m_Ref.GetLuaState();
m_Ref.UnRef();
}
}
m_CS = nullptr;
// Remove from LuaState's callback-tracking:
if (luaState == nullptr)
{
return;
}
cLuaState(luaState).UntrackRef(*this);
}
bool cLuaState::cTrackedRef::IsValid(void)
{
auto cs = m_CS;
if (cs == nullptr)
{
return false;
}
cCSLock lock(*cs);
return m_Ref.IsValid();
}
bool cLuaState::cTrackedRef::IsSameLuaState(cLuaState & a_LuaState)
{
auto cs = m_CS;
if (cs == nullptr)
{
return false;
}
cCSLock lock(*cs);
if (!m_Ref.IsValid())
{
return false;
}
auto canonState = a_LuaState.QueryCanonLuaState();
if (canonState == nullptr)
{
return false;
}
return (m_Ref.GetLuaState() == static_cast<lua_State *>(*canonState));
}
void cLuaState::cTrackedRef::Invalidate(void)
{
auto cs = m_CS;
if (cs == nullptr)
{
// Already invalid
return;
}
cCSLock Lock(*cs);
if (!m_Ref.IsValid())
{
LOGD("%s: Inconsistent callback at %p, has a CS but an invalid Ref. This should not happen",
__FUNCTION__, reinterpret_cast<void *>(this)
);
return;
}
m_Ref.UnRef();
m_CS = nullptr;
}
////////////////////////////////////////////////////////////////////////////////
// cLuaState::cCallback:
bool cLuaState::cCallback::RefStack(cLuaState & a_LuaState, int a_StackPos)
{
// Check if the stack contains a function:
if (!lua_isfunction(a_LuaState, a_StackPos))
{
return false;
}
return Super::RefStack(a_LuaState, a_StackPos);
}
////////////////////////////////////////////////////////////////////////////////
// cLuaState::cOptionalCallback:
bool cLuaState::cOptionalCallback::RefStack(cLuaState & a_LuaState, int a_StackPos)
{
// If the stack pos is nil, make this an empty callback:
if (lua_isnil(a_LuaState, a_StackPos))
{
Clear();
return true;
}
// Use default cCallback implementation:
return Super::RefStack(a_LuaState, a_StackPos);
}
////////////////////////////////////////////////////////////////////////////////
// cLuaState::cTableRef:
bool cLuaState::cTableRef::RefStack(cLuaState & a_LuaState, int a_StackPos)
{
// Check if the stack contains a table:
if (!lua_istable(a_LuaState, a_StackPos))
{
return false;
}
return Super::RefStack(a_LuaState, a_StackPos);
}
////////////////////////////////////////////////////////////////////////////////
// cLuaState::cStackTable:
cLuaState::cStackTable::cStackTable(cLuaState & a_LuaState, int a_StackPos):
m_LuaState(a_LuaState),
m_StackPos(a_StackPos)
{
ASSERT(lua_istable(a_LuaState, a_StackPos));
}
void cLuaState::cStackTable::ForEachArrayElement(std::function<bool(cLuaState & a_LuaState, int a_Index)> a_ElementCallback) const
{
auto numElements = luaL_getn(m_LuaState, m_StackPos);
#ifdef _DEBUG
auto stackTop = lua_gettop(m_LuaState);
#endif
for (int idx = 1; idx <= numElements; idx++)
{
// Push the idx-th element of the array onto stack top and call the callback:
lua_rawgeti(m_LuaState, m_StackPos, idx);
auto shouldAbort = a_ElementCallback(m_LuaState, idx);
ASSERT(lua_gettop(m_LuaState) == stackTop + 1); // The element callback must not change the Lua stack below the value
lua_pop(m_LuaState, 1);
if (shouldAbort)
{
// The callback wants to abort
return;
}
}
}
void cLuaState::cStackTable::ForEachElement(std::function<bool(cLuaState & a_LuaState)> a_ElementCallback) const
{
#ifdef _DEBUG
auto stackTop = lua_gettop(m_LuaState);
#endif
lua_pushvalue(m_LuaState, m_StackPos); // Stk: <table>
lua_pushnil(m_LuaState); // Stk: <table> nil
while (lua_next(m_LuaState, -2)) // Stk: <table> <key> <val>
{
auto shouldAbort = a_ElementCallback(m_LuaState);
ASSERT(lua_gettop(m_LuaState) == stackTop + 3); // The element callback must not change the Lua stack below the value
lua_pop(m_LuaState, 1); // Stk: <table> <key>
if (shouldAbort)
{
// The callback wants to abort
lua_pop(m_LuaState, 2); // Stk: empty
return;
}
}
// Stk: <table>
lua_pop(m_LuaState, 1); // Stk: empty
}
////////////////////////////////////////////////////////////////////////////////
// cLuaState:
cLuaState::cLuaState(const AString & a_SubsystemName) :
m_LuaState(nullptr),
m_IsOwned(false),
m_SubsystemName(a_SubsystemName),
m_NumCurrentFunctionArgs(-1)
{
}
cLuaState::cLuaState(lua_State * a_AttachState) :
m_LuaState(a_AttachState),
m_IsOwned(false),
m_SubsystemName("<attached>"),
m_NumCurrentFunctionArgs(-1)
{
}
cLuaState::~cLuaState()
{
if (IsValid())
{
if (m_IsOwned)
{
Close();
}
else
{
Detach();
}
}
}
void cLuaState::Create(void)
{
if (m_LuaState != nullptr)
{
LOGWARNING("%s: Trying to create an already-existing LuaState, ignoring.", __FUNCTION__);
return;
}
m_LuaState = lua_open();
luaL_openlibs(m_LuaState);
m_IsOwned = true;
cLuaStateTracker::Add(*this);
// Add the CanonLuaState value into the Lua state, so that we can get it from anywhere:
lua_pushlightuserdata(m_LuaState, reinterpret_cast<void *>(this));
lua_setglobal(m_LuaState, g_CanonLuaStateGlobalName);
}
void cLuaState::RegisterAPILibs(void)
{
auto top = lua_gettop(m_LuaState);
tolua_AllToLua_open(m_LuaState);
ASSERT(top == lua_gettop(m_LuaState));
cManualBindings::Bind(m_LuaState);
ASSERT(top == lua_gettop(m_LuaState));
DeprecatedBindings::Bind(m_LuaState);
ASSERT(top == lua_gettop(m_LuaState));
cLuaJson::Bind(*this);
ASSERT(top == lua_gettop(m_LuaState));
luaopen_lsqlite3(m_LuaState);
if (top == lua_gettop(m_LuaState) - 1)
{
// lsqlite3 left the "sqlite3" table on the stack, pop it:
lua_pop(m_LuaState, 1);
}
ASSERT(top == lua_gettop(m_LuaState));
luaopen_lxp(m_LuaState);
if (top == lua_gettop(m_LuaState) - 1)
{
// lxp left the unregistered "lxp" table on the stack, register and pop it (#3304):
lua_setglobal(m_LuaState, "lxp");
}
ASSERT(top == lua_gettop(m_LuaState));
}
void cLuaState::Close(void)
{
if (m_LuaState == nullptr)
{
LOGWARNING("%s: Trying to close an invalid LuaState, ignoring.", __FUNCTION__);
return;
}
if (!m_IsOwned)
{
LOGWARNING(
"%s: Detected mis-use, calling Close() on an attached state (0x%p). Detaching instead.",
__FUNCTION__, static_cast<void *>(m_LuaState)
);
Detach();
return;
}
// Invalidate all tracked refs:
{
cCSLock Lock(m_CSTrackedRefs);
for (auto & r: m_TrackedRefs)
{
r->Invalidate();
}
}
cLuaStateTracker::Del(*this);
lua_close(m_LuaState);
m_LuaState = nullptr;
m_IsOwned = false;
}
void cLuaState::Attach(lua_State * a_State)
{
if (m_LuaState != nullptr)
{
LOGINFO("%s: Already contains a LuaState (0x%p), will be closed / detached.", __FUNCTION__, static_cast<void *>(m_LuaState));
if (m_IsOwned)
{
Close();
}
else
{
Detach();
}
}
m_LuaState = a_State;
m_IsOwned = false;
}
void cLuaState::Detach(void)
{
if (m_LuaState == nullptr)
{
return;
}
if (m_IsOwned)
{
LOGWARNING(
"%s: Detected a mis-use, calling Detach() when the state is owned. Closing the owned state (0x%p).",
__FUNCTION__, static_cast<void *>(m_LuaState)
);
Close();
return;
}
m_LuaState = nullptr;
}
void cLuaState::AddPackagePath(const AString & a_PathVariable, const AString & a_Path)
{
// Get the current path:
lua_getfield(m_LuaState, LUA_GLOBALSINDEX, "package"); // Stk: <package>
lua_getfield(m_LuaState, -1, a_PathVariable.c_str()); // Stk: <package> <package.path>
size_t len = 0;
const char * PackagePath = lua_tolstring(m_LuaState, -1, &len);
// Append the new path:
AString NewPackagePath(PackagePath, len);
NewPackagePath.append(LUA_PATHSEP);
NewPackagePath.append(a_Path);
// Set the new path to the environment:
lua_pop(m_LuaState, 1); // Stk: <package>
lua_pushlstring(m_LuaState, NewPackagePath.c_str(), NewPackagePath.length()); // Stk: <package> <NewPackagePath>
lua_setfield(m_LuaState, -2, a_PathVariable.c_str()); // Stk: <package>
lua_pop(m_LuaState, 1); // Stk: -
}
bool cLuaState::LoadFile(const AString & a_FileName, bool a_LogWarnings)
{
ASSERT(IsValid());
// Load the file:
int s = luaL_loadfile(m_LuaState, a_FileName.c_str());
if (s != 0)
{
if (a_LogWarnings)
{
LOGWARNING("Can't load %s because of a load error in file %s: %d (%s)", m_SubsystemName.c_str(), a_FileName.c_str(), s, lua_tostring(m_LuaState, -1));
}
lua_pop(m_LuaState, 1);
return false;
}
// Execute the globals:
s = lua_pcall(m_LuaState, 0, LUA_MULTRET, 0);
if (s != 0)
{
if (a_LogWarnings)
{
LOGWARNING("Can't load %s because of an initialization error in file %s: %d (%s)", m_SubsystemName.c_str(), a_FileName.c_str(), s, lua_tostring(m_LuaState, -1));
}
lua_pop(m_LuaState, 1);
return false;
}
return true;
}
bool cLuaState::LoadString(const AString & a_StringToLoad, const AString & a_FileName, bool a_LogWarnings)
{
ASSERT(IsValid());
// Load the file:
int s = luaL_loadstring(m_LuaState, a_StringToLoad.c_str());
if (s != 0)
{
if (a_LogWarnings)
{
LOGWARNING("Can't load %s because of a load error in string from \"%s\": %d (%s)", m_SubsystemName.c_str(), a_FileName.c_str(), s, lua_tostring(m_LuaState, -1));
}
lua_pop(m_LuaState, 1);
return false;
}
// Execute the globals:
s = lua_pcall(m_LuaState, 0, LUA_MULTRET, 0);
if (s != 0)
{
if (a_LogWarnings)
{
LOGWARNING("Can't load %s because of an initialization error in string from \"%s\": %d (%s)", m_SubsystemName.c_str(), a_FileName.c_str(), s, lua_tostring(m_LuaState, -1));
}
lua_pop(m_LuaState, 1);
return false;
}
return true;
}
bool cLuaState::HasFunction(const char * a_FunctionName)
{
if (!IsValid())
{
// This happens if cPlugin::Initialize() fails with an error
return false;
}
lua_getglobal(m_LuaState, a_FunctionName);
bool res = (!lua_isnil(m_LuaState, -1) && lua_isfunction(m_LuaState, -1));
lua_pop(m_LuaState, 1);
return res;
}
bool cLuaState::PushFunction(const char * a_FunctionName)
{
ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack
if (!IsValid())
{
// This happens if cPlugin::Initialize() fails with an error
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, 2);
return false;
}
m_CurrentFunctionName.assign(a_FunctionName);
m_NumCurrentFunctionArgs = 0;
return true;
}
bool cLuaState::PushFunction(const cRef & 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, static_cast<int>(a_FnRef)); // same as lua_getref()
if (!lua_isfunction(m_LuaState, -1))
{
lua_pop(m_LuaState, 2);
return false;
}
m_CurrentFunctionName = "<callback>";
m_NumCurrentFunctionArgs = 0;
return true;
}
bool cLuaState::PushFunction(const cRef & a_TableRef, const char * a_FnName)
{
ASSERT(IsValid());
ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack
if (!a_TableRef.IsValid())
{
return false;
}
// Push the error handler for lua_pcall()
lua_pushcfunction(m_LuaState, &ReportFnCallErrors);
// Get the function from the table:
lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, static_cast<int>(a_TableRef));
if (!lua_istable(m_LuaState, -1))
{
// Not a table, bail out
lua_pop(m_LuaState, 2);
return false;
}
lua_getfield(m_LuaState, -1, a_FnName);
if (lua_isnil(m_LuaState, -1) || !lua_isfunction(m_LuaState, -1))
{
// Not a valid function, bail out
lua_pop(m_LuaState, 3);
return false;
}
// Pop the table off the stack:
lua_remove(m_LuaState, -2);
Printf(m_CurrentFunctionName, "<table-callback %s>", a_FnName);
m_NumCurrentFunctionArgs = 0;
return true;
}
void cLuaState::Push(const AString & a_String)
{
ASSERT(IsValid());
lua_pushlstring(m_LuaState, a_String.data(), a_String.size());
}
void cLuaState::Push(const AStringMap & a_Dictionary)
{
ASSERT(IsValid());
lua_createtable(m_LuaState, 0, static_cast<int>(a_Dictionary.size()));
int newTable = lua_gettop(m_LuaState);
for (const auto & item: a_Dictionary)
{
Push(item.first); // key
Push(item.second); // value
lua_rawset(m_LuaState, newTable);
}
}
void cLuaState::Push(const AStringVector & a_Vector)
{
ASSERT(IsValid());
lua_createtable(m_LuaState, static_cast<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)
{
tolua_pushstring(m_LuaState, itr->c_str());
lua_rawseti(m_LuaState, newTable, index);
}
}
void cLuaState::Push(const cCraftingGrid * a_Grid)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<cCraftingGrid *>(a_Grid)), "cCraftingGrid");
}
void cLuaState::Push(const cCraftingRecipe * a_Recipe)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<cCraftingRecipe *>(a_Recipe)), "cCraftingRecipe");
}
void cLuaState::Push(const char * a_Value)
{
ASSERT(IsValid());
tolua_pushstring(m_LuaState, a_Value);
}
void cLuaState::Push(const cItems & a_Items)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<cItems *>(&a_Items)), "cItems");
}
void cLuaState::Push(const cNil & a_Nil)
{
ASSERT(IsValid());
lua_pushnil(m_LuaState);
}
void cLuaState::Push(const cPlayer * a_Player)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<cPlayer *>(a_Player)), "cPlayer");
}
void cLuaState::Push(const cLuaState::cRef & a_Ref)
{
ASSERT(IsValid());
lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, static_cast<int>(a_Ref));
}
void cLuaState::Push(const HTTPRequest * a_Request)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<HTTPRequest *>(a_Request)), "HTTPRequest");
}
void cLuaState::Push(const HTTPTemplateRequest * a_Request)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<HTTPTemplateRequest *>(a_Request)), "HTTPTemplateRequest");
}
void cLuaState::Push(const Vector3d & a_Vector)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<Vector3d *>(&a_Vector)), "Vector3<double>");
}
void cLuaState::Push(const Vector3d * a_Vector)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<Vector3d *>(a_Vector)), "Vector3<double>");
}
void cLuaState::Push(const Vector3i & a_Vector)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<Vector3i *>(&a_Vector)), "Vector3<int>");
}
void cLuaState::Push(const Vector3i * a_Vector)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<Vector3i *>(a_Vector)), "Vector3<int>");
}
void cLuaState::Push(bool a_Value)
{
ASSERT(IsValid());
tolua_pushboolean(m_LuaState, a_Value ? 1 : 0);
}
void cLuaState::Push(cEntity * a_Entity)
{
ASSERT(IsValid());
if (a_Entity == nullptr)
{
lua_pushnil(m_LuaState);
}
else
{
switch (a_Entity->GetEntityType())
{
case cEntity::etMonster:
{
// Don't push specific mob types, as those are not exported in the API:
tolua_pushusertype(m_LuaState, a_Entity, "cMonster");
break;
}
case cEntity::etPlayer:
{
tolua_pushusertype(m_LuaState, a_Entity, "cPlayer");
break;
}
case cEntity::etPickup:
{
tolua_pushusertype(m_LuaState, a_Entity, "cPickup");
break;
}
case cEntity::etTNT:
{
tolua_pushusertype(m_LuaState, a_Entity, "cTNTEntity");
break;
}
case cEntity::etProjectile:
{
tolua_pushusertype(m_LuaState, a_Entity, a_Entity->GetClass());
break;
}
case cEntity::etFloater:
{
tolua_pushusertype(m_LuaState, a_Entity, "cFloater");
break;
}
case cEntity::etEntity:
case cEntity::etEnderCrystal:
case cEntity::etFallingBlock:
case cEntity::etMinecart:
case cEntity::etBoat:
case cEntity::etExpOrb:
case cEntity::etItemFrame:
case cEntity::etPainting:
{
// Push the generic entity class type:
tolua_pushusertype(m_LuaState, a_Entity, "cEntity");
}
} // switch (EntityType)
}
}
void cLuaState::Push(cLuaServerHandle * a_ServerHandle)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, a_ServerHandle, "cServerHandle");
}
void cLuaState::Push(cLuaTCPLink * a_TCPLink)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, a_TCPLink, "cTCPLink");
}
void cLuaState::Push(cLuaUDPEndpoint * a_UDPEndpoint)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, a_UDPEndpoint, "cUDPEndpoint");
}
void cLuaState::Push(double a_Value)
{
ASSERT(IsValid());
tolua_pushnumber(m_LuaState, a_Value);
}
void cLuaState::Push(int a_Value)
{
ASSERT(IsValid());
tolua_pushnumber(m_LuaState, a_Value);
}
void cLuaState::Push(long a_Value)
{
ASSERT(IsValid());
tolua_pushnumber(m_LuaState, static_cast<lua_Number>(a_Value));
}
void cLuaState::Push(UInt32 a_Value)
{
ASSERT(IsValid());
tolua_pushnumber(m_LuaState, a_Value);
}
void cLuaState::Push(std::chrono::milliseconds a_Value)
{
ASSERT(IsValid());
tolua_pushnumber(m_LuaState, static_cast<lua_Number>(a_Value.count()));
}
void cLuaState::Pop(int a_NumValuesToPop)
{
ASSERT(IsValid());
lua_pop(m_LuaState, a_NumValuesToPop);
}
bool cLuaState::GetStackValue(int a_StackPos, AString & a_Value)
{
size_t len = 0;
const char * data = lua_tolstring(m_LuaState, a_StackPos, &len);
if (data != nullptr)
{
a_Value.assign(data, len);
return true;
}
return false;
}
bool cLuaState::GetStackValue(int a_StackPos, AStringMap & a_Value)
{
// Retrieve all values in a string => string dictionary table:
if (!lua_istable(m_LuaState, a_StackPos))
{
return false;
}
cStackTable tbl(*this, a_StackPos);
bool isValid = true;
tbl.ForEachElement([&isValid, &a_Value](cLuaState & a_LuaState)
{
AString key, val;
if (a_LuaState.GetStackValues(-2, key, val))
{
a_Value[key] = val;
}
else
{
isValid = false;
return true;
}
return false;
}
);
return isValid;
}
bool cLuaState::GetStackValue(int a_StackPos, bool & a_ReturnedVal)
{
a_ReturnedVal = (tolua_toboolean(m_LuaState, a_StackPos, a_ReturnedVal ? 1 : 0) > 0);
return true;
}
bool cLuaState::GetStackValue(int a_StackPos, cCallback & a_Callback)
{
return a_Callback.RefStack(*this, a_StackPos);
}
bool cLuaState::GetStackValue(int a_StackPos, cCallbackPtr & a_Callback)
{
if (a_Callback == nullptr)
{
a_Callback = cpp14::make_unique<cCallback>();
}
return a_Callback->RefStack(*this, a_StackPos);
}
bool cLuaState::GetStackValue(int a_StackPos, cOptionalCallback & a_Callback)
{
return a_Callback.RefStack(*this, a_StackPos);
}
bool cLuaState::GetStackValue(int a_StackPos, cOptionalCallbackPtr & a_Callback)
{
if (a_Callback == nullptr)
{
a_Callback = cpp14::make_unique<cOptionalCallback>();
}
return a_Callback->RefStack(*this, a_StackPos);
}
bool cLuaState::GetStackValue(int a_StackPos, cCallbackSharedPtr & a_Callback)
{
if (a_Callback == nullptr)
{
a_Callback = std::make_shared<cCallback>();
}
return a_Callback->RefStack(*this, a_StackPos);
}
bool cLuaState::GetStackValue(int a_StackPos, cPluginManager::CommandResult & a_Result)
{
if (lua_isnumber(m_LuaState, a_StackPos))
{
a_Result = static_cast<cPluginManager::CommandResult>(static_cast<int>((tolua_tonumber(m_LuaState, a_StackPos, a_Result))));
return true;
}
return false;
}
bool cLuaState::GetStackValue(int a_StackPos, cRef & a_Ref)
{
a_Ref.RefStack(*this, a_StackPos);
return true;
}
bool cLuaState::GetStackValue(int a_StackPos, cStackTablePtr & a_StackTable)
{
// Only allow tables to be stored in a_StackTable:
if (!lua_istable(m_LuaState, a_StackPos))
{
return false;
}
// Assign the StackTable to the specified stack position:
a_StackTable = cpp14::make_unique<cStackTable>(*this, a_StackPos);
return true;
}
bool cLuaState::GetStackValue(int a_StackPos, cTableRef & a_TableRef)
{
return a_TableRef.RefStack(*this, a_StackPos);
}
bool cLuaState::GetStackValue(int a_StackPos, cTableRefPtr & a_TableRef)
{
if (a_TableRef == nullptr)
{
a_TableRef = cpp14::make_unique<cTableRef>();
}
return a_TableRef->RefStack(*this, a_StackPos);
}
bool cLuaState::GetStackValue(int a_StackPos, cTrackedRef & a_Ref)
{
return a_Ref.RefStack(*this, a_StackPos);
}
bool cLuaState::GetStackValue(int a_StackPos, cTrackedRefPtr & a_Ref)
{
if (a_Ref == nullptr)
{
a_Ref = cpp14::make_unique<cTrackedRef>();
}
return a_Ref->RefStack(*this, a_StackPos);
}
bool cLuaState::GetStackValue(int a_StackPos, cTrackedRefSharedPtr & a_Ref)
{
if (a_Ref == nullptr)
{
a_Ref = std::make_shared<cTrackedRef>();
}
return a_Ref->RefStack(*this, a_StackPos);
}
bool cLuaState::GetStackValue(int a_StackPos, double & a_ReturnedVal)
{
if (lua_isnumber(m_LuaState, a_StackPos))
{
a_ReturnedVal = tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal);
return true;
}
return false;
}
bool cLuaState::GetStackValue(int a_StackPos, eBlockFace & a_ReturnedVal)
{
if (!lua_isnumber(m_LuaState, a_StackPos))
{
return false;
}
a_ReturnedVal = static_cast<eBlockFace>(Clamp(
static_cast<int>(tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal)),
static_cast<int>(BLOCK_FACE_MIN), static_cast<int>(BLOCK_FACE_MAX))
);
return true;
}
bool cLuaState::GetStackValue(int a_StackPos, eWeather & a_ReturnedVal)
{
if (!lua_isnumber(m_LuaState, a_StackPos))
{
return false;
}
a_ReturnedVal = static_cast<eWeather>(Clamp(
static_cast<int>(tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal)),
static_cast<int>(wSunny), static_cast<int>(wThunderstorm))
);
return true;
}
bool cLuaState::GetStackValue(int a_StackPos, float & a_ReturnedVal)
{
if (lua_isnumber(m_LuaState, a_StackPos))
{
a_ReturnedVal = static_cast<float>(tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal));
return true;
}
return false;
}
cLuaState::cStackValue cLuaState::WalkToValue(const AString & a_Name)
{
// There needs to be at least one value on the stack:
ASSERT(lua_gettop(m_LuaState) > 0);
// Iterate over path and replace the top of the stack with the walked element
lua_pushvalue(m_LuaState, -1); // Copy the stack value into the "working area"
auto path = StringSplit(a_Name, ".");
for (const auto & elem: path)
{
// If the value is not a table, bail out (error):
if (!lua_istable(m_LuaState, -1))
{
lua_pop(m_LuaState, 1);
return cStackValue();
}
// Get the next part of the path:
lua_getfield(m_LuaState, -1, elem.c_str());
// Remove the previous value from the stack (keep only the new one):
lua_remove(m_LuaState, -2);
} // for elem - path[]
if (lua_isnil(m_LuaState, -1))
{
lua_pop(m_LuaState, 1);
return cStackValue();
}
return cStackValue(*this);
}
cLuaState::cStackValue cLuaState::WalkToNamedGlobal(const AString & a_Name)
{
// Iterate over path and replace the top of the stack with the walked element
lua_getglobal(m_LuaState, "_G");
auto path = StringSplit(a_Name, ".");
for (const auto & elem: path)
{
// If the value is not a table, bail out (error):
if (!lua_istable(m_LuaState, -1))
{
lua_pop(m_LuaState, 1);
return cStackValue();
}
// Get the next part of the path:
lua_getfield(m_LuaState, -1, elem.c_str());
// Remove the previous value from the stack (keep only the new one):
lua_remove(m_LuaState, -2);
} // for elem - path[]
if (lua_isnil(m_LuaState, -1))
{
lua_pop(m_LuaState, 1);
return cStackValue();
}
return cStackValue(*this);
}
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)); // The function to call
ASSERT(lua_isfunction(m_LuaState, -m_NumCurrentFunctionArgs - 2)); // The error handler
// Save the current "stack" state and reset, in case the callback calls another function:
AString CurrentFunctionName;
std::swap(m_CurrentFunctionName, CurrentFunctionName);
int NumArgs = m_NumCurrentFunctionArgs;
m_NumCurrentFunctionArgs = -1;
// Call the function:
int s = lua_pcall(m_LuaState, NumArgs, a_NumResults, -NumArgs - 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(), CurrentFunctionName.c_str());
return false;
}
// Remove the error handler from the stack:
lua_remove(m_LuaState, -a_NumResults - 1);
return true;
}
bool cLuaState::CheckParamUserTable(int a_StartParam, const char * a_UserTable, int a_EndParam)
{
ASSERT(IsValid());
if (a_EndParam < 0)
{
a_EndParam = a_StartParam;
}
tolua_Error tolua_err;
for (int i = a_StartParam; i <= a_EndParam; i++)
{
if (tolua_isusertable(m_LuaState, i, a_UserTable, 0, &tolua_err))
{
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("#ferror in function '%s'.", (entry.name != nullptr) ? entry.name : "?");
tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
return false;
} // for i - Param
// All params checked ok
return true;
}
bool cLuaState::CheckParamUserType(int a_StartParam, const char * a_UserType, int a_EndParam)
{
ASSERT(IsValid());
if (a_EndParam < 0)
{
a_EndParam = a_StartParam;
}
tolua_Error tolua_err;
for (int i = a_StartParam; i <= a_EndParam; i++)
{
if (tolua_isusertype(m_LuaState, i, a_UserType, 0, &tolua_err))
{
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("#ferror in function '%s'.", (entry.name != nullptr) ? entry.name : "?");
tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
return false;
} // for i - Param
// All params checked ok
return true;
}
bool cLuaState::CheckParamTable(int a_StartParam, int a_EndParam)
{
ASSERT(IsValid());
if (a_EndParam < 0)
{
a_EndParam = a_StartParam;
}
tolua_Error tolua_err;
for (int i = a_StartParam; i <= a_EndParam; i++)
{
if (tolua_istable(m_LuaState, i, 0, &tolua_err))
{
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("#ferror in function '%s'.", (entry.name != nullptr) ? entry.name : "?");
BreakIntoDebugger(m_LuaState);
tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
return false;
} // for i - Param
// All params checked ok
return true;
}
bool cLuaState::CheckParamNumber(int a_StartParam, int a_EndParam)
{
ASSERT(IsValid());
if (a_EndParam < 0)
{
a_EndParam = a_StartParam;
}
tolua_Error tolua_err;
for (int i = a_StartParam; i <= a_EndParam; i++)
{
if (tolua_isnumber(m_LuaState, i, 0, &tolua_err))
{
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("#ferror in function '%s'.", (entry.name != nullptr) ? entry.name : "?");
tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
return false;
} // for i - Param
// All params checked ok
return true;
}
bool cLuaState::CheckParamBool(int a_StartParam, int a_EndParam)
{
ASSERT(IsValid());
if (a_EndParam < 0)
{
a_EndParam = a_StartParam;
}
tolua_Error tolua_err;
for (int i = a_StartParam; i <= a_EndParam; i++)
{
if (tolua_isboolean(m_LuaState, i, 0, &tolua_err))
{
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("#ferror in function '%s'.", (entry.name != nullptr) ? entry.name : "?");
tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
return false;
} // for i - Param
// All params checked ok
return true;
}
bool cLuaState::CheckParamString(int a_StartParam, int a_EndParam)
{
ASSERT(IsValid());
if (a_EndParam < 0)
{
a_EndParam = a_StartParam;
}
tolua_Error tolua_err;
for (int i = a_StartParam; i <= a_EndParam; i++)
{
if (lua_isstring(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));
tolua_err.array = 0;
tolua_err.type = "string";
tolua_err.index = i;
AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != nullptr) ? entry.name : "?");
tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
return false;
} // for i - Param
// All params checked ok
return true;
}
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));
luaL_error(m_LuaState, "Error in function '%s' parameter #%d. Function expected, got %s",
(entry.name != nullptr) ? entry.name : "?", i, GetTypeText(i).c_str()
);
return false;
} // for i - Param
// All params checked ok
return true;
}
bool cLuaState::CheckParamFunctionOrNil(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) || lua_isnil(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));
luaL_error(m_LuaState, "Error in function '%s' parameter #%d. Function expected, got %s",
(entry.name != nullptr) ? entry.name : "?", i, GetTypeText(i).c_str()
);
return false;
} // for i - Param
// All params checked ok
return true;
}
bool cLuaState::CheckParamEnd(int a_Param)
{
tolua_Error tolua_err;
if (tolua_isnoobj(m_LuaState, a_Param, &tolua_err) == 1)
{
return true;
}
// Not the correct parameter
lua_Debug entry;
VERIFY(lua_getstack(m_LuaState, 0, &entry));
VERIFY(lua_getinfo (m_LuaState, "n", &entry));
AString ErrMsg = Printf("#ferror in function '%s': Too many arguments.", (entry.name != nullptr) ? entry.name : "?");
tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
return false;
}
bool cLuaState::IsParamUserType(int a_Param, AString a_UserType)
{
ASSERT(IsValid());
tolua_Error tolua_err;
return (tolua_isusertype(m_LuaState, a_Param, a_UserType.c_str(), 0, &tolua_err) == 1);
}
bool cLuaState::IsParamNumber(int a_Param)
{
ASSERT(IsValid());
tolua_Error tolua_err;
return (tolua_isnumber(m_LuaState, a_Param, 0, &tolua_err) == 1);
}
bool cLuaState::ReportErrors(int a_Status)
{
return ReportErrors(m_LuaState, a_Status);
}
bool cLuaState::ReportErrors(lua_State * a_LuaState, int a_Status)
{
if (a_Status == 0)
{
// No error to report
return false;
}
LOGWARNING("LUA: %d - %s", a_Status, lua_tostring(a_LuaState, -1));
lua_pop(a_LuaState, 1);
return true;
}
void cLuaState::LogStackTrace(int a_StartingDepth)
{
LogStackTrace(m_LuaState, a_StartingDepth);
}
void cLuaState::LogStackTrace(lua_State * a_LuaState, int a_StartingDepth)
{
LOGWARNING("Stack trace:");
lua_Debug entry;
int depth = a_StartingDepth;
while (lua_getstack(a_LuaState, depth, &entry))
{
lua_getinfo(a_LuaState, "Sln", &entry);
LOGWARNING(" %s(%d): %s", entry.short_src, entry.currentline, entry.name ? entry.name : "(no name)");
depth++;
}
LOGWARNING("Stack trace end");
}
AString cLuaState::GetTypeText(int a_StackPos)
{
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_settop(m_LuaState, OldTop);
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_settop(m_LuaState, OldTop);
m_NumCurrentFunctionArgs = -1;
m_CurrentFunctionName.clear();
return -1;
}
// Call the function, with an error handler:
int s = lua_pcall(m_LuaState, a_SrcParamEnd - a_SrcParamStart + 1, LUA_MULTRET, OldTop + 1);
if (ReportErrors(s))
{
LOGWARN("Error while calling function '%s' in '%s'", a_FunctionName.c_str(), m_SubsystemName.c_str());
// Reset the stack:
lua_settop(m_LuaState, OldTop);
// Reset the internal checking mechanisms:
m_NumCurrentFunctionArgs = -1;
m_CurrentFunctionName.clear();
// Make Lua think everything is okay and return 0 values, so that plugins continue executing.
// The failure is indicated by the zero return values.
return 0;
}
// Reset the internal checking mechanisms:
m_NumCurrentFunctionArgs = -1;
m_CurrentFunctionName.clear();
// 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, int a_NumAllowedNestingLevels)
{
/*
// 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)
{
if (!CopySingleValueFrom(a_SrcLuaState, i, a_NumAllowedNestingLevels))
{
lua_pop(m_LuaState, i - a_SrcStart);
return -1;
}
}
return a_SrcEnd - a_SrcStart + 1;
}
bool cLuaState::CopyTableFrom(cLuaState & a_SrcLuaState, int a_SrcStackIdx, int a_NumAllowedNestingLevels)
{
// Create the dest table:
#ifdef _DEBUG
auto srcTop = lua_gettop(a_SrcLuaState);
auto dstTop = lua_gettop(m_LuaState);
#endif
lua_createtable(m_LuaState, 0, 0); // DST: <table>
lua_pushvalue(a_SrcLuaState, a_SrcStackIdx); // SRC: <table>
lua_pushnil(a_SrcLuaState); // SRC: <table> <key>
while (lua_next(a_SrcLuaState, -2) != 0) // SRC: <table> <key> <value>
{
ASSERT(lua_gettop(a_SrcLuaState) == srcTop + 3);
ASSERT(lua_gettop(m_LuaState) == dstTop + 1);
// Copy the key:
if (!CopySingleValueFrom(a_SrcLuaState, -2, a_NumAllowedNestingLevels)) // DST: <table> <key>
{
lua_pop(m_LuaState, 1);
lua_pop(a_SrcLuaState, 3);
ASSERT(lua_gettop(a_SrcLuaState) == srcTop);
ASSERT(lua_gettop(m_LuaState) == dstTop);
return false;
}
ASSERT(lua_gettop(a_SrcLuaState) == srcTop + 3);
ASSERT(lua_gettop(m_LuaState) == dstTop + 2);
// Copy the value:
if (!CopySingleValueFrom(a_SrcLuaState, -1, a_NumAllowedNestingLevels - 1)) // DST: <table> <key> <value>
{
lua_pop(m_LuaState, 2); // DST: empty
lua_pop(a_SrcLuaState, 3); // SRC: empty
ASSERT(lua_gettop(a_SrcLuaState) == srcTop);
ASSERT(lua_gettop(m_LuaState) == dstTop);
return false;
}
ASSERT(lua_gettop(a_SrcLuaState) == srcTop + 3);
ASSERT(lua_gettop(m_LuaState) == dstTop + 3);
// Set the value and fix up stacks:
lua_rawset(m_LuaState, -3); // DST: <table>
lua_pop(a_SrcLuaState, 1); // SRC: <table> <key>
ASSERT(lua_gettop(a_SrcLuaState) == srcTop + 2);
ASSERT(lua_gettop(m_LuaState) == dstTop + 1);
}
lua_pop(a_SrcLuaState, 1); // SRC: empty
ASSERT(lua_gettop(a_SrcLuaState) == srcTop);
ASSERT(lua_gettop(m_LuaState) == dstTop + 1);
return true;
}
bool cLuaState::CopySingleValueFrom(cLuaState & a_SrcLuaState, int a_StackIdx, int a_NumAllowedNestingLevels)
{
int t = lua_type(a_SrcLuaState, a_StackIdx);
switch (t)
{
case LUA_TNIL:
{
lua_pushnil(m_LuaState);
return true;
}
case LUA_TSTRING:
{
AString s;
a_SrcLuaState.ToString(a_StackIdx, s);
Push(s);
return true;
}
case LUA_TBOOLEAN:
{
bool b = (tolua_toboolean(a_SrcLuaState, a_StackIdx, false) != 0);
Push(b);
return true;
}
case LUA_TNUMBER:
{
lua_Number d = tolua_tonumber(a_SrcLuaState, a_StackIdx, 0);
Push(d);
return true;
}
case LUA_TUSERDATA:
{
// Get the class name:
const char * type = nullptr;
if (lua_getmetatable(a_SrcLuaState, a_StackIdx) == 0)
{
LOGWARNING("%s: Unknown class in pos %d, cannot copy.", __FUNCTION__, a_StackIdx);
return false;
}
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, a_StackIdx, nullptr);
tolua_pushusertype(m_LuaState, ud, type);
return true;
}
case LUA_TTABLE:
{
if (!CopyTableFrom(a_SrcLuaState, a_StackIdx, a_NumAllowedNestingLevels - 1))
{
LOGWARNING("%s: Failed to copy table in pos %d.", __FUNCTION__, a_StackIdx);
return false;
}
return true;
}
default:
{
LOGWARNING("%s: Unsupported value: '%s' at stack position %d. Can only copy numbers, strings, bools, classes and simple tables!",
__FUNCTION__, lua_typename(a_SrcLuaState, t), a_StackIdx
);
return false;
}
}
}
void cLuaState::ToString(int a_StackPos, AString & a_String)
{
size_t len;
const char * s = lua_tolstring(m_LuaState, a_StackPos, &len);
if (s != nullptr)
{
a_String.assign(s, len);
}
}
void cLuaState::LogStackValues(const char * a_Header)
{
LogStackValues(m_LuaState, a_Header);
}
void cLuaState::LogStackValues(lua_State * a_LuaState, const char * a_Header)
{
// Format string consisting only of %s is used to appease the compiler
LOG("%s", (a_Header != nullptr) ? 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", static_cast<double>(lua_tonumber(a_LuaState, i))); break;
case LUA_TSTRING: Printf(Value, "%s", lua_tostring(a_LuaState, i)); break;
case LUA_TTABLE: Printf(Value, "%p", lua_topointer(a_LuaState, i)); break;
case LUA_TUSERDATA: Printf(Value, "%p (%s)", lua_touserdata(a_LuaState, i), tolua_typename(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
}
cLuaState * cLuaState::QueryCanonLuaState(void)
{
// Get the CanonLuaState global from Lua:
auto cb = WalkToNamedGlobal(g_CanonLuaStateGlobalName);
if (!cb.IsValid())
{
return nullptr;
}
return reinterpret_cast<cLuaState *>(lua_touserdata(m_LuaState, -1));
}
int cLuaState::ReportFnCallErrors(lua_State * a_LuaState)
{
LOGWARNING("LUA: %s", lua_tostring(a_LuaState, -1));
LogStackTrace(a_LuaState, 1);
BreakIntoDebugger(a_LuaState);
return 1; // We left the error message on the stack as the return value
}
int cLuaState::BreakIntoDebugger(lua_State * a_LuaState)
{
// Call the BreakIntoDebugger function, if available:
lua_getglobal(a_LuaState, "BreakIntoDebugger");
if (!lua_isfunction(a_LuaState, -1))
{
LOGD("LUA: BreakIntoDebugger() not found / not a function");
lua_pop(a_LuaState, 1);
return 1;
}
lua_insert(a_LuaState, -2); // Copy the string that has been passed to us
LOGD("Calling BreakIntoDebugger()...");
lua_call(a_LuaState, 1, 0);
LOGD("Returned from BreakIntoDebugger().");
return 0;
}
void cLuaState::TrackRef(cTrackedRef & a_Ref)
{
// Get the CanonLuaState global from Lua:
auto canonState = QueryCanonLuaState();
if (canonState == nullptr)
{
LOGWARNING("%s: Lua state %p has invalid CanonLuaState!", __FUNCTION__, reinterpret_cast<void *>(m_LuaState));
return;
}
// Add the callback:
cCSLock Lock(canonState->m_CSTrackedRefs);
canonState->m_TrackedRefs.push_back(&a_Ref);
}
void cLuaState::UntrackRef(cTrackedRef & a_Ref)
{
// Get the CanonLuaState global from Lua:
auto canonState = QueryCanonLuaState();
if (canonState == nullptr)
{
LOGWARNING("%s: Lua state %p has invalid CanonLuaState!", __FUNCTION__, reinterpret_cast<void *>(m_LuaState));
return;
}
// Remove the callback:
cCSLock Lock(canonState->m_CSTrackedRefs);
auto & trackedRefs = canonState->m_TrackedRefs;
trackedRefs.erase(std::remove_if(trackedRefs.begin(), trackedRefs.end(),
[&a_Ref](cTrackedRef * a_StoredRef)
{
return (a_StoredRef == &a_Ref);
}
));
}
////////////////////////////////////////////////////////////////////////////////
// cLuaState::cRef:
cLuaState::cRef::cRef(void) :
m_LuaState(nullptr),
m_Ref(LUA_REFNIL)
{
}
cLuaState::cRef::cRef(cLuaState & a_LuaState, int a_StackPos) :
m_LuaState(nullptr),
m_Ref(LUA_REFNIL)
{
RefStack(a_LuaState, a_StackPos);
}
cLuaState::cRef::cRef(cRef && a_FromRef):
m_LuaState(a_FromRef.m_LuaState),
m_Ref(a_FromRef.m_Ref)
{
a_FromRef.m_LuaState = nullptr;
a_FromRef.m_Ref = LUA_REFNIL;
}
cLuaState::cRef::~cRef()
{
if (m_LuaState != nullptr)
{
UnRef();
}
}
void cLuaState::cRef::RefStack(cLuaState & a_LuaState, int a_StackPos)
{
ASSERT(a_LuaState.IsValid());
if (m_LuaState != nullptr)
{
UnRef();
}
m_LuaState = a_LuaState;
lua_pushvalue(a_LuaState, a_StackPos); // Push a copy of the value at a_StackPos onto the stack
m_Ref = luaL_ref(a_LuaState, LUA_REGISTRYINDEX);
}
void cLuaState::cRef::UnRef(void)
{
if (IsValid())
{
luaL_unref(m_LuaState, LUA_REGISTRYINDEX, m_Ref);
}
m_LuaState = nullptr;
m_Ref = LUA_REFNIL;
}