diff --git a/src/cpp/shared-table.cpp b/src/cpp/shared-table.cpp index f1ed309..f1c8e54 100644 --- a/src/cpp/shared-table.cpp +++ b/src/cpp/shared-table.cpp @@ -1,4 +1,5 @@ #include "shared-table.h" +#include "function.h" #include "utils.h" @@ -191,11 +192,34 @@ void SharedTable::luaNewIndex(const sol::stack_object& luaKey, const sol::stack_ } RETHROW_WITH_PREFIX("effil.table"); } -sol::object SharedTable::luaIndex(const sol::stack_object& luaKey, sol::this_state state) { - DEFFINE_METAMETHOD_CALL("__index", *this, luaKey) +sol::object SharedTable::luaIndex(const sol::stack_object& luaKey, sol::this_state state) const { + REQUIRE(luaKey.valid()) << "Indexing by nil"; try { - return rawGet(luaKey, state); + StoredObject key = createStoredObject(luaKey); + if (sol::object result = get(key, state)) { + return result; + } } RETHROW_WITH_PREFIX("effil.table"); + + SharedLock lock(ctx_->lock); + if (ctx_->metatable != GCNull) { + const auto tableHolder = GC::instance().get(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(iter->second)) { + mt_lock.unlock(); + return tbl->luaIndex(luaKey, state); + } + else if (const auto func = storedObjectTo(iter->second)) { + mt_lock.unlock(); + return func->loadFunction(state).as()(*this, luaKey); + } + } + } + return sol::nil; } StoredArray SharedTable::luaCall(sol::this_state state, const sol::variadic_args& args) { diff --git a/src/cpp/shared-table.h b/src/cpp/shared-table.h index 1fc6fde..fb1d058 100644 --- a/src/cpp/shared-table.h +++ b/src/cpp/shared-table.h @@ -40,7 +40,7 @@ public: // These functions are metamethods available in Lua 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 luaLength(sol::this_state state); PairsIterator luaPairs(sol::this_state); diff --git a/src/cpp/stored-object.cpp b/src/cpp/stored-object.cpp index c954215..eb21d6b 100644 --- a/src/cpp/stored-object.cpp +++ b/src/cpp/stored-object.cpp @@ -250,4 +250,20 @@ sol::optional storedObjectToString(const StoredObject& sobj) { return getPrimitiveHolderData(sobj); } +template<> +sol::optional storedObjectTo(const StoredObject& obj) { + if (const auto ptr = std::dynamic_pointer_cast(obj)) { + return GC::instance().get(ptr->gcHandle()); + } + return sol::nullopt; +} + +template<> +sol::optional storedObjectTo(const StoredObject& obj) { + if (const auto ptr = std::dynamic_pointer_cast(obj)) { + return GC::instance().get(ptr->gcHandle()); + } + return sol::nullopt; +} + } // effil diff --git a/src/cpp/stored-object.h b/src/cpp/stored-object.h index 7ec16fe..748bc34 100644 --- a/src/cpp/stored-object.h +++ b/src/cpp/stored-object.h @@ -57,4 +57,7 @@ sol::optional storedObjectToDouble(const StoredObject&); sol::optional storedObjectToIndexType(const StoredObject&); sol::optional storedObjectToString(const StoredObject&); +template +sol::optional storedObjectTo(const StoredObject&); + } // effil diff --git a/tests/lua/metatable.lua b/tests/lua/metatable.lua index c5f7e79..a929360 100644 --- a/tests/lua/metatable.lua +++ b/tests/lua/metatable.lua @@ -21,11 +21,11 @@ end test.metatable.index_p = function (metatable) local share = effil.table() metatable.__index = function(t, key) - return "mt_" .. effil.rawget(t, key) + return "mt_" .. effil.rawget(t, key .. "_origin") end effil.setmetatable(share, metatable) - share.table_key = "table_value" + share.table_key_origin = "table_value" test.equal(share.table_key, "mt_table_value") end @@ -164,9 +164,9 @@ test.shared_table_with_metatable.as_shared_table = function() -- Only __index metamethod mt.__index = function(t, key) - return "mt_" .. effil.rawget(t, key) + return "mt_" .. effil.rawget(t, key .. "_origin") end - share.table_key = "table_value" + share.table_key_origin = "table_value" test.equal(share.table_key, "mt_table_value") -- Both __index and __newindex metamethods @@ -174,7 +174,7 @@ test.shared_table_with_metatable.as_shared_table = function() effil.rawset(t, key, "mt_" .. value) end 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 mt.__index = nil @@ -182,6 +182,26 @@ test.shared_table_with_metatable.as_shared_table = function() test.equal(share.table_key, "mt_table_value") 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() local visited = {a = 1, [2] = 3, [true] = "asd", [2.2] = "qwe"} local share = effil.table(visited)