1
0
Fork 0

Merge pull request #3341 from cuberite/LuaStateImprovements

cLuaState improvements
This commit is contained in:
Mattes D 2016-08-22 20:26:11 +02:00 committed by GitHub
commit a42033db1c
6 changed files with 134 additions and 50 deletions

View File

@ -234,7 +234,6 @@ local function OutputLuaStateHelpers(a_Package)
if not(g_HasCustomPushImplementation[item.name]) then if not(g_HasCustomPushImplementation[item.name]) then
f:write("void cLuaState::Push(" .. item.name .. " * a_Value)\n{\n\tASSERT(IsValid());\n") f:write("void cLuaState::Push(" .. item.name .. " * a_Value)\n{\n\tASSERT(IsValid());\n")
f:write("\ttolua_pushusertype(m_LuaState, a_Value, \"" .. item.name .. "\");\n"); f:write("\ttolua_pushusertype(m_LuaState, a_Value, \"" .. item.name .. "\");\n");
f:write("\tm_NumCurrentFunctionArgs += 1;\n")
f:write("}\n\n\n\n\n\n") f:write("}\n\n\n\n\n\n")
end end
end end

View File

@ -82,7 +82,7 @@ void PushJsonValue(const Json::Value & a_Value, cLuaState & a_LuaState)
{ {
case Json::nullValue: case Json::nullValue:
{ {
a_LuaState.PushNil(); a_LuaState.Push(cLuaState::Nil);
break; break;
} }
@ -228,8 +228,7 @@ static int tolua_cJson_Parse(lua_State * a_LuaState)
Json::Reader reader; Json::Reader reader;
if (!reader.parse(input, root, false)) if (!reader.parse(input, root, false))
{ {
L.PushNil(); L.Push(cLuaState::Nil, Printf("Parsing Json failed: %s", reader.getFormattedErrorMessages().c_str()));
L.Push(Printf("Parsing Json failed: %s", reader.getFormattedErrorMessages().c_str()));
return 2; return 2;
} }

View File

@ -42,6 +42,7 @@ extern "C"
const cLuaState::cRet cLuaState::Return = {}; 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. /** 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. */ This way any cLuaState can reference the main cLuaState's TrackedCallbacks, mutex etc. */
@ -341,6 +342,33 @@ void cLuaState::cStackTable::ForEachArrayElement(std::function<bool(cLuaState &
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:
@ -724,24 +752,29 @@ bool cLuaState::PushFunction(const cRef & a_TableRef, const char * a_FnName)
void cLuaState::PushNil(void) void cLuaState::Push(const AString & a_String)
{ {
ASSERT(IsValid()); ASSERT(IsValid());
lua_pushnil(m_LuaState); lua_pushlstring(m_LuaState, a_String.data(), a_String.size());
m_NumCurrentFunctionArgs += 1;
} }
void cLuaState::Push(const AString & a_String) void cLuaState::Push(const AStringMap & a_Dictionary)
{ {
ASSERT(IsValid()); ASSERT(IsValid());
lua_pushlstring(m_LuaState, a_String.data(), a_String.size()); lua_createtable(m_LuaState, 0, static_cast<int>(a_Dictionary.size()));
m_NumCurrentFunctionArgs += 1; 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);
}
} }
@ -760,7 +793,6 @@ void cLuaState::Push(const AStringVector & a_Vector)
tolua_pushstring(m_LuaState, itr->c_str()); tolua_pushstring(m_LuaState, itr->c_str());
lua_rawseti(m_LuaState, newTable, index); lua_rawseti(m_LuaState, newTable, index);
} }
m_NumCurrentFunctionArgs += 1;
} }
@ -772,7 +804,6 @@ void cLuaState::Push(const cCraftingGrid * a_Grid)
ASSERT(IsValid()); ASSERT(IsValid());
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<cCraftingGrid *>(a_Grid)), "cCraftingGrid"); tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<cCraftingGrid *>(a_Grid)), "cCraftingGrid");
m_NumCurrentFunctionArgs += 1;
} }
@ -784,7 +815,6 @@ void cLuaState::Push(const cCraftingRecipe * a_Recipe)
ASSERT(IsValid()); ASSERT(IsValid());
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<cCraftingRecipe *>(a_Recipe)), "cCraftingRecipe"); tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<cCraftingRecipe *>(a_Recipe)), "cCraftingRecipe");
m_NumCurrentFunctionArgs += 1;
} }
@ -796,7 +826,6 @@ void cLuaState::Push(const char * a_Value)
ASSERT(IsValid()); ASSERT(IsValid());
tolua_pushstring(m_LuaState, a_Value); tolua_pushstring(m_LuaState, a_Value);
m_NumCurrentFunctionArgs += 1;
} }
@ -808,7 +837,17 @@ void cLuaState::Push(const cItems & a_Items)
ASSERT(IsValid()); ASSERT(IsValid());
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<cItems *>(&a_Items)), "cItems"); tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<cItems *>(&a_Items)), "cItems");
m_NumCurrentFunctionArgs += 1; }
void cLuaState::Push(const cNil & a_Nil)
{
ASSERT(IsValid());
lua_pushnil(m_LuaState);
} }
@ -820,7 +859,6 @@ void cLuaState::Push(const cPlayer * a_Player)
ASSERT(IsValid()); ASSERT(IsValid());
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<cPlayer *>(a_Player)), "cPlayer"); tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<cPlayer *>(a_Player)), "cPlayer");
m_NumCurrentFunctionArgs += 1;
} }
@ -832,7 +870,6 @@ void cLuaState::Push(const cLuaState::cRef & a_Ref)
ASSERT(IsValid()); ASSERT(IsValid());
lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, static_cast<int>(a_Ref)); lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, static_cast<int>(a_Ref));
m_NumCurrentFunctionArgs += 1;
} }
@ -844,7 +881,6 @@ void cLuaState::Push(const HTTPRequest * a_Request)
ASSERT(IsValid()); ASSERT(IsValid());
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<HTTPRequest *>(a_Request)), "HTTPRequest"); tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<HTTPRequest *>(a_Request)), "HTTPRequest");
m_NumCurrentFunctionArgs += 1;
} }
@ -856,7 +892,6 @@ void cLuaState::Push(const HTTPTemplateRequest * a_Request)
ASSERT(IsValid()); ASSERT(IsValid());
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<HTTPTemplateRequest *>(a_Request)), "HTTPTemplateRequest"); tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<HTTPTemplateRequest *>(a_Request)), "HTTPTemplateRequest");
m_NumCurrentFunctionArgs += 1;
} }
@ -868,7 +903,6 @@ void cLuaState::Push(const Vector3d & a_Vector)
ASSERT(IsValid()); ASSERT(IsValid());
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<Vector3d *>(&a_Vector)), "Vector3<double>"); tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<Vector3d *>(&a_Vector)), "Vector3<double>");
m_NumCurrentFunctionArgs += 1;
} }
@ -880,7 +914,6 @@ void cLuaState::Push(const Vector3d * a_Vector)
ASSERT(IsValid()); ASSERT(IsValid());
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<Vector3d *>(a_Vector)), "Vector3<double>"); tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<Vector3d *>(a_Vector)), "Vector3<double>");
m_NumCurrentFunctionArgs += 1;
} }
@ -892,7 +925,6 @@ void cLuaState::Push(const Vector3i & a_Vector)
ASSERT(IsValid()); ASSERT(IsValid());
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<Vector3i *>(&a_Vector)), "Vector3<int>"); tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<Vector3i *>(&a_Vector)), "Vector3<int>");
m_NumCurrentFunctionArgs += 1;
} }
@ -904,7 +936,6 @@ void cLuaState::Push(const Vector3i * a_Vector)
ASSERT(IsValid()); ASSERT(IsValid());
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<Vector3i *>(a_Vector)), "Vector3<int>"); tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<Vector3i *>(a_Vector)), "Vector3<int>");
m_NumCurrentFunctionArgs += 1;
} }
@ -916,7 +947,6 @@ void cLuaState::Push(bool a_Value)
ASSERT(IsValid()); ASSERT(IsValid());
tolua_pushboolean(m_LuaState, a_Value ? 1 : 0); tolua_pushboolean(m_LuaState, a_Value ? 1 : 0);
m_NumCurrentFunctionArgs += 1;
} }
@ -981,8 +1011,6 @@ void cLuaState::Push(cEntity * a_Entity)
} }
} // switch (EntityType) } // switch (EntityType)
} }
m_NumCurrentFunctionArgs += 1;
} }
@ -994,7 +1022,6 @@ void cLuaState::Push(cLuaServerHandle * a_ServerHandle)
ASSERT(IsValid()); ASSERT(IsValid());
tolua_pushusertype(m_LuaState, a_ServerHandle, "cServerHandle"); tolua_pushusertype(m_LuaState, a_ServerHandle, "cServerHandle");
m_NumCurrentFunctionArgs += 1;
} }
@ -1006,7 +1033,6 @@ void cLuaState::Push(cLuaTCPLink * a_TCPLink)
ASSERT(IsValid()); ASSERT(IsValid());
tolua_pushusertype(m_LuaState, a_TCPLink, "cTCPLink"); tolua_pushusertype(m_LuaState, a_TCPLink, "cTCPLink");
m_NumCurrentFunctionArgs += 1;
} }
@ -1018,7 +1044,6 @@ void cLuaState::Push(cLuaUDPEndpoint * a_UDPEndpoint)
ASSERT(IsValid()); ASSERT(IsValid());
tolua_pushusertype(m_LuaState, a_UDPEndpoint, "cUDPEndpoint"); tolua_pushusertype(m_LuaState, a_UDPEndpoint, "cUDPEndpoint");
m_NumCurrentFunctionArgs += 1;
} }
@ -1030,7 +1055,6 @@ void cLuaState::Push(double a_Value)
ASSERT(IsValid()); ASSERT(IsValid());
tolua_pushnumber(m_LuaState, a_Value); tolua_pushnumber(m_LuaState, a_Value);
m_NumCurrentFunctionArgs += 1;
} }
@ -1042,7 +1066,6 @@ void cLuaState::Push(int a_Value)
ASSERT(IsValid()); ASSERT(IsValid());
tolua_pushnumber(m_LuaState, a_Value); tolua_pushnumber(m_LuaState, a_Value);
m_NumCurrentFunctionArgs += 1;
} }
@ -1054,7 +1077,6 @@ void cLuaState::Push(long a_Value)
ASSERT(IsValid()); ASSERT(IsValid());
tolua_pushnumber(m_LuaState, static_cast<lua_Number>(a_Value)); tolua_pushnumber(m_LuaState, static_cast<lua_Number>(a_Value));
m_NumCurrentFunctionArgs += 1;
} }
@ -1066,7 +1088,6 @@ void cLuaState::Push(UInt32 a_Value)
ASSERT(IsValid()); ASSERT(IsValid());
tolua_pushnumber(m_LuaState, a_Value); tolua_pushnumber(m_LuaState, a_Value);
m_NumCurrentFunctionArgs += 1;
} }
@ -1078,7 +1099,6 @@ void cLuaState::Push(std::chrono::milliseconds a_Value)
ASSERT(IsValid()); ASSERT(IsValid());
tolua_pushnumber(m_LuaState, static_cast<lua_Number>(a_Value.count())); tolua_pushnumber(m_LuaState, static_cast<lua_Number>(a_Value.count()));
m_NumCurrentFunctionArgs += 1;
} }
@ -1090,7 +1110,6 @@ void cLuaState::Pop(int a_NumValuesToPop)
ASSERT(IsValid()); ASSERT(IsValid());
lua_pop(m_LuaState, a_NumValuesToPop); lua_pop(m_LuaState, a_NumValuesToPop);
m_NumCurrentFunctionArgs -= a_NumValuesToPop;
} }
@ -1113,6 +1132,37 @@ bool cLuaState::GetStackValue(int a_StackPos, AString & a_Value)
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) bool cLuaState::GetStackValue(int a_StackPos, bool & a_ReturnedVal)
{ {
a_ReturnedVal = (tolua_toboolean(m_LuaState, a_StackPos, a_ReturnedVal ? 1 : 0) > 0); a_ReturnedVal = (tolua_toboolean(m_LuaState, a_StackPos, a_ReturnedVal ? 1 : 0) > 0);

View File

@ -309,14 +309,39 @@ public:
typedef UniquePtr<cTableRef> cTableRefPtr; typedef UniquePtr<cTableRef> cTableRefPtr;
/** Represents a parameter that is optional - calling a GetStackValue() with this object will not fail if the value on the Lua stack is nil.
Note that the GetStackValue() will still fail if the param is present but of a different type.
The class itself is just a marker so that the template magic will select the correct GetStackValue() overload. */
template <typename T>
class cOptionalParam
{
public:
explicit cOptionalParam(T & a_Dest):
m_Dest(a_Dest)
{
}
T & GetDest(void) { return m_Dest; }
protected:
T & m_Dest;
};
/** A dummy class that's used only to delimit function args from return values for cLuaState::Call() */ /** A dummy class that's used only to delimit function args from return values for cLuaState::Call() */
class cRet class cRet
{ {
} ; } ;
static const cRet Return; // Use this constant to delimit function args from return values for cLuaState::Call() static const cRet Return; // Use this constant to delimit function args from return values for cLuaState::Call()
/** A dummy class that's used only to push a constant nil as a function parameter in Call(). */
class cNil
{
};
static const cNil Nil; // Use this constant to give a function a nil parameter in Call()
/** A RAII class for values pushed onto the Lua stack. /** A RAII class for values pushed onto the Lua stack.
Will pop the value off the stack in the destructor. */ Will pop the value off the stack in the destructor. */
class cStackValue class cStackValue
@ -471,15 +496,15 @@ public:
Push(std::forward<Arg2>(a_Arg2), std::forward<Args>(a_Args)...); Push(std::forward<Arg2>(a_Arg2), std::forward<Args>(a_Args)...);
} }
void PushNil(void);
// Push a const value onto the stack (keep alpha-sorted): // Push a const value onto the stack (keep alpha-sorted):
void Push(const AString & a_String); void Push(const AString & a_String);
void Push(const AStringMap & a_Dictionary);
void Push(const AStringVector & a_Vector); void Push(const AStringVector & a_Vector);
void Push(const cCraftingGrid * a_Grid); void Push(const cCraftingGrid * a_Grid);
void Push(const cCraftingRecipe * a_Recipe); void Push(const cCraftingRecipe * a_Recipe);
void Push(const char * a_Value); void Push(const char * a_Value);
void Push(const cItems & a_Items); void Push(const cItems & a_Items);
void Push(const cNil & a_Nil);
void Push(const cPlayer * a_Player); void Push(const cPlayer * a_Player);
void Push(const cRef & a_Ref); void Push(const cRef & a_Ref);
void Push(const HTTPRequest * a_Request); void Push(const HTTPRequest * a_Request);
@ -508,6 +533,7 @@ public:
// Returns whether value was changed // Returns whether value was changed
// Enum values are checked for their allowed values and fail if the value is not assigned. // Enum values are checked for their allowed values and fail if the value is not assigned.
bool GetStackValue(int a_StackPos, AString & a_Value); bool GetStackValue(int a_StackPos, AString & a_Value);
bool GetStackValue(int a_StackPos, AStringMap & a_Value);
bool GetStackValue(int a_StackPos, bool & a_Value); bool GetStackValue(int a_StackPos, bool & a_Value);
bool GetStackValue(int a_StackPos, cCallback & a_Callback); bool GetStackValue(int a_StackPos, cCallback & a_Callback);
bool GetStackValue(int a_StackPos, cCallbackPtr & a_Callback); bool GetStackValue(int a_StackPos, cCallbackPtr & a_Callback);
@ -549,6 +575,17 @@ public:
return true; return true;
} }
/** Retrieves an optional value on the stack - doesn't fail if the stack contains nil instead of the value. */
template <typename T>
bool GetStackValue(int a_StackPos, cOptionalParam<T> && a_ReturnedVal)
{
if (lua_isnoneornil(m_LuaState, a_StackPos))
{
return true;
}
return GetStackValue(a_StackPos, a_ReturnedVal.GetDest());
}
/** Pushes the named value in the table at the top of the stack. /** Pushes the named value in the table at the top of the stack.
a_Name may be a path containing multiple table levels, such as "cChatColor.Blue". a_Name may be a path containing multiple table levels, such as "cChatColor.Blue".
If the value is found, it is pushed on top of the stack and the returned cStackValue is valid. If the value is found, it is pushed on top of the stack and the returned cStackValue is valid.
@ -597,6 +634,7 @@ public:
template <typename FnT, typename... Args> template <typename FnT, typename... Args>
bool Call(const FnT & a_Function, Args &&... args) bool Call(const FnT & a_Function, Args &&... args)
{ {
m_NumCurrentFunctionArgs = -1;
if (!PushFunction(std::forward<const FnT &>(a_Function))) if (!PushFunction(std::forward<const FnT &>(a_Function)))
{ {
// Pushing the function failed // Pushing the function failed
@ -606,14 +644,14 @@ public:
} }
/** Retrieves a list of values from the Lua stack, starting at the specified index. */ /** Retrieves a list of values from the Lua stack, starting at the specified index. */
template <typename T, typename... Args> template <typename Arg1, typename... Args>
inline bool GetStackValues(int a_StartStackPos, T & a_Ret, Args &&... args) inline bool GetStackValues(int a_StartStackPos, Arg1 && a_Arg1, Args &&... args)
{ {
if (!GetStackValue(a_StartStackPos, a_Ret)) if (!GetStackValue(a_StartStackPos, std::forward<Arg1>(a_Arg1)))
{ {
return false; return false;
} }
return GetStackValues(a_StartStackPos + 1, args...); return GetStackValues(a_StartStackPos + 1, std::forward<Args>(args)...);
} }
/** Returns true if the specified parameters on the stack are of the specified usertable type; also logs warning if not. Used for static functions */ /** Returns true if the specified parameters on the stack are of the specified usertable type; also logs warning if not. Used for static functions */
@ -758,6 +796,7 @@ protected:
inline bool PushCallPop(T && a_Param, Args &&... args) inline bool PushCallPop(T && a_Param, Args &&... args)
{ {
Push(std::forward<T>(a_Param)); Push(std::forward<T>(a_Param));
m_NumCurrentFunctionArgs += 1;
return PushCallPop(std::forward<Args>(args)...); return PushCallPop(std::forward<Args>(args)...);
} }

View File

@ -2058,8 +2058,7 @@ static int tolua_cUrlParser_Parse(lua_State * a_LuaState)
if (!res.first) if (!res.first)
{ {
// Error, return nil and error msg: // Error, return nil and error msg:
L.PushNil(); L.Push(cLuaState::Nil, res.second);
L.Push(res.second);
return 2; return 2;
} }
L.Push(scheme, username, password, host, port, path, query, fragment); L.Push(scheme, username, password, host, port, path, query, fragment);
@ -2099,8 +2098,7 @@ static int tolua_cUrlParser_ParseAuthorityPart(lua_State * a_LuaState)
if (!res.first) if (!res.first)
{ {
// Error, return nil and error msg: // Error, return nil and error msg:
L.PushNil(); L.Push(cLuaState::Nil, res.second);
L.Push(res.second);
return 2; return 2;
} }
L.Push(username, password, host, port); L.Push(username, password, host, port);

View File

@ -649,8 +649,7 @@ static int tolua_cTCPLink_StartTLSClient(lua_State * L)
AString res = Link->StartTLSClient(OwnCert, OwnPrivKey, OwnPrivKeyPassword); AString res = Link->StartTLSClient(OwnCert, OwnPrivKey, OwnPrivKeyPassword);
if (!res.empty()) if (!res.empty())
{ {
S.PushNil(); S.Push(cLuaState::Nil, Printf("Cannot start TLS on link to %s:%d: %s", Link->GetRemoteIP().c_str(), Link->GetRemotePort(), res.c_str()));
S.Push(Printf("Cannot start TLS on link to %s:%d: %s", Link->GetRemoteIP().c_str(), Link->GetRemotePort(), res.c_str()));
return 2; return 2;
} }
return 1; return 1;
@ -695,10 +694,10 @@ static int tolua_cTCPLink_StartTLSServer(lua_State * L)
AString res = Link->StartTLSServer(OwnCert, OwnPrivKey, OwnPrivKeyPassword, StartTLSData); AString res = Link->StartTLSServer(OwnCert, OwnPrivKey, OwnPrivKeyPassword, StartTLSData);
if (!res.empty()) if (!res.empty())
{ {
S.PushNil(); S.Push(cLuaState::Nil, Printf("Cannot start TLS on link to %s:%d: %s", Link->GetRemoteIP().c_str(), Link->GetRemotePort(), res.c_str()));
S.Push(Printf("Cannot start TLS on link to %s:%d: %s", Link->GetRemoteIP().c_str(), Link->GetRemotePort(), res.c_str()));
return 2; return 2;
} }
S.Push(true);
return 1; return 1;
} }