#include "stored-object.h" #include "shared-table.h" #include "utils.h" #include #include #include #include namespace effil { namespace { template class PrimitiveHolder : public BaseHolder { public: PrimitiveHolder(const sol::stack_object& luaObject) noexcept : data_(luaObject.as()) {} PrimitiveHolder(const sol::object& luaObject) noexcept : data_(luaObject.as()) {} PrimitiveHolder(const StoredType& init) noexcept : data_(init) {} bool rawCompare(const BaseHolder* other) const noexcept final { return data_ < static_cast*>(other)->data_; } sol::object unpack(sol::this_state state) const final { return sol::make_object(state, data_); } StoredType getData() { return data_; } private: StoredType data_; }; class FunctionHolder : public BaseHolder { public: template FunctionHolder(SolObject luaObject) noexcept { sol::state_view lua(luaObject.lua_state()); sol::function dumper = lua["string"]["dump"]; if (!dumper.valid()) throw Exception() << "Invalid string.dump()"; function_ = dumper(luaObject); } bool rawCompare(const BaseHolder* other) const noexcept final { return function_ < static_cast(other)->function_; } sol::object unpack(sol::this_state state) const final { sol::state_view lua((lua_State*)state); sol::function loader = lua["loadstring"]; REQUIRE(loader.valid()) << "Invalid loadstring()"; sol::function result = loader(function_); // The result of restaring always is valid function. assert(result.valid()); return sol::make_object(state, result); } private: std::string function_; }; class TableHolder : public BaseHolder { public: template TableHolder(const SolType& luaObject) { assert(luaObject.template is()); handle_ = luaObject.template as().handle(); assert(getGC().has(handle_)); } TableHolder(GCObjectHandle handle) : handle_(handle) {} bool rawCompare(const BaseHolder *other) const final { return handle_ < static_cast(other)->handle_; } sol::object unpack(sol::this_state state) const final { return sol::make_object(state, *static_cast(getGC().get(handle_))); } GCObjectHandle gcHandle() const override { return handle_; } private: GCObjectHandle handle_; }; // This class is used as a storage for visited sol::tables // TODO: try to use map or unordered map instead of linear search in vector // TODO: Trick is - sol::object has only operator==:/ typedef std::vector> SolTableToShared; void dumpTable(SharedTable* target, sol::table luaTable, SolTableToShared& visited); StoredObject makeStoredObject(sol::object luaObject, SolTableToShared& visited) { if (luaObject.get_type() == sol::type::table) { sol::table luaTable = luaObject; auto comparator = [&luaTable](const std::pair& element){ return element.first == luaTable; }; auto st = std::find_if(visited.begin(), visited.end(), comparator); if (st == std::end(visited)) { SharedTable table = getGC().create(); visited.emplace_back(std::make_pair(luaTable, table.handle())); dumpTable(&table, luaTable, visited); return createStoredObject(table.handle()); } else { return createStoredObject(st->second); } } else { return createStoredObject(luaObject); } } void dumpTable(SharedTable* target, sol::table luaTable, SolTableToShared& visited) { for(auto& row : luaTable) { target->set(makeStoredObject(row.first, visited), makeStoredObject(row.second, visited)); } } template StoredObject fromSolObject(const SolObject& luaObject) { switch(luaObject.get_type()) { case sol::type::nil: break; case sol::type::boolean: return std::make_unique>(luaObject); case sol::type::number: return std::make_unique>(luaObject); case sol::type::string: return std::make_unique>(luaObject); case sol::type::userdata: return std::make_unique(luaObject); case sol::type::function: return std::make_unique(luaObject); case sol::type::table: { sol::table luaTable = luaObject; // Tables pool is used to store tables. // Right now not defiantly clear how ownership between states works. SharedTable table = getGC().create(); SolTableToShared visited{{luaTable, table.handle()}}; // Let's dump table and all subtables // SolTableToShared is used to prevent from infinity recursion // in recursive tables dumpTable(&table, luaTable, visited); return std::make_unique(table.handle()); } default: throw Exception() << "Unable to store object of that type: " << (int)luaObject.get_type() << "\n"; } return nullptr; } } // namespace StoredObject createStoredObject(bool value) { return std::make_unique>(value); } StoredObject createStoredObject(double value) { return std::make_unique>(value); } StoredObject createStoredObject(const std::string& value) { return std::make_unique>(value); } StoredObject createStoredObject(const sol::object &object) { return fromSolObject(object); } StoredObject createStoredObject(const sol::stack_object &object) { return fromSolObject(object); } StoredObject createStoredObject(GCObjectHandle handle) { return std::make_unique(handle); } template sol::optional getPrimitiveHolderData(const StoredObject& sobj) { auto ptr = dynamic_cast*>(sobj.get()); if (ptr) return ptr->getData(); return sol::optional(); } sol::optional storedObjectToBool(const StoredObject& sobj) { return getPrimitiveHolderData(sobj); } sol::optional storedObjectToDouble(const StoredObject& sobj) { return getPrimitiveHolderData(sobj); } sol::optional storedObjectToString(const StoredObject& sobj) { return getPrimitiveHolderData(sobj); } } // effil