osb/source/core/StarLua.hpp
2024-02-25 15:46:47 +01:00

2226 lines
71 KiB
C++

#pragma once
#include <typeindex>
#include <type_traits>
#include <lua.hpp>
#include "StarLexicalCast.hpp"
#include "StarString.hpp"
#include "StarJson.hpp"
#include "StarRefPtr.hpp"
#include "StarDirectives.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 = 0;
};
// Not meant to be used directly, exposes a raw interface for wrapped C++
// functions to be wrapped with the least amount of overhead. Arguments are
// passed non-const so that they can be moved into wrapped functions that
// take values without copying.
typedef Variant<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;
StringView view() const;
};
bool operator==(LuaString const& s1, LuaString const& s2);
bool operator==(LuaString const& s1, char const* s2);
bool operator==(LuaString const& s1, std::string const& s2);
bool operator==(LuaString const& s1, String const& s2);
bool operator==(char const* s1, LuaString const& s2);
bool operator==(std::string const& s1, LuaString const& s2);
bool operator==(String const& s1, LuaString const& s2);
bool operator!=(LuaString const& s1, LuaString const& s2);
bool operator!=(LuaString const& s1, char const* s2);
bool operator!=(LuaString const& s1, std::string const& s2);
bool operator!=(LuaString const& s1, String const& s2);
bool operator!=(char const* s1, LuaString const& s2);
bool operator!=(std::string const& s1, LuaString const& s2);
bool operator!=(String const& s1, LuaString const& s2);
class LuaTable : public LuaReference {
public:
using LuaReference::LuaReference;
template <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;
using LuaTable::handleIndex;
// Splits the path by '.' character, so can get / set values in tables inside
// other tables. If any table in the path is not a table but is accessed as
// one, instead returns LuaNil.
template <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);
};
template <typename T>
struct LuaNullTermWrapper : T {
LuaNullTermWrapper() : T() {}
LuaNullTermWrapper(LuaNullTermWrapper const& nt) : T(nt) {}
LuaNullTermWrapper(LuaNullTermWrapper&& nt) : T(std::move(nt)) {}
LuaNullTermWrapper(T const& bt) : T(bt) {}
LuaNullTermWrapper(T&& bt) : T(std::move(bt)) {}
using T::T;
LuaNullTermWrapper& operator=(LuaNullTermWrapper const& rhs) {
T::operator=(rhs);
return *this;
}
LuaNullTermWrapper& operator=(LuaNullTermWrapper&& rhs) {
T::operator=(std::move(rhs));
return *this;
}
LuaNullTermWrapper& operator=(T&& other) {
T::operator=(std::forward<T>(other));
return *this;
}
};
class LuaNullEnforcer {
public:
LuaNullEnforcer(LuaEngine& engine);
~LuaNullEnforcer();
private:
LuaEngine* m_engine;
};
// Types that want to participate in automatic lua conversion should specialize
// this template and provide static to and from methods on it. The method
// signatures will be called like:
// LuaValue from(LuaEngine& engine, T t);
// Maybe<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(int narr = 0, int nrec = 0);
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);
LuaFunction createWrappedFunction(LuaDetail::LuaWrappedFunction function);
LuaFunction createRawFunction(lua_CFunction func);
LuaFunction createFunctionFromSource(int handleIndex, char const* contents, size_t size, char const* name);
LuaThread createThread();
template <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;
// Enforce null-terminated string conversion as long as the returned enforcer object is in scope.
LuaNullEnforcer nullTerminate();
// Disables null-termination enforcement
void setNullTerminated(bool nullTerminated);
private:
friend struct LuaDetail::LuaHandle;
friend class LuaReference;
friend class LuaString;
friend class LuaTable;
friend class LuaFunction;
friend class LuaThread;
friend class LuaUserData;
friend class LuaContext;
friend class LuaNullEnforcer;
LuaEngine() = default;
// Get the LuaEngine* out of the lua registry magic entry. Uses 1 stack
// space, and does not call lua_checkstack.
static LuaEngine* luaEnginePtr(lua_State* state);
// Counts instructions when instruction limiting is enabled.
static void countHook(lua_State* state, lua_Debug* ar);
static void* allocate(void* userdata, void* ptr, size_t oldSize, size_t newSize);
// Pops lua error from stack and throws LuaException
void handleError(lua_State* state, int res);
// lua_pcall with a better message handler that includes a traceback.
int pcallWithTraceback(lua_State* state, int nargs, int nresults);
// override for lua coroutine resume with traceback
static int coresumeWithTraceback(lua_State* state);
// propagates errors from one state to another, i.e. past thread boundaries
// pops error off the top of the from stack and pushes onto the to stack
static void propagateErrorWithTraceback(lua_State* from, lua_State* to);
char const* stringPtr(int handleIndex);
size_t stringLength(int handleIndex);
String string(int handleIndex);
StringView stringView(int handleIndex);
LuaValue tableGet(bool raw, int handleIndex, LuaValue const& key);
LuaValue tableGet(bool raw, int handleIndex, char const* key);
void tableSet(bool raw, int handleIndex, LuaValue const& key, LuaValue const& value);
void tableSet(bool raw, int handleIndex, char const* key, LuaValue const& value);
LuaInt tableLength(bool raw, int handleIndex);
void tableIterate(int handleIndex, function<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();
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;
int m_nullTerminated;
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 v.get<LuaString>().toString();
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>(std::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<Directives> {
static LuaValue from(LuaEngine& engine, Directives const& v) {
if (String const* ptr = v.stringPtr())
return engine.createString(*ptr);
else
return engine.createString("");
}
};
template <>
struct LuaConverter<LuaString> {
static LuaValue from(LuaEngine&, LuaString v) {
return LuaValue(std::move(v));
}
static Maybe<LuaString> to(LuaEngine& engine, LuaValue v) {
if (v.is<LuaString>())
return LuaString(std::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 std::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>(std::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>(std::move(key));
auto contValue = engine.luaMaybeTo<typename T::mapped_type>(std::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>(std::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(std::move(engine)), handleIndex(handleIndex) {}
inline LuaHandle::~LuaHandle() {
if (engine)
engine->destroyHandle(handleIndex);
}
inline LuaHandle::LuaHandle(LuaHandle const& other) {
engine = other.engine;
if (engine)
handleIndex = engine->copyHandle(other.handleIndex);
}
inline LuaHandle::LuaHandle(LuaHandle&& other) {
engine = take(other.engine);
handleIndex = take(other.handleIndex);
}
inline LuaHandle& LuaHandle::operator=(LuaHandle const& other) {
if (engine)
engine->destroyHandle(handleIndex);
engine = other.engine;
if (engine)
handleIndex = engine->copyHandle(other.handleIndex);
return *this;
}
inline LuaHandle& LuaHandle::operator=(LuaHandle&& other) {
if (engine)
engine->destroyHandle(handleIndex);
engine = take(other.engine);
handleIndex = take(other.handleIndex);
return *this;
}
template <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>(std::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>(std::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 = std::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(std::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 = std::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(std::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 = std::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(std::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 = std::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(std::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(std::forward<Function>(func));
}
template <typename Return, typename Function, typename... Args>
LuaWrappedFunction wrapFunctionArgs(Function&& func, VariadicTypedef<Args...> const&) {
return wrapFunctionWithSignature<Return, Args...>(std::forward<Function>(func));
}
template <typename Function>
LuaWrappedFunction wrapFunction(Function&& func) {
return wrapFunctionArgs<typename FunctionTraits<Function>::Return>(
std::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 = std::move(func)](LuaEngine& engine, size_t argc, LuaValue* argv) mutable {
if (argc == 0)
throw LuaException("No object argument passed to wrapped method");
return toWrappedReturn(engine,
(Return const&)func(argv[0].get<LuaUserData>().get<T>(), ArgGet<Args>::get(engine, argc - 1, argv + 1, Indexes)...));
};
}
template <typename Function>
static LuaWrappedFunction wrap(Function&& func) {
return wrapIndexes(std::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 = std::move(func)](LuaEngine& engine, size_t argc, LuaValue* argv) {
if (argc == 0)
throw LuaException("No object argument passed to wrapped method");
func(argv[0].get<LuaUserData>().get<T>(), ArgGet<Args>::get(engine, argc - 1, argv + 1, Indexes)...);
return LuaFunctionReturn();
};
}
template <typename Function>
static LuaWrappedFunction wrap(Function func) {
return wrapIndexes(std::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 = std::move(func)](LuaEngine& engine, size_t argc, LuaValue* argv) {
if (argc == 0)
throw LuaException("No object argument passed to wrapped method");
return toWrappedReturn(
engine,
(Return const&)func(argv[0].get<LuaUserData>().get<T>(), engine, ArgGet<Args>::get(engine, argc - 1, argv + 1, Indexes)...));
};
}
template <typename Function>
static LuaWrappedFunction wrap(Function func) {
return wrapIndexes(std::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 = std::move(func)](LuaEngine& engine, size_t argc, LuaValue* argv) {
if (argc == 0)
throw LuaException("No object argument passed to wrapped method");
func(argv[0].get<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(std::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(std::forward<Function>(func));
}
template <typename Return, typename Function, typename... Args>
LuaWrappedFunction wrapMethodArgs(Function&& func, VariadicTypedef<Args...> const&) {
return wrapMethodWithSignature<Return, Args...>(std::forward<Function>(func));
}
template <typename Function>
LuaWrappedFunction wrapMethod(Function&& func) {
return wrapMethodArgs<typename FunctionTraits<Function>::Return>(
std::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 = std::move(func)](LuaValue key, LuaValue value) -> bool {
return func(engine, engine.luaTo<Key>(std::move(key)), engine.luaTo<Value>(std::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 = std::move(func)](LuaValue key, LuaValue value) -> bool {
func(engine, engine.luaTo<Key>(std::move(key)), engine.luaTo<Value>(std::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 = std::move(func)](LuaValue key, LuaValue value) -> bool {
return func(engine.luaTo<Key>(std::move(key)), engine.luaTo<Value>(std::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 = std::move(func)](LuaValue key, LuaValue value) -> bool {
func(engine.luaTo<Key>(std::move(key)), engine.luaTo<Value>(std::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, std::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, std::forward<Function>(func));
}
template <typename Function>
function<bool(LuaValue, LuaValue)> wrapTableIterator(LuaEngine& engine, Function&& func) {
return wrapTableIteratorArgs<typename FunctionTraits<Function>::Return>(
engine, std::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(std::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(std::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(std::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(std::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=(std::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...> && )std::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...>(std::forward<Types>(args)...);
}
inline LuaReference::LuaReference(LuaDetail::LuaHandle handle) : m_handle(std::move(handle)) {}
inline bool LuaReference::operator==(LuaReference const& rhs) const {
return tie(m_handle.engine, m_handle.handleIndex) == tie(rhs.m_handle.engine, rhs.m_handle.handleIndex);
}
inline bool LuaReference::operator!=(LuaReference const& rhs) const {
return tie(m_handle.engine, m_handle.handleIndex) != tie(rhs.m_handle.engine, rhs.m_handle.handleIndex);
}
inline LuaEngine& LuaReference::engine() const {
return *m_handle.engine;
}
inline int LuaReference::handleIndex() const {
return m_handle.handleIndex;
}
inline char const* LuaString::ptr() const {
return engine().stringPtr(handleIndex());
}
inline size_t LuaString::length() const {
return engine().stringLength(handleIndex());
}
inline String LuaString::toString() const {
return engine().string(handleIndex());
}
inline StringView LuaString::view() const {
return engine().stringView(handleIndex());
}
inline bool operator==(LuaString const& s1, LuaString const& s2) {
return s1.view() == s2.view();
}
inline bool operator==(LuaString const& s1, char const* s2) {
return s1.view() == s2;
}
inline bool operator==(LuaString const& s1, std::string const& s2) {
return s1.view() == s2;
}
inline bool operator==(LuaString const& s1, String const& s2) {
return s1.view() == s2;
}
inline bool operator==(char const* s1, LuaString const& s2) {
return s2.view() == s1;
}
inline bool operator==(std::string const& s1, LuaString const& s2) {
return s2.view() == s1;
}
inline bool operator==(String const& s1, LuaString const& s2) {
return s2.view() == s1;
}
inline bool operator!=(LuaString const& s1, LuaString const& s2) {
return !(s1 == s2);
}
inline bool operator!=(LuaString const& s1, char const* s2) {
return !(s1 == s2);
}
inline bool operator!=(LuaString const& s1, std::string const& s2) {
return !(s1 == s2);
}
inline bool operator!=(LuaString const& s1, String const& s2) {
return !(s1 == s2);
}
inline bool operator!=(char const* s1, LuaString const& s2) {
return !(s1 == s2);
}
inline bool operator!=(std::string const& s1, LuaString const& s2) {
return !(s1 == s2);
}
inline bool operator!=(String const& s1, LuaString const& s2) {
return !(s1 == s2);
}
template <typename T, typename K>
T LuaTable::get(K key) const {
return engine().luaTo<T>(engine().tableGet(false, handleIndex(), engine().luaFrom(std::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(std::move(key)), engine().luaFrom(std::move(value)));
}
template <typename T>
void LuaTable::set(char const* key, T value) const {
engine().tableSet(false, handleIndex(), key, engine().luaFrom(std::move(value)));
}
template <typename K>
bool LuaTable::contains(K key) const {
return engine().tableGet(false, handleIndex(), engine().luaFrom(std::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(), std::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(), std::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(std::forward<Function>(func))).second)
throw LuaException::format("Lua callback '{}' 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...>(std::forward<Function>(func))).second)
throw LuaException::format("Lua callback '{}' 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(std::forward<Function>(std::move(func)))).second)
throw LuaException::format("Lua method '{}' 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...>(std::forward<Function>(std::move(func))))
.second)
throw LuaException::format("Lua method '{}' 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(), std::move(path)));
}
template <typename T>
void LuaContext::setPath(String key, T value) {
engine().contextSetPath(handleIndex(), std::move(key), engine().luaFrom<T>(std::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 '{}' which is not function type", key);
}
template <typename T>
LuaValue LuaContext::luaFrom(T&& t) {
return engine().luaFrom(std::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(std::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>(std::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(std::forward<Function>(func));
}
template <typename Return, typename... Args, typename Function>
LuaFunction LuaContext::createFunctionWithSignature(Function&& func) {
return engine().createFunctionWithSignature<Return, Args...>(std::forward<Function>(func));
}
template <typename T>
LuaUserData LuaContext::createUserData(T t) {
return engine().createUserData(std::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(std::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, std::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, std::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>(std::move(v)))
return res.take();
throw LuaConversionException::format("Error converting LuaValue to type '{}'", 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 '{}'", typeid(T).name());
}
template <typename Container>
LuaTable LuaEngine::createTable(Container const& map) {
auto table = createTable(0, map.size());
for (auto const& p : map)
table.set(p.first, p.second);
return table;
}
template <typename Container>
LuaTable LuaEngine::createArrayTable(Container const& array) {
auto table = createTable(array.size(), 0);
int i = 1;
for (auto const& elem : array) {
table.set(LuaInt(i), elem);
++i;
}
return table;
}
template <typename Function>
LuaFunction LuaEngine::createFunction(Function&& func) {
return createWrappedFunction(LuaDetail::wrapFunction(std::forward<Function>(func)));
}
template <typename Return, typename... Args, typename Function>
LuaFunction LuaEngine::createFunctionWithSignature(Function&& func) {
return createWrappedFunction(LuaDetail::wrapFunctionWithSignature<Return, Args...>(std::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(std::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(std::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(std::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(std::move(key)));
pushLuaValue(m_state, luaFrom(std::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 {}, not registered", typeid(T).name());
lua_checkstack(m_state, 3);
pushHandle(m_state, handleIndex);
T* userdata = (T*)lua_touserdata(m_state, -1);
if (lua_getmetatable(m_state, -1) == 0) {
lua_pop(m_state, 1);
throw LuaException("Cannot get userdata from lua type, no metatable found");
}
lua_rawgeti(m_state, LUA_REGISTRYINDEX, typeRef);
if (!lua_rawequal(m_state, -1, -2)) {
lua_pop(m_state, 3);
throw LuaException::format("Improper conversion from userdata to type {}", typeid(T).name());
}
lua_pop(m_state, 3);
return userdata;
}
inline void LuaEngine::destroyHandle(int handleIndex) {
// We don't bother setting the entry in the handle stack to nil, we just wait
// for it to be reused and overwritten. We could provide a way to
// periodically ensure that the entire free list is niled out if this becomes
// a memory issue?
m_handleFree.append(handleIndex);
}
template <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...);
}
}
template <> struct fmt::formatter<Star::LuaValue> : ostream_formatter {};