// Copyright (C) 2015 Jérôme Leclercq // This file is part of the "Nazara Engine - Lua scripting module" // For conditions of distribution and use, see copyright notice in Config.hpp #include #include #include #include #include #include #include #include namespace Nz { inline LuaInstance::LuaInstance(LuaInstance&& instance) noexcept : m_memoryLimit(instance.m_memoryLimit), m_memoryUsage(instance.m_memoryUsage), m_timeLimit(instance.m_timeLimit), m_clock(std::move(instance.m_clock)), m_lastError(std::move(instance.m_lastError)), m_state(instance.m_state), m_level(instance.m_level) { instance.m_state = nullptr; } inline lua_State* LuaInstance::GetInternalState() const { return m_state; } inline String LuaInstance::GetLastError() const { return m_lastError; } inline std::size_t LuaInstance::GetMemoryLimit() const { return m_memoryLimit; } inline std::size_t LuaInstance::GetMemoryUsage() const { return m_memoryUsage; } inline UInt32 LuaInstance::GetTimeLimit() const { return m_timeLimit; } inline LuaInstance& LuaInstance::operator=(LuaInstance&& instance) noexcept { m_clock = std::move(instance.m_clock); m_lastError = std::move(instance.m_lastError); m_level = instance.m_level; m_memoryLimit = instance.m_memoryLimit; m_memoryUsage = instance.m_memoryUsage; m_state = instance.m_state; m_timeLimit = instance.m_timeLimit; instance.m_state = nullptr; return *this; } // Functions args inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, bool* arg, TypeTag) { *arg = instance.CheckBoolean(index); return 1; } inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, bool* arg, bool defValue, TypeTag) { *arg = instance.CheckBoolean(index, defValue); return 1; } inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, std::string* arg, TypeTag) { std::size_t strLength = 0; const char* str = instance.CheckString(index, &strLength); arg->assign(str, strLength); return 1; } inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, String* arg, TypeTag) { std::size_t strLength = 0; const char* str = instance.CheckString(index, &strLength); arg->Set(str, strLength); return 1; } template std::enable_if_t::value, unsigned int> LuaImplQueryArg(const LuaInstance& instance, int index, T* arg, TypeTag) { using UnderlyingT = std::underlying_type_t; return LuaImplQueryArg(instance, index, reinterpret_cast(arg), TypeTag()); } template std::enable_if_t::value, unsigned int> LuaImplQueryArg(const LuaInstance& instance, int index, T* arg, T defValue, TypeTag) { using UnderlyingT = std::underlying_type_t; return LuaImplQueryArg(instance, index, reinterpret_cast(arg), static_cast(defValue), TypeTag()); } template std::enable_if_t::value, unsigned int> LuaImplQueryArg(const LuaInstance& instance, int index, T* arg, TypeTag) { *arg = static_cast(instance.CheckNumber(index)); return 1; } template std::enable_if_t::value, unsigned int> LuaImplQueryArg(const LuaInstance& instance, int index, T* arg, T defValue, TypeTag) { *arg = static_cast(instance.CheckNumber(index, static_cast(defValue))); return 1; } template std::enable_if_t::value, unsigned int> LuaImplQueryArg(const LuaInstance& instance, int index, T* arg, TypeTag) { *arg = instance.CheckBoundInteger(index); return 1; } template std::enable_if_t::value, unsigned int> LuaImplQueryArg(const LuaInstance& instance, int index, T* arg, T defValue, TypeTag) { *arg = instance.CheckBoundInteger(index, defValue); return 1; } template std::enable_if_t::value && !std::is_enum::value && !std::is_floating_point::value, unsigned int> LuaImplQueryArg(const LuaInstance& instance, int index, T* arg, const T& defValue, TypeTag tag) { if (instance.IsValid(index)) return LuaImplQueryArg(instance, index, arg, tag); else { *arg = defValue; return 1; } } template unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, T* arg, TypeTag) { return LuaImplQueryArg(instance, index, arg, TypeTag()); } template unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, T* arg, const T& defValue, TypeTag) { return LuaImplQueryArg(instance, index, arg, defValue, TypeTag()); } // Function returns inline int LuaImplReplyVal(const LuaInstance& instance, bool val, TypeTag) { instance.PushBoolean(val); return 1; } inline int LuaImplReplyVal(const LuaInstance& instance, double val, TypeTag) { instance.PushNumber(val); return 1; } inline int LuaImplReplyVal(const LuaInstance& instance, float val, TypeTag) { instance.PushNumber(val); return 1; } template std::enable_if_t::value, int> LuaImplReplyVal(const LuaInstance& instance, T val, TypeTag) { using EnumT = typename std::underlying_type::type; return LuaImplReplyVal(instance, static_cast(val), TypeTag()); } template std::enable_if_t::value, int> LuaImplReplyVal(const LuaInstance& instance, T val, TypeTag) { instance.PushInteger(val); return 1; } template std::enable_if_t::value || std::is_enum::value, int> LuaImplReplyVal(const LuaInstance& instance, T val, TypeTag) { return LuaImplReplyVal(instance, val, TypeTag()); } template std::enable_if_t::value || std::is_enum::value, int> LuaImplReplyVal(const LuaInstance& instance, T val, TypeTag) { return LuaImplReplyVal(instance, val, TypeTag()); } template std::enable_if_t::value && !std::is_enum::value, int> LuaImplReplyVal(const LuaInstance& instance, T val, TypeTag) { return LuaImplReplyVal(instance, std::move(val), TypeTag()); } template std::enable_if_t::value && !std::is_enum::value, int> LuaImplReplyVal(const LuaInstance& instance, T val, TypeTag) { return LuaImplReplyVal(instance, std::move(val), TypeTag()); } template int LuaImplReplyVal(const LuaInstance& instance, T&& val, TypeTag) { return LuaImplReplyVal(instance, std::forward(val), TypeTag()); } inline int LuaImplReplyVal(const LuaInstance& instance, std::string&& val, TypeTag) { instance.PushString(val.c_str(), val.size()); return 1; } template inline int LuaImplReplyVal(const LuaInstance& instance, std::vector&& valContainer, TypeTag>) { std::size_t index = 1; instance.PushTable(valContainer.size()); for (T& val : valContainer) { instance.PushInteger(index++); if (LuaImplReplyVal(instance, std::move(val), TypeTag()) != 1) { instance.Error("Couldn't create table: type need more than one place to store"); return 0; } instance.SetTable(); } return 1; } inline int LuaImplReplyVal(const LuaInstance& instance, ByteArray&& val, TypeTag) { instance.PushString(reinterpret_cast(val.GetConstBuffer()), val.GetSize()); return 1; } inline int LuaImplReplyVal(const LuaInstance& instance, String&& val, TypeTag) { instance.PushString(std::move(val)); return 1; } template int LuaImplReplyVal(const LuaInstance& instance, std::pair&& val, TypeTag>) { int retVal = 0; retVal += LuaImplReplyVal(instance, std::move(val.first), TypeTag()); retVal += LuaImplReplyVal(instance, std::move(val.second), TypeTag()); return retVal; } template struct LuaImplArgProcesser; template<> struct LuaImplArgProcesser { template static unsigned int Process(const LuaInstance& instance, unsigned int argIndex, ArgContainer& args, DefArgContainer& defArgs) { return LuaImplQueryArg(instance, argIndex, &std::get(args), std::get() - N - 1>(defArgs), TypeTag()); } }; template<> struct LuaImplArgProcesser { template static unsigned int Process(const LuaInstance& instance, unsigned int argIndex, ArgContainer& args, DefArgContainer& defArgs) { NazaraUnused(defArgs); return LuaImplQueryArg(instance, argIndex, &std::get(args), TypeTag()); } }; template class LuaImplFunctionProxy { public: template class Impl { static constexpr std::size_t ArgCount = sizeof...(Args); static constexpr std::size_t DefArgCount = sizeof...(DefArgs); static_assert(ArgCount >= DefArgCount, "There cannot be more default arguments than argument"); static constexpr std::size_t FirstDefArg = ArgCount - DefArgCount; public: Impl(DefArgs... defArgs) : m_defaultArgs(std::forward(defArgs)...) { } void ProcessArguments(const LuaInstance& instance) const { m_index = 1; ProcessArgs<0, Args...>(instance); } int Invoke(const LuaInstance& instance, void(*func)(Args...)) const { NazaraUnused(instance); Apply(func, m_args); return 0; } template int Invoke(const LuaInstance& instance, Ret(*func)(Args...)) const { return LuaImplReplyVal(instance, std::move(Apply(func, m_args)), TypeTag()); } private: using ArgContainer = std::tuple>...>; using DefArgContainer = std::tuple>...>; template void ProcessArgs(const LuaInstance& instance) const { NazaraUnused(instance); // No argument to process } template void ProcessArgs(const LuaInstance& instance) const { LuaImplArgProcesser<(N >= FirstDefArg)>::template Process(instance, m_index, m_args, m_defaultArgs); } template void ProcessArgs(const LuaInstance& instance) const { ProcessArgs(instance); ProcessArgs(instance); } mutable ArgContainer m_args; DefArgContainer m_defaultArgs; mutable unsigned int m_index; }; }; template class LuaImplMethodProxy { public: template class Impl { static constexpr std::size_t ArgCount = sizeof...(Args); static constexpr std::size_t DefArgCount = sizeof...(DefArgs); static_assert(ArgCount >= DefArgCount, "There cannot be more default arguments than argument"); static constexpr std::size_t FirstDefArg = ArgCount - DefArgCount; public: Impl(DefArgs... defArgs) : m_defaultArgs(std::forward(defArgs)...) { } void ProcessArguments(const LuaInstance& instance) const { m_index = 2; //< 1 being the instance ProcessArgs<0, Args...>(instance); } template std::enable_if_t::value, int> Invoke(const LuaInstance& instance, T& object, void(P::*func)(Args...)) const { NazaraUnused(instance); Apply(object, func, m_args); return 0; } template std::enable_if_t::value, int> Invoke(const LuaInstance& instance, T& object, Ret(P::*func)(Args...)) const { return LuaImplReplyVal(instance, std::move(Apply(object, func, m_args)), TypeTag()); } template std::enable_if_t::value, int> Invoke(const LuaInstance& instance, T& object, T&(P::*func)(Args...)) const { T& r = Apply(object, func, m_args); if (&r == &object) { instance.PushValue(1); //< Userdata return 1; } else return LuaImplReplyVal(instance, r, TypeTag()); } template std::enable_if_t::value, int> Invoke(const LuaInstance& instance, const T& object, void(P::*func)(Args...) const) const { NazaraUnused(instance); Apply(object, func, m_args); return 0; } template std::enable_if_t::value, int> Invoke(const LuaInstance& instance, const T& object, Ret(P::*func)(Args...) const) const { return LuaImplReplyVal(instance, std::move(Apply(object, func, m_args)), TypeTag()); } template std::enable_if_t::value, int> Invoke(const LuaInstance& instance, const T& object, const T&(P::*func)(Args...) const) const { const T& r = Apply(object, func, m_args); if (&r == &object) { instance.PushValue(1); //< Userdata return 1; } else return LuaImplReplyVal(instance, r, TypeTag()); } template std::enable_if_t::type>::value, int> Invoke(const LuaInstance& instance, T& object, void(P::*func)(Args...)) const { if (!object) { instance.Error("Invalid object"); return 0; } Apply(*object, func, m_args); return 0; } template std::enable_if_t::type>::value, int> Invoke(const LuaInstance& instance, T& object, Ret(P::*func)(Args...)) const { if (!object) { instance.Error("Invalid object"); return 0; } return LuaImplReplyVal(instance, std::move(Apply(*object, func, m_args)), TypeTag()); } template std::enable_if_t::type>::value, int> Invoke(const LuaInstance& instance, T& object, typename PointedType::type&(P::*func)(Args...) const) const { if (!object) { instance.Error("Invalid object"); return 0; } const typename PointedType::type& r = Apply(*object, func, m_args); if (&r == &*object) { instance.PushValue(1); //< Userdata return 1; } else return LuaImplReplyVal(instance, r, TypeTag()); } template std::enable_if_t::type>::value, int> Invoke(const LuaInstance& instance, const T& object, void(P::*func)(Args...) const) const { if (!object) { instance.Error("Invalid object"); return 0; } Apply(*object, func, m_args); return 0; } template std::enable_if_t::type>::value, int> Invoke(const LuaInstance& instance, const T& object, Ret(P::*func)(Args...) const) const { if (!object) { instance.Error("Invalid object"); return 0; } return LuaImplReplyVal(instance, std::move(Apply(*object, func, m_args)), TypeTag()); } template std::enable_if_t::type>::value, int> Invoke(const LuaInstance& instance, const T& object, const typename PointedType::type&(P::*func)(Args...) const) const { if (!object) { instance.Error("Invalid object"); return 0; } const typename PointedType::type& r = Apply(*object, func, m_args); if (&r == &*object) { instance.PushValue(1); //< Userdata return 1; } else return LuaImplReplyVal(instance, r, TypeTag()); } private: using ArgContainer = std::tuple>...>; using DefArgContainer = std::tuple>...>; template void ProcessArgs(const LuaInstance& instance) const { NazaraUnused(instance); // No argument to process } template void ProcessArgs(const LuaInstance& instance) const { m_index += LuaImplArgProcesser<(N >= FirstDefArg)>::template Process(instance, m_index, m_args, m_defaultArgs); } template void ProcessArgs(const LuaInstance& instance) const { ProcessArgs(instance); ProcessArgs(instance); } mutable ArgContainer m_args; DefArgContainer m_defaultArgs; mutable unsigned int m_index; }; }; template T LuaInstance::Check(int* index) const { NazaraAssert(index, "Invalid index pointer"); T object; *index += LuaImplQueryArg(*this, *index, &object, TypeTag()); return object; } template T LuaInstance::Check(int* index, T defValue) const { NazaraAssert(index, "Invalid index pointer"); T object; *index += LuaImplQueryArg(*this, *index, &object, defValue, TypeTag()); return object; } template inline T LuaInstance::CheckBoundInteger(int index) const { return CheckBounds(index, CheckInteger(index)); } template inline T LuaInstance::CheckBoundInteger(int index, T defValue) const { return CheckBounds(index, CheckInteger(index, defValue)); } template T LuaInstance::CheckField(const char* fieldName, int tableIndex) const { T object; GetField(fieldName, tableIndex); tableIndex += LuaImplQueryArg(*this, -1, &object, TypeTag()); Pop(); return object; } template T LuaInstance::CheckField(const String& fieldName, int tableIndex) const { return CheckField(fieldName.GetConstBuffer(), tableIndex); } template T LuaInstance::CheckField(const char* fieldName, T defValue, int tableIndex) const { T object; GetField(fieldName, tableIndex); tableIndex += LuaImplQueryArg(*this, -1, &object, defValue, TypeTag()); Pop(); return object; } template T LuaInstance::CheckField(const String& fieldName, T defValue, int tableIndex) const { return CheckField(fieldName.GetConstBuffer(), defValue, tableIndex); } template T LuaInstance::CheckGlobal(const char* fieldName) const { T object; GetGlobal(fieldName); LuaImplQueryArg(*this, -1, &object, TypeTag()); Pop(); return object; } template T LuaInstance::CheckGlobal(const String& fieldName) const { return CheckGlobal(fieldName.GetConstBuffer()); } template T LuaInstance::CheckGlobal(const char* fieldName, T defValue) const { T object; GetGlobal(fieldName); LuaImplQueryArg(*this, -1, &object, defValue, TypeTag()); Pop(); return object; } template T LuaInstance::CheckGlobal(const String& fieldName, T defValue) const { return CheckGlobal(fieldName.GetConstBuffer(), defValue); } template int LuaInstance::Push(T arg) const { return LuaImplReplyVal(*this, std::move(arg), TypeTag()); } template int LuaInstance::Push(T firstArg, T2 secondArg, Args... args) const { int valCount = 0; valCount += Push(std::move(firstArg)); valCount += Push(secondArg, std::forward(args)...); return valCount; } template void LuaInstance::PushField(const char* name, T&& arg, int tableIndex) const { Push(std::forward(arg)); SetField(name, tableIndex); } template void LuaInstance::PushField(const String& name, T&& arg, int tableIndex) const { PushField(name.GetConstBuffer(), std::forward(arg), tableIndex); } template void LuaInstance::PushFunction(R(*func)(Args...), DefArgs&&... defArgs) const { typename LuaImplFunctionProxy::template Impl handler(std::forward(defArgs)...); PushFunction([func, handler] (LuaInstance& lua) -> int { handler.ProcessArguments(lua); return handler.Invoke(lua, func); }); } template void LuaInstance::PushGlobal(const char* name, T&& arg) { Push(std::forward(arg)); SetGlobal(name); } template void LuaInstance::PushGlobal(const String& name, T&& arg) { PushGlobal(name.GetConstBuffer(), std::forward(arg)); } template void LuaInstance::PushInstance(const char* tname, const T& instance) const { T* userdata = static_cast(PushUserdata(sizeof(T))); PlacementNew(userdata, instance); SetMetatable(tname); } template void LuaInstance::PushInstance(const char* tname, T&& instance) const { T* userdata = static_cast(PushUserdata(sizeof(T))); PlacementNew(userdata, std::move(instance)); SetMetatable(tname); } template void LuaInstance::PushInstance(const char* tname, Args&&... args) const { T* userdata = static_cast(PushUserdata(sizeof(T))); PlacementNew(userdata, std::forward(args)...); SetMetatable(tname); } template T LuaInstance::CheckBounds(int index, long long value) const { constexpr long long minBounds = std::numeric_limits::min(); constexpr long long maxBounds = std::numeric_limits::max(); if (value < minBounds || value > maxBounds) { Nz::StringStream stream; stream << "Argument #" << index << " is outside value range [" << minBounds << ", " << maxBounds << "] (" << value << ')'; Error(stream); } return static_cast(value); } }