// Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors. // All rights reserved. // Code licensed under the BSD License. // http://www.anki3d.org/LICENSE #pragma once #include #include #include #include #include #include #include #ifndef ANKI_LUA_HPP # error "Wrong LUA header included" #endif namespace anki { // Forward class LuaUserData; #define ANKI_LUA_ERROR() luaL_error(l, "Glue error at: " ANKI_FILE ":" ANKI_STRINGIZE(__LINE__) " %s", ANKI_FUNC) #define ANKI_LUA_ERROR_MSG(message) luaL_error(l, "Glue error: " message " (" ANKI_FILE ":" ANKI_STRINGIZE(__LINE__) " %s)", ANKI_FUNC) using LuaUserDataSerializeCallback = void (*)(LuaUserData& self, void* data, PtrSize& size); using LuaUserDataDeserializeCallback = void (*)(const void* data, LuaUserData& self); class LuaUserDataTypeInfo { public: I64 m_signature; const char* m_typeName; PtrSize m_structureSize; LuaUserDataSerializeCallback m_serializeCallback; LuaUserDataDeserializeCallback m_deserializeCallback; }; // LUA userdata. class LuaUserData { public: // Note: NEVER ADD A DESTRUCTOR. LUA cannot call that. ~LuaUserData() = delete; I64 getSig() const { return m_sig; } void initGarbageCollected(const LuaUserDataTypeInfo* info) { ANKI_ASSERT(info); m_sig = info->m_signature; m_info = info; m_addressOrGarbageCollect = kGarbageCollectedMagic; } // Note: Accepting const void* is wrong because getData returns a mutable pointer. Fix that. void initPointed(const LuaUserDataTypeInfo* info, const void* ptrToObject) { ANKI_ASSERT(info); m_sig = info->m_signature; m_info = info; const U64 addr = ptrToNumber(ptrToObject); ANKI_ASSERT(addr != kGarbageCollectedMagic && "Can't use this address"); m_addressOrGarbageCollect = addr; } Bool isGarbageCollected() const { ANKI_ASSERT(m_addressOrGarbageCollect != 0); return m_addressOrGarbageCollect == kGarbageCollectedMagic; } template T* getData() { ANKI_ASSERT(m_addressOrGarbageCollect != 0); ANKI_ASSERT(getDataTypeInfoFor().m_signature == m_sig); T* out = nullptr; if(isGarbageCollected()) { // Garbage collected, the data -in memory- are after this object PtrSize mem = ptrToNumber(this); mem += getAlignedRoundUp(alignof(T), sizeof(LuaUserData)); out = numberToPtr(mem); } else { // Pointed PtrSize mem = static_cast(m_addressOrGarbageCollect); out = numberToPtr(mem); } ANKI_ASSERT(out); ANKI_ASSERT(isAligned(alignof(T), out)); return out; } template static PtrSize computeSizeForGarbageCollected() { return getAlignedRoundUp(alignof(T), sizeof(LuaUserData)) + sizeof(T); } const LuaUserDataTypeInfo& getDataTypeInfo() const { ANKI_ASSERT(m_info); return *m_info; } template static const LuaUserDataTypeInfo& getDataTypeInfoFor(); private: static constexpr U64 kGarbageCollectedMagic = 0xFAFC0FEEDEADB1FF; I64 m_sig = 0; ///< Signature to identify the user data. U64 m_addressOrGarbageCollect = 0; ///< Encodes an address or a special address if it's for garbage collection. const LuaUserDataTypeInfo* m_info = nullptr; }; class LuaBinderSerializeGlobalsCallback { public: virtual void write(const void* data, PtrSize dataSize) = 0; }; // Lua binder class. A wrapper on top of LUA class LuaBinder { public: LuaBinder(); LuaBinder(const LuaBinder&) = delete; // Non-copyable ~LuaBinder(); LuaBinder& operator=(const LuaBinder&) = delete; // Non-copyable lua_State* getLuaState() { ANKI_ASSERT(m_l); return m_l; } // Expose a variable to the lua state template static void exposeVariable(lua_State* state, CString name, T* y) { void* ptr = lua_newuserdata(state, sizeof(LuaUserData)); LuaUserData* ud = static_cast(ptr); ud->initPointed(&LuaUserData::getDataTypeInfoFor(), y); luaL_setmetatable(state, LuaUserData::getDataTypeInfoFor().m_typeName); lua_setglobal(state, name.cstr()); } template static void pushVariableToTheStack(lua_State* state, T* y) { void* ptr = lua_newuserdata(state, sizeof(LuaUserData)); LuaUserData* ud = static_cast(ptr); ud->initPointed(&LuaUserData::getDataTypeInfoFor(), y); luaL_setmetatable(state, LuaUserData::getDataTypeInfoFor().m_typeName); } // Evaluate a string static Error evalString(lua_State* state, const CString& str); static void garbageCollect(lua_State* state) { lua_gc(state, LUA_GCCOLLECT, 0); } // For debugging purposes static void stackDump(lua_State* l); // Create a new LUA class static void createClass(lua_State* l, const LuaUserDataTypeInfo* typeInfo); // Add new function in a class that it's already in the stack static void pushLuaCFuncMethod(lua_State* l, const char* name, lua_CFunction luafunc); // Add a new static function in the class. static void pushLuaCFuncStaticMethod(lua_State* l, const char* className, const char* name, lua_CFunction luafunc); // Add a new function. static void pushLuaCFunc(lua_State* l, const char* name, lua_CFunction luafunc); // Dump global variables. static void serializeGlobals(lua_State* l, LuaBinderSerializeGlobalsCallback& callback); // Deserialize global variables. static void deserializeGlobals(lua_State* l, const void* data, PtrSize dataSize); // Make sure that the arguments match the argsCount number static Error checkArgsCount(lua_State* l, const Char* file, U32 line, const Char* func, I argsCount); // Get a number from the stack. template static Error checkNumber(lua_State* l, const Char* file, U32 line, const Char* func, I32 stackIdx, TNumber& number) { lua_Number lnum; Error err = checkNumberInternal(l, file, line, func, stackIdx, lnum); if(!err) { number = TNumber(lnum); } return err; } // Get a string from the stack. static Error checkString(lua_State* l, const Char* file, U32 line, const Char* func, I32 stackIdx, const char*& out); // Get some user data from the stack. // The function uses the type signature to validate the type and not the // typeName. That is supposed to be faster. static Error checkUserData(lua_State* l, const Char* file, U32 line, const Char* func, I32 stackIdx, const LuaUserDataTypeInfo& typeInfo, LuaUserData*& out); private: lua_State* m_l = nullptr; ScriptHashMap m_userDataSigToDataInfo; static void* luaAllocCallback(void* userData, void* ptr, PtrSize osize, PtrSize nsize); static Error checkNumberInternal(lua_State* l, const Char* file, U32 line, const Char* func, I32 stackIdx, lua_Number& number); }; } // end namespace anki