effil/src/cpp/stored-object.cpp
2017-02-15 16:30:17 +03:00

212 lines
6.7 KiB
C++

#include "stored-object.h"
#include "shared-table.h"
#include "utils.h"
#include <map>
#include <vector>
#include <algorithm>
#include <cassert>
namespace effil {
namespace {
template<typename StoredType>
class PrimitiveHolder : public BaseHolder {
public:
PrimitiveHolder(const sol::stack_object& luaObject) noexcept
: data_(luaObject.as<StoredType>()) {}
PrimitiveHolder(const sol::object& luaObject) noexcept
: data_(luaObject.as<StoredType>()) {}
PrimitiveHolder(const StoredType& init) noexcept
: data_(init) {}
bool rawCompare(const BaseHolder* other) const noexcept final {
return data_ < static_cast<const PrimitiveHolder<StoredType>*>(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<typename SolObject>
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<const FunctionHolder*>(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<typename SolType>
TableHolder(const SolType& luaObject) {
assert(luaObject.template is<SharedTable>());
handle_ = luaObject.template as<SharedTable>().handle();
assert(getGC().has(handle_));
}
TableHolder(GCObjectHandle handle)
: handle_(handle) {}
bool rawCompare(const BaseHolder *other) const final {
return handle_ < static_cast<const TableHolder*>(other)->handle_;
}
sol::object unpack(sol::this_state state) const final {
return sol::make_object(state, *static_cast<SharedTable*>(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<std::pair<sol::object, GCObjectHandle>> 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<sol::table, GCObjectHandle>& element){
return element.first == luaTable;
};
auto st = std::find_if(visited.begin(), visited.end(), comparator);
if (st == std::end(visited)) {
SharedTable table = getGC().create<SharedTable>();
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<typename SolObject>
StoredObject fromSolObject(const SolObject& luaObject) {
switch(luaObject.get_type()) {
case sol::type::nil:
break;
case sol::type::boolean:
return std::make_unique<PrimitiveHolder<bool>>(luaObject);
case sol::type::number:
return std::make_unique<PrimitiveHolder<double>>(luaObject);
case sol::type::string:
return std::make_unique<PrimitiveHolder<std::string>>(luaObject);
case sol::type::userdata:
return std::make_unique<TableHolder>(luaObject);
case sol::type::function:
return std::make_unique<FunctionHolder>(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<SharedTable>();
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<TableHolder>(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<PrimitiveHolder<bool>>(value);
}
StoredObject createStoredObject(double value) {
return std::make_unique<PrimitiveHolder<double>>(value);
}
StoredObject createStoredObject(const std::string& value) {
return std::make_unique<PrimitiveHolder<std::string>>(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<TableHolder>(handle);
}
template<typename DataType>
sol::optional<DataType> getPrimitiveHolderData(const StoredObject& sobj) {
auto ptr = dynamic_cast<PrimitiveHolder<DataType>*>(sobj.get());
if (ptr)
return ptr->getData();
return sol::optional<DataType>();
}
sol::optional<bool> storedObjectToBool(const StoredObject& sobj) {
return getPrimitiveHolderData<bool>(sobj);
}
sol::optional<double> storedObjectToDouble(const StoredObject& sobj) {
return getPrimitiveHolderData<double>(sobj);
}
sol::optional<std::string> storedObjectToString(const StoredObject& sobj) {
return getPrimitiveHolderData<std::string>(sobj);
}
} // effil