Fix metatable serialization/deserialization and __eq operator logic (#144)
This commit is contained in:
parent
616fdb86cc
commit
89e98f9d4d
@ -3,6 +3,15 @@
|
|||||||
namespace effil {
|
namespace effil {
|
||||||
|
|
||||||
Function::Function(const sol::function& luaObject) {
|
Function::Function(const sol::function& luaObject) {
|
||||||
|
SolTableToShared visited;
|
||||||
|
construct(luaObject, visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
Function::Function(const sol::function& luaObject, SolTableToShared& visited) {
|
||||||
|
construct(luaObject, visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Function::construct(const sol::function& luaObject, SolTableToShared& visited) {
|
||||||
assert(luaObject.valid());
|
assert(luaObject.valid());
|
||||||
assert(luaObject.get_type() == sol::type::function);
|
assert(luaObject.get_type() == sol::type::function);
|
||||||
|
|
||||||
@ -39,7 +48,7 @@ Function::Function(const sol::function& luaObject) {
|
|||||||
StoredObject storedObject;
|
StoredObject storedObject;
|
||||||
try {
|
try {
|
||||||
const auto& upvalue = sol::stack::pop<sol::object>(state);
|
const auto& upvalue = sol::stack::pop<sol::object>(state);
|
||||||
storedObject = createStoredObject(upvalue);
|
storedObject = createStoredObject(upvalue, visited);
|
||||||
assert(storedObject.get() != nullptr);
|
assert(storedObject.get() != nullptr);
|
||||||
}
|
}
|
||||||
catch(const std::exception& err) {
|
catch(const std::exception& err) {
|
||||||
|
|||||||
@ -24,7 +24,9 @@ public:
|
|||||||
private:
|
private:
|
||||||
using Converter = std::function<sol::object(const StoredObject&)>;
|
using Converter = std::function<sol::object(const StoredObject&)>;
|
||||||
sol::object convert(lua_State* state, const Converter& clbk) const;
|
sol::object convert(lua_State* state, const Converter& clbk) const;
|
||||||
|
void construct(const sol::function& luaObject, SolTableToShared& visited);
|
||||||
|
|
||||||
|
explicit Function(const sol::function& luaObject, SolTableToShared& visited);
|
||||||
explicit Function(const sol::function& luaObject);
|
explicit Function(const sol::function& luaObject);
|
||||||
friend class GC;
|
friend class GC;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -109,6 +109,12 @@ sol::object SharedTable::luaDump(sol::this_state state, BaseHolder::DumpCache& c
|
|||||||
result.set(pair.first->convertToLua(state, cache),
|
result.set(pair.first->convertToLua(state, cache),
|
||||||
pair.second->convertToLua(state, cache));
|
pair.second->convertToLua(state, cache));
|
||||||
}
|
}
|
||||||
|
if (ctx_->metatable) {
|
||||||
|
const auto mt = GC::instance().get<SharedTable>(ctx_->metatable);
|
||||||
|
lock.unlock();
|
||||||
|
|
||||||
|
result[sol::metatable_key] = mt.luaDump(state, cache);
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
return sol::table(state.L, sol::ref_index(iter->second));
|
return sol::table(state.L, sol::ref_index(iter->second));
|
||||||
@ -134,7 +140,7 @@ sol::object SharedTable::luaDump(sol::this_state state, BaseHolder::DumpCache& c
|
|||||||
#define PROXY_METAMETHOD_IMPL(tableMethod, methodName, errMsg) \
|
#define PROXY_METAMETHOD_IMPL(tableMethod, methodName, errMsg) \
|
||||||
sol::object SharedTable:: tableMethod(sol::this_state state, \
|
sol::object SharedTable:: tableMethod(sol::this_state state, \
|
||||||
const sol::stack_object& leftObject, const sol::stack_object& rightObject) { \
|
const sol::stack_object& leftObject, const sol::stack_object& rightObject) { \
|
||||||
return basicMetaMethod(methodName, errMsg, state, leftObject, rightObject); \
|
return basicBinaryMetaMethod(methodName, errMsg, state, leftObject, rightObject); \
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -143,7 +149,7 @@ const std::string COMPARE_ERR_MSG = "attempt to compare a effil::table value";
|
|||||||
const std::string CONCAT_ERR_MSG = "attempt to concatenate a effil::table value";
|
const std::string CONCAT_ERR_MSG = "attempt to concatenate a effil::table value";
|
||||||
}
|
}
|
||||||
|
|
||||||
sol::object SharedTable::basicMetaMethod(const std::string& metamethodName, const std::string& errMsg,
|
sol::object SharedTable::basicBinaryMetaMethod(const std::string& metamethodName, const std::string& errMsg,
|
||||||
sol::this_state state, const sol::stack_object& leftObject, const sol::stack_object& rightObject) {
|
sol::this_state state, const sol::stack_object& leftObject, const sol::stack_object& rightObject) {
|
||||||
if (isSharedTable(leftObject)) {
|
if (isSharedTable(leftObject)) {
|
||||||
SharedTable table = leftObject.as<SharedTable>();
|
SharedTable table = leftObject.as<SharedTable>();
|
||||||
@ -167,7 +173,25 @@ PROXY_METAMETHOD_IMPL(luaMod, "__mod", ARITHMETIC_ERR_MSG)
|
|||||||
PROXY_METAMETHOD_IMPL(luaPow, "__pow", ARITHMETIC_ERR_MSG)
|
PROXY_METAMETHOD_IMPL(luaPow, "__pow", ARITHMETIC_ERR_MSG)
|
||||||
PROXY_METAMETHOD_IMPL(luaLe, "__le", ARITHMETIC_ERR_MSG)
|
PROXY_METAMETHOD_IMPL(luaLe, "__le", ARITHMETIC_ERR_MSG)
|
||||||
PROXY_METAMETHOD_IMPL(luaLt, "__lt", ARITHMETIC_ERR_MSG)
|
PROXY_METAMETHOD_IMPL(luaLt, "__lt", ARITHMETIC_ERR_MSG)
|
||||||
PROXY_METAMETHOD_IMPL(luaEq, "__eq", ARITHMETIC_ERR_MSG)
|
|
||||||
|
sol::object SharedTable::luaEq(sol::this_state state, const sol::stack_object& leftObject,
|
||||||
|
const sol::stack_object& rightObject) {
|
||||||
|
if (isSharedTable(leftObject) && isSharedTable(rightObject)) {
|
||||||
|
{
|
||||||
|
SharedTable table = leftObject.as<SharedTable>();
|
||||||
|
auto ctx_ = table.ctx_;
|
||||||
|
DEFFINE_METAMETHOD_CALL("__eq", table, rightObject)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
SharedTable table = rightObject.as<SharedTable>();
|
||||||
|
auto ctx_ = table.ctx_;
|
||||||
|
DEFFINE_METAMETHOD_CALL("__eq", leftObject, table)
|
||||||
|
}
|
||||||
|
const bool isEqual = leftObject.as<SharedTable>().handle() == rightObject.as<SharedTable>().handle();
|
||||||
|
return sol::make_object(state, isEqual);
|
||||||
|
}
|
||||||
|
return sol::make_object(state, false);
|
||||||
|
}
|
||||||
|
|
||||||
sol::object SharedTable::luaUnm(sol::this_state state) {
|
sol::object SharedTable::luaUnm(sol::this_state state) {
|
||||||
DEFFINE_METAMETHOD_CALL_0("__unm")
|
DEFFINE_METAMETHOD_CALL_0("__unm")
|
||||||
@ -304,27 +328,39 @@ SharedTable::PairsIterator SharedTable::luaIPairs(sol::this_state state) {
|
|||||||
sol::make_object(state, *this));
|
sol::make_object(state, *this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SharedTable SharedTable::setMetatable(const sol::optional<SharedTable>& metaTable) {
|
||||||
|
UniqueLock lock(ctx_->lock);
|
||||||
|
if (ctx_->metatable != GCNull) {
|
||||||
|
ctx_->removeReference(ctx_->metatable);
|
||||||
|
ctx_->metatable = GCNull;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metaTable) {
|
||||||
|
ctx_->metatable = metaTable->handle();
|
||||||
|
ctx_->addReference(ctx_->metatable);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Lua static API functions
|
* Lua static API functions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
SharedTable SharedTable::luaSetMetatable(const sol::stack_object& tbl, const sol::stack_object& mt) {
|
SharedTable SharedTable::luaSetMetatable(const sol::stack_object& tbl, const sol::stack_object& mt) {
|
||||||
REQUIRE(isAnyTable(tbl)) << "bad argument #1 to 'effil.setmetatable' (table expected, got " << luaTypename(tbl) << ")";
|
REQUIRE(isAnyTable(tbl))
|
||||||
REQUIRE(isAnyTable(mt)) << "bad argument #2 to 'effil.setmetatable' (table expected, got " << luaTypename(mt) << ")";
|
<< "bad argument #1 to 'effil.setmetatable' (table expected, got "
|
||||||
|
<< luaTypename(tbl) << ")";
|
||||||
|
REQUIRE(isAnyTable(mt) || !mt.valid())
|
||||||
|
<< "bad argument #2 to 'effil.setmetatable' (table or nil expected, got "
|
||||||
|
<< luaTypename(mt) << ")";
|
||||||
|
|
||||||
SharedTable stable = GC::instance().get<SharedTable>(createStoredObject(tbl)->gcHandle());
|
SolTableToShared cache;
|
||||||
|
SharedTable table = GC::instance().get<SharedTable>(createStoredObject(tbl, cache)->gcHandle());
|
||||||
UniqueLock lock(stable.ctx_->lock);
|
sol::optional<SharedTable> metatable;
|
||||||
if (stable.ctx_->metatable != GCNull) {
|
if (mt.valid()) {
|
||||||
stable.ctx_->removeReference(stable.ctx_->metatable);
|
metatable = GC::instance().get<SharedTable>(createStoredObject(mt, cache)->gcHandle());
|
||||||
stable.ctx_->metatable = GCNull;
|
|
||||||
}
|
}
|
||||||
|
return table.setMetatable(metatable);
|
||||||
const auto mtObj = createStoredObject(mt);
|
|
||||||
stable.ctx_->metatable = mtObj->gcHandle();
|
|
||||||
stable.ctx_->addReference(stable.ctx_->metatable);
|
|
||||||
|
|
||||||
return stable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sol::object SharedTable::luaGetMetatable(const sol::stack_object& tbl, sol::this_state state) {
|
sol::object SharedTable::luaGetMetatable(const sol::stack_object& tbl, sol::this_state state) {
|
||||||
|
|||||||
@ -35,8 +35,10 @@ public:
|
|||||||
void rawSet(const sol::stack_object& luaKey, const sol::stack_object& luaValue);
|
void rawSet(const sol::stack_object& luaKey, const sol::stack_object& luaValue);
|
||||||
sol::object get(const StoredObject& key, sol::this_state state) const;
|
sol::object get(const StoredObject& key, sol::this_state state) const;
|
||||||
sol::object rawGet(const sol::stack_object& key, sol::this_state state) const;
|
sol::object rawGet(const sol::stack_object& key, sol::this_state state) const;
|
||||||
static sol::object basicMetaMethod(const std::string&, const std::string&, sol::this_state,
|
static sol::object basicBinaryMetaMethod(
|
||||||
|
const std::string&, const std::string&, sol::this_state,
|
||||||
const sol::stack_object&, const sol::stack_object&);
|
const sol::stack_object&, const sol::stack_object&);
|
||||||
|
SharedTable setMetatable(const sol::optional<SharedTable>& metaTable);
|
||||||
|
|
||||||
// These functions are metamethods available in Lua
|
// These functions are metamethods available in Lua
|
||||||
void luaNewIndex(const sol::stack_object& luaKey, const sol::stack_object& luaValue, sol::this_state);
|
void luaNewIndex(const sol::stack_object& luaKey, const sol::stack_object& luaValue, sol::this_state);
|
||||||
|
|||||||
@ -118,42 +118,35 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// This class is used as a storage for visited sol::tables
|
void dumpTable(SharedTable& target, const sol::table& luaTable, SolTableToShared& visited);
|
||||||
// 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, GCHandle>> SolTableToShared;
|
|
||||||
|
|
||||||
void dumpTable(SharedTable* target, sol::table luaTable, SolTableToShared& visited);
|
StoredObject makeStoredObject(const sol::object& luaObject, SolTableToShared& visited) {
|
||||||
|
|
||||||
StoredObject makeStoredObject(sol::object luaObject, SolTableToShared& visited) {
|
|
||||||
if (luaObject.get_type() == sol::type::table) {
|
if (luaObject.get_type() == sol::type::table) {
|
||||||
sol::table luaTable = luaObject;
|
sol::table luaTable = luaObject;
|
||||||
auto comparator = [&luaTable](const std::pair<sol::table, GCHandle>& element) {
|
auto st = std::find_if(visited.begin(), visited.end(), [&](const auto& pair) {
|
||||||
return element.first == luaTable;
|
return pair.first == luaObject;
|
||||||
};
|
});
|
||||||
auto st = std::find_if(visited.begin(), visited.end(), comparator);
|
if (st == visited.end()) {
|
||||||
|
|
||||||
if (st == std::end(visited)) {
|
|
||||||
SharedTable table = GC::instance().create<SharedTable>();
|
SharedTable table = GC::instance().create<SharedTable>();
|
||||||
visited.emplace_back(std::make_pair(luaTable, table.handle()));
|
visited.push_back({luaTable, table.handle()});
|
||||||
dumpTable(&table, luaTable, visited);
|
dumpTable(table, luaTable, visited);
|
||||||
return std::make_unique<SharedTableHolder>(table.handle());
|
return std::make_unique<SharedTableHolder>(table.handle());
|
||||||
} else {
|
} else {
|
||||||
return std::make_unique<SharedTableHolder>(st->second);
|
return std::make_unique<SharedTableHolder>(st->second);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return createStoredObject(luaObject);
|
return createStoredObject(luaObject, visited);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void dumpTable(SharedTable* target, sol::table luaTable, SolTableToShared& visited) {
|
void dumpTable(SharedTable& target, const sol::table& luaTable, SolTableToShared& visited) {
|
||||||
for (auto& row : luaTable) {
|
for (auto& row : luaTable) {
|
||||||
target->set(makeStoredObject(row.first, visited), makeStoredObject(row.second, visited));
|
target.set(makeStoredObject(row.first, visited), makeStoredObject(row.second, visited));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename SolObject>
|
template <typename SolObject>
|
||||||
StoredObject fromSolObject(const SolObject& luaObject) {
|
StoredObject fromSolObject(const SolObject& luaObject, SolTableToShared& visited) {
|
||||||
switch (luaObject.get_type()) {
|
switch (luaObject.get_type()) {
|
||||||
case sol::type::nil:
|
case sol::type::nil:
|
||||||
return std::make_unique<NilHolder>();
|
return std::make_unique<NilHolder>();
|
||||||
@ -190,20 +183,34 @@ StoredObject fromSolObject(const SolObject& luaObject) {
|
|||||||
else
|
else
|
||||||
throw Exception() << "Unable to store userdata object";
|
throw Exception() << "Unable to store userdata object";
|
||||||
case sol::type::function: {
|
case sol::type::function: {
|
||||||
Function func = GC::instance().create<Function>(luaObject);
|
Function func = GC::instance().create<Function>(luaObject, visited);
|
||||||
return std::make_unique<FunctionHolder>(func.handle());
|
return std::make_unique<FunctionHolder>(func.handle());
|
||||||
}
|
}
|
||||||
case sol::type::table: {
|
case sol::type::table: {
|
||||||
sol::table luaTable = luaObject;
|
sol::table luaTable = luaObject;
|
||||||
|
|
||||||
|
const auto iter = std::find_if(visited.begin(), visited.end(), [&](const auto& pair) {
|
||||||
|
return pair.first == luaTable;
|
||||||
|
});
|
||||||
|
if (iter != visited.end()) {
|
||||||
|
return std::make_unique<SharedTableHolder>(iter->second);
|
||||||
|
}
|
||||||
// Tables pool is used to store tables.
|
// Tables pool is used to store tables.
|
||||||
// Right now not defiantly clear how ownership between states works.
|
// Right now not defiantly clear how ownership between states works.
|
||||||
SharedTable table = GC::instance().create<SharedTable>();
|
SharedTable table = GC::instance().create<SharedTable>();
|
||||||
SolTableToShared visited{{luaTable, table.handle()}};
|
visited.push_back({luaTable, table.handle()});
|
||||||
|
|
||||||
// Let's dump table and all subtables
|
// Let's dump table and all subtables
|
||||||
// SolTableToShared is used to prevent from infinity recursion
|
// SolTableToShared is used to prevent from infinity recursion
|
||||||
// in recursive tables
|
// in recursive tables
|
||||||
dumpTable(&table, luaTable, visited);
|
dumpTable(table, luaTable, visited);
|
||||||
|
|
||||||
|
const sol::table luaMetatable = luaTable[sol::metatable_key];
|
||||||
|
if (luaMetatable.valid()) {
|
||||||
|
SharedTable metaTable = GC::instance().create<SharedTable>();
|
||||||
|
dumpTable(metaTable, luaMetatable, visited);
|
||||||
|
table.setMetatable(metaTable);
|
||||||
|
}
|
||||||
return std::make_unique<SharedTableHolder>(table.handle());
|
return std::make_unique<SharedTableHolder>(table.handle());
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -228,9 +235,23 @@ StoredObject createStoredObject(const char* value) {
|
|||||||
return std::make_unique<PrimitiveHolder<std::string>>(value);
|
return std::make_unique<PrimitiveHolder<std::string>>(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
StoredObject createStoredObject(const sol::object& object) { return fromSolObject(object); }
|
StoredObject createStoredObject(const sol::object& object) {
|
||||||
|
SolTableToShared visited;
|
||||||
|
return fromSolObject(object, visited);
|
||||||
|
}
|
||||||
|
|
||||||
StoredObject createStoredObject(const sol::stack_object& object) { return fromSolObject(object); }
|
StoredObject createStoredObject(const sol::stack_object& object) {
|
||||||
|
SolTableToShared visited;
|
||||||
|
return fromSolObject(object, visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
StoredObject createStoredObject(const sol::object& obj, SolTableToShared& visited) {
|
||||||
|
return fromSolObject(obj, visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
StoredObject createStoredObject(const sol::stack_object& obj, SolTableToShared& visited) {
|
||||||
|
return fromSolObject(obj, visited);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename DataType>
|
template <typename DataType>
|
||||||
sol::optional<DataType> getPrimitiveHolderData(const StoredObject& sobj) {
|
sol::optional<DataType> getPrimitiveHolderData(const StoredObject& sobj) {
|
||||||
|
|||||||
@ -52,6 +52,11 @@ StoredObject createStoredObject(const char*);
|
|||||||
StoredObject createStoredObject(const sol::object&);
|
StoredObject createStoredObject(const sol::object&);
|
||||||
StoredObject createStoredObject(const sol::stack_object&);
|
StoredObject createStoredObject(const sol::stack_object&);
|
||||||
|
|
||||||
|
using SolTableToShared = std::vector<std::pair<sol::table, GCHandle>>;
|
||||||
|
|
||||||
|
StoredObject createStoredObject(const sol::object& obj, SolTableToShared& visited);
|
||||||
|
StoredObject createStoredObject(const sol::stack_object& obj, SolTableToShared& visited);
|
||||||
|
|
||||||
sol::optional<bool> storedObjectToBool(const StoredObject&);
|
sol::optional<bool> storedObjectToBool(const StoredObject&);
|
||||||
sol::optional<double> storedObjectToDouble(const StoredObject&);
|
sol::optional<double> storedObjectToDouble(const StoredObject&);
|
||||||
sol::optional<LUA_INDEX_TYPE> storedObjectToIndexType(const StoredObject&);
|
sol::optional<LUA_INDEX_TYPE> storedObjectToIndexType(const StoredObject&);
|
||||||
|
|||||||
@ -88,3 +88,16 @@ test.dump_table.upvalues_with_loop = function()
|
|||||||
result.foo()
|
result.foo()
|
||||||
test.equal(result.key, "value")
|
test.equal(result.key, "value")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test.dump_table.with_metatable = function()
|
||||||
|
local tbl = effil.setmetatable({}, effil.setmetatable({a=1}, {b = 2}))
|
||||||
|
local dumped = effil.dump(tbl)
|
||||||
|
|
||||||
|
local mt = getmetatable(dumped)
|
||||||
|
test.not_equal(mt, nil)
|
||||||
|
test.equal(mt.a, 1)
|
||||||
|
|
||||||
|
local mt2 = getmetatable(mt)
|
||||||
|
test.not_equal(mt2, nil)
|
||||||
|
test.equal(mt2.b, 2)
|
||||||
|
end
|
||||||
|
|||||||
@ -182,6 +182,52 @@ test.shared_table_with_metatable.as_shared_table = function()
|
|||||||
test.equal(share.table_key, "mt_table_value")
|
test.equal(share.table_key, "mt_table_value")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test.shared_table_with_metatable.metatable_serialization = function()
|
||||||
|
local table_with_mt = setmetatable({}, {a=1})
|
||||||
|
local tbl = effil.table(table_with_mt)
|
||||||
|
|
||||||
|
test.not_equal(effil.getmetatable(tbl), nil)
|
||||||
|
test.equal(effil.getmetatable(tbl).a, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
test.shared_table_with_metatable.metatable_with_function_with_upvalues = function()
|
||||||
|
local common_table = {aa=2}
|
||||||
|
local tbl = setmetatable({}, {
|
||||||
|
a = function() return common_table end,
|
||||||
|
b = function() return common_table end
|
||||||
|
})
|
||||||
|
|
||||||
|
local mt = effil.getmetatable(effil.table(tbl))
|
||||||
|
test.equal(
|
||||||
|
select(2, debug.getupvalue(mt.a, 1)),
|
||||||
|
select(2, debug.getupvalue(mt.b, 1))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
test.shared_table_with_metatable.check_eq_metamethod = function()
|
||||||
|
local left_table = effil.table()
|
||||||
|
local right_table = effil.table()
|
||||||
|
local left_table_clone = (effil.table{ left_table }[1]) -- userdata will change
|
||||||
|
|
||||||
|
test.is_false(left_table == right_table)
|
||||||
|
test.is_true(left_table == left_table_clone)
|
||||||
|
test.is_false(left_table == effil.channel())
|
||||||
|
|
||||||
|
effil.setmetatable(left_table, { __eq = function() return false end })
|
||||||
|
test.is_false(left_table == left_table_clone)
|
||||||
|
|
||||||
|
effil.setmetatable(left_table, { __eq = function() return true end })
|
||||||
|
test.is_true(left_table == right_table)
|
||||||
|
test.is_false(left_table == effil.channel())
|
||||||
|
|
||||||
|
effil.setmetatable(left_table, { __eq = function() return false end })
|
||||||
|
effil.setmetatable(right_table, { __eq = function() return true end })
|
||||||
|
test.is_false(left_table == right_table)
|
||||||
|
|
||||||
|
effil.setmetatable(left_table, nil)
|
||||||
|
test.is_true(left_table == right_table)
|
||||||
|
end
|
||||||
|
|
||||||
test.shared_table_with_metatable.table_as_index = function()
|
test.shared_table_with_metatable.table_as_index = function()
|
||||||
local tbl = effil.table{}
|
local tbl = effil.table{}
|
||||||
local mt = effil.table{ a = 1 }
|
local mt = effil.table{ a = 1 }
|
||||||
|
|||||||
@ -88,7 +88,7 @@ local function generate_tests()
|
|||||||
-- effil.setmetatable
|
-- effil.setmetatable
|
||||||
if typename ~= "table" and typename ~= "effil.table" then
|
if typename ~= "table" and typename ~= "effil.table" then
|
||||||
test.type_mismatch.input_types_mismatch_p(1, "table", "setmetatable", type_instance, 44)
|
test.type_mismatch.input_types_mismatch_p(1, "table", "setmetatable", type_instance, 44)
|
||||||
test.type_mismatch.input_types_mismatch_p(2, "table", "setmetatable", {}, type_instance)
|
test.type_mismatch.input_types_mismatch_p(2, "table or nil", "setmetatable", {}, type_instance)
|
||||||
end
|
end
|
||||||
|
|
||||||
if typename ~= "effil.table" then
|
if typename ~= "effil.table" then
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user