diff options
author | Kae <80987908+Novaenia@users.noreply.github.com> | 2023-06-20 14:33:09 +1000 |
---|---|---|
committer | Kae <80987908+Novaenia@users.noreply.github.com> | 2023-06-20 14:33:09 +1000 |
commit | 6352e8e3196f78388b6c771073f9e03eaa612673 (patch) | |
tree | e23772f79a7fbc41bc9108951e9e136857484bf4 /source/core/StarLua.hpp | |
parent | 6741a057e5639280d85d0f88ba26f000baa58f61 (diff) |
everything everywhere
all at once
Diffstat (limited to 'source/core/StarLua.hpp')
-rw-r--r-- | source/core/StarLua.hpp | 2163 |
1 files changed, 2163 insertions, 0 deletions
diff --git a/source/core/StarLua.hpp b/source/core/StarLua.hpp new file mode 100644 index 0000000..1c34cc4 --- /dev/null +++ b/source/core/StarLua.hpp @@ -0,0 +1,2163 @@ +#ifndef STAR_LUA_HPP +#define STAR_LUA_HPP + +#include <typeindex> +#include <type_traits> +#include <lua.hpp> + +#include "StarLexicalCast.hpp" +#include "StarString.hpp" +#include "StarJson.hpp" +#include "StarRefPtr.hpp" + +namespace Star { + +class LuaEngine; +typedef RefPtr<LuaEngine> 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 '<eof>' 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<LuaNilType, LuaBoolean, LuaInt, LuaFloat, LuaString, LuaTable, LuaFunction, LuaThread, LuaUserData> 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 <typename T> +class LuaVariadic : public List<T> { +public: + using List<T>::List; +}; + +// Unpack a container and apply each of the arguments separately to a lua +// function, similar to lua's unpack. +template <typename Container> +LuaVariadic<typename std::decay<Container>::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 <typename... Types> +class LuaTupleReturn : public tuple<Types...> { +public: + typedef tuple<Types...> Base; + + explicit LuaTupleReturn(Types const&... args); + template <typename... UTypes> + explicit LuaTupleReturn(UTypes&&... args); + template <typename... UTypes> + explicit LuaTupleReturn(UTypes const&... args); + LuaTupleReturn(LuaTupleReturn const& rhs); + LuaTupleReturn(LuaTupleReturn&& rhs); + template <typename... UTypes> + LuaTupleReturn(LuaTupleReturn<UTypes...> const& rhs); + template <typename... UTypes> + LuaTupleReturn(LuaTupleReturn<UTypes...>&& rhs); + + LuaTupleReturn& operator=(LuaTupleReturn const& rhs); + LuaTupleReturn& operator=(LuaTupleReturn&& rhs); + template <typename... UTypes> + LuaTupleReturn& operator=(LuaTupleReturn<UTypes...> const& rhs); + template <typename... UTypes> + LuaTupleReturn& operator=(LuaTupleReturn<UTypes...>&& rhs); +}; + +// std::tie for LuaTupleReturn +template <typename... Types> +LuaTupleReturn<Types&...> luaTie(Types&... args); + +// Constructs a LuaTupleReturn from the given arguments similar to make_tuple +template <typename... Types> +LuaTupleReturn<typename std::decay<Types>::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; + }; + + // 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<LuaValue, LuaVariadic<LuaValue>> LuaFunctionReturn; + typedef function<LuaFunctionReturn(LuaEngine&, size_t argc, LuaValue* argv)> 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; +}; + +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 <typename T = LuaValue, typename K> + T get(K key) const; + template <typename T = LuaValue> + T get(char const* key) const; + + template <typename T, typename K> + void set(K key, T t) const; + template <typename T> + void set(char const* key, T t) const; + + // Shorthand for get(path) != LuaNil + template <typename K> + bool contains(K key) const; + bool contains(char const* key) const; + + // Shorthand for setting to LuaNil + template <typename K> + 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 <typename Function> + void iterate(Function&& iterator) const; + + template <typename Return, typename... Args, typename Function> + void iterateWithSignature(Function&& func) const; + + Maybe<LuaTable> getMetatable() const; + void setMetatable(LuaTable const& table) const; + + template <typename T = LuaValue, typename K> + T rawGet(K key) const; + template <typename T = LuaValue> + T rawGet(char const* key) const; + + template <typename T, typename K> + void rawSet(K key, T t) const; + template <typename T> + void rawSet(char const* key, T t) const; + + LuaInt rawLength() const; +}; + +class LuaFunction : public LuaReference { +public: + using LuaReference::LuaReference; + + template <typename Ret = LuaValue, typename... Args> + 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 <typename Ret = LuaValue, typename... Args> + Maybe<Ret> 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 <typename T> + bool is() const; + + template <typename T> + T& get() const; +}; + +LuaValue const LuaNil = LuaValue(); + +class LuaCallbacks { +public: + template <typename Function> + void registerCallback(String name, Function&& func); + + template <typename Return, typename... Args, typename Function> + void registerCallbackWithSignature(String name, Function&& func); + + LuaCallbacks& merge(LuaCallbacks const& callbacks); + + StringMap<LuaDetail::LuaWrappedFunction> const& callbacks() const; + +private: + StringMap<LuaDetail::LuaWrappedFunction> m_callbacks; +}; + +template <typename T> +class LuaMethods { +public: + template <typename Function> + void registerMethod(String name, Function&& func); + + template <typename Return, typename... Args, typename Function> + void registerMethodWithSignature(String name, Function&& func); + + StringMap<LuaDetail::LuaWrappedFunction> const& methods() const; + +private: + StringMap<LuaDetail::LuaWrappedFunction> 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<void(LuaContext&, LuaString const&)> RequireFunction; + + using LuaTable::LuaTable; + + using LuaTable::get; + using LuaTable::set; + using LuaTable::contains; + using LuaTable::remove; + using LuaTable::engine; + + // 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 <typename T = LuaValue> + 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 <typename T> + 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 <typename T = LuaValue> + 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 <typename Ret = LuaValue, typename... Args> + Ret invokePath(String const& key, Args const&... args) const; + + // For convenience, calls to LuaEngine conversion / create functions are + // duplicated here. + + template <typename T> + LuaValue luaFrom(T&& t); + template <typename T> + LuaValue luaFrom(T const& t); + template <typename T> + Maybe<T> luaMaybeTo(LuaValue&& v); + template <typename T> + Maybe<T> luaMaybeTo(LuaValue const& v); + template <typename T> + T luaTo(LuaValue const& v); + template <typename T> + T luaTo(LuaValue&& v); + + LuaString createString(String const& str); + LuaString createString(char const* str); + + LuaTable createTable(); + + template <typename Container> + LuaTable createTable(Container const& map); + + template <typename Container> + LuaTable createArrayTable(Container const& array); + + template <typename Function> + LuaFunction createFunction(Function&& func); + + template <typename Return, typename... Args, typename Function> + LuaFunction createFunctionWithSignature(Function&& func); + + template <typename T> + LuaUserData createUserData(T t); +}; + +// 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<T> 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 <typename T> +struct LuaConverter; + +// UserData types that want to expose methods to lua should specialize this +// template. +template <typename T> +struct LuaUserDataMethods { + static LuaMethods<T> make(); +}; + +// Convenience converter that simply converts to/from LuaUserData, can be +// derived from by a declared converter. +template <typename T> +struct LuaUserDataConverter { + static LuaValue from(LuaEngine& engine, T t); + static Maybe<T> 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<String> name; + // Scope of the function, if it can be determined + Maybe<String> 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<tuple<String, unsigned>, shared_ptr<LuaProfileEntry>> 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<LuaProfileEntry> 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()); + + // Generic from/to lua conversion, calls template specialization of + // LuaConverter for actual conversion. + template <typename T> + LuaValue luaFrom(T&& t); + template <typename T> + LuaValue luaFrom(T const& t); + template <typename T> + Maybe<T> luaMaybeTo(LuaValue&& v); + template <typename T> + Maybe<T> luaMaybeTo(LuaValue const& v); + + // Wraps luaMaybeTo, throws an exception if conversion fails. + template <typename T> + T luaTo(LuaValue const& v); + template <typename T> + T luaTo(LuaValue&& v); + + LuaString createString(String const& str); + LuaString createString(char const* str); + + LuaTable createTable(); + + template <typename Container> + LuaTable createTable(Container const& map); + + template <typename Container> + 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 <typename Function> + 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 <typename Return, typename... Args, typename Function> + LuaFunction createFunctionWithSignature(Function&& func); + + LuaThread createThread(); + + template <typename T> + LuaUserData createUserData(T t); + + LuaContext createContext(); + + // Global environment changes only affect newly created contexts + + template <typename T = LuaValue, typename K> + T getGlobal(K key); + template <typename T = LuaValue> + T getGlobal(char const* key); + + template <typename T, typename K> + void setGlobal(K key, T value); + + template <typename T> + void setGlobal(char const* key, T value); + + // Perform either a full or incremental garbage collection. + void collectGarbage(Maybe<unsigned> 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; + +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; + + 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); + + 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<bool(LuaValue, LuaValue)> iterator); + + Maybe<LuaTable> tableGetMetatable(int handleIndex); + void tableSetMetatable(int handleIndex, LuaTable const& table); + + template <typename... Args> + LuaDetail::LuaFunctionReturn callFunction(int handleIndex, Args const&... args); + + template <typename... Args> + Maybe<LuaDetail::LuaFunctionReturn> resumeThread(int handleIndex, Args const&... args); + void threadPushFunction(int threadIndex, int functionIndex); + LuaThread::Status threadStatus(int handleIndex); + + template <typename T> + void registerUserDataType(); + + template <typename T> + bool userDataIsType(int handleIndex); + + template <typename T> + 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(); + + LuaFunction createWrappedFunction(LuaDetail::LuaWrappedFunction function); + LuaFunction createRawFunction(lua_CFunction func); + + void pushLuaValue(lua_State* state, LuaValue const& luaValue); + LuaValue popLuaValue(lua_State* state); + + template <typename T> + size_t pushArgument(lua_State* state, T const& arg); + + template <typename T> + size_t pushArgument(lua_State* state, LuaVariadic<T> const& args); + + size_t doPushArguments(lua_State*); + template <typename First, typename... Rest> + size_t doPushArguments(lua_State* state, First const& first, Rest const&... rest); + + template <typename... Args> + 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<std::type_index, int> m_registeredUserDataTypes; + + lua_State* m_handleThread; + int m_handleStackSize; + int m_handleStackMax; + List<int> m_handleFree; + + uint64_t m_instructionLimit; + bool m_profilingEnabled; + unsigned m_instructionMeasureInterval; + uint64_t m_instructionCount; + unsigned m_recursionLevel; + unsigned m_recursionLimit; + HashMap<tuple<String, unsigned>, shared_ptr<LuaProfileEntry>> m_profileEntries; +}; + +// Built in conversions + +template <> +struct LuaConverter<bool> { + static LuaValue from(LuaEngine&, bool v) { + return v; + } + + static Maybe<bool> to(LuaEngine&, LuaValue const& v) { + if (auto b = v.ptr<LuaBoolean>()) + return *b; + if (v == LuaNil) + return false; + return true; + } +}; + +template <typename T> +struct LuaIntConverter { + static LuaValue from(LuaEngine&, T v) { + return LuaInt(v); + } + + static Maybe<T> to(LuaEngine&, LuaValue const& v) { + if (auto n = v.ptr<LuaInt>()) + return *n; + if (auto n = v.ptr<LuaFloat>()) + return *n; + if (auto s = v.ptr<LuaString>()) { + if (auto n = maybeLexicalCast<LuaInt>(s->ptr())) + return *n; + if (auto n = maybeLexicalCast<LuaFloat>(s->ptr())) + return *n; + } + return {}; + } +}; + +template <> +struct LuaConverter<char> : LuaIntConverter<char> {}; + +template <> +struct LuaConverter<unsigned char> : LuaIntConverter<unsigned char> {}; + +template <> +struct LuaConverter<short> : LuaIntConverter<short> {}; + +template <> +struct LuaConverter<unsigned short> : LuaIntConverter<unsigned short> {}; + +template <> +struct LuaConverter<long> : LuaIntConverter<long> {}; + +template <> +struct LuaConverter<unsigned long> : LuaIntConverter<unsigned long> {}; + +template <> +struct LuaConverter<int> : LuaIntConverter<int> {}; + +template <> +struct LuaConverter<unsigned int> : LuaIntConverter<unsigned int> {}; + +template <> +struct LuaConverter<long long> : LuaIntConverter<long long> {}; + +template <> +struct LuaConverter<unsigned long long> : LuaIntConverter<unsigned long long> {}; + +template <typename T> +struct LuaFloatConverter { + static LuaValue from(LuaEngine&, T v) { + return LuaFloat(v); + } + + static Maybe<T> to(LuaEngine&, LuaValue const& v) { + if (auto n = v.ptr<LuaFloat>()) + return *n; + if (auto n = v.ptr<LuaInt>()) + return *n; + if (auto s = v.ptr<LuaString>()) { + if (auto n = maybeLexicalCast<LuaFloat>(s->ptr())) + return *n; + if (auto n = maybeLexicalCast<LuaInt>(s->ptr())) + return *n; + } + return {}; + } +}; + +template <> +struct LuaConverter<float> : LuaFloatConverter<float> {}; + +template <> +struct LuaConverter<double> : LuaFloatConverter<double> {}; + +template <> +struct LuaConverter<String> { + static LuaValue from(LuaEngine& engine, String const& v) { + return engine.createString(v); + } + + static Maybe<String> to(LuaEngine&, LuaValue const& v) { + if (v.is<LuaString>()) + return String(v.get<LuaString>().ptr()); + if (v.is<LuaInt>()) + return String(toString(v.get<LuaInt>())); + if (v.is<LuaFloat>()) + return String(toString(v.get<LuaFloat>())); + return {}; + } +}; + +template <> +struct LuaConverter<std::string> { + static LuaValue from(LuaEngine& engine, std::string const& v) { + return engine.createString(v.c_str()); + } + + static Maybe<std::string> to(LuaEngine& engine, LuaValue v) { + return engine.luaTo<String>(move(v)).takeUtf8(); + } +}; + +template <> +struct LuaConverter<char const*> { + static LuaValue from(LuaEngine& engine, char const* v) { + return engine.createString(v); + } +}; + +template <size_t s> +struct LuaConverter<char[s]> { + static LuaValue from(LuaEngine& engine, char const v[s]) { + return engine.createString(v); + } +}; + +template <> +struct LuaConverter<LuaString> { + static LuaValue from(LuaEngine&, LuaString v) { + return LuaValue(move(v)); + } + + static Maybe<LuaString> to(LuaEngine& engine, LuaValue v) { + if (v.is<LuaString>()) + return LuaString(move(v.get<LuaString>())); + if (v.is<LuaInt>()) + return engine.createString(toString(v.get<LuaInt>())); + if (v.is<LuaFloat>()) + return engine.createString(toString(v.get<LuaFloat>())); + return {}; + } +}; + +template <typename T> +struct LuaValueConverter { + static LuaValue from(LuaEngine&, T v) { + return v; + } + + static Maybe<T> to(LuaEngine&, LuaValue v) { + if (auto p = v.ptr<T>()) { + return move(*p); + } + return {}; + } +}; + +template <> +struct LuaConverter<LuaTable> : LuaValueConverter<LuaTable> {}; + +template <> +struct LuaConverter<LuaFunction> : LuaValueConverter<LuaFunction> {}; + +template <> +struct LuaConverter<LuaThread> : LuaValueConverter<LuaThread> {}; + +template <> +struct LuaConverter<LuaUserData> : LuaValueConverter<LuaUserData> {}; + +template <> +struct LuaConverter<LuaValue> { + static LuaValue from(LuaEngine&, LuaValue v) { + return v; + } + + static LuaValue to(LuaEngine&, LuaValue v) { + return v; + } +}; + +template <typename T> +struct LuaConverter<Maybe<T>> { + static LuaValue from(LuaEngine& engine, Maybe<T> const& v) { + if (v) + return engine.luaFrom<T>(*v); + else + return LuaNil; + } + + static LuaValue from(LuaEngine& engine, Maybe<T>&& v) { + if (v) + return engine.luaFrom<T>(v.take()); + else + return LuaNil; + } + + static Maybe<Maybe<T>> to(LuaEngine& engine, LuaValue const& v) { + if (v != LuaNil) { + if (auto conv = engine.luaMaybeTo<T>(v)) + return conv; + else + return {}; + } else { + return Maybe<T>(); + } + } + + static Maybe<Maybe<T>> to(LuaEngine& engine, LuaValue&& v) { + if (v != LuaNil) { + if (auto conv = engine.luaMaybeTo<T>(move(v))) + return conv; + else + return {}; + } else { + return Maybe<T>(); + } + } +}; + +template <typename T> +struct LuaMapConverter { + static LuaValue from(LuaEngine& engine, T const& v) { + return engine.createTable(v); + } + + static Maybe<T> to(LuaEngine& engine, LuaValue const& v) { + auto table = v.ptr<LuaTable>(); + if (!table) + return {}; + + T result; + bool failed = false; + table->iterate([&result, &failed, &engine](LuaValue key, LuaValue value) { + auto contKey = engine.luaMaybeTo<typename T::key_type>(move(key)); + auto contValue = engine.luaMaybeTo<typename T::mapped_type>(move(value)); + if (!contKey || !contValue) { + failed = true; + return false; + } + result[contKey.take()] = contValue.take(); + return true; + }); + + if (failed) + return {}; + + return result; + } +}; + +template <typename T> +struct LuaContainerConverter { + static LuaValue from(LuaEngine& engine, T const& v) { + return engine.createArrayTable(v); + } + + static Maybe<T> to(LuaEngine& engine, LuaValue const& v) { + auto table = v.ptr<LuaTable>(); + if (!table) + return {}; + + T result; + bool failed = false; + table->iterate([&result, &failed, &engine](LuaValue key, LuaValue value) { + if (!key.is<LuaInt>()) { + failed = true; + return false; + } + auto contVal = engine.luaMaybeTo<typename T::value_type>(move(value)); + if (!contVal) { + failed = true; + return false; + } + result.insert(result.end(), contVal.take()); + return true; + }); + + if (failed) + return {}; + + return result; + } +}; + +template <typename T, typename Allocator> +struct LuaConverter<List<T, Allocator>> : LuaContainerConverter<List<T, Allocator>> {}; + +template <typename T, size_t MaxSize> +struct LuaConverter<StaticList<T, MaxSize>> : LuaContainerConverter<StaticList<T, MaxSize>> {}; + +template <typename T, size_t MaxStackSize> +struct LuaConverter<SmallList<T, MaxStackSize>> : LuaContainerConverter<SmallList<T, MaxStackSize>> {}; + +template <> +struct LuaConverter<StringList> : LuaContainerConverter<StringList> {}; + +template <typename T, typename BaseSet> +struct LuaConverter<Set<T, BaseSet>> : LuaContainerConverter<Set<T, BaseSet>> {}; + +template <typename T, typename BaseSet> +struct LuaConverter<HashSet<T, BaseSet>> : LuaContainerConverter<HashSet<T, BaseSet>> {}; + +template <typename Key, typename Value, typename Compare, typename Allocator> +struct LuaConverter<Map<Key, Value, Compare, Allocator>> : LuaMapConverter<Map<Key, Value, Compare, Allocator>> {}; + +template <typename Key, typename Value, typename Hash, typename Equals, typename Allocator> +struct LuaConverter<HashMap<Key, Value, Hash, Equals, Allocator>> : LuaMapConverter<HashMap<Key, Value, Hash, Equals, Allocator>> {}; + +template <> +struct LuaConverter<Json> { + static LuaValue from(LuaEngine& engine, Json const& v); + static Maybe<Json> to(LuaEngine& engine, LuaValue const& v); +}; + +template <> +struct LuaConverter<JsonObject> { + static LuaValue from(LuaEngine& engine, JsonObject v); + static Maybe<JsonObject> to(LuaEngine& engine, LuaValue v); +}; + +template <> +struct LuaConverter<JsonArray> { + static LuaValue from(LuaEngine& engine, JsonArray v); + static Maybe<JsonArray> to(LuaEngine& engine, LuaValue v); +}; + +namespace LuaDetail { + inline LuaHandle::LuaHandle(LuaEnginePtr engine, int handleIndex) + : engine(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 <typename T> + struct FromFunctionReturn { + static T convert(LuaEngine& engine, LuaFunctionReturn const& ret) { + if (auto l = ret.ptr<LuaValue>()) { + return engine.luaTo<T>(*l); + } else if (auto vec = ret.ptr<LuaVariadic<LuaValue>>()) { + return engine.luaTo<T>(vec->at(0)); + } else { + return engine.luaTo<T>(LuaNil); + } + } + }; + + template <typename T> + struct FromFunctionReturn<LuaVariadic<T>> { + static LuaVariadic<T> convert(LuaEngine& engine, LuaFunctionReturn const& ret) { + if (auto l = ret.ptr<LuaValue>()) { + return {engine.luaTo<T>(*l)}; + } else if (auto vec = ret.ptr<LuaVariadic<LuaValue>>()) { + LuaVariadic<T> ret(vec->size()); + for (size_t i = 0; i < vec->size(); ++i) + ret[i] = engine.luaTo<T>((*vec)[i]); + return ret; + } else { + return {}; + } + } + }; + + template <typename ArgFirst, typename... ArgRest> + struct FromFunctionReturn<LuaTupleReturn<ArgFirst, ArgRest...>> { + static LuaTupleReturn<ArgFirst, ArgRest...> convert(LuaEngine& engine, LuaFunctionReturn const& ret) { + if (auto l = ret.ptr<LuaValue>()) { + return doConvertSingle(engine, *l, typename GenIndexSequence<0, sizeof...(ArgRest)>::type()); + } else if (auto vec = ret.ptr<LuaVariadic<LuaValue>>()) { + return doConvertMulti(engine, *vec, typename GenIndexSequence<0, sizeof...(ArgRest)>::type()); + } else { + return doConvertNone(engine, typename GenIndexSequence<0, sizeof...(ArgRest)>::type()); + } + } + + template <size_t... Indexes> + static LuaTupleReturn<ArgFirst, ArgRest...> doConvertSingle( + LuaEngine& engine, LuaValue const& single, IndexSequence<Indexes...> const&) { + return LuaTupleReturn<ArgFirst, ArgRest...>(engine.luaTo<ArgFirst>(single), engine.luaTo<ArgRest>(LuaNil)...); + } + + template <size_t... Indexes> + static LuaTupleReturn<ArgFirst, ArgRest...> doConvertMulti( + LuaEngine& engine, LuaVariadic<LuaValue> const& multi, IndexSequence<Indexes...> const&) { + return LuaTupleReturn<ArgFirst, ArgRest...>( + engine.luaTo<ArgFirst>(multi.at(0)), engine.luaTo<ArgRest>(multi.get(Indexes + 1))...); + } + + template <size_t... Indexes> + static LuaTupleReturn<ArgFirst, ArgRest...> doConvertNone(LuaEngine& engine, IndexSequence<Indexes...> const&) { + return LuaTupleReturn<ArgFirst, ArgRest...>(engine.luaTo<ArgFirst>(LuaNil), engine.luaTo<ArgRest>(LuaNil)...); + } + }; + + template <typename... Args, size_t... Indexes> + LuaVariadic<LuaValue> toVariadicReturn( + LuaEngine& engine, LuaTupleReturn<Args...> const& vals, IndexSequence<Indexes...> const&) { + return LuaVariadic<LuaValue>{engine.luaFrom(get<Indexes>(vals))...}; + } + + template <typename... Args> + LuaVariadic<LuaValue> toWrappedReturn(LuaEngine& engine, LuaTupleReturn<Args...> const& vals) { + return toVariadicReturn(engine, vals, typename GenIndexSequence<0, sizeof...(Args)>::type()); + } + + template <typename T> + LuaVariadic<LuaValue> toWrappedReturn(LuaEngine& engine, LuaVariadic<T> const& vals) { + LuaVariadic<LuaValue> ret(vals.size()); + for (size_t i = 0; i < vals.size(); ++i) + ret[i] = engine.luaFrom(vals[i]); + return ret; + } + + template <typename T> + LuaValue toWrappedReturn(LuaEngine& engine, T const& t) { + return engine.luaFrom(t); + } + + template <typename T> + struct ArgGet { + static T get(LuaEngine& engine, size_t argc, LuaValue* argv, size_t index) { + if (index < argc) + return engine.luaTo<T>(move(argv[index])); + return engine.luaTo<T>(LuaNil); + } + }; + + template <typename T> + struct ArgGet<LuaVariadic<T>> { + static LuaVariadic<T> get(LuaEngine& engine, size_t argc, LuaValue* argv, size_t index) { + if (index >= argc) + return {}; + + LuaVariadic<T> subargs(argc - index); + for (size_t i = index; i < argc; ++i) + subargs[i - index] = engine.luaTo<T>(move(argv[i])); + return subargs; + } + }; + + template <typename Return, typename... Args> + struct FunctionWrapper { + template <typename Function, size_t... Indexes> + static LuaWrappedFunction wrapIndexes(Function func, IndexSequence<Indexes...> const&) { + return [func = move(func)](LuaEngine& engine, size_t argc, LuaValue* argv) { + return toWrappedReturn(engine, (Return const&)func(ArgGet<Args>::get(engine, argc, argv, Indexes)...)); + }; + } + + template <typename Function> + static LuaWrappedFunction wrap(Function func) { + return wrapIndexes(forward<Function>(func), typename GenIndexSequence<0, sizeof...(Args)>::type()); + } + }; + + template <typename... Args> + struct FunctionWrapper<void, Args...> { + template <typename Function, size_t... Indexes> + static LuaWrappedFunction wrapIndexes(Function func, IndexSequence<Indexes...> const&) { + return [func = move(func)](LuaEngine& engine, size_t argc, LuaValue* argv) { + func(ArgGet<Args>::get(engine, argc, argv, Indexes)...); + return LuaFunctionReturn(); + }; + } + + template <typename Function> + static LuaWrappedFunction wrap(Function func) { + return wrapIndexes(forward<Function>(func), typename GenIndexSequence<0, sizeof...(Args)>::type()); + } + }; + + template <typename Return, typename... Args> + struct FunctionWrapper<Return, LuaEngine, Args...> { + template <typename Function, size_t... Indexes> + static LuaWrappedFunction wrapIndexes(Function func, IndexSequence<Indexes...> const&) { + return [func = move(func)](LuaEngine& engine, size_t argc, LuaValue* argv) { + return toWrappedReturn(engine, (Return const&)func(engine, ArgGet<Args>::get(engine, argc, argv, Indexes)...)); + }; + } + + template <typename Function> + static LuaWrappedFunction wrap(Function func) { + return wrapIndexes(forward<Function>(func), typename GenIndexSequence<0, sizeof...(Args)>::type()); + } + }; + + template <typename... Args> + struct FunctionWrapper<void, LuaEngine, Args...> { + template <typename Function, size_t... Indexes> + static LuaWrappedFunction wrapIndexes(Function func, IndexSequence<Indexes...> const&) { + return [func = move(func)](LuaEngine& engine, size_t argc, LuaValue* argv) { + func(engine, ArgGet<Args>::get(engine, argc, argv, Indexes)...); + return LuaFunctionReturn(); + }; + } + + template <typename Function> + static LuaWrappedFunction wrap(Function func) { + return wrapIndexes(forward<Function>(func), typename GenIndexSequence<0, sizeof...(Args)>::type()); + } + }; + + template <typename Return, typename... Args, typename Function> + LuaWrappedFunction wrapFunctionWithSignature(Function&& func) { + return FunctionWrapper<Return, typename std::decay<Args>::type...>::wrap(forward<Function>(func)); + } + + template <typename Return, typename Function, typename... Args> + LuaWrappedFunction wrapFunctionArgs(Function&& func, VariadicTypedef<Args...> const&) { + return wrapFunctionWithSignature<Return, Args...>(forward<Function>(func)); + } + + template <typename Function> + LuaWrappedFunction wrapFunction(Function&& func) { + return wrapFunctionArgs<typename FunctionTraits<Function>::Return>( + forward<Function>(func), typename FunctionTraits<Function>::Args()); + } + + template <typename Return, typename T, typename... Args> + struct MethodWrapper { + template <typename Function, size_t... Indexes> + static LuaWrappedFunction wrapIndexes(Function func, IndexSequence<Indexes...> const&) { + return [func = 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<LuaUserData>().get<T>(), ArgGet<Args>::get(engine, argc - 1, argv + 1, Indexes)...)); + }; + } + + template <typename Function> + static LuaWrappedFunction wrap(Function&& func) { + return wrapIndexes(forward<Function>(func), typename GenIndexSequence<0, sizeof...(Args)>::type()); + } + }; + + template <typename T, typename... Args> + struct MethodWrapper<void, T, Args...> { + template <typename Function, size_t... Indexes> + static LuaWrappedFunction wrapIndexes(Function func, IndexSequence<Indexes...> const&) { + return [func = 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<LuaUserData>().get<T>(), ArgGet<Args>::get(engine, argc - 1, argv + 1, Indexes)...); + return LuaFunctionReturn(); + }; + } + + template <typename Function> + static LuaWrappedFunction wrap(Function func) { + return wrapIndexes(forward<Function>(func), typename GenIndexSequence<0, sizeof...(Args)>::type()); + } + }; + + template <typename Return, typename T, typename... Args> + struct MethodWrapper<Return, T, LuaEngine, Args...> { + template <typename Function, size_t... Indexes> + static LuaWrappedFunction wrapIndexes(Function func, IndexSequence<Indexes...> const&) { + return [func = 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<LuaUserData>().get<T>(), engine, ArgGet<Args>::get(engine, argc - 1, argv + 1, Indexes)...)); + }; + } + + template <typename Function> + static LuaWrappedFunction wrap(Function func) { + return wrapIndexes(forward<Function>(func), typename GenIndexSequence<0, sizeof...(Args)>::type()); + } + }; + + template <typename T, typename... Args> + struct MethodWrapper<void, T, LuaEngine, Args...> { + template <typename Function, size_t... Indexes> + static LuaWrappedFunction wrapIndexes(Function func, IndexSequence<Indexes...> const&) { + return [func = 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<LuaUserData>().get<T>(), engine, ArgGet<Args>::get(engine, argc - 1, argv + 1, Indexes)...); + return LuaValue(); + }; + } + + template <typename Function> + static LuaWrappedFunction wrap(Function func) { + return wrapIndexes(forward<Function>(func), typename GenIndexSequence<0, sizeof...(Args)>::type()); + } + }; + + template <typename Return, typename... Args, typename Function> + LuaWrappedFunction wrapMethodWithSignature(Function&& func) { + return MethodWrapper<Return, typename std::decay<Args>::type...>::wrap(forward<Function>(func)); + } + + template <typename Return, typename Function, typename... Args> + LuaWrappedFunction wrapMethodArgs(Function&& func, VariadicTypedef<Args...> const&) { + return wrapMethodWithSignature<Return, Args...>(forward<Function>(func)); + } + + template <typename Function> + LuaWrappedFunction wrapMethod(Function&& func) { + return wrapMethodArgs<typename FunctionTraits<Function>::Return>( + forward<Function>(func), typename FunctionTraits<Function>::Args()); + } + + template <typename Ret, typename... Args> + struct TableIteratorWrapper; + + template <typename Key, typename Value> + struct TableIteratorWrapper<bool, LuaEngine&, Key, Value> { + template <typename Function> + static function<bool(LuaValue, LuaValue)> wrap(LuaEngine& engine, Function&& func) { + return [&engine, func = move(func)](LuaValue key, LuaValue value) -> bool { + return func(engine, engine.luaTo<Key>(move(key)), engine.luaTo<Value>(move(value))); + }; + } + }; + + template <typename Key, typename Value> + struct TableIteratorWrapper<void, LuaEngine&, Key, Value> { + template <typename Function> + static function<bool(LuaValue, LuaValue)> wrap(LuaEngine& engine, Function&& func) { + return [&engine, func = move(func)](LuaValue key, LuaValue value) -> bool { + func(engine, engine.luaTo<Key>(move(key)), engine.luaTo<Value>(move(value))); + return true; + }; + } + }; + + template <typename Key, typename Value> + struct TableIteratorWrapper<bool, Key, Value> { + template <typename Function> + static function<bool(LuaValue, LuaValue)> wrap(LuaEngine& engine, Function&& func) { + return [&engine, func = move(func)](LuaValue key, LuaValue value) -> bool { + return func(engine.luaTo<Key>(move(key)), engine.luaTo<Value>(move(value))); + }; + } + }; + + template <typename Key, typename Value> + struct TableIteratorWrapper<void, Key, Value> { + template <typename Function> + static function<bool(LuaValue, LuaValue)> wrap(LuaEngine& engine, Function&& func) { + return [&engine, func = move(func)](LuaValue key, LuaValue value) -> bool { + func(engine.luaTo<Key>(move(key)), engine.luaTo<Value>(move(value))); + return true; + }; + } + }; + + template <typename Return, typename... Args, typename Function> + function<bool(LuaValue, LuaValue)> wrapTableIteratorWithSignature(LuaEngine& engine, Function&& func) { + return TableIteratorWrapper<Return, typename std::decay<Args>::type...>::wrap(engine, forward<Function>(func)); + } + + template <typename Return, typename Function, typename... Args> + function<bool(LuaValue, LuaValue)> wrapTableIteratorArgs( + LuaEngine& engine, Function&& func, VariadicTypedef<Args...> const&) { + return wrapTableIteratorWithSignature<Return, Args...>(engine, forward<Function>(func)); + } + + template <typename Function> + function<bool(LuaValue, LuaValue)> wrapTableIterator(LuaEngine& engine, Function&& func) { + return wrapTableIteratorArgs<typename FunctionTraits<Function>::Return>( + engine, forward<Function>(func), typename FunctionTraits<Function>::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); + + // 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<Json> 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(); + + // *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<LuaInt> asInteger(LuaValue const& v); +} + +template <typename Container> +LuaVariadic<typename std::decay<Container>::type::value_type> luaUnpack(Container&& c) { + LuaVariadic<typename std::decay<Container>::type::value_type> ret; + if (std::is_rvalue_reference<Container&&>::value) { + for (auto& e : c) + ret.append(move(e)); + } else { + for (auto const& e : c) + ret.append(e); + } + return ret; +} + +template <typename... Types> +LuaTupleReturn<Types...>::LuaTupleReturn(Types const&... args) + : Base(args...) {} + +template <typename... Types> +template <typename... UTypes> +LuaTupleReturn<Types...>::LuaTupleReturn(UTypes&&... args) + : Base(move(args)...) {} + +template <typename... Types> +template <typename... UTypes> +LuaTupleReturn<Types...>::LuaTupleReturn(UTypes const&... args) + : Base(args...) {} + +template <typename... Types> +LuaTupleReturn<Types...>::LuaTupleReturn(LuaTupleReturn const& rhs) + : Base(rhs) {} + +template <typename... Types> +LuaTupleReturn<Types...>::LuaTupleReturn(LuaTupleReturn&& rhs) + : Base(move(rhs)) {} + +template <typename... Types> +template <typename... UTypes> +LuaTupleReturn<Types...>::LuaTupleReturn(LuaTupleReturn<UTypes...> const& rhs) + : Base(rhs) {} + +template <typename... Types> +template <typename... UTypes> +LuaTupleReturn<Types...>::LuaTupleReturn(LuaTupleReturn<UTypes...>&& rhs) + : Base(move(rhs)) {} + +template <typename... Types> +LuaTupleReturn<Types...>& LuaTupleReturn<Types...>::operator=(LuaTupleReturn const& rhs) { + Base::operator=(rhs); + return *this; +} + +template <typename... Types> +LuaTupleReturn<Types...>& LuaTupleReturn<Types...>::operator=(LuaTupleReturn&& rhs) { + Base::operator=(move(rhs)); + return *this; +} + +template <typename... Types> +template <typename... UTypes> +LuaTupleReturn<Types...>& LuaTupleReturn<Types...>::operator=(LuaTupleReturn<UTypes...> const& rhs) { + Base::operator=((tuple<UTypes...> const&)rhs); + return *this; +} + +template <typename... Types> +template <typename... UTypes> +LuaTupleReturn<Types...>& LuaTupleReturn<Types...>::operator=(LuaTupleReturn<UTypes...>&& rhs) { + Base::operator=((tuple<UTypes...> && )move(rhs)); + return *this; +} + +template <typename... Types> +LuaTupleReturn<Types&...> luaTie(Types&... args) { + return LuaTupleReturn<Types&...>(args...); +} + +template <typename... Types> +LuaTupleReturn<typename std::decay<Types>::type...> luaTupleReturn(Types&&... args) { + return LuaTupleReturn<typename std::decay<Types>::type...>(forward<Types>(args)...); +} + +inline LuaReference::LuaReference(LuaDetail::LuaHandle handle) : m_handle(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 String(ptr()); +} + +inline bool operator==(LuaString const& s1, LuaString const& s2) { + return std::strcmp(s1.ptr(), s2.ptr()) == 0; +} + +inline bool operator==(LuaString const& s1, char const* s2) { + return std::strcmp(s1.ptr(), s2) == 0; +} + +inline bool operator==(LuaString const& s1, std::string const& s2) { + return s1.ptr() == s2; +} + +inline bool operator==(LuaString const& s1, String const& s2) { + return s1.ptr() == s2; +} + +inline bool operator==(char const* s1, LuaString const& s2) { + return std::strcmp(s1, s2.ptr()) == 0; +} + +inline bool operator==(std::string const& s1, LuaString const& s2) { + return s1 == s2.ptr(); +} + +inline bool operator==(String const& s1, LuaString const& s2) { + return s1 == s2.ptr(); +} + +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 <typename T, typename K> +T LuaTable::get(K key) const { + return engine().luaTo<T>(engine().tableGet(false, handleIndex(), engine().luaFrom(move(key)))); +} + +template <typename T> +T LuaTable::get(char const* key) const { + return engine().luaTo<T>(engine().tableGet(false, handleIndex(), key)); +} + +template <typename T, typename K> +void LuaTable::set(K key, T value) const { + engine().tableSet(false, handleIndex(), engine().luaFrom(move(key)), engine().luaFrom(move(value))); +} + +template <typename T> +void LuaTable::set(char const* key, T value) const { + engine().tableSet(false, handleIndex(), key, engine().luaFrom(move(value))); +} + +template <typename K> +bool LuaTable::contains(K key) const { + return engine().tableGet(false, handleIndex(), engine().luaFrom(move(key))) != LuaNil; +} + +template <typename K> +void LuaTable::remove(K key) const { + engine().tableSet(false, handleIndex(), engine().luaFrom(key), LuaValue()); +} + +template <typename Function> +void LuaTable::iterate(Function&& function) const { + return engine().tableIterate(handleIndex(), LuaDetail::wrapTableIterator(engine(), forward<Function>(function))); +} + +template <typename Return, typename... Args, typename Function> +void LuaTable::iterateWithSignature(Function&& func) const { + return engine().tableIterate(handleIndex(), LuaDetail::wrapTableIteratorWithSignature<Return, Args...>(engine(), forward<Function>(func))); +} + +template <typename T, typename K> +T LuaTable::rawGet(K key) const { + return engine().luaTo<T>(engine().tableGet(true, handleIndex(), engine().luaFrom(key))); +} + +template <typename T> +T LuaTable::rawGet(char const* key) const { + return engine().luaTo<T>(engine().tableGet(true, handleIndex(), key)); +} + +template <typename T, typename K> +void LuaTable::rawSet(K key, T value) const { + engine().tableSet(true, handleIndex(), engine().luaFrom(key), engine().luaFrom(value)); +} + +template <typename T> +void LuaTable::rawSet(char const* key, T value) const { + engine().tableSet(true, handleIndex(), engine().luaFrom(key), engine().luaFrom(value)); +} + +template <typename Ret, typename... Args> +Ret LuaFunction::invoke(Args const&... args) const { + return LuaDetail::FromFunctionReturn<Ret>::convert(engine(), engine().callFunction(handleIndex(), args...)); +} + +template <typename Ret, typename... Args> +Maybe<Ret> LuaThread::resume(Args const&... args) const { + auto res = engine().resumeThread(handleIndex(), args...); + if (!res) + return {}; + return LuaDetail::FromFunctionReturn<Ret>::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 <typename T> +bool LuaUserData::is() const { + return engine().userDataIsType<T>(handleIndex()); +} + +template <typename T> +T& LuaUserData::get() const { + return *engine().getUserData<T>(handleIndex()); +} + +template <typename Function> +void LuaCallbacks::registerCallback(String name, Function&& func) { + if (!m_callbacks.insert(name, LuaDetail::wrapFunction(forward<Function>(func))).second) + throw LuaException::format("Lua callback '%s' was registered twice", name); +} + +template <typename Return, typename... Args, typename Function> +void LuaCallbacks::registerCallbackWithSignature(String name, Function&& func) { + if (!m_callbacks.insert(name, LuaDetail::wrapFunctionWithSignature<Return, Args...>(forward<Function>(func))).second) + throw LuaException::format("Lua callback '%s' was registered twice", name); +} + +template <typename T> +template <typename Function> +void LuaMethods<T>::registerMethod(String name, Function&& func) { + if (!m_methods.insert(name, LuaDetail::wrapMethod(forward<Function>(move(func)))).second) + throw LuaException::format("Lua method '%s' was registered twice", name); +} + +template <typename T> +template <typename Return, typename... Args, typename Function> +void LuaMethods<T>::registerMethodWithSignature(String name, Function&& func) { + if (!m_methods.insert(name, LuaDetail::wrapMethodWithSignature<Return, Args...>(forward<Function>(move(func)))) + .second) + throw LuaException::format("Lua method '%s' was registered twice", name); +} + +template <typename T> +StringMap<LuaDetail::LuaWrappedFunction> const& LuaMethods<T>::methods() const { + return m_methods; +} + +template <typename T> +T LuaContext::getPath(String path) const { + return engine().luaTo<T>(engine().contextGetPath(handleIndex(), move(path))); +} + +template <typename T> +void LuaContext::setPath(String key, T value) { + engine().contextSetPath(handleIndex(), move(key), engine().luaFrom<T>(move(value))); +} + +template <typename Ret> +Ret LuaContext::eval(String const& lua) { + return LuaDetail::FromFunctionReturn<Ret>::convert(engine(), engine().contextEval(handleIndex(), lua)); +} + +template <typename Ret, typename... Args> +Ret LuaContext::invokePath(String const& key, Args const&... args) const { + auto p = getPath(key); + if (auto f = p.ptr<LuaFunction>()) + return f->invoke<Ret>(args...); + throw LuaException::format("invokePath called on path '%s' which is not function type", key); +} + +template <typename T> +LuaValue LuaContext::luaFrom(T&& t) { + return engine().luaFrom(forward<T>(t)); +} + +template <typename T> +LuaValue LuaContext::luaFrom(T const& t) { + return engine().luaFrom(t); +} + +template <typename T> +Maybe<T> LuaContext::luaMaybeTo(LuaValue&& v) { + return engine().luaFrom(move(v)); +} + +template <typename T> +Maybe<T> LuaContext::luaMaybeTo(LuaValue const& v) { + return engine().luaFrom(v); +} + +template <typename T> +T LuaContext::luaTo(LuaValue&& v) { + return engine().luaTo<T>(move(v)); +} + +template <typename T> +T LuaContext::luaTo(LuaValue const& v) { + return engine().luaTo<T>(v); +} + +template <typename Container> +LuaTable LuaContext::createTable(Container const& map) { + return engine().createTable(map); +} + +template <typename Container> +LuaTable LuaContext::createArrayTable(Container const& array) { + return engine().createArrayTable(array); +} + +template <typename Function> +LuaFunction LuaContext::createFunction(Function&& func) { + return engine().createFunction(forward<Function>(func)); +} + +template <typename Return, typename... Args, typename Function> +LuaFunction LuaContext::createFunctionWithSignature(Function&& func) { + return engine().createFunctionWithSignature<Return, Args...>(forward<Function>(func)); +} + +template <typename T> +LuaUserData LuaContext::createUserData(T t) { + return engine().createUserData(move(t)); +} + +template <typename T> +LuaMethods<T> LuaUserDataMethods<T>::make() { + return LuaMethods<T>(); +} + +template <typename T> +LuaValue LuaUserDataConverter<T>::from(LuaEngine& engine, T t) { + return engine.createUserData(move(t)); +} + +template <typename T> +Maybe<T> LuaUserDataConverter<T>::to(LuaEngine&, LuaValue const& v) { + if (auto ud = v.ptr<LuaUserData>()) { + if (ud->is<T>()) + return ud->get<T>(); + } + return {}; +} + +template <typename T> +LuaValue LuaEngine::luaFrom(T&& t) { + return LuaConverter<typename std::decay<T>::type>::from(*this, forward<T>(t)); +} + +template <typename T> +LuaValue LuaEngine::luaFrom(T const& t) { + return LuaConverter<T>::from(*this, t); +} + +template <typename T> +Maybe<T> LuaEngine::luaMaybeTo(LuaValue&& v) { + return LuaConverter<T>::to(*this, move(v)); +} + +template <typename T> +Maybe<T> LuaEngine::luaMaybeTo(LuaValue const& v) { + return LuaConverter<T>::to(*this, v); +} + +template <typename T> +T LuaEngine::luaTo(LuaValue&& v) { + if (auto res = luaMaybeTo<T>(move(v))) + return res.take(); + throw LuaConversionException::format("Error converting LuaValue to type '%s'", typeid(T).name()); +} + +template <typename T> +T LuaEngine::luaTo(LuaValue const& v) { + if (auto res = luaMaybeTo<T>(v)) + return res.take(); + throw LuaConversionException::format("Error converting LuaValue to type '%s'", typeid(T).name()); +} + +template <typename Container> +LuaTable LuaEngine::createTable(Container const& map) { + auto table = createTable(); + for (auto const& p : map) + table.set(p.first, p.second); + return table; +} + +template <typename Container> +LuaTable LuaEngine::createArrayTable(Container const& array) { + auto table = createTable(); + int i = 1; + for (auto const& elem : array) { + table.set(LuaInt(i), elem); + ++i; + } + return table; +} + +template <typename Function> +LuaFunction LuaEngine::createFunction(Function&& func) { + return createWrappedFunction(LuaDetail::wrapFunction(forward<Function>(func))); +} + +template <typename Return, typename... Args, typename Function> +LuaFunction LuaEngine::createFunctionWithSignature(Function&& func) { + return createWrappedFunction(LuaDetail::wrapFunctionWithSignature<Return, Args...>(forward<Function>(func))); +} + +template <typename... Args> +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<LuaValue> ret(returnValues); + for (int i = returnValues - 1; i >= 0; --i) + ret[i] = popLuaValue(m_state); + return ret; + } +} + +template <typename... Args> +Maybe<LuaDetail::LuaFunctionReturn> 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<LuaValue> ret(returnValues); + for (int i = returnValues - 1; i >= 0; --i) + ret[i] = popLuaValue(threadState); + return LuaDetail::LuaFunctionReturn(move(ret)); + } +} + +template <typename T> +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<T>::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 <typename T> +LuaUserData LuaEngine::createUserData(T t) { + registerUserDataType<T>(); + + int typeMetatable = m_registeredUserDataTypes.get(typeid(T)); + + lua_checkstack(m_state, 2); + + new (lua_newuserdata(m_state, sizeof(T))) T(move(t)); + + lua_rawgeti(m_state, LUA_REGISTRYINDEX, typeMetatable); + lua_setmetatable(m_state, -2); + + return LuaUserData(LuaDetail::LuaHandle(RefPtr<LuaEngine>(this), popHandle(m_state))); +} + +template <typename T, typename K> +T LuaEngine::getGlobal(K key) { + lua_checkstack(m_state, 1); + lua_rawgeti(m_state, LUA_REGISTRYINDEX, m_scriptDefaultEnvRegistryId); + pushLuaValue(m_state, luaFrom(move(key))); + lua_rawget(m_state, -2); + + LuaValue v = popLuaValue(m_state); + lua_pop(m_state, 1); + + return luaTo<T>(v); +} + +template <typename T> +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<T>(v); +} + +template <typename T, typename K> +void LuaEngine::setGlobal(K key, T value) { + lua_checkstack(m_state, 1); + + lua_rawgeti(m_state, LUA_REGISTRYINDEX, m_scriptDefaultEnvRegistryId); + pushLuaValue(m_state, luaFrom(move(key))); + pushLuaValue(m_state, luaFrom(move(value))); + + lua_rawset(m_state, -3); + lua_pop(m_state, 1); +} + +template <typename T> +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 <typename T> +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 <typename T> +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 %s, 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 %s", 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 <typename T> +size_t LuaEngine::pushArgument(lua_State* state, T const& arg) { + pushLuaValue(state, luaFrom(arg)); + return 1; +} + +template <typename T> +size_t LuaEngine::pushArgument(lua_State* state, LuaVariadic<T> 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 <typename First, typename... Rest> +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 <typename... Args> +size_t LuaEngine::pushArguments(lua_State* state, Args const&... args) { + lua_checkstack(state, sizeof...(args)); + return doPushArguments(state, args...); +} + +} + +#endif |