table support in __index metamethod (#143)

* table support in __index metamethod

* fix after merge
This commit is contained in:
mihacooper 2020-03-03 19:34:56 +03:00 committed by GitHub
parent cfe78b12b0
commit 616fdb86cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 72 additions and 9 deletions

View File

@ -1,4 +1,5 @@
#include "shared-table.h" #include "shared-table.h"
#include "function.h"
#include "utils.h" #include "utils.h"
@ -191,11 +192,34 @@ void SharedTable::luaNewIndex(const sol::stack_object& luaKey, const sol::stack_
} RETHROW_WITH_PREFIX("effil.table"); } RETHROW_WITH_PREFIX("effil.table");
} }
sol::object SharedTable::luaIndex(const sol::stack_object& luaKey, sol::this_state state) { sol::object SharedTable::luaIndex(const sol::stack_object& luaKey, sol::this_state state) const {
DEFFINE_METAMETHOD_CALL("__index", *this, luaKey) REQUIRE(luaKey.valid()) << "Indexing by nil";
try { try {
return rawGet(luaKey, state); StoredObject key = createStoredObject(luaKey);
if (sol::object result = get(key, state)) {
return result;
}
} RETHROW_WITH_PREFIX("effil.table"); } RETHROW_WITH_PREFIX("effil.table");
SharedLock lock(ctx_->lock);
if (ctx_->metatable != GCNull) {
const auto tableHolder = GC::instance().get<SharedTable>(ctx_->metatable);
lock.unlock();
SharedLock mt_lock(tableHolder.ctx_->lock);
const auto iter = tableHolder.ctx_->entries.find(createStoredObject("__index"));
if (iter != tableHolder.ctx_->entries.end()) {
if (const auto tbl = storedObjectTo<SharedTable>(iter->second)) {
mt_lock.unlock();
return tbl->luaIndex(luaKey, state);
}
else if (const auto func = storedObjectTo<Function>(iter->second)) {
mt_lock.unlock();
return func->loadFunction(state).as<sol::function>()(*this, luaKey);
}
}
}
return sol::nil;
} }
StoredArray SharedTable::luaCall(sol::this_state state, const sol::variadic_args& args) { StoredArray SharedTable::luaCall(sol::this_state state, const sol::variadic_args& args) {

View File

@ -40,7 +40,7 @@ public:
// 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);
sol::object luaIndex(const sol::stack_object& key, sol::this_state state); sol::object luaIndex(const sol::stack_object& key, sol::this_state state) const;
sol::object luaToString(sol::this_state state); sol::object luaToString(sol::this_state state);
sol::object luaLength(sol::this_state state); sol::object luaLength(sol::this_state state);
PairsIterator luaPairs(sol::this_state); PairsIterator luaPairs(sol::this_state);

View File

@ -250,4 +250,20 @@ sol::optional<std::string> storedObjectToString(const StoredObject& sobj) {
return getPrimitiveHolderData<std::string>(sobj); return getPrimitiveHolderData<std::string>(sobj);
} }
template<>
sol::optional<SharedTable> storedObjectTo(const StoredObject& obj) {
if (const auto ptr = std::dynamic_pointer_cast<SharedTableHolder>(obj)) {
return GC::instance().get<SharedTable>(ptr->gcHandle());
}
return sol::nullopt;
}
template<>
sol::optional<Function> storedObjectTo(const StoredObject& obj) {
if (const auto ptr = std::dynamic_pointer_cast<FunctionHolder>(obj)) {
return GC::instance().get<Function>(ptr->gcHandle());
}
return sol::nullopt;
}
} // effil } // effil

View File

@ -57,4 +57,7 @@ sol::optional<double> storedObjectToDouble(const StoredObject&);
sol::optional<LUA_INDEX_TYPE> storedObjectToIndexType(const StoredObject&); sol::optional<LUA_INDEX_TYPE> storedObjectToIndexType(const StoredObject&);
sol::optional<std::string> storedObjectToString(const StoredObject&); sol::optional<std::string> storedObjectToString(const StoredObject&);
template<typename T>
sol::optional<T> storedObjectTo(const StoredObject&);
} // effil } // effil

View File

@ -21,11 +21,11 @@ end
test.metatable.index_p = function (metatable) test.metatable.index_p = function (metatable)
local share = effil.table() local share = effil.table()
metatable.__index = function(t, key) metatable.__index = function(t, key)
return "mt_" .. effil.rawget(t, key) return "mt_" .. effil.rawget(t, key .. "_origin")
end end
effil.setmetatable(share, metatable) effil.setmetatable(share, metatable)
share.table_key = "table_value" share.table_key_origin = "table_value"
test.equal(share.table_key, "mt_table_value") test.equal(share.table_key, "mt_table_value")
end end
@ -164,9 +164,9 @@ test.shared_table_with_metatable.as_shared_table = function()
-- Only __index metamethod -- Only __index metamethod
mt.__index = function(t, key) mt.__index = function(t, key)
return "mt_" .. effil.rawget(t, key) return "mt_" .. effil.rawget(t, key .. "_origin")
end end
share.table_key = "table_value" share.table_key_origin = "table_value"
test.equal(share.table_key, "mt_table_value") test.equal(share.table_key, "mt_table_value")
-- Both __index and __newindex metamethods -- Both __index and __newindex metamethods
@ -174,7 +174,7 @@ test.shared_table_with_metatable.as_shared_table = function()
effil.rawset(t, key, "mt_" .. value) effil.rawset(t, key, "mt_" .. value)
end end
share.table_key = "table_value" share.table_key = "table_value"
test.equal(share.table_key, "mt_mt_table_value") test.equal(share.table_key, "mt_table_value")
-- Remove __index, use only __newindex metamethods -- Remove __index, use only __newindex metamethods
mt.__index = nil mt.__index = nil
@ -182,6 +182,26 @@ 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.table_as_index = function()
local tbl = effil.table{}
local mt = effil.table{ a = 1 }
local mt2 = effil.table{ b = 2 }
local mt3 = effil.table{ c = 2 }
effil.setmetatable(tbl, {__index=mt})
test.equal(tbl.a, 1)
test.equal(tbl.b, nil)
effil.setmetatable(mt, {__index=mt2})
test.equal(tbl.a, 1)
test.equal(tbl.b, 2)
effil.setmetatable(mt2, {__index=mt3})
test.equal(tbl.a, 1)
test.equal(tbl.b, 2)
test.equal(tbl.c, 2)
end
test.shared_table_with_metatable.next_iterator = function() test.shared_table_with_metatable.next_iterator = function()
local visited = {a = 1, [2] = 3, [true] = "asd", [2.2] = "qwe"} local visited = {a = 1, [2] = 3, [true] = "asd", [2.2] = "qwe"}
local share = effil.table(visited) local share = effil.table(visited)