#pragma once #include #include #include #include "StarLexicalCast.hpp" #include "StarString.hpp" #include "StarJson.hpp" #include "StarRefPtr.hpp" #include "StarDirectives.hpp" namespace Star { class LuaEngine; typedef RefPtr LuaEnginePtr; // Basic unspecified lua exception STAR_EXCEPTION(LuaException, StarException); // Thrown when trying to parse an incomplete statement, useful for implementing // REPL loops, uses the incomplete statement marker '' as the standard lua // repl does. STAR_EXCEPTION(LuaIncompleteStatementException, LuaException); // Thrown when the instruction limit is reached, if the instruction limit is // set. STAR_EXCEPTION(LuaInstructionLimitReached, LuaException); // Thrown when the engine recursion limit is reached, if the recursion limit is // set. STAR_EXCEPTION(LuaRecursionLimitReached, LuaException); // Thrown when an incorrect lua type is passed to something in C++ expecting a // different type. STAR_EXCEPTION(LuaConversionException, LuaException); typedef Empty LuaNilType; typedef bool LuaBoolean; typedef lua_Integer LuaInt; typedef lua_Number LuaFloat; class LuaString; class LuaTable; class LuaFunction; class LuaThread; class LuaUserData; typedef Variant LuaValue; // Used to wrap multiple return values from calling a lua function or to pass // multiple values as arguments to a lua function from a container. If this is // used as an argument to a lua callback function, it must be the final // argument of the function! template class LuaVariadic : public List { public: using List::List; }; // Unpack a container and apply each of the arguments separately to a lua // function, similar to lua's unpack. template LuaVariadic::type::value_type> luaUnpack(Container&& c); // Similar to LuaVariadic, but a tuple type so automatic per-entry type // conversion is done. This can only be used as the return value of a wrapped // c++ function, or as a type for the return value of calling a lua function. template class LuaTupleReturn : public tuple { public: typedef tuple Base; explicit LuaTupleReturn(Types const&... args); template explicit LuaTupleReturn(UTypes&&... args); template explicit LuaTupleReturn(UTypes const&... args); LuaTupleReturn(LuaTupleReturn const& rhs); LuaTupleReturn(LuaTupleReturn&& rhs); template LuaTupleReturn(LuaTupleReturn const& rhs); template LuaTupleReturn(LuaTupleReturn&& rhs); LuaTupleReturn& operator=(LuaTupleReturn const& rhs); LuaTupleReturn& operator=(LuaTupleReturn&& rhs); template LuaTupleReturn& operator=(LuaTupleReturn const& rhs); template LuaTupleReturn& operator=(LuaTupleReturn&& rhs); }; // std::tie for LuaTupleReturn template LuaTupleReturn luaTie(Types&... args); // Constructs a LuaTupleReturn from the given arguments similar to make_tuple template LuaTupleReturn::type...> luaTupleReturn(Types&&... args); namespace LuaDetail { struct LuaHandle { LuaHandle(LuaEnginePtr engine, int handleIndex); ~LuaHandle(); LuaHandle(LuaHandle const& other); LuaHandle(LuaHandle&& other); LuaHandle& operator=(LuaHandle const& other); LuaHandle& operator=(LuaHandle&& other); LuaEnginePtr engine; int handleIndex = 0; }; // Not meant to be used directly, exposes a raw interface for wrapped C++ // functions to be wrapped with the least amount of overhead. Arguments are // passed non-const so that they can be moved into wrapped functions that // take values without copying. typedef Variant> LuaFunctionReturn; typedef function LuaWrappedFunction; } // Prints the lua value similar to lua's print function, except it makes an // attempt at printing tables. std::ostream& operator<<(std::ostream& os, LuaValue const& value); // Holds a reference to a LuaEngine and a value held internally inside the // registry of that engine. The lifetime of the LuaEngine will be extended // until all LuaReferences referencing it are destroyed. class LuaReference { public: LuaReference(LuaDetail::LuaHandle handle); LuaReference(LuaReference&&) = default; LuaReference& operator=(LuaReference&&) = default; LuaReference(LuaReference const&) = default; LuaReference& operator=(LuaReference const&) = default; bool operator==(LuaReference const& rhs) const; bool operator!=(LuaReference const& rhs) const; LuaEngine& engine() const; int handleIndex() const; private: LuaDetail::LuaHandle m_handle; }; class LuaString : public LuaReference { public: using LuaReference::LuaReference; char const* ptr() const; size_t length() const; String toString() const; StringView view() const; }; bool operator==(LuaString const& s1, LuaString const& s2); bool operator==(LuaString const& s1, char const* s2); bool operator==(LuaString const& s1, std::string const& s2); bool operator==(LuaString const& s1, String const& s2); bool operator==(char const* s1, LuaString const& s2); bool operator==(std::string const& s1, LuaString const& s2); bool operator==(String const& s1, LuaString const& s2); bool operator!=(LuaString const& s1, LuaString const& s2); bool operator!=(LuaString const& s1, char const* s2); bool operator!=(LuaString const& s1, std::string const& s2); bool operator!=(LuaString const& s1, String const& s2); bool operator!=(char const* s1, LuaString const& s2); bool operator!=(std::string const& s1, LuaString const& s2); bool operator!=(String const& s1, LuaString const& s2); class LuaTable : public LuaReference { public: using LuaReference::LuaReference; template T get(K key) const; template T get(char const* key) const; template void set(K key, T t) const; template void set(char const* key, T t) const; // Shorthand for get(path) != LuaNil template bool contains(K key) const; bool contains(char const* key) const; // Shorthand for setting to LuaNil template void remove(K key) const; void remove(char const* key) const; // Result of lua # operator LuaInt length() const; // If iteration function returns bool, returning false signals stopping. template void iterate(Function&& iterator) const; template void iterateWithSignature(Function&& func) const; Maybe getMetatable() const; void setMetatable(LuaTable const& table) const; template T rawGet(K key) const; template T rawGet(char const* key) const; template void rawSet(K key, T t) const; template void rawSet(char const* key, T t) const; LuaInt rawLength() const; }; class LuaFunction : public LuaReference { public: using LuaReference::LuaReference; template Ret invoke(Args const&... args) const; }; class LuaThread : public LuaReference { public: using LuaReference::LuaReference; enum class Status { Dead, Active, Error }; // Will return a value if the thread has yielded a value, and nothing if the // thread has finished execution template Maybe resume(Args const&... args) const; void pushFunction(LuaFunction const& func) const; Status status() const; }; // Keeping LuaReferences in LuaUserData will lead to circular references to // LuaEngine, in addition to circular references in Lua which the Lua // garbage collector can't collect. Don't put LuaReferences in LuaUserData. class LuaUserData : public LuaReference { public: using LuaReference::LuaReference; template bool is() const; template T& get() const; }; LuaValue const LuaNil = LuaValue(); class LuaCallbacks { public: template void registerCallback(String name, Function&& func); template void registerCallbackWithSignature(String name, Function&& func); LuaCallbacks& merge(LuaCallbacks const& callbacks); StringMap const& callbacks() const; private: StringMap m_callbacks; }; template class LuaMethods { public: template void registerMethod(String name, Function&& func); template void registerMethodWithSignature(String name, Function&& func); StringMap const& methods() const; private: StringMap m_methods; }; // A single execution context from a LuaEngine that manages a (mostly) distinct // lua environment. Each LuaContext's global environment is separate and one // LuaContext can (mostly) not affect any other. class LuaContext : protected LuaTable { public: typedef function RequireFunction; using LuaTable::LuaTable; using LuaTable::get; using LuaTable::set; using LuaTable::contains; using LuaTable::remove; using LuaTable::engine; using LuaTable::handleIndex; // Splits the path by '.' character, so can get / set values in tables inside // other tables. If any table in the path is not a table but is accessed as // one, instead returns LuaNil. template T getPath(String path) const; // Shorthand for getPath != LuaNil bool containsPath(String path) const; // Will create new tables if the key contains paths that are nil template void setPath(String path, T value); // Load the given code (either source or bytecode) into this context as a new // chunk. It is not necessary to provide the name again if given bytecode. void load(char const* contents, size_t size, char const* name = nullptr); void load(String const& contents, String const& name = String()); void load(ByteArray const& contents, String const& name = String()); // Evaluate a piece of lua code in this context, similar to the lua repl. // Can evaluate both expressions and statements. template T eval(String const& lua); // Override the built-in require function with the given function that takes // this LuaContext and the module name to load. void setRequireFunction(RequireFunction requireFunction); void setCallbacks(String const& tableName, LuaCallbacks const& callbacks) const; // For convenience, invokePath methods are equivalent to calling getPath(key) // to get a function, and then invoking it. template Ret invokePath(String const& key, Args const&... args) const; // For convenience, calls to LuaEngine conversion / create functions are // duplicated here. template LuaValue luaFrom(T&& t); template LuaValue luaFrom(T const& t); template Maybe luaMaybeTo(LuaValue&& v); template Maybe luaMaybeTo(LuaValue const& v); template T luaTo(LuaValue const& v); template T luaTo(LuaValue&& v); LuaString createString(String const& str); LuaString createString(char const* str); LuaTable createTable(); template LuaTable createTable(Container const& map); template LuaTable createArrayTable(Container const& array); template LuaFunction createFunction(Function&& func); template LuaFunction createFunctionWithSignature(Function&& func); template LuaUserData createUserData(T t); }; template struct LuaNullTermWrapper : T { LuaNullTermWrapper() : T() {} LuaNullTermWrapper(LuaNullTermWrapper const& nt) : T(nt) {} LuaNullTermWrapper(LuaNullTermWrapper&& nt) : T(std::move(nt)) {} LuaNullTermWrapper(T const& bt) : T(bt) {} LuaNullTermWrapper(T&& bt) : T(std::move(bt)) {} using T::T; LuaNullTermWrapper& operator=(LuaNullTermWrapper const& rhs) { T::operator=(rhs); return *this; } LuaNullTermWrapper& operator=(LuaNullTermWrapper&& rhs) { T::operator=(std::move(rhs)); return *this; } LuaNullTermWrapper& operator=(T&& other) { T::operator=(std::forward(other)); return *this; } }; class LuaNullEnforcer { public: LuaNullEnforcer(LuaEngine& engine); ~LuaNullEnforcer(); private: LuaEngine* m_engine; }; // Types that want to participate in automatic lua conversion should specialize // this template and provide static to and from methods on it. The method // signatures will be called like: // LuaValue from(LuaEngine& engine, T t); // Maybe to(LuaEngine& engine, LuaValue v); // The methods can also take 'T const&' or 'LuaValue const&' as parameters, and // the 'to' method can also return a bare T if conversion cannot fail. template struct LuaConverter; // UserData types that want to expose methods to lua should specialize this // template. template struct LuaUserDataMethods { static LuaMethods make(); }; // Convenience converter that simply converts to/from LuaUserData, can be // derived from by a declared converter. template struct LuaUserDataConverter { static LuaValue from(LuaEngine& engine, T t); static Maybe to(LuaEngine& engine, LuaValue const& v); }; struct LuaProfileEntry { // Source name of the chunk the function was defined in String source; // Line number in the chunk of the beginning of the function definition unsigned sourceLine; // Name of the function, if it can be determined Maybe name; // Scope of the function, if it can be determined Maybe nameScope; // Time taken within this function itself int64_t selfTime; // Total time taken within this function or sub functions int64_t totalTime; // Calls from this function HashMap, shared_ptr> calls; }; // This class represents one execution engine in lua, holding a single // lua_State. Multiple contexts can be created, and they will have separate // global environments and cannot affect each other. Individual LuaEngines / // LuaContexts are not thread safe, use one LuaEngine per thread. class LuaEngine : public RefCounter { public: // If 'safe' is true, then creates a lua engine with all builtin lua // functions that can affect the real world disabled. static LuaEnginePtr create(bool safe = true); ~LuaEngine(); LuaEngine(LuaEngine const&) = delete; LuaEngine(LuaEngine&&) = default; LuaEngine& operator=(LuaEngine const&) = delete; LuaEngine& operator=(LuaEngine&&) = default; // Set the instruction limit for computation sequences in the engine. During // any function invocation, thread resume, or code evaluation, an instruction // counter will be started. In the event that the instruction counter // becomes greater than the given limit, a LuaException will be thrown. The // count is only reset when the initial entry into LuaEngine is returned, // recursive entries into LuaEngine accumulate the same instruction counter. // 0 disables the instruction limit. void setInstructionLimit(uint64_t instructionLimit = 0); uint64_t instructionLimit() const; // If profiling is enabled, then every 'measureInterval' instructions, the // function call stack will be recorded, and a summary of function timing can // be printed using profileReport void setProfilingEnabled(bool profilingEnabled); bool profilingEnabled() const; // Print a summary of the profiling data gathered since profiling was last // enabled. List getProfile(); // If an instruction limit is set or profiling is neabled, this field // describes the resolution of instruction count measurement, and affects the // accuracy of profiling and the instruction count limit. Defaults to 1000 void setInstructionMeasureInterval(unsigned measureInterval = 1000); unsigned instructionMeasureInterval() const; // Sets the LuaEngine recursion limit, limiting the number of times a // LuaEngine call may directly or inderectly trigger a call back into the // LuaEngine, preventing a C++ stack overflow. 0 disables the limit. void setRecursionLimit(unsigned recursionLimit = 0); unsigned recursionLimit() const; // Compile a given script into bytecode. If name is given, then it will be // used as the internal name for the resulting chunk and will provide better // error messages. // // Unfortunately the only way to completely ensure that a single script will // execute in two separate contexts and truly be isolated is to compile the // script to bytecode and load once in each context as a separate chunk. ByteArray compile(char const* contents, size_t size, char const* name = nullptr); ByteArray compile(String const& contents, String const& name = String()); ByteArray compile(ByteArray const& contents, String const& name = String()); // Returns the debug info of the state. lua_Debug const& debugInfo(int level = 1, const char* what = "nSlu"); // Generic from/to lua conversion, calls template specialization of // LuaConverter for actual conversion. template LuaValue luaFrom(T&& t); template LuaValue luaFrom(T const& t); template Maybe luaMaybeTo(LuaValue&& v); template Maybe luaMaybeTo(LuaValue const& v); // Wraps luaMaybeTo, throws an exception if conversion fails. template T luaTo(LuaValue const& v); template T luaTo(LuaValue&& v); LuaString createString(String const& str); LuaString createString(char const* str); LuaTable createTable(int narr = 0, int nrec = 0); template LuaTable createTable(Container const& map); template LuaTable createArrayTable(Container const& array); // Creates a function and deduces the signature of the function using // FunctionTraits. As a convenience, the given function may optionally take // a LuaEngine& parameter as the first parameter, and if it does, when called // the function will get a reference to the calling LuaEngine. template LuaFunction createFunction(Function&& func); // If the function signature is not deducible using FunctionTraits, you can // specify the return and argument types manually using this createFunction // version. template LuaFunction createFunctionWithSignature(Function&& func); LuaFunction createWrappedFunction(LuaDetail::LuaWrappedFunction function); LuaFunction createRawFunction(lua_CFunction func); LuaFunction createFunctionFromSource(int handleIndex, char const* contents, size_t size, char const* name); LuaThread createThread(); template LuaUserData createUserData(T t); LuaContext createContext(); // Global environment changes only affect newly created contexts template T getGlobal(K key); template T getGlobal(char const* key); template void setGlobal(K key, T value); template void setGlobal(char const* key, T value); // Perform either a full or incremental garbage collection. void collectGarbage(Maybe steps = {}); // Stop / start automatic garbage collection void setAutoGarbageCollection(bool autoGarbageColleciton); // Tune the pause and step values of the lua garbage collector void tuneAutoGarbageCollection(float pause, float stepMultiplier); // Bytes in use by lua size_t memoryUsage() const; // Enforce null-terminated string conversion as long as the returned enforcer object is in scope. LuaNullEnforcer nullTerminate(); // Disables null-termination enforcement void setNullTerminated(bool nullTerminated); private: friend struct LuaDetail::LuaHandle; friend class LuaReference; friend class LuaString; friend class LuaTable; friend class LuaFunction; friend class LuaThread; friend class LuaUserData; friend class LuaContext; friend class LuaNullEnforcer; LuaEngine() = default; // Get the LuaEngine* out of the lua registry magic entry. Uses 1 stack // space, and does not call lua_checkstack. static LuaEngine* luaEnginePtr(lua_State* state); // Counts instructions when instruction limiting is enabled. static void countHook(lua_State* state, lua_Debug* ar); static void* allocate(void* userdata, void* ptr, size_t oldSize, size_t newSize); // Pops lua error from stack and throws LuaException void handleError(lua_State* state, int res); // lua_pcall with a better message handler that includes a traceback. int pcallWithTraceback(lua_State* state, int nargs, int nresults); // override for lua coroutine resume with traceback static int coresumeWithTraceback(lua_State* state); // propagates errors from one state to another, i.e. past thread boundaries // pops error off the top of the from stack and pushes onto the to stack static void propagateErrorWithTraceback(lua_State* from, lua_State* to); char const* stringPtr(int handleIndex); size_t stringLength(int handleIndex); String string(int handleIndex); StringView stringView(int handleIndex); LuaValue tableGet(bool raw, int handleIndex, LuaValue const& key); LuaValue tableGet(bool raw, int handleIndex, char const* key); void tableSet(bool raw, int handleIndex, LuaValue const& key, LuaValue const& value); void tableSet(bool raw, int handleIndex, char const* key, LuaValue const& value); LuaInt tableLength(bool raw, int handleIndex); void tableIterate(int handleIndex, function iterator); Maybe tableGetMetatable(int handleIndex); void tableSetMetatable(int handleIndex, LuaTable const& table); template LuaDetail::LuaFunctionReturn callFunction(int handleIndex, Args const&... args); template Maybe resumeThread(int handleIndex, Args const&... args); void threadPushFunction(int threadIndex, int functionIndex); LuaThread::Status threadStatus(int handleIndex); template void registerUserDataType(); template bool userDataIsType(int handleIndex); template T* getUserData(int handleIndex); void setContextRequire(int handleIndex, LuaContext::RequireFunction requireFunction); void contextLoad(int handleIndex, char const* contents, size_t size, char const* name); LuaDetail::LuaFunctionReturn contextEval(int handleIndex, String const& lua); LuaValue contextGetPath(int handleIndex, String path); void contextSetPath(int handleIndex, String path, LuaValue const& value); int popHandle(lua_State* state); void pushHandle(lua_State* state, int handleIndex); int copyHandle(int handleIndex); void destroyHandle(int handleIndex); int placeHandle(); void pushLuaValue(lua_State* state, LuaValue const& luaValue); LuaValue popLuaValue(lua_State* state); template size_t pushArgument(lua_State* state, T const& arg); template size_t pushArgument(lua_State* state, LuaVariadic const& args); size_t doPushArguments(lua_State*); template size_t doPushArguments(lua_State* state, First const& first, Rest const&... rest); template size_t pushArguments(lua_State* state, Args const&... args); void incrementRecursionLevel(); void decrementRecursionLevel(); void updateCountHook(); // The following fields exist to use their addresses as unique lightuserdata, // as is recommended by the lua docs. static int s_luaInstructionLimitExceptionKey; static int s_luaRecursionLimitExceptionKey; lua_State* m_state; int m_pcallTracebackMessageHandlerRegistryId; int m_scriptDefaultEnvRegistryId; int m_wrappedFunctionMetatableRegistryId; int m_requireFunctionMetatableRegistryId; HashMap m_registeredUserDataTypes; lua_State* m_handleThread; int m_handleStackSize; int m_handleStackMax; List m_handleFree; uint64_t m_instructionLimit; bool m_profilingEnabled; unsigned m_instructionMeasureInterval; uint64_t m_instructionCount; unsigned m_recursionLevel; unsigned m_recursionLimit; int m_nullTerminated; HashMap, shared_ptr> m_profileEntries; lua_Debug m_debugInfo; }; // Built in conversions template <> struct LuaConverter { static LuaValue from(LuaEngine&, bool v) { return v; } static Maybe to(LuaEngine&, LuaValue const& v) { if (auto b = v.ptr()) return *b; if (v == LuaNil) return false; return true; } }; template struct LuaIntConverter { static LuaValue from(LuaEngine&, T v) { return LuaInt(v); } static Maybe to(LuaEngine&, LuaValue const& v) { if (auto n = v.ptr()) return *n; if (auto n = v.ptr()) return *n; if (auto s = v.ptr()) { if (auto n = maybeLexicalCast(s->ptr())) return *n; if (auto n = maybeLexicalCast(s->ptr())) return *n; } return {}; } }; template <> struct LuaConverter : LuaIntConverter {}; template <> struct LuaConverter : LuaIntConverter {}; template <> struct LuaConverter : LuaIntConverter {}; template <> struct LuaConverter : LuaIntConverter {}; template <> struct LuaConverter : LuaIntConverter {}; template <> struct LuaConverter : LuaIntConverter {}; template <> struct LuaConverter : LuaIntConverter {}; template <> struct LuaConverter : LuaIntConverter {}; template <> struct LuaConverter : LuaIntConverter {}; template <> struct LuaConverter : LuaIntConverter {}; template struct LuaFloatConverter { static LuaValue from(LuaEngine&, T v) { return LuaFloat(v); } static Maybe to(LuaEngine&, LuaValue const& v) { if (auto n = v.ptr()) return *n; if (auto n = v.ptr()) return *n; if (auto s = v.ptr()) { if (auto n = maybeLexicalCast(s->ptr())) return *n; if (auto n = maybeLexicalCast(s->ptr())) return *n; } return {}; } }; template <> struct LuaConverter : LuaFloatConverter {}; template <> struct LuaConverter : LuaFloatConverter {}; template <> struct LuaConverter { static LuaValue from(LuaEngine& engine, String const& v) { return engine.createString(v); } static Maybe to(LuaEngine&, LuaValue const& v) { if (v.is()) return v.get().toString(); if (v.is()) return String(toString(v.get())); if (v.is()) return String(toString(v.get())); return {}; } }; template <> struct LuaConverter { static LuaValue from(LuaEngine& engine, std::string const& v) { return engine.createString(v.c_str()); } static Maybe to(LuaEngine& engine, LuaValue v) { return engine.luaTo(std::move(v)).takeUtf8(); } }; template <> struct LuaConverter { static LuaValue from(LuaEngine& engine, char const* v) { return engine.createString(v); } }; template struct LuaConverter { static LuaValue from(LuaEngine& engine, char const v[s]) { return engine.createString(v); } }; template <> struct LuaConverter { static LuaValue from(LuaEngine& engine, Directives const& v) { if (String const* ptr = v.stringPtr()) return engine.createString(*ptr); else return engine.createString(""); } }; template <> struct LuaConverter { static LuaValue from(LuaEngine&, LuaString v) { return LuaValue(std::move(v)); } static Maybe to(LuaEngine& engine, LuaValue v) { if (v.is()) return LuaString(std::move(v.get())); if (v.is()) return engine.createString(toString(v.get())); if (v.is()) return engine.createString(toString(v.get())); return {}; } }; template struct LuaValueConverter { static LuaValue from(LuaEngine&, T v) { return v; } static Maybe to(LuaEngine&, LuaValue v) { if (auto p = v.ptr()) { return std::move(*p); } return {}; } }; template <> struct LuaConverter : LuaValueConverter {}; template <> struct LuaConverter : LuaValueConverter {}; template <> struct LuaConverter : LuaValueConverter {}; template <> struct LuaConverter : LuaValueConverter {}; template <> struct LuaConverter { static LuaValue from(LuaEngine&, LuaValue v) { return v; } static LuaValue to(LuaEngine&, LuaValue v) { return v; } }; template struct LuaConverter> { static LuaValue from(LuaEngine& engine, Maybe const& v) { if (v) return engine.luaFrom(*v); else return LuaNil; } static LuaValue from(LuaEngine& engine, Maybe&& v) { if (v) return engine.luaFrom(v.take()); else return LuaNil; } static Maybe> to(LuaEngine& engine, LuaValue const& v) { if (v != LuaNil) { if (auto conv = engine.luaMaybeTo(v)) return conv; else return {}; } else { return Maybe(); } } static Maybe> to(LuaEngine& engine, LuaValue&& v) { if (v != LuaNil) { if (auto conv = engine.luaMaybeTo(std::move(v))) return conv; else return {}; } else { return Maybe(); } } }; template struct LuaMapConverter { static LuaValue from(LuaEngine& engine, T const& v) { return engine.createTable(v); } static Maybe to(LuaEngine& engine, LuaValue const& v) { auto table = v.ptr(); if (!table) return {}; T result; bool failed = false; table->iterate([&result, &failed, &engine](LuaValue key, LuaValue value) { auto contKey = engine.luaMaybeTo(std::move(key)); auto contValue = engine.luaMaybeTo(std::move(value)); if (!contKey || !contValue) { failed = true; return false; } result[contKey.take()] = contValue.take(); return true; }); if (failed) return {}; return result; } }; template struct LuaContainerConverter { static LuaValue from(LuaEngine& engine, T const& v) { return engine.createArrayTable(v); } static Maybe to(LuaEngine& engine, LuaValue const& v) { auto table = v.ptr(); if (!table) return {}; T result; bool failed = false; table->iterate([&result, &failed, &engine](LuaValue key, LuaValue value) { if (!key.is()) { failed = true; return false; } auto contVal = engine.luaMaybeTo(std::move(value)); if (!contVal) { failed = true; return false; } result.insert(result.end(), contVal.take()); return true; }); if (failed) return {}; return result; } }; template struct LuaConverter> : LuaContainerConverter> {}; template struct LuaConverter> : LuaContainerConverter> {}; template struct LuaConverter> : LuaContainerConverter> {}; template <> struct LuaConverter : LuaContainerConverter {}; template struct LuaConverter> : LuaContainerConverter> {}; template struct LuaConverter> : LuaContainerConverter> {}; template struct LuaConverter> : LuaMapConverter> {}; template struct LuaConverter> : LuaMapConverter> {}; template <> struct LuaConverter { static LuaValue from(LuaEngine& engine, Json const& v); static Maybe to(LuaEngine& engine, LuaValue const& v); }; template <> struct LuaConverter { static LuaValue from(LuaEngine& engine, JsonObject v); static Maybe to(LuaEngine& engine, LuaValue v); }; template <> struct LuaConverter { static LuaValue from(LuaEngine& engine, JsonArray v); static Maybe to(LuaEngine& engine, LuaValue v); }; namespace LuaDetail { inline LuaHandle::LuaHandle(LuaEnginePtr engine, int handleIndex) : engine(std::move(engine)), handleIndex(handleIndex) {} inline LuaHandle::~LuaHandle() { if (engine) engine->destroyHandle(handleIndex); } inline LuaHandle::LuaHandle(LuaHandle const& other) { engine = other.engine; if (engine) handleIndex = engine->copyHandle(other.handleIndex); } inline LuaHandle::LuaHandle(LuaHandle&& other) { engine = take(other.engine); handleIndex = take(other.handleIndex); } inline LuaHandle& LuaHandle::operator=(LuaHandle const& other) { if (engine) engine->destroyHandle(handleIndex); engine = other.engine; if (engine) handleIndex = engine->copyHandle(other.handleIndex); return *this; } inline LuaHandle& LuaHandle::operator=(LuaHandle&& other) { if (engine) engine->destroyHandle(handleIndex); engine = take(other.engine); handleIndex = take(other.handleIndex); return *this; } template struct FromFunctionReturn { static T convert(LuaEngine& engine, LuaFunctionReturn const& ret) { if (auto l = ret.ptr()) { return engine.luaTo(*l); } else if (auto vec = ret.ptr>()) { return engine.luaTo(vec->at(0)); } else { return engine.luaTo(LuaNil); } } }; template struct FromFunctionReturn> { static LuaVariadic convert(LuaEngine& engine, LuaFunctionReturn const& ret) { if (auto l = ret.ptr()) { return {engine.luaTo(*l)}; } else if (auto vec = ret.ptr>()) { LuaVariadic ret(vec->size()); for (size_t i = 0; i < vec->size(); ++i) ret[i] = engine.luaTo((*vec)[i]); return ret; } else { return {}; } } }; template struct FromFunctionReturn> { static LuaTupleReturn convert(LuaEngine& engine, LuaFunctionReturn const& ret) { if (auto l = ret.ptr()) { return doConvertSingle(engine, *l, typename GenIndexSequence<0, sizeof...(ArgRest)>::type()); } else if (auto vec = ret.ptr>()) { return doConvertMulti(engine, *vec, typename GenIndexSequence<0, sizeof...(ArgRest)>::type()); } else { return doConvertNone(engine, typename GenIndexSequence<0, sizeof...(ArgRest)>::type()); } } template static LuaTupleReturn doConvertSingle( LuaEngine& engine, LuaValue const& single, IndexSequence const&) { return LuaTupleReturn(engine.luaTo(single), engine.luaTo(LuaNil)...); } template static LuaTupleReturn doConvertMulti( LuaEngine& engine, LuaVariadic const& multi, IndexSequence const&) { return LuaTupleReturn( engine.luaTo(multi.at(0)), engine.luaTo(multi.get(Indexes + 1))...); } template static LuaTupleReturn doConvertNone(LuaEngine& engine, IndexSequence const&) { return LuaTupleReturn(engine.luaTo(LuaNil), engine.luaTo(LuaNil)...); } }; template LuaVariadic toVariadicReturn( LuaEngine& engine, LuaTupleReturn const& vals, IndexSequence const&) { return LuaVariadic{engine.luaFrom(get(vals))...}; } template LuaVariadic toWrappedReturn(LuaEngine& engine, LuaTupleReturn const& vals) { return toVariadicReturn(engine, vals, typename GenIndexSequence<0, sizeof...(Args)>::type()); } template LuaVariadic toWrappedReturn(LuaEngine& engine, LuaVariadic const& vals) { LuaVariadic ret(vals.size()); for (size_t i = 0; i < vals.size(); ++i) ret[i] = engine.luaFrom(vals[i]); return ret; } template LuaValue toWrappedReturn(LuaEngine& engine, T const& t) { return engine.luaFrom(t); } template struct ArgGet { static T get(LuaEngine& engine, size_t argc, LuaValue* argv, size_t index) { if (index < argc) return engine.luaTo(std::move(argv[index])); return engine.luaTo(LuaNil); } }; template struct ArgGet> { static LuaVariadic get(LuaEngine& engine, size_t argc, LuaValue* argv, size_t index) { if (index >= argc) return {}; LuaVariadic subargs(argc - index); for (size_t i = index; i < argc; ++i) subargs[i - index] = engine.luaTo(std::move(argv[i])); return subargs; } }; template struct FunctionWrapper { template static LuaWrappedFunction wrapIndexes(Function func, IndexSequence const&) { return [func = std::move(func)](LuaEngine& engine, size_t argc, LuaValue* argv) { return toWrappedReturn(engine, (Return const&)func(ArgGet::get(engine, argc, argv, Indexes)...)); }; } template static LuaWrappedFunction wrap(Function func) { return wrapIndexes(std::forward(func), typename GenIndexSequence<0, sizeof...(Args)>::type()); } }; template struct FunctionWrapper { template static LuaWrappedFunction wrapIndexes(Function func, IndexSequence const&) { return [func = std::move(func)](LuaEngine& engine, size_t argc, LuaValue* argv) { func(ArgGet::get(engine, argc, argv, Indexes)...); return LuaFunctionReturn(); }; } template static LuaWrappedFunction wrap(Function func) { return wrapIndexes(std::forward(func), typename GenIndexSequence<0, sizeof...(Args)>::type()); } }; template struct FunctionWrapper { template static LuaWrappedFunction wrapIndexes(Function func, IndexSequence const&) { return [func = std::move(func)](LuaEngine& engine, size_t argc, LuaValue* argv) { return toWrappedReturn(engine, (Return const&)func(engine, ArgGet::get(engine, argc, argv, Indexes)...)); }; } template static LuaWrappedFunction wrap(Function func) { return wrapIndexes(std::forward(func), typename GenIndexSequence<0, sizeof...(Args)>::type()); } }; template struct FunctionWrapper { template static LuaWrappedFunction wrapIndexes(Function func, IndexSequence const&) { return [func = std::move(func)](LuaEngine& engine, size_t argc, LuaValue* argv) { func(engine, ArgGet::get(engine, argc, argv, Indexes)...); return LuaFunctionReturn(); }; } template static LuaWrappedFunction wrap(Function func) { return wrapIndexes(std::forward(func), typename GenIndexSequence<0, sizeof...(Args)>::type()); } }; template LuaWrappedFunction wrapFunctionWithSignature(Function&& func) { return FunctionWrapper::type...>::wrap(std::forward(func)); } template LuaWrappedFunction wrapFunctionArgs(Function&& func, VariadicTypedef const&) { return wrapFunctionWithSignature(std::forward(func)); } template LuaWrappedFunction wrapFunction(Function&& func) { return wrapFunctionArgs::Return>( std::forward(func), typename FunctionTraits::Args()); } template struct MethodWrapper { template static LuaWrappedFunction wrapIndexes(Function func, IndexSequence const&) { return [func = std::move(func)](LuaEngine& engine, size_t argc, LuaValue* argv) mutable { if (argc == 0) throw LuaException("No object argument passed to wrapped method"); return toWrappedReturn(engine, (Return const&)func(argv[0].get().get(), ArgGet::get(engine, argc - 1, argv + 1, Indexes)...)); }; } template static LuaWrappedFunction wrap(Function&& func) { return wrapIndexes(std::forward(func), typename GenIndexSequence<0, sizeof...(Args)>::type()); } }; template struct MethodWrapper { template static LuaWrappedFunction wrapIndexes(Function func, IndexSequence const&) { return [func = std::move(func)](LuaEngine& engine, size_t argc, LuaValue* argv) { if (argc == 0) throw LuaException("No object argument passed to wrapped method"); func(argv[0].get().get(), ArgGet::get(engine, argc - 1, argv + 1, Indexes)...); return LuaFunctionReturn(); }; } template static LuaWrappedFunction wrap(Function func) { return wrapIndexes(std::forward(func), typename GenIndexSequence<0, sizeof...(Args)>::type()); } }; template struct MethodWrapper { template static LuaWrappedFunction wrapIndexes(Function func, IndexSequence const&) { return [func = std::move(func)](LuaEngine& engine, size_t argc, LuaValue* argv) { if (argc == 0) throw LuaException("No object argument passed to wrapped method"); return toWrappedReturn( engine, (Return const&)func(argv[0].get().get(), engine, ArgGet::get(engine, argc - 1, argv + 1, Indexes)...)); }; } template static LuaWrappedFunction wrap(Function func) { return wrapIndexes(std::forward(func), typename GenIndexSequence<0, sizeof...(Args)>::type()); } }; template struct MethodWrapper { template static LuaWrappedFunction wrapIndexes(Function func, IndexSequence const&) { return [func = std::move(func)](LuaEngine& engine, size_t argc, LuaValue* argv) { if (argc == 0) throw LuaException("No object argument passed to wrapped method"); func(argv[0].get().get(), engine, ArgGet::get(engine, argc - 1, argv + 1, Indexes)...); return LuaValue(); }; } template static LuaWrappedFunction wrap(Function func) { return wrapIndexes(std::forward(func), typename GenIndexSequence<0, sizeof...(Args)>::type()); } }; template LuaWrappedFunction wrapMethodWithSignature(Function&& func) { return MethodWrapper::type...>::wrap(std::forward(func)); } template LuaWrappedFunction wrapMethodArgs(Function&& func, VariadicTypedef const&) { return wrapMethodWithSignature(std::forward(func)); } template LuaWrappedFunction wrapMethod(Function&& func) { return wrapMethodArgs::Return>( std::forward(func), typename FunctionTraits::Args()); } template struct TableIteratorWrapper; template struct TableIteratorWrapper { template static function wrap(LuaEngine& engine, Function&& func) { return [&engine, func = std::move(func)](LuaValue key, LuaValue value) -> bool { return func(engine, engine.luaTo(std::move(key)), engine.luaTo(std::move(value))); }; } }; template struct TableIteratorWrapper { template static function wrap(LuaEngine& engine, Function&& func) { return [&engine, func = std::move(func)](LuaValue key, LuaValue value) -> bool { func(engine, engine.luaTo(std::move(key)), engine.luaTo(std::move(value))); return true; }; } }; template struct TableIteratorWrapper { template static function wrap(LuaEngine& engine, Function&& func) { return [&engine, func = std::move(func)](LuaValue key, LuaValue value) -> bool { return func(engine.luaTo(std::move(key)), engine.luaTo(std::move(value))); }; } }; template struct TableIteratorWrapper { template static function wrap(LuaEngine& engine, Function&& func) { return [&engine, func = std::move(func)](LuaValue key, LuaValue value) -> bool { func(engine.luaTo(std::move(key)), engine.luaTo(std::move(value))); return true; }; } }; template function wrapTableIteratorWithSignature(LuaEngine& engine, Function&& func) { return TableIteratorWrapper::type...>::wrap(engine, std::forward(func)); } template function wrapTableIteratorArgs( LuaEngine& engine, Function&& func, VariadicTypedef const&) { return wrapTableIteratorWithSignature(engine, std::forward(func)); } template function wrapTableIterator(LuaEngine& engine, Function&& func) { return wrapTableIteratorArgs::Return>( engine, std::forward(func), typename FunctionTraits::Args()); } // Like lua_setfield / lua_getfield but raw. void rawSetField(lua_State* state, int index, char const* key); void rawGetField(lua_State* state, int index, char const* key); // Shallow copies a lua table at the given index into the table at the target // index. void shallowCopy(lua_State* state, int sourceIndex, int targetIndex); LuaTable insertJsonMetatable(LuaEngine& engine, LuaTable const& table, Json::Type type); // Creates a custom lua table from a JsonArray or JsonObject that has // slightly different behavior than a standard lua table. The table // remembers nil entries, as well as whether it was initially constructed // from a JsonArray or JsonObject as a hint on how to convert it back into a // Json. The custom containers are meant to act nearly identical to standard // lua tables, so iterating over the table with pairs or ipairs works exactly // like a standard lua table, so will skip over nil entries and in the case // of ipairs, stop at the first nil entry. LuaTable jsonContainerToTable(LuaEngine& engine, Json const& container); // popJsonContainer must be called with a lua table on the top of the stack. // Uses the table contents, as well as any hint entries if the table was // created originally from a Json, to determine whether a JsonArray or // JsonObject is more appropriate. Maybe tableToJsonContainer(LuaTable const& t); // Special lua functions to operate on our custom jarray / jobject container // types. Should always do some "sensible" action if given a regular lua // table instead of a custom json container one. // Create a JsonList container table Json jarrayCreate(); // Create a JsonMap container table Json jobjectCreate(); // Adds the Json array metatable to a Lua table or creates one. LuaTable jarray(LuaEngine& engine, Maybe table); // Adds the Json object metatable to a Lua table or creates one. LuaTable jobject(LuaEngine& engine, Maybe table); // *Really* remove an entry from a JsonList or JsonMap container table, // including removing it from the __nils table. If the given table is not a // special container table, is equivalent to setting the key entry to nil. void jcontRemove(LuaTable const& t, LuaValue const& key); // Returns the element count of the lua table argument, or, in the case of a // special JsonList container table, returns the "true" element count // including any nil entries. size_t jcontSize(LuaTable const& t); // Resize the given lua table by removing any indexed entries greater than the // target size, and in the case of a special JsonList container table, pads // to the end of the new size with nil entries. void jcontResize(LuaTable const& t, size_t size); // Coerces a values (strings, floats, ints) into an integer, but fails if the // number looks fractional (does not parse as int, float is not an exact // integer) Maybe asInteger(LuaValue const& v); } template LuaVariadic::type::value_type> luaUnpack(Container&& c) { LuaVariadic::type::value_type> ret; if (std::is_rvalue_reference::value) { for (auto& e : c) ret.append(std::move(e)); } else { for (auto const& e : c) ret.append(e); } return ret; } template LuaTupleReturn::LuaTupleReturn(Types const&... args) : Base(args...) {} template template LuaTupleReturn::LuaTupleReturn(UTypes&&... args) : Base(std::move(args)...) {} template template LuaTupleReturn::LuaTupleReturn(UTypes const&... args) : Base(args...) {} template LuaTupleReturn::LuaTupleReturn(LuaTupleReturn const& rhs) : Base(rhs) {} template LuaTupleReturn::LuaTupleReturn(LuaTupleReturn&& rhs) : Base(std::move(rhs)) {} template template LuaTupleReturn::LuaTupleReturn(LuaTupleReturn const& rhs) : Base(rhs) {} template template LuaTupleReturn::LuaTupleReturn(LuaTupleReturn&& rhs) : Base(std::move(rhs)) {} template LuaTupleReturn& LuaTupleReturn::operator=(LuaTupleReturn const& rhs) { Base::operator=(rhs); return *this; } template LuaTupleReturn& LuaTupleReturn::operator=(LuaTupleReturn&& rhs) { Base::operator=(std::move(rhs)); return *this; } template template LuaTupleReturn& LuaTupleReturn::operator=(LuaTupleReturn const& rhs) { Base::operator=((tuple const&)rhs); return *this; } template template LuaTupleReturn& LuaTupleReturn::operator=(LuaTupleReturn&& rhs) { Base::operator=((tuple && )std::move(rhs)); return *this; } template LuaTupleReturn luaTie(Types&... args) { return LuaTupleReturn(args...); } template LuaTupleReturn::type...> luaTupleReturn(Types&&... args) { return LuaTupleReturn::type...>(std::forward(args)...); } inline LuaReference::LuaReference(LuaDetail::LuaHandle handle) : m_handle(std::move(handle)) {} inline bool LuaReference::operator==(LuaReference const& rhs) const { return tie(m_handle.engine, m_handle.handleIndex) == tie(rhs.m_handle.engine, rhs.m_handle.handleIndex); } inline bool LuaReference::operator!=(LuaReference const& rhs) const { return tie(m_handle.engine, m_handle.handleIndex) != tie(rhs.m_handle.engine, rhs.m_handle.handleIndex); } inline LuaEngine& LuaReference::engine() const { return *m_handle.engine; } inline int LuaReference::handleIndex() const { return m_handle.handleIndex; } inline char const* LuaString::ptr() const { return engine().stringPtr(handleIndex()); } inline size_t LuaString::length() const { return engine().stringLength(handleIndex()); } inline String LuaString::toString() const { return engine().string(handleIndex()); } inline StringView LuaString::view() const { return engine().stringView(handleIndex()); } inline bool operator==(LuaString const& s1, LuaString const& s2) { return s1.view() == s2.view(); } inline bool operator==(LuaString const& s1, char const* s2) { return s1.view() == s2; } inline bool operator==(LuaString const& s1, std::string const& s2) { return s1.view() == s2; } inline bool operator==(LuaString const& s1, String const& s2) { return s1.view() == s2; } inline bool operator==(char const* s1, LuaString const& s2) { return s2.view() == s1; } inline bool operator==(std::string const& s1, LuaString const& s2) { return s2.view() == s1; } inline bool operator==(String const& s1, LuaString const& s2) { return s2.view() == s1; } inline bool operator!=(LuaString const& s1, LuaString const& s2) { return !(s1 == s2); } inline bool operator!=(LuaString const& s1, char const* s2) { return !(s1 == s2); } inline bool operator!=(LuaString const& s1, std::string const& s2) { return !(s1 == s2); } inline bool operator!=(LuaString const& s1, String const& s2) { return !(s1 == s2); } inline bool operator!=(char const* s1, LuaString const& s2) { return !(s1 == s2); } inline bool operator!=(std::string const& s1, LuaString const& s2) { return !(s1 == s2); } inline bool operator!=(String const& s1, LuaString const& s2) { return !(s1 == s2); } template T LuaTable::get(K key) const { return engine().luaTo(engine().tableGet(false, handleIndex(), engine().luaFrom(std::move(key)))); } template T LuaTable::get(char const* key) const { return engine().luaTo(engine().tableGet(false, handleIndex(), key)); } template void LuaTable::set(K key, T value) const { engine().tableSet(false, handleIndex(), engine().luaFrom(std::move(key)), engine().luaFrom(std::move(value))); } template void LuaTable::set(char const* key, T value) const { engine().tableSet(false, handleIndex(), key, engine().luaFrom(std::move(value))); } template bool LuaTable::contains(K key) const { return engine().tableGet(false, handleIndex(), engine().luaFrom(std::move(key))) != LuaNil; } template void LuaTable::remove(K key) const { engine().tableSet(false, handleIndex(), engine().luaFrom(key), LuaValue()); } template void LuaTable::iterate(Function&& function) const { return engine().tableIterate(handleIndex(), LuaDetail::wrapTableIterator(engine(), std::forward(function))); } template void LuaTable::iterateWithSignature(Function&& func) const { return engine().tableIterate(handleIndex(), LuaDetail::wrapTableIteratorWithSignature(engine(), std::forward(func))); } template T LuaTable::rawGet(K key) const { return engine().luaTo(engine().tableGet(true, handleIndex(), engine().luaFrom(key))); } template T LuaTable::rawGet(char const* key) const { return engine().luaTo(engine().tableGet(true, handleIndex(), key)); } template void LuaTable::rawSet(K key, T value) const { engine().tableSet(true, handleIndex(), engine().luaFrom(key), engine().luaFrom(value)); } template void LuaTable::rawSet(char const* key, T value) const { engine().tableSet(true, handleIndex(), engine().luaFrom(key), engine().luaFrom(value)); } template Ret LuaFunction::invoke(Args const&... args) const { return LuaDetail::FromFunctionReturn::convert(engine(), engine().callFunction(handleIndex(), args...)); } template Maybe LuaThread::resume(Args const&... args) const { auto res = engine().resumeThread(handleIndex(), args...); if (!res) return {}; return LuaDetail::FromFunctionReturn::convert(engine(), res.take()); } inline void LuaThread::pushFunction(LuaFunction const& func) const { engine().threadPushFunction(handleIndex(), func.handleIndex()); } inline LuaThread::Status LuaThread::status() const { return engine().threadStatus(handleIndex()); } template bool LuaUserData::is() const { return engine().userDataIsType(handleIndex()); } template T& LuaUserData::get() const { return *engine().getUserData(handleIndex()); } template void LuaCallbacks::registerCallback(String name, Function&& func) { if (!m_callbacks.insert(name, LuaDetail::wrapFunction(std::forward(func))).second) throw LuaException::format("Lua callback '{}' was registered twice", name); } template void LuaCallbacks::registerCallbackWithSignature(String name, Function&& func) { if (!m_callbacks.insert(name, LuaDetail::wrapFunctionWithSignature(std::forward(func))).second) throw LuaException::format("Lua callback '{}' was registered twice", name); } template template void LuaMethods::registerMethod(String name, Function&& func) { if (!m_methods.insert(name, LuaDetail::wrapMethod(std::forward(std::move(func)))).second) throw LuaException::format("Lua method '{}' was registered twice", name); } template template void LuaMethods::registerMethodWithSignature(String name, Function&& func) { if (!m_methods.insert(name, LuaDetail::wrapMethodWithSignature(std::forward(std::move(func)))) .second) throw LuaException::format("Lua method '{}' was registered twice", name); } template StringMap const& LuaMethods::methods() const { return m_methods; } template T LuaContext::getPath(String path) const { return engine().luaTo(engine().contextGetPath(handleIndex(), std::move(path))); } template void LuaContext::setPath(String key, T value) { engine().contextSetPath(handleIndex(), std::move(key), engine().luaFrom(std::move(value))); } template Ret LuaContext::eval(String const& lua) { return LuaDetail::FromFunctionReturn::convert(engine(), engine().contextEval(handleIndex(), lua)); } template Ret LuaContext::invokePath(String const& key, Args const&... args) const { auto p = getPath(key); if (auto f = p.ptr()) return f->invoke(args...); throw LuaException::format("invokePath called on path '{}' which is not function type", key); } template LuaValue LuaContext::luaFrom(T&& t) { return engine().luaFrom(std::forward(t)); } template LuaValue LuaContext::luaFrom(T const& t) { return engine().luaFrom(t); } template Maybe LuaContext::luaMaybeTo(LuaValue&& v) { return engine().luaFrom(std::move(v)); } template Maybe LuaContext::luaMaybeTo(LuaValue const& v) { return engine().luaFrom(v); } template T LuaContext::luaTo(LuaValue&& v) { return engine().luaTo(std::move(v)); } template T LuaContext::luaTo(LuaValue const& v) { return engine().luaTo(v); } template LuaTable LuaContext::createTable(Container const& map) { return engine().createTable(map); } template LuaTable LuaContext::createArrayTable(Container const& array) { return engine().createArrayTable(array); } template LuaFunction LuaContext::createFunction(Function&& func) { return engine().createFunction(std::forward(func)); } template LuaFunction LuaContext::createFunctionWithSignature(Function&& func) { return engine().createFunctionWithSignature(std::forward(func)); } template LuaUserData LuaContext::createUserData(T t) { return engine().createUserData(std::move(t)); } template LuaMethods LuaUserDataMethods::make() { return LuaMethods(); } template LuaValue LuaUserDataConverter::from(LuaEngine& engine, T t) { return engine.createUserData(std::move(t)); } template Maybe LuaUserDataConverter::to(LuaEngine&, LuaValue const& v) { if (auto ud = v.ptr()) { if (ud->is()) return ud->get(); } return {}; } template LuaValue LuaEngine::luaFrom(T&& t) { return LuaConverter::type>::from(*this, std::forward(t)); } template LuaValue LuaEngine::luaFrom(T const& t) { return LuaConverter::from(*this, t); } template Maybe LuaEngine::luaMaybeTo(LuaValue&& v) { return LuaConverter::to(*this, std::move(v)); } template Maybe LuaEngine::luaMaybeTo(LuaValue const& v) { return LuaConverter::to(*this, v); } template T LuaEngine::luaTo(LuaValue&& v) { if (auto res = luaMaybeTo(std::move(v))) return res.take(); throw LuaConversionException::format("Error converting LuaValue to type '{}'", typeid(T).name()); } template T LuaEngine::luaTo(LuaValue const& v) { if (auto res = luaMaybeTo(v)) return res.take(); throw LuaConversionException::format("Error converting LuaValue to type '{}'", typeid(T).name()); } template LuaTable LuaEngine::createTable(Container const& map) { auto table = createTable(0, map.size()); for (auto const& p : map) table.set(p.first, p.second); return table; } template LuaTable LuaEngine::createArrayTable(Container const& array) { auto table = createTable(array.size(), 0); int i = 1; for (auto const& elem : array) { table.set(LuaInt(i), elem); ++i; } return table; } template LuaFunction LuaEngine::createFunction(Function&& func) { return createWrappedFunction(LuaDetail::wrapFunction(std::forward(func))); } template LuaFunction LuaEngine::createFunctionWithSignature(Function&& func) { return createWrappedFunction(LuaDetail::wrapFunctionWithSignature(std::forward(func))); } template LuaDetail::LuaFunctionReturn LuaEngine::callFunction(int handleIndex, Args const&... args) { lua_checkstack(m_state, 1); int stackSize = lua_gettop(m_state); pushHandle(m_state, handleIndex); size_t argSize = pushArguments(m_state, args...); incrementRecursionLevel(); int res = pcallWithTraceback(m_state, argSize, LUA_MULTRET); decrementRecursionLevel(); handleError(m_state, res); int returnValues = lua_gettop(m_state) - stackSize; if (returnValues == 0) { return LuaDetail::LuaFunctionReturn(); } else if (returnValues == 1) { return popLuaValue(m_state); } else { LuaVariadic ret(returnValues); for (int i = returnValues - 1; i >= 0; --i) ret[i] = popLuaValue(m_state); return ret; } } template Maybe LuaEngine::resumeThread(int handleIndex, Args const&... args) { lua_checkstack(m_state, 1); pushHandle(m_state, handleIndex); lua_State* threadState = lua_tothread(m_state, -1); lua_pop(m_state, 1); if (lua_status(threadState) != LUA_YIELD && lua_gettop(threadState) == 0) { throw LuaException("cannot resume a dead or errored thread"); } size_t argSize = pushArguments(threadState, args...); incrementRecursionLevel(); int res = lua_resume(threadState, nullptr, argSize); decrementRecursionLevel(); if (res != LUA_OK && res != LUA_YIELD) { propagateErrorWithTraceback(threadState, m_state); handleError(m_state, res); } int returnValues = lua_gettop(threadState); if (returnValues == 0) { return LuaDetail::LuaFunctionReturn(); } else if (returnValues == 1) { return LuaDetail::LuaFunctionReturn(popLuaValue(threadState)); } else { LuaVariadic ret(returnValues); for (int i = returnValues - 1; i >= 0; --i) ret[i] = popLuaValue(threadState); return LuaDetail::LuaFunctionReturn(std::move(ret)); } } template void LuaEngine::registerUserDataType() { if (m_registeredUserDataTypes.contains(typeid(T))) return; lua_checkstack(m_state, 2); lua_newtable(m_state); // Set the __index on the metatable to itself lua_pushvalue(m_state, -1); LuaDetail::rawSetField(m_state, -2, "__index"); lua_pushboolean(m_state, 0); LuaDetail::rawSetField(m_state, -2, "__metatable"); // protect metatable // Set the __gc function to the userdata destructor auto gcFunction = [](lua_State* state) { T& t = *(T*)(lua_touserdata(state, 1)); t.~T(); return 0; }; lua_pushcfunction(m_state, gcFunction); LuaDetail::rawSetField(m_state, -2, "__gc"); auto methods = LuaUserDataMethods::make(); for (auto& p : methods.methods()) { pushLuaValue(m_state, createWrappedFunction(p.second)); LuaDetail::rawSetField(m_state, -2, p.first.utf8Ptr()); } m_registeredUserDataTypes.add(typeid(T), luaL_ref(m_state, LUA_REGISTRYINDEX)); } template LuaUserData LuaEngine::createUserData(T t) { registerUserDataType(); int typeMetatable = m_registeredUserDataTypes.get(typeid(T)); lua_checkstack(m_state, 2); new (lua_newuserdata(m_state, sizeof(T))) T(std::move(t)); lua_rawgeti(m_state, LUA_REGISTRYINDEX, typeMetatable); lua_setmetatable(m_state, -2); return LuaUserData(LuaDetail::LuaHandle(RefPtr(this), popHandle(m_state))); } template T LuaEngine::getGlobal(K key) { lua_checkstack(m_state, 1); lua_rawgeti(m_state, LUA_REGISTRYINDEX, m_scriptDefaultEnvRegistryId); pushLuaValue(m_state, luaFrom(std::move(key))); lua_rawget(m_state, -2); LuaValue v = popLuaValue(m_state); lua_pop(m_state, 1); return luaTo(v); } template T LuaEngine::getGlobal(char const* key) { lua_checkstack(m_state, 1); lua_rawgeti(m_state, LUA_REGISTRYINDEX, m_scriptDefaultEnvRegistryId); lua_getfield(m_state, -1, key); LuaValue v = popLuaValue(m_state); lua_pop(m_state, 1); return luaTo(v); } template void LuaEngine::setGlobal(K key, T value) { lua_checkstack(m_state, 1); lua_rawgeti(m_state, LUA_REGISTRYINDEX, m_scriptDefaultEnvRegistryId); pushLuaValue(m_state, luaFrom(std::move(key))); pushLuaValue(m_state, luaFrom(std::move(value))); lua_rawset(m_state, -3); lua_pop(m_state, 1); } template void LuaEngine::setGlobal(char const* key, T value) { lua_checkstack(m_state, 1); lua_rawgeti(m_state, LUA_REGISTRYINDEX, m_scriptDefaultEnvRegistryId); pushLuaValue(m_state, value); lua_setfield(m_state, -2, key); lua_pop(m_state, 1); } template bool LuaEngine::userDataIsType(int handleIndex) { int typeRef = m_registeredUserDataTypes.value(typeid(T), LUA_NOREF); if (typeRef == LUA_NOREF) return false; lua_checkstack(m_state, 3); pushHandle(m_state, handleIndex); if (lua_getmetatable(m_state, -1) == 0) { lua_pop(m_state, 1); throw LuaException("Userdata missing metatable in userDataIsType"); } lua_rawgeti(m_state, LUA_REGISTRYINDEX, typeRef); bool typesEqual = lua_rawequal(m_state, -1, -2); lua_pop(m_state, 3); return typesEqual; } template T* LuaEngine::getUserData(int handleIndex) { int typeRef = m_registeredUserDataTypes.value(typeid(T), LUA_NOREF); if (typeRef == LUA_NOREF) throw LuaException::format("Cannot convert userdata type of {}, not registered", typeid(T).name()); lua_checkstack(m_state, 3); pushHandle(m_state, handleIndex); T* userdata = (T*)lua_touserdata(m_state, -1); if (lua_getmetatable(m_state, -1) == 0) { lua_pop(m_state, 1); throw LuaException("Cannot get userdata from lua type, no metatable found"); } lua_rawgeti(m_state, LUA_REGISTRYINDEX, typeRef); if (!lua_rawequal(m_state, -1, -2)) { lua_pop(m_state, 3); throw LuaException::format("Improper conversion from userdata to type {}", typeid(T).name()); } lua_pop(m_state, 3); return userdata; } inline void LuaEngine::destroyHandle(int handleIndex) { // We don't bother setting the entry in the handle stack to nil, we just wait // for it to be reused and overwritten. We could provide a way to // periodically ensure that the entire free list is niled out if this becomes // a memory issue? m_handleFree.append(handleIndex); } template size_t LuaEngine::pushArgument(lua_State* state, T const& arg) { pushLuaValue(state, luaFrom(arg)); return 1; } template size_t LuaEngine::pushArgument(lua_State* state, LuaVariadic const& args) { // If the argument list was empty then we've checked one extra space on the // stack, oh well. if (args.empty()) return 0; // top-level pushArguments does a stack check of the total size of the // argument list, for variadic arguments, it could be more than one // argument so check the stack for the arguments in the variadic list minus // one. lua_checkstack(state, args.size() - 1); for (size_t i = 0; i < args.size(); ++i) pushLuaValue(state, luaFrom(args[i])); return args.size(); } inline size_t LuaEngine::doPushArguments(lua_State*) { return 0; } template size_t LuaEngine::doPushArguments(lua_State* state, First const& first, Rest const&... rest) { size_t s = pushArgument(state, first); return s + doPushArguments(state, rest...); } template size_t LuaEngine::pushArguments(lua_State* state, Args const&... args) { lua_checkstack(state, sizeof...(args)); return doPushArguments(state, args...); } } template <> struct fmt::formatter : ostream_formatter {};