diff --git a/include/Nazara/Lua.hpp b/include/Nazara/Lua.hpp new file mode 100644 index 000000000..403488e90 --- /dev/null +++ b/include/Nazara/Lua.hpp @@ -0,0 +1,38 @@ +// This file was automatically generated on 24 Apr 2013 at 11:49:37 + +/* + Nazara Engine - Lua scripting module + + Copyright (C) 2013 Jérôme Leclercq (lynix680@gmail.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do + so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#pragma once + +#ifndef NAZARA_GLOBAL_LUA_HPP +#define NAZARA_GLOBAL_LUA_HPP + +#include +#include +#include +#include +#include + +#endif // NAZARA_GLOBAL_LUA_HPP diff --git a/include/Nazara/Lua/Config.hpp b/include/Nazara/Lua/Config.hpp new file mode 100644 index 000000000..8dc12f288 --- /dev/null +++ b/include/Nazara/Lua/Config.hpp @@ -0,0 +1,38 @@ +/* + Nazara Engine - Lua scripting module + + Copyright (C) 2013 Jérôme Leclercq (lynix680@gmail.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do + so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#pragma once + +#ifndef NAZARA_CONFIG_LUA_HPP +#define NAZARA_CONFIG_LUA_HPP + +/// Chaque modification d'un paramètre du module nécessite une recompilation de celui-ci + +// Utilise un tracker pour repérer les éventuels leaks (Ralentit l'exécution) +#define NAZARA_LUA_MEMORYLEAKTRACKER 0 + +// Active les tests de sécurité basés sur le code (Conseillé pour le développement) +#define NAZARA_LUA_SAFE 1 + +#endif // NAZARA_CONFIG_LUA_HPP diff --git a/include/Nazara/Lua/Debug.hpp b/include/Nazara/Lua/Debug.hpp new file mode 100644 index 000000000..cf58e12dd --- /dev/null +++ b/include/Nazara/Lua/Debug.hpp @@ -0,0 +1,11 @@ +// Copyright (C) 2013 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 +#if NAZARA_LUA_MEMORYLEAKTRACKER || defined(NAZARA_DEBUG) + #include + + #define delete NzMemoryManager::NextFree(__FILE__, __LINE__), delete + #define new new(__FILE__, __LINE__) +#endif diff --git a/include/Nazara/Lua/DebugOff.hpp b/include/Nazara/Lua/DebugOff.hpp new file mode 100644 index 000000000..fea405a98 --- /dev/null +++ b/include/Nazara/Lua/DebugOff.hpp @@ -0,0 +1,8 @@ +// Copyright (C) 2013 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 + +#if NAZARA_LUA_MEMORYLEAKTRACKER || defined(NAZARA_DEBUG) + #undef delete + #undef new +#endif diff --git a/include/Nazara/Lua/Enums.hpp b/include/Nazara/Lua/Enums.hpp new file mode 100644 index 000000000..0f5975f40 --- /dev/null +++ b/include/Nazara/Lua/Enums.hpp @@ -0,0 +1,48 @@ +// Copyright (C) 2013 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 + +#pragma once + +#ifndef NAZARA_ENUMS_LUA_HPP +#define NAZARA_ENUMS_LUA_HPP + +enum nzLuaComparison +{ + nzLuaComparison_Equality, + nzLuaComparison_Less, + nzLuaComparison_LessOrEqual, + + nzLuaComparison_Max = nzLuaComparison_LessOrEqual +}; + +enum nzLuaOperation +{ + nzLuaOperation_Addition, + nzLuaOperation_Division, + nzLuaOperation_Exponentiation, + nzLuaOperation_Modulo, + nzLuaOperation_Multiplication, + nzLuaOperation_Negation, + nzLuaOperation_Substraction, + + nzLuaOperation_Max = nzLuaOperation_Substraction +}; + +enum nzLuaType +{ + nzLuaType_Boolean, + nzLuaType_Function, + nzLuaType_LightUserdata, + nzLuaType_Nil, + nzLuaType_Number, + nzLuaType_None, + nzLuaType_String, + nzLuaType_Table, + nzLuaType_Thread, + nzLuaType_Userdata, + + nzLuaType_Max = nzLuaType_Userdata +}; + +#endif // NAZARA_ENUMS_LUA_HPP diff --git a/include/Nazara/Lua/LuaClass.hpp b/include/Nazara/Lua/LuaClass.hpp new file mode 100644 index 000000000..02a1fb5a9 --- /dev/null +++ b/include/Nazara/Lua/LuaClass.hpp @@ -0,0 +1,53 @@ +// Copyright (C) 2013 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 + +#pragma once + +#ifndef NAZARA_LUACLASS_HPP +#define NAZARA_LUACLASS_HPP + +#include +#include +#include +#include + +template +class NzLuaClass : public NzLuaClassImpl +{ + public: + using ClassFunc = int (*)(NzLuaInstance& lua, T& instance); + using ClassIndexFunc = bool (*)(NzLuaInstance& lua, T& instance); + using ConstructorFunc = T* (*)(NzLuaInstance& lua); + using FinalizerFunc = bool (*)(NzLuaInstance& lua, T& instance); + + NzLuaClass(const NzString& name); + + //template void Inherit(NzLuaClass& parent); + + void Register(NzLuaInstance& lua); + + void SetConstructor(ConstructorFunc constructor); + void SetFinalizer(FinalizerFunc finalizer); + void SetGetter(ClassIndexFunc getter); + void SetMethod(const NzString& name, ClassFunc method); + void SetSetter(ClassIndexFunc setter); + + private: + static int ConstructorProxy(lua_State* state); + static int FinalizerProxy(lua_State* state); + static int GetterProxy(lua_State* state); + static int MethodProxy(lua_State* state); + static int SetterProxy(lua_State* state); + + std::map m_methods; + ClassIndexFunc m_getter; + ClassIndexFunc m_setter; + ConstructorFunc m_constructor; + FinalizerFunc m_finalizer; + NzString m_name; +}; + +#include + +#endif // NAZARA_LUACLASS_HPP diff --git a/include/Nazara/Lua/LuaClass.inl b/include/Nazara/Lua/LuaClass.inl new file mode 100644 index 000000000..b18209a9f --- /dev/null +++ b/include/Nazara/Lua/LuaClass.inl @@ -0,0 +1,200 @@ +// Copyright (C) 2013 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 + +template +NzLuaClass::NzLuaClass(const NzString& name) : +m_getter(nullptr), +m_setter(nullptr), +m_constructor(nullptr), +m_finalizer(nullptr), +m_name(name) +{ +} + +template +void NzLuaClass::Register(NzLuaInstance& lua) +{ + if (!lua.NewMetatable(m_name)) + NazaraWarning("Class \"" + m_name + "\" already registred in this instance"); + { + FinalizerFunc* finalizer = static_cast(lua.PushUserdata(sizeof(FinalizerFunc))); + *finalizer = m_finalizer; + lua.PushString(m_name); + + lua.PushCFunction(FinalizerProxy, 2); + lua.SetField("__gc"); + + if (m_getter) + { + ClassIndexFunc* getter = static_cast(lua.PushUserdata(sizeof(ClassIndexFunc))); + *getter = m_getter; + lua.PushString(m_name); + + lua.PushCFunction(GetterProxy, 2); + } + else + lua.PushValue(-1); + + lua.SetField("__index"); + + if (m_setter) + { + ClassIndexFunc* setter = static_cast(lua.PushUserdata(sizeof(ClassIndexFunc))); + *setter = m_setter; + lua.PushString(m_name); + + lua.PushCFunction(SetterProxy, 2); + lua.SetField("__newindex"); + } + + for (auto it = m_methods.begin(); it != m_methods.end(); ++it) + { + ClassFunc* method = static_cast(lua.PushUserdata(sizeof(ClassFunc))); + *method = it->second; + lua.PushString(m_name); + + lua.PushCFunction(MethodProxy, 2); + lua.SetField(it->first); + } + + } + lua.Pop(); + + if (m_constructor) + { + ConstructorFunc* ptr = static_cast(lua.PushUserdata(sizeof(ConstructorFunc))); + *ptr = m_constructor; + lua.PushString(m_name); + + lua.PushCFunction(ConstructorProxy, 2); + lua.SetGlobal(m_name); + } +} + +template +void NzLuaClass::SetConstructor(ConstructorFunc constructor) +{ + m_constructor = constructor; +} + +template +void NzLuaClass::SetFinalizer(FinalizerFunc finalizer) +{ + m_finalizer = finalizer; +} + +template +void NzLuaClass::SetGetter(ClassIndexFunc getter) +{ + m_getter = getter; +} + +template +void NzLuaClass::SetMethod(const NzString& name, ClassFunc method) +{ + m_methods[name] = method; +} + +template +void NzLuaClass::SetSetter(ClassIndexFunc setter) +{ + m_setter = setter; +} + +template +int NzLuaClass::ConstructorProxy(lua_State* state) +{ + NzLuaInstance& lua = *NzLuaInstance::GetInstance(state); + + ConstructorFunc func = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); + const char* className = lua.ToString(lua.GetIndexOfUpValue(2)); + + T* instance = func(lua); + if (!instance) + { + lua.Error("Constructor failed"); + return 0; // Normalement pas nécessaire + } + + T** ud = static_cast(lua.PushUserdata(sizeof(T*))); + lua.SetMetatable(className); + *ud = instance; + + return 1; +} + +template +int NzLuaClass::FinalizerProxy(lua_State* state) +{ + NzLuaInstance& lua = *NzLuaInstance::GetInstance(state); + + FinalizerFunc func = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); + const char* className = lua.ToString(lua.GetIndexOfUpValue(2)); + + T* instance = *static_cast(lua.CheckUserdata(1, className)); + lua.Remove(1); + + if (!func || func(lua, *instance)) + delete instance; + + return 0; +} + +template +int NzLuaClass::GetterProxy(lua_State* state) +{ + NzLuaInstance& lua = *NzLuaInstance::GetInstance(state); + + ClassIndexFunc func = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); + const char* className = lua.ToString(lua.GetIndexOfUpValue(2)); + + T& instance = *(*static_cast(lua.CheckUserdata(1, className))); + lua.Remove(1); + + if (!func(lua, instance)) + { + // On accède alors à la table + lua.PushMetatable(className); + lua.PushValue(1); + lua.GetTable(); + } + + return 1; +} + +template +int NzLuaClass::MethodProxy(lua_State* state) +{ + NzLuaInstance& lua = *NzLuaInstance::GetInstance(state); + + ClassFunc func = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); + const char* className = lua.ToString(lua.GetIndexOfUpValue(2)); + + T& instance = *(*static_cast(lua.CheckUserdata(1, className))); + lua.Remove(1); + + return (*func)(lua, instance); +} + +template +int NzLuaClass::SetterProxy(lua_State* state) +{ + NzLuaInstance& lua = *NzLuaInstance::GetInstance(state); + + ClassIndexFunc func = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); + const char* className = lua.ToString(lua.GetIndexOfUpValue(2)); + + T& instance = *(*static_cast(lua.CheckUserdata(1, className))); + lua.Remove(1); + + if (!func(lua, instance)) + lua.Error("Field not found"); + + return 1; +} + +#include diff --git a/include/Nazara/Lua/LuaInstance.hpp b/include/Nazara/Lua/LuaInstance.hpp new file mode 100644 index 000000000..375607c8f --- /dev/null +++ b/include/Nazara/Lua/LuaInstance.hpp @@ -0,0 +1,152 @@ +// Copyright (C) 2013 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 + +#pragma once + +#ifndef NAZARA_LUASTATE_HPP +#define NAZARA_LUASTATE_HPP + +#include +#include +#include +#include +#include +#include + +struct lua_Debug; +struct lua_State; + +class NzLuaInstance; + +using NzLuaCFunction = int (*)(lua_State* state); +using NzLuaFunction = int (*)(NzLuaInstance& instance); + +class NAZARA_API NzLuaInstance : NzNonCopyable +{ + public: + NzLuaInstance(); + ~NzLuaInstance(); + + void CheckAny(int index) const; + int CheckInteger(int index) const; + int CheckInteger(int index, int defValue) const; + double CheckNumber(int index) const; + double CheckNumber(int index, double defValue) const; + void CheckStack(int space, const char* error = nullptr) const; + void CheckStack(int space, const NzString& error) const; + const char* CheckString(int index, std::size_t* length = nullptr) const; + const char* CheckString(int index, const char* defValue, std::size_t* length = nullptr) const; + void CheckType(int index, nzLuaType type) const; + unsigned int CheckUnsigned(int index) const; + unsigned int CheckUnsigned(int index, unsigned int defValue) const; + void* CheckUserdata(int index, const char* tname) const; + void* CheckUserdata(int index, const NzString& tname) const; + + bool Compare(int index1, int index2, nzLuaComparison comparison) const; + void Compute(nzLuaOperation operation); + + void Concatenate(int count); + + void Error(const char* message); + void Error(const NzString& message); + + bool Execute(const NzString& code); + bool ExecuteFromFile(const NzString& filePath); + bool ExecuteFromMemory(const void* data, unsigned int size); + bool ExecuteFromStream(NzInputStream& stream); + + int GetAbsIndex(int index) const; + void GetField(const char* fieldName, int index = -1) const; + void GetField(const NzString& fieldName, int index = -1) const; + void GetGlobal(const char* name) const; + void GetGlobal(const NzString& name) const; + lua_State* GetInternalState() const; + NzString GetLastError() const; + nzUInt32 GetMemoryLimit() const; + nzUInt32 GetMemoryUsage() const; + void GetMetatable(const char* tname) const; + void GetMetatable(const NzString& tname) const; + bool GetMetatable(int index) const; + unsigned int GetStackTop() const; + void GetTable(int index = -2) const; + nzUInt32 GetTimeLimit() const; + nzLuaType GetType(int index) const; + const char* GetTypeName(nzLuaType type) const; + + void Insert(int index); + + bool IsOfType(int index, nzLuaType type) const; + bool IsOfType(int index, const char* tname) const; + bool IsOfType(int index, const NzString& tname) const; + bool IsValid(int index) const; + + unsigned int Length(int index) const; + + void MoveTo(NzLuaInstance* instance, int n); + + bool NewMetatable(const char* str); + bool NewMetatable(const NzString& str); + bool Next(int index = -2); + + void Pop(unsigned int n = 1U); + + void PushBoolean(bool value); + void PushCFunction(NzLuaCFunction func, int valueCount = 0); + void PushFunction(NzLuaFunction func); + void PushInteger(int value); + void PushLightUserdata(void* value); + void PushMetatable(const char* str); + void PushMetatable(const NzString& str); + void PushNil(); + void PushNumber(double value); + void PushString(const char* str); + void PushString(const NzString& str); + void PushTable(unsigned int sequenceElementCount = 0, unsigned int arrayElementCount = 0); + void PushUnsigned(unsigned int value); + void* PushUserdata(unsigned int size); + void PushValue(int index); + + void Remove(int index); + void Replace(int index); + + void SetField(const char* name, int index = -2); + void SetField(const NzString& name, int index = -2); + void SetGlobal(const char* name); + void SetGlobal(const NzString& name); + void SetMetatable(const char* tname); + void SetMetatable(const NzString& tname); + void SetMetatable(int index); + void SetMemoryLimit(nzUInt32 memoryLimit); + void SetTable(int index = -3); + void SetTimeLimit(nzUInt32 timeLimit); + + bool ToBoolean(int index) const; + int ToInteger(int index, bool* succeeded = nullptr) const; + double ToNumber(int index, bool* succeeded = nullptr) const; + const char* ToString(int index, std::size_t* length = nullptr) const; + unsigned int ToUnsigned(int index, bool* succeeded = nullptr) const; + void* ToUserdata(int index) const; + void* ToUserdata(int index, const char* tname) const; + void* ToUserdata(int index, const NzString& tname) const; + + static int GetIndexOfUpValue(int upValue); + static NzLuaInstance* GetInstance(lua_State* state); + + private: + bool Run(); + + static void* MemoryAllocator(void *ud, void *ptr, std::size_t osize, std::size_t nsize); + static int ProxyFunc(lua_State* state); + static void TimeLimiter(lua_State* state, lua_Debug* debug); + + nzUInt32 m_memoryLimit; + nzUInt32 m_memoryUsage; + nzUInt32 m_timeLimit; + NzClock m_clock; + NzString m_lastError; + lua_State* m_state; + unsigned int m_level; +}; + +#endif // NAZARA_LUASTATE_HPP diff --git a/src/Nazara/Lua/Debug/Leaks.cpp b/src/Nazara/Lua/Debug/Leaks.cpp new file mode 100644 index 000000000..2f583b2ca --- /dev/null +++ b/src/Nazara/Lua/Debug/Leaks.cpp @@ -0,0 +1,29 @@ +// Copyright (C) 2013 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 +#if NAZARA_LUA_MEMORYLEAKTRACKER || defined(NAZARA_DEBUG) +#include +#include + +void* operator new(std::size_t size) +{ + return NzMemoryManager::Allocate(size, false); +} + +void* operator new[](std::size_t size) +{ + return NzMemoryManager::Allocate(size, true); +} + +void operator delete(void* pointer) noexcept +{ + NzMemoryManager::Free(pointer, false); +} + +void operator delete[](void* pointer) noexcept +{ + NzMemoryManager::Free(pointer, true); +} +#endif diff --git a/src/Nazara/Lua/Lua.cpp b/src/Nazara/Lua/Lua.cpp new file mode 100644 index 000000000..5d872bc64 --- /dev/null +++ b/src/Nazara/Lua/Lua.cpp @@ -0,0 +1,56 @@ +// Copyright (C) 2013 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 + +bool NzLua::Initialize() +{ + if (s_moduleReferenceCounter++ != 0) + return true; // Déjà initialisé + + // Initialisation des dépendances + if (!NzCore::Initialize()) + { + NazaraError("Failed to initialize core module"); + return false; + } + + // Initialisation du module + + NazaraNotice("Initialized: Lua module"); + + return true; +} + +bool NzLua::IsInitialized() +{ + return s_moduleReferenceCounter != 0; +} + +void NzLua::Uninitialize() +{ + if (s_moduleReferenceCounter != 1) + { + // Le module est soit encore utilisé, soit pas initialisé + if (s_moduleReferenceCounter > 1) + s_moduleReferenceCounter--; + + return; + } + + // Libération du module + s_moduleReferenceCounter = 0; + + NazaraNotice("Uninitialized: Lua module"); + + // Libération des dépendances + NzCore::Uninitialize(); +} + +unsigned int NzLua::s_moduleReferenceCounter = 0; diff --git a/src/Nazara/Lua/LuaInstance.cpp b/src/Nazara/Lua/LuaInstance.cpp new file mode 100644 index 000000000..2144e3c37 --- /dev/null +++ b/src/Nazara/Lua/LuaInstance.cpp @@ -0,0 +1,790 @@ +// Copyright (C) 2013 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 +#include +#include +#include +#include + +namespace +{ + struct StreamData + { + NzInputStream* stream; + char buffer[NAZARA_CORE_FILE_BUFFERSIZE]; + }; + + int AtPanic(lua_State* state) + { + NazaraUnused(state); + + throw std::runtime_error("Lua panic !"); + } + + const char* StreamReader(lua_State* state, void* data, std::size_t* size) + { + NazaraUnused(state); + + StreamData* streamData = static_cast(data); + + if (streamData->stream->EndOfStream()) + return nullptr; + else + { + *size = streamData->stream->Read(streamData->buffer, NAZARA_CORE_FILE_BUFFERSIZE); + return streamData->buffer; + } + } + + int s_comparisons[nzLuaComparison_Max+1] = { + LUA_OPEQ, // nzLuaComparison_Equality + LUA_OPLT, // nzLuaComparison_Less + LUA_OPLE // nzLuaComparison_LessOrEqual + }; + + int s_operations[nzLuaOperation_Max+1] = { + LUA_OPADD, // nzLuaOperation_Addition + LUA_OPDIV, // nzLuaOperation_Division + LUA_OPPOW, // nzLuaOperation_Exponentiation + LUA_OPMOD, // nzLuaOperation_Modulo + LUA_OPMUL, // nzLuaOperation_Multiplication + LUA_OPUNM, // nzLuaOperation_Negation + LUA_OPSUB // nzLuaOperation_Substraction + }; + + int s_types[nzLuaType_Max+1] = { + LUA_TBOOLEAN, // nzLuaType_Boolean + LUA_TFUNCTION, // nzLuaType_Function + LUA_TLIGHTUSERDATA, // nzLuaType_LightUserdata + LUA_TNIL, // nzLuaType_Nil + LUA_TNUMBER, // nzLuaType_Number + LUA_TNONE, // nzLuaType_None + LUA_TSTRING, // nzLuaType_String + LUA_TTABLE, // nzLuaType_Table + LUA_TTHREAD, // nzLuaType_Thread + LUA_TUSERDATA // nzLuaType_Userdata + }; +} + +NzLuaInstance::NzLuaInstance() : +m_memoryLimit(0), +m_memoryUsage(0), +m_timeLimit(1000), +m_level(0) +{ + m_state = lua_newstate(MemoryAllocator, this); + lua_atpanic(m_state, AtPanic); + lua_sethook(m_state, TimeLimiter, LUA_MASKCOUNT, 1000); + luaL_openlibs(m_state); +} + +NzLuaInstance::~NzLuaInstance() +{ + lua_close(m_state); +} + +void NzLuaInstance::CheckAny(int index) const +{ + luaL_checkany(m_state, index); +} + +int NzLuaInstance::CheckInteger(int index) const +{ + return luaL_checkint(m_state, index); +} + +int NzLuaInstance::CheckInteger(int index, int defValue) const +{ + return luaL_optint(m_state, index, defValue); +} + +double NzLuaInstance::CheckNumber(int index) const +{ + return luaL_checknumber(m_state, index); +} + +double NzLuaInstance::CheckNumber(int index, double defValue) const +{ + return luaL_optnumber(m_state, index, defValue); +} + +void NzLuaInstance::CheckStack(int space, const char* error) const +{ + luaL_checkstack(m_state, space, error); +} + +void NzLuaInstance::CheckStack(int space, const NzString& error) const +{ + CheckStack(space, error.GetConstBuffer()); +} + +const char* NzLuaInstance::CheckString(int index, std::size_t* length) const +{ + return luaL_checklstring(m_state, index, length); +} + +const char* NzLuaInstance::CheckString(int index, const char* defValue, std::size_t* length) const +{ + return luaL_optlstring(m_state, index, defValue, length); +} + +void NzLuaInstance::CheckType(int index, nzLuaType type) const +{ + #ifdef NAZARA_DEBUG + if (type > nzLuaType_Max) + { + NazaraError("Lua type out of enum"); + return; + } + #endif + + luaL_checktype(m_state, index, s_types[type]); +} + +unsigned int NzLuaInstance::CheckUnsigned(int index) const +{ + return luaL_checkunsigned(m_state, index); +} + +unsigned int NzLuaInstance::CheckUnsigned(int index, unsigned int defValue) const +{ + return luaL_optunsigned(m_state, index, defValue); +} + +void* NzLuaInstance::CheckUserdata(int index, const char* tname) const +{ + return luaL_checkudata(m_state, index, tname); +} + +void* NzLuaInstance::CheckUserdata(int index, const NzString& tname) const +{ + return luaL_checkudata(m_state, index, tname.GetConstBuffer()); +} + +bool NzLuaInstance::Compare(int index1, int index2, nzLuaComparison comparison) const +{ + #ifdef NAZARA_DEBUG + if (comparison > nzLuaComparison_Max) + { + NazaraError("Lua comparison out of enum"); + return false; + } + #endif + + return lua_compare(m_state, index1, index2, s_comparisons[comparison]); +} + +void NzLuaInstance::Compute(nzLuaOperation operation) +{ + #ifdef NAZARA_DEBUG + if (operation > nzLuaOperation_Max) + { + NazaraError("Lua operation out of enum"); + return; + } + #endif + + lua_arith(m_state, s_operations[operation]); +} + +void NzLuaInstance::Concatenate(int count) +{ + lua_concat(m_state, count); +} + +void NzLuaInstance::Error(const char* message) +{ + luaL_error(m_state, message); +} + +void NzLuaInstance::Error(const NzString& message) +{ + luaL_error(m_state, message.GetConstBuffer()); +} + +bool NzLuaInstance::Execute(const NzString& code) +{ + if (code.IsEmpty()) + return true; + + if (luaL_loadstring(m_state, code.GetConstBuffer()) != 0) + { + m_lastError = lua_tostring(m_state, -1); + lua_pop(m_state, 1); + + return false; + } + + if (m_level++ == 0) + m_clock.Restart(); + + int status = lua_pcall(m_state, 0, LUA_MULTRET, 0); + + m_level--; + + if (status != 0) + { + m_lastError = lua_tostring(m_state, -1); + lua_pop(m_state, 1); + + return false; + } + + return true; +} + +bool NzLuaInstance::ExecuteFromFile(const NzString& filePath) +{ + NzFile file(filePath); + if (!file.Open(NzFile::ReadOnly | NzFile::Text)) + { + NazaraError("Failed to open file"); + return false; + } + + unsigned int length = file.GetSize(); + + NzString source; + source.Resize(length); + + if (file.Read(&source[0], length) != length) + { + NazaraError("Failed to read file"); + return false; + } + + file.Close(); + + return Execute(source); +} + +bool NzLuaInstance::ExecuteFromMemory(const void* data, unsigned int size) +{ + NzMemoryStream stream(data, size); + return ExecuteFromStream(stream); +} + +bool NzLuaInstance::ExecuteFromStream(NzInputStream& stream) +{ + StreamData data; + data.stream = &stream; + + if (lua_load(m_state, StreamReader, &data, "C", nullptr) != 0) + { + m_lastError = lua_tostring(m_state, -1); + lua_pop(m_state, 1); + + return false; + } + + return Run(); +} + +int NzLuaInstance::GetAbsIndex(int index) const +{ + return lua_absindex(m_state, index); +} + +void NzLuaInstance::GetField(const char* fieldName, int index) const +{ + lua_getfield(m_state, index, fieldName); +} + +void NzLuaInstance::GetField(const NzString& fieldName, int index) const +{ + lua_getfield(m_state, index, fieldName.GetConstBuffer()); +} + +void NzLuaInstance::GetGlobal(const char* name) const +{ + lua_getglobal(m_state, name); +} + +void NzLuaInstance::GetGlobal(const NzString& name) const +{ + lua_getglobal(m_state, name.GetConstBuffer()); +} + +lua_State* NzLuaInstance::GetInternalState() const +{ + return m_state; +} + +NzString NzLuaInstance::GetLastError() const +{ + return m_lastError; +} + +nzUInt32 NzLuaInstance::GetMemoryLimit() const +{ + return m_memoryLimit; +} + +nzUInt32 NzLuaInstance::GetMemoryUsage() const +{ + return m_memoryUsage; +} + +void NzLuaInstance::GetMetatable(const char* tname) const +{ + luaL_getmetatable(m_state, tname); +} + +void NzLuaInstance::GetMetatable(const NzString& tname) const +{ + luaL_getmetatable(m_state, tname.GetConstBuffer()); +} + +bool NzLuaInstance::GetMetatable(int index) const +{ + return lua_getmetatable(m_state, index); +} + +unsigned int NzLuaInstance::GetStackTop() const +{ + return lua_gettop(m_state); +} + +void NzLuaInstance::GetTable(int index) const +{ + lua_gettable(m_state, index); +} + +nzUInt32 NzLuaInstance::GetTimeLimit() const +{ + return m_timeLimit; +} + +nzLuaType NzLuaInstance::GetType(int index) const +{ + switch (lua_type(m_state, index)) + { + case LUA_TBOOLEAN: + return nzLuaType_Boolean; + + case LUA_TFUNCTION: + return nzLuaType_Function; + + case LUA_TLIGHTUSERDATA: + return nzLuaType_LightUserdata; + + case LUA_TNIL: + return nzLuaType_Nil; + + case LUA_TNONE: + return nzLuaType_None; + + case LUA_TNUMBER: + return nzLuaType_Number; + + case LUA_TSTRING: + return nzLuaType_String; + + case LUA_TTABLE: + return nzLuaType_Table; + + case LUA_TTHREAD: + return nzLuaType_Thread; + + case LUA_TUSERDATA: + return nzLuaType_Userdata; + + default: + return nzLuaType_None; + } +} + +const char* NzLuaInstance::GetTypeName(nzLuaType type) const +{ + #ifdef NAZARA_DEBUG + if (type > nzLuaType_Max) + { + NazaraError("Lua type out of enum"); + return nullptr; + } + #endif + + return lua_typename(m_state, s_types[type]); +} + +void NzLuaInstance::Insert(int index) +{ + lua_insert(m_state, index); +} + +bool NzLuaInstance::IsOfType(int index, nzLuaType type) const +{ + switch (type) + { + case nzLuaType_Boolean: + return lua_isboolean(m_state, index); + + case nzLuaType_Function: + return lua_isfunction(m_state, index); + + case nzLuaType_LightUserdata: + return lua_islightuserdata(m_state, index); + + case nzLuaType_Nil: + return lua_isnil(m_state, index); + + case nzLuaType_None: + return lua_isnone(m_state, index); + + case nzLuaType_Number: + return lua_isnumber(m_state, index); + + case nzLuaType_String: + return lua_isstring(m_state, index); + + case nzLuaType_Table: + return lua_istable(m_state, index); + + case nzLuaType_Thread: + return lua_isthread(m_state, index); + + case nzLuaType_Userdata: + return lua_isuserdata(m_state, index); + } + + NazaraError("Lua type unhandled (0x" + NzString::Number(type, 16) + ')'); + return false; +} + +bool NzLuaInstance::IsOfType(int index, const char* tname) const +{ + void* ud = luaL_testudata(m_state, index, tname); + return ud != nullptr; +} + +bool NzLuaInstance::IsOfType(int index, const NzString& tname) const +{ + return IsOfType(index, tname.GetConstBuffer()); +} + +bool NzLuaInstance::IsValid(int index) const +{ + return !lua_isnoneornil(m_state, index); +} + +unsigned int NzLuaInstance::Length(int index) const +{ + return luaL_len(m_state, index); +} + +void NzLuaInstance::MoveTo(NzLuaInstance* instance, int n) +{ + lua_xmove(m_state, instance->m_state, n); +} + +bool NzLuaInstance::NewMetatable(const char* str) +{ + return luaL_newmetatable(m_state, str); +} + +bool NzLuaInstance::NewMetatable(const NzString& str) +{ + return luaL_newmetatable(m_state, str.GetConstBuffer()); +} + +bool NzLuaInstance::Next(int index) +{ + return lua_next(m_state, index); +} + +void NzLuaInstance::Pop(unsigned int n) +{ + lua_pop(m_state, n); +} + +void NzLuaInstance::PushBoolean(bool value) +{ + lua_pushboolean(m_state, value); +} + +void NzLuaInstance::PushCFunction(NzLuaCFunction func, int valueCount) +{ + lua_pushcclosure(m_state, func, valueCount); +} + +void NzLuaInstance::PushFunction(NzLuaFunction func) +{ + NzLuaFunction* luaFunc = reinterpret_cast(lua_newuserdata(m_state, sizeof(NzLuaFunction))); + *luaFunc = func; + + lua_pushcclosure(m_state, ProxyFunc, 1); +} + +void NzLuaInstance::PushInteger(int value) +{ + lua_pushinteger(m_state, value); +} + +void NzLuaInstance::PushLightUserdata(void* value) +{ + lua_pushlightuserdata(m_state, value); +} + +void NzLuaInstance::PushMetatable(const char* str) +{ + luaL_getmetatable(m_state, str); +} + +void NzLuaInstance::PushMetatable(const NzString& str) +{ + luaL_getmetatable(m_state, str.GetConstBuffer()); +} + +void NzLuaInstance::PushNil() +{ + lua_pushnil(m_state); +} + +void NzLuaInstance::PushNumber(double value) +{ + lua_pushnumber(m_state, value); +} + +void NzLuaInstance::PushString(const char* str) +{ + lua_pushstring(m_state, str); +} + +void NzLuaInstance::PushString(const NzString& str) +{ + lua_pushlstring(m_state, str.GetConstBuffer(), str.GetSize()); +} + +void NzLuaInstance::PushTable(unsigned int sequenceElementCount, unsigned int arrayElementCount) +{ + lua_createtable(m_state, sequenceElementCount, arrayElementCount); +} + +void NzLuaInstance::PushUnsigned(unsigned int value) +{ + lua_pushunsigned(m_state, value); +} + +void* NzLuaInstance::PushUserdata(unsigned int size) +{ + return lua_newuserdata(m_state, size); +} + +void NzLuaInstance::PushValue(int index) +{ + lua_pushvalue(m_state, index); +} + +void NzLuaInstance::Remove(int index) +{ + lua_remove(m_state, index); +} + +void NzLuaInstance::Replace(int index) +{ + lua_replace(m_state, index); +} + +void NzLuaInstance::SetField(const char* name, int index) +{ + lua_setfield(m_state, index, name); +} + +void NzLuaInstance::SetField(const NzString& name, int index) +{ + lua_setfield(m_state, index, name.GetConstBuffer()); +} + +void NzLuaInstance::SetGlobal(const char* name) +{ + lua_setglobal(m_state, name); +} + +void NzLuaInstance::SetGlobal(const NzString& name) +{ + lua_setglobal(m_state, name.GetConstBuffer()); +} + +void NzLuaInstance::SetMetatable(const char* tname) +{ + luaL_setmetatable(m_state, tname); +} + +void NzLuaInstance::SetMetatable(const NzString& tname) +{ + luaL_setmetatable(m_state, tname.GetConstBuffer()); +} + +void NzLuaInstance::SetMetatable(int index) +{ + lua_setmetatable(m_state, index); +} + +void NzLuaInstance::SetMemoryLimit(nzUInt32 memoryLimit) +{ + m_memoryLimit = memoryLimit; +} + +void NzLuaInstance::SetTable(int index) +{ + lua_settable(m_state, index); +} + +void NzLuaInstance::SetTimeLimit(nzUInt32 timeLimit) +{ + if (m_timeLimit != timeLimit) + { + if (m_timeLimit == 0) + lua_sethook(m_state, TimeLimiter, LUA_MASKCOUNT, 1000); + else if (timeLimit == 0) + lua_sethook(m_state, TimeLimiter, 0, 1000); + + m_timeLimit = timeLimit; + } +} + +bool NzLuaInstance::ToBoolean(int index) const +{ + return lua_toboolean(m_state, index); +} + +int NzLuaInstance::ToInteger(int index, bool* succeeded) const +{ + int success; + int result = lua_tointegerx(m_state, index, &success); + + if (succeeded) + *succeeded = success; + + return result; +} + +double NzLuaInstance::ToNumber(int index, bool* succeeded) const +{ + int success; + double result = lua_tonumberx(m_state, index, &success); + + if (succeeded) + *succeeded = success; + + return result; +} + +const char* NzLuaInstance::ToString(int index, std::size_t* length) const +{ + return lua_tolstring(m_state, index, length); +} + +unsigned int NzLuaInstance::ToUnsigned(int index, bool* succeeded) const +{ + int success; + unsigned int result = lua_tounsignedx(m_state, index, &success); + + if (succeeded) + *succeeded = success; + + return result; +} + +void* NzLuaInstance::ToUserdata(int index) const +{ + return lua_touserdata(m_state, index); +} + +void* NzLuaInstance::ToUserdata(int index, const char* tname) const +{ + return luaL_testudata(m_state, index, tname); +} + +void* NzLuaInstance::ToUserdata(int index, const NzString& tname) const +{ + return luaL_testudata(m_state, index, tname.GetConstBuffer()); +} + +int NzLuaInstance::GetIndexOfUpValue(int upValue) +{ + return lua_upvalueindex(upValue); +} + +NzLuaInstance* NzLuaInstance::GetInstance(lua_State* state) +{ + NzLuaInstance* instance; + lua_getallocf(state, reinterpret_cast(&instance)); + + return instance; +} + +bool NzLuaInstance::Run() +{ + if (m_level++ == 0) + m_clock.Restart(); + + int status = lua_pcall(m_state, 0, 0, 0); + + m_level--; + + if (status != 0) + { + m_lastError = lua_tostring(m_state, -1); + lua_pop(m_state, 1); + + return false; + } + + return true; +} + +void* NzLuaInstance::MemoryAllocator(void* ud, void* ptr, std::size_t osize, std::size_t nsize) +{ + NzLuaInstance* instance = static_cast(ud); + nzUInt32& memoryLimit = instance->m_memoryLimit; + nzUInt32& memoryUsage = instance->m_memoryUsage; + + if (nsize == 0) + { + memoryUsage -= osize; + std::free(ptr); + + return nullptr; + } + else + { + nzUInt32 usage = memoryUsage + nsize; + if (ptr) + usage -= osize; + + if (memoryLimit != 0 && usage > memoryLimit) + { + NazaraError("Lua memory usage is over memory limit (" + NzString::Number(usage) + " > " + NzString::Number(memoryLimit) + ')'); + return nullptr; + } + + memoryUsage = usage; + + return std::realloc(ptr, nsize); + } +} + +int NzLuaInstance::ProxyFunc(lua_State* state) +{ + NzLuaFunction* func = static_cast(lua_touserdata(state, lua_upvalueindex(1))); + return (*func)(*GetInstance(state)); +} + +void NzLuaInstance::TimeLimiter(lua_State* state, lua_Debug* debug) +{ + NazaraUnused(debug); + + NzLuaInstance* instance = GetInstance(state); + if (instance->m_clock.GetMilliseconds() > instance->m_timeLimit) + luaL_error(state, "maximum execution time exceeded"); +}