Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/core/StarLua.hpp
diff options
context:
space:
mode:
authorKae <80987908+Novaenia@users.noreply.github.com>2023-06-20 14:33:09 +1000
committerKae <80987908+Novaenia@users.noreply.github.com>2023-06-20 14:33:09 +1000
commit6352e8e3196f78388b6c771073f9e03eaa612673 (patch)
treee23772f79a7fbc41bc9108951e9e136857484bf4 /source/core/StarLua.hpp
parent6741a057e5639280d85d0f88ba26f000baa58f61 (diff)
everything everywhere
all at once
Diffstat (limited to 'source/core/StarLua.hpp')
-rw-r--r--source/core/StarLua.hpp2163
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