From c6d11a2bac056c71f716586d8b3e45fadfc58702 Mon Sep 17 00:00:00 2001 From: ThePhD Date: Sun, 28 May 2017 19:14:18 -0400 Subject: [PATCH] fix index and newindex semantics on simple_usertypes we do not have to hit the metatable by-name since we go to special lengths to ensure we have a simple usertype table we can use --- examples/usertype_simple.cpp | 6 +- single/sol/sol.hpp | 280 +++++++++++++++++++++--------- sol/simple_usertype_metatable.hpp | 138 ++++++++++----- sol/types.hpp | 36 +++- sol/usertype_metatable.hpp | 102 +++++++---- test_simple_usertypes.cpp | 36 ++++ 6 files changed, 426 insertions(+), 172 deletions(-) diff --git a/examples/usertype_simple.cpp b/examples/usertype_simple.cpp index f851f0e5..dce8d229 100644 --- a/examples/usertype_simple.cpp +++ b/examples/usertype_simple.cpp @@ -57,7 +57,8 @@ int main() { // Mix it all together! lua.script(R"( mdata = my_data.new() - +)"); + lua.script(R"( local g = generator.new() g.data = mdata.first list1 = g:generate_list() @@ -65,7 +66,8 @@ g.data = mdata.second list2 = g:generate_list() g.data = mdata.third list3 = g:generate_list() - +)"); + lua.script(R"( print("From lua: ") for i = 1, #list1 do print("\tlist1[" .. i .. "] = " .. list1[i]) diff --git a/single/sol/sol.hpp b/single/sol/sol.hpp index 45cecd9b..1b6d8b15 100644 --- a/single/sol/sol.hpp +++ b/single/sol/sol.hpp @@ -20,8 +20,8 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // This file was generated with a script. -// Generated 2017-05-21 00:02:50.302747 UTC -// This header was generated with sol v2.17.4 (revision 6047f48) +// Generated 2017-05-28 23:13:00.247039 UTC +// This header was generated with sol v2.17.4 (revision acee4db) // https://github.com/ThePhD/sol2 #ifndef SOL_SINGLE_INCLUDE_HPP @@ -3177,26 +3177,46 @@ namespace sol { struct upvalue_index { int index; - upvalue_index(int idx) : index(lua_upvalueindex(idx)) {} - operator int() const { return index; } + upvalue_index(int idx) : index(lua_upvalueindex(idx)) { + + } + + operator int() const { + return index; + } }; struct raw_index { int index; - raw_index(int i) : index(i) {} - operator int() const { return index; } + raw_index(int i) : index(i) { + + } + + operator int() const { + return index; + } }; struct absolute_index { int index; - absolute_index(lua_State* L, int idx) : index(lua_absindex(L, idx)) {} - operator int() const { return index; } + absolute_index(lua_State* L, int idx) : index(lua_absindex(L, idx)) { + + } + + operator int() const { + return index; + } }; struct ref_index { int index; - ref_index(int idx) : index(idx) {} - operator int() const { return index; } + ref_index(int idx) : index(idx) { + + } + + operator int() const { + return index; + } }; struct lightuserdata_value { @@ -10565,6 +10585,15 @@ namespace sol { namespace sol { namespace usertype_detail { + const int metatable_index = 2; + const int metatable_core_index = 3; + const int filler_index = 4; + const int magic_index = 5; + + const int simple_metatable_index = 2; + const int index_function_index = 3; + const int newindex_function_index = 4; + typedef void(*base_walk)(lua_State*, bool&, int&, string_detail::string_shim&); typedef int(*member_search)(lua_State*, void*, int); @@ -10608,10 +10637,15 @@ namespace sol { const char* metakey; variable_map variables; function_map functions; + object index; + object newindex; base_walk indexbaseclasspropogation; base_walk newindexbaseclasspropogation; - simple_map(const char* mkey, base_walk index, base_walk newindex, variable_map&& vars, function_map&& funcs) : metakey(mkey), variables(std::move(vars)), functions(std::move(funcs)), indexbaseclasspropogation(index), newindexbaseclasspropogation(newindex) {} + simple_map(const char* mkey, base_walk index, base_walk newindex, object i, object ni, variable_map&& vars, function_map&& funcs) + : metakey(mkey), variables(std::move(vars)), functions(std::move(funcs)), + index(std::move(i)), newindex(std::move(ni)), + indexbaseclasspropogation(index), newindexbaseclasspropogation(newindex) {} }; } @@ -10680,20 +10714,32 @@ namespace sol { } }; - inline bool is_indexer(string_detail::string_shim s) { - return s == to_string(meta_function::index) || s == to_string(meta_function::new_index); + inline int is_indexer(string_detail::string_shim s) { + if (s == to_string(meta_function::index)) { + return 1; + } + else if (s == to_string(meta_function::new_index)) { + return 2; + } + return 0; } - inline bool is_indexer(meta_function mf) { - return mf == meta_function::index || mf == meta_function::new_index; + inline int is_indexer(meta_function mf) { + if (mf == meta_function::index) { + return 1; + } + else if (mf == meta_function::new_index) { + return 2; + } + return 0; } - inline bool is_indexer(call_construction) { - return false; + inline int is_indexer(call_construction) { + return 0; } - inline bool is_indexer(base_classes_tag) { - return false; + inline int is_indexer(base_classes_tag) { + return 0; } inline auto make_shim(string_detail::string_shim s) { @@ -10735,7 +10781,7 @@ namespace sol { }; inline int runtime_object_call(lua_State* L, void*, int runtimetarget) { - usertype_metatable_core& umc = stack::get>(L, upvalue_index(2)); + usertype_metatable_core& umc = stack::get>(L, upvalue_index(metatable_core_index)); std::vector& runtime = umc.runtime; object& runtimeobj = runtime[runtimetarget]; return stack::push(L, runtimeobj); @@ -10750,7 +10796,7 @@ namespace sol { return luaL_error(L, "sol: attempt to index (get) nil value \"%s\" on userdata (bad (misspelled?) key name or does not exist)", accessor.c_str()); #else int isnum = 0; - lua_Integer magic = lua_tointegerx(L, upvalue_index(4), &isnum); + lua_Integer magic = lua_tointegerx(L, upvalue_index(magic_index), &isnum); if (isnum != 0 && magic == toplevel_magic) { if (lua_getmetatable(L, 1) == 1) { int metatarget = lua_gettop(L); @@ -10774,11 +10820,11 @@ namespace sol { template inline int metatable_newindex(lua_State* L) { int isnum = 0; - lua_Integer magic = lua_tointegerx(L, upvalue_index(4), &isnum); + lua_Integer magic = lua_tointegerx(L, upvalue_index(magic_index), &isnum); if (isnum != 0 && magic == toplevel_magic) { auto non_indexable = [&L]() { if (is_simple) { - simple_map& sm = stack::get>(L, upvalue_index(1)); + simple_map& sm = stack::get>(L, upvalue_index(simple_metatable_index)); function_map& functions = sm.functions; optional maybeaccessor = stack::get>(L, 2); if (!maybeaccessor) { @@ -10794,7 +10840,7 @@ namespace sol { } return; } - usertype_metatable_core& umc = stack::get>(L, upvalue_index(2)); + usertype_metatable_core& umc = stack::get>(L, upvalue_index(metatable_core_index)); bool mustindex = umc.mustindex; if (!mustindex) return; @@ -10855,7 +10901,7 @@ namespace sol { } inline int runtime_new_index(lua_State* L, void*, int runtimetarget) { - usertype_metatable_core& umc = stack::get>(L, upvalue_index(2)); + usertype_metatable_core& umc = stack::get>(L, upvalue_index(metatable_core_index)); std::vector& runtime = umc.runtime; object& runtimeobj = runtime[runtimetarget]; runtimeobj = object(L, 3); @@ -10985,7 +11031,7 @@ namespace sol { bool contains_index() const { bool idx = false; - (void)detail::swallow{ 0, ((idx |= usertype_detail::is_indexer(std::get(functions))), 0) ... }; + (void)detail::swallow{ 0, ((idx |= (usertype_detail::is_indexer(std::get(functions)) != 0)), 0) ... }; return idx; } @@ -11101,7 +11147,11 @@ namespace sol { if (is_variable_binding(f.functions))>::value) { return real_call_with(L, f); } - int upvalues = stack::push(L, light(f)); + // set up upvalues + // for a chained call + int upvalues = 0; + upvalues += stack::push(L, nullptr); + upvalues += stack::push(L, light(f)); auto cfunc = &call; return stack::push(L, c_closure(cfunc, upvalues)); } @@ -11114,7 +11164,9 @@ namespace sol { template static int core_indexing_call(lua_State* L) { - usertype_metatable& f = toplevel ? stack::get>(L, upvalue_index(1)) : stack::pop>(L); + usertype_metatable& f = toplevel + ? stack::get>(L, upvalue_index(usertype_detail::metatable_index)) + : stack::pop>(L); static const int keyidx = -2 + static_cast(is_index); if (toplevel && stack::get(L, keyidx) != type::string) { return is_index ? f.indexfunc(L) : f.newindexfunc(L); @@ -11150,7 +11202,7 @@ namespace sol { template static int real_call(lua_State* L) { - usertype_metatable& f = stack::get>(L, upvalue_index(1)); + usertype_metatable& f = stack::get>(L, upvalue_index(usertype_detail::metatable_index)); return real_call_with(L, f); } @@ -11267,8 +11319,10 @@ namespace sol { } luaL_newmetatable(L, metakey); stack_reference t(L, -1); - stack::push(L, make_light(um)); - luaL_setfuncs(L, metaregs, 1); + int upvalues = 0; + upvalues += stack::push(L, nullptr); + upvalues += stack::push(L, make_light(um)); + luaL_setfuncs(L, metaregs, upvalues); if (um.baseclasscheck != nullptr) { stack::set_field(L, detail::base_class_check_key(), um.baseclasscheck, t.stack_index()); @@ -11277,14 +11331,14 @@ namespace sol { stack::set_field(L, detail::base_class_cast_key(), um.baseclasscast, t.stack_index()); } - stack::set_field(L, detail::base_class_index_propogation_key(), make_closure(um.indexbase, make_light(um), make_light(umc)), t.stack_index()); - stack::set_field(L, detail::base_class_new_index_propogation_key(), make_closure(um.newindexbase, make_light(um), make_light(umc)), t.stack_index()); + stack::set_field(L, detail::base_class_index_propogation_key(), make_closure(um.indexbase, nullptr, make_light(um), make_light(umc)), t.stack_index()); + stack::set_field(L, detail::base_class_new_index_propogation_key(), make_closure(um.newindexbase, nullptr, make_light(um), make_light(umc)), t.stack_index()); if (mustindex) { // Basic index pushing: specialize // index and newindex to give variables and stuff - stack::set_field(L, meta_function::index, make_closure(umt_t::index_call, make_light(um), make_light(umc)), t.stack_index()); - stack::set_field(L, meta_function::new_index, make_closure(umt_t::new_index_call, make_light(um), make_light(umc)), t.stack_index()); + stack::set_field(L, meta_function::index, make_closure(umt_t::index_call, nullptr, make_light(um), make_light(umc)), t.stack_index()); + stack::set_field(L, meta_function::new_index, make_closure(umt_t::new_index_call, nullptr, make_light(um), make_light(umc)), t.stack_index()); } else { // If there's only functions, we can use the fast index version @@ -11295,11 +11349,11 @@ namespace sol { lua_createtable(L, 0, 3); stack_reference metabehind(L, -1); if (um.callconstructfunc != nullptr) { - stack::set_field(L, meta_function::call_function, make_closure(um.callconstructfunc, make_light(um), make_light(umc)), metabehind.stack_index()); + stack::set_field(L, meta_function::call_function, make_closure(um.callconstructfunc, nullptr, make_light(um), make_light(umc)), metabehind.stack_index()); } if (um.secondarymeta) { - stack::set_field(L, meta_function::index, make_closure(umt_t::index_call, make_light(um), make_light(umc)), metabehind.stack_index()); - stack::set_field(L, meta_function::new_index, make_closure(umt_t::new_index_call, make_light(um), make_light(umc)), metabehind.stack_index()); + stack::set_field(L, meta_function::index, make_closure(umt_t::index_call, nullptr, make_light(um), make_light(umc)), metabehind.stack_index()); + stack::set_field(L, meta_function::new_index, make_closure(umt_t::new_index_call, nullptr, make_light(um), make_light(umc)), metabehind.stack_index()); } stack::set_field(L, metatable_key, metabehind, t.stack_index()); metabehind.pop(); @@ -11311,17 +11365,19 @@ namespace sol { // Now for the shim-table that actually gets assigned to the name luaL_newmetatable(L, &usertype_traits::user_metatable()[0]); stack_reference t(L, -1); - stack::push(L, make_light(um)); - luaL_setfuncs(L, value_table.data(), 1); + int upvalues = 0; + upvalues += stack::push(L, nullptr); + upvalues += stack::push(L, make_light(um)); + luaL_setfuncs(L, value_table.data(), upvalues); { lua_createtable(L, 0, 3); stack_reference metabehind(L, -1); if (um.callconstructfunc != nullptr) { - stack::set_field(L, meta_function::call_function, make_closure(um.callconstructfunc, make_light(um), make_light(umc)), metabehind.stack_index()); + stack::set_field(L, meta_function::call_function, make_closure(um.callconstructfunc, nullptr, make_light(um), make_light(umc)), metabehind.stack_index()); } - stack::set_field(L, meta_function::index, make_closure(umt_t::index_call, make_light(um), make_light(umc), 0, usertype_detail::toplevel_magic), metabehind.stack_index()); - stack::set_field(L, meta_function::new_index, make_closure(umt_t::new_index_call, make_light(um), make_light(umc), 0, usertype_detail::toplevel_magic), metabehind.stack_index()); + stack::set_field(L, meta_function::index, make_closure(umt_t::index_call, nullptr, make_light(um), make_light(umc), nullptr, usertype_detail::toplevel_magic), metabehind.stack_index()); + stack::set_field(L, meta_function::new_index, make_closure(umt_t::new_index_call, nullptr, make_light(um), make_light(umc), nullptr, usertype_detail::toplevel_magic), metabehind.stack_index()); stack::set_field(L, metatable_key, metabehind, t.stack_index()); metabehind.pop(); @@ -11342,16 +11398,36 @@ namespace sol { namespace sol { namespace usertype_detail { - template + inline int call_indexing_object(lua_State* L, object& f) { + int before = lua_gettop(L); + f.push(); + for (int i = 1; i <= before; ++i) { + lua_pushvalue(L, i); + } + lua_callk(L, before, LUA_MULTRET, 0, nullptr); + int after = lua_gettop(L); + return after - before; + } + + template inline int simple_core_indexing_call(lua_State* L) { - simple_map& sm = toplevel ? stack::get>(L, upvalue_index(1)) : stack::pop>(L); + simple_map& sm = toplevel + ? stack::get>(L, upvalue_index(simple_metatable_index)) + : stack::pop>(L); variable_map& variables = sm.variables; function_map& functions = sm.functions; static const int keyidx = -2 + static_cast(is_index); if (toplevel) { if (stack::get(L, keyidx) != type::string) { - lua_CFunction indexingfunc = is_index ? stack::get(L, upvalue_index(2)) : stack::get(L, upvalue_index(3)); - return indexingfunc(L); + if (has_indexing) { + object& indexingfunc = is_index + ? sm.index + : sm.newindex; + return call_indexing_object(L, indexingfunc); + } + else { + return usertype_detail::indexing_fail(L); + } } } string_detail::string_shim accessor = stack::get(L, keyidx); @@ -11371,11 +11447,20 @@ namespace sol { return stack::push(L, func); } else { - lua_CFunction indexingfunc = is_index ? stack::get(L, upvalue_index(2)) : stack::get(L, upvalue_index(3)); - return indexingfunc(L); + if (has_indexing) { + object& indexingfunc = is_index + ? sm.index + : sm.newindex; + return call_indexing_object(L, indexingfunc); + } + else { + return is_index + ? usertype_detail::indexing_fail(L) + : usertype_detail::metatable_newindex(L); + } } } - // Check table storage first for a method that works + /* Check table storage first for a method that works luaL_getmetatable(L, sm.metakey); if (type_of(L, -1) != type::lua_nil) { stack::get_field(L, accessor.c_str(), lua_gettop(L)); @@ -11387,7 +11472,8 @@ namespace sol { lua_pop(L, 1); } lua_pop(L, 1); - + */ + int ret = 0; bool found = false; // Otherwise, we need to do propagating calls through the bases @@ -11401,26 +11487,37 @@ namespace sol { return ret; } if (toplevel) { - lua_CFunction indexingfunc = is_index ? stack::get(L, upvalue_index(2)) : stack::get(L, upvalue_index(3)); - return indexingfunc(L); + if (has_indexing) { + object& indexingfunc = is_index + ? sm.index + : sm.newindex; + return call_indexing_object(L, indexingfunc); + } + else { + return usertype_detail::metatable_newindex(L); + } } return -1; } + template inline int simple_real_index_call(lua_State* L) { - return simple_core_indexing_call(L); + return simple_core_indexing_call(L); } + template inline int simple_real_new_index_call(lua_State* L) { - return simple_core_indexing_call(L); + return simple_core_indexing_call(L); } + template inline int simple_index_call(lua_State* L) { - return detail::static_trampoline<(&simple_real_index_call)>(L); + return detail::static_trampoline<(&simple_real_index_call)>(L); } + template inline int simple_new_index_call(lua_State* L) { - return detail::static_trampoline<(&simple_real_new_index_call)>(L); + return detail::static_trampoline<(&simple_real_new_index_call)>(L); } } @@ -11432,8 +11529,8 @@ namespace sol { usertype_detail::function_map registrations; usertype_detail::variable_map varmap; object callconstructfunc; - lua_CFunction indexfunc; - lua_CFunction newindexfunc; + object indexfunc; + object newindexfunc; lua_CFunction indexbase; lua_CFunction newindexbase; usertype_detail::base_walk indexbaseclasspropogation; @@ -11446,6 +11543,15 @@ namespace sol { template void insert(N&& n, object&& o) { std::string key = usertype_detail::make_string(std::forward(n)); + int is_indexer = static_cast(usertype_detail::is_indexer(n)); + if (is_indexer == 1) { + indexfunc = o; + mustindex = true; + } + else if (is_indexer == 2) { + newindexfunc = o; + mustindex = true; + } auto hint = registrations.find(key); if (hint == registrations.cend()) { registrations.emplace_hint(hint, std::move(key), std::move(o)); @@ -11572,8 +11678,8 @@ namespace sol { template simple_usertype_metatable(usertype_detail::verified_tag, std::index_sequence, lua_State* L, Tuple&& args) : callconstructfunc(lua_nil), - indexfunc(&usertype_detail::indexing_fail), newindexfunc(&usertype_detail::metatable_newindex), - indexbase(&usertype_detail::simple_core_indexing_call), newindexbase(&usertype_detail::simple_core_indexing_call), + indexfunc(lua_nil), newindexfunc(lua_nil), + indexbase(&usertype_detail::simple_core_indexing_call), newindexbase(&usertype_detail::simple_core_indexing_call), indexbaseclasspropogation(usertype_detail::walk_all_bases), newindexbaseclasspropogation(&usertype_detail::walk_all_bases), baseclasscheck(nullptr), baseclasscast(nullptr), mustindex(false), secondarymeta(false) { @@ -11642,6 +11748,7 @@ namespace sol { const char* gcmetakey = &usertype_traits::gc_table()[0]; stack::push>(L, metatable_key, uniquegcmetakey, &usertype_traits::metatable()[0], umx.indexbaseclasspropogation, umx.newindexbaseclasspropogation, + std::move(umx.indexfunc), std::move(umx.newindexfunc), std::move(umx.varmap), std::move(umx.registrations) ); stack_reference stackvarmap(L, -1); @@ -11654,7 +11761,11 @@ namespace sol { } static int push(lua_State* L, umt_t&& umx) { + bool hasindex = umx.indexfunc.valid(); + bool hasnewindex = umx.newindexfunc.valid(); auto& varmap = make_cleanup(L, umx); + auto sic = hasindex ? &usertype_detail::simple_index_call : &usertype_detail::simple_index_call; + auto snic = hasnewindex ? &usertype_detail::simple_new_index_call : &usertype_detail::simple_new_index_call; bool hasequals = false; bool hasless = false; bool haslessequals = false; @@ -11669,10 +11780,10 @@ namespace sol { haslessequals = true; } else if (first == to_string(meta_function::index)) { - umx.indexfunc = second.template as(); + umx.indexfunc = second; } else if (first == to_string(meta_function::new_index)) { - umx.newindexfunc = second.template as(); + umx.newindexfunc = second; } switch (i) { case 0: @@ -11693,7 +11804,6 @@ namespace sol { stack::set_field(L, first, second, t.stack_index()); }; for (std::size_t i = 0; i < 3; ++i) { - // Pointer types, AKA "references" from C++ const char* metakey = nullptr; switch (i) { case 0: @@ -11746,16 +11856,14 @@ namespace sol { if (umx.mustindex) { // use indexing function stack::set_field(L, meta_function::index, - make_closure(&usertype_detail::simple_index_call, - make_light(varmap), - umx.indexfunc, - umx.newindexfunc + make_closure(sic, + nullptr, + make_light(varmap) ), t.stack_index()); stack::set_field(L, meta_function::new_index, - make_closure(&usertype_detail::simple_new_index_call, - make_light(varmap), - umx.indexfunc, - umx.newindexfunc + make_closure(snic, + nullptr, + make_light(varmap) ), t.stack_index()); } else { @@ -11771,16 +11879,14 @@ namespace sol { } if (umx.secondarymeta) { stack::set_field(L, meta_function::index, - make_closure(&usertype_detail::simple_index_call, - make_light(varmap), - umx.indexfunc, - umx.newindexfunc + make_closure(sic, + nullptr, + make_light(varmap) ), metabehind.stack_index()); stack::set_field(L, meta_function::new_index, - make_closure(&usertype_detail::simple_new_index_call, - make_light(varmap), - umx.indexfunc, - umx.newindexfunc + make_closure(snic, + nullptr, + make_light(varmap) ), metabehind.stack_index()); } stack::set_field(L, metatable_key, metabehind, t.stack_index()); @@ -11805,17 +11911,19 @@ namespace sol { } // use indexing function stack::set_field(L, meta_function::index, - make_closure(&usertype_detail::simple_index_call, + make_closure(sic, + nullptr, make_light(varmap), - umx.indexfunc, - umx.newindexfunc, + nullptr, + nullptr, usertype_detail::toplevel_magic ), metabehind.stack_index()); stack::set_field(L, meta_function::new_index, - make_closure(&usertype_detail::simple_new_index_call, + make_closure(snic, + nullptr, make_light(varmap), - umx.indexfunc, - umx.newindexfunc, + nullptr, + nullptr, usertype_detail::toplevel_magic ), metabehind.stack_index()); stack::set_field(L, metatable_key, metabehind, t.stack_index()); diff --git a/sol/simple_usertype_metatable.hpp b/sol/simple_usertype_metatable.hpp index d721ded9..dfda15f3 100644 --- a/sol/simple_usertype_metatable.hpp +++ b/sol/simple_usertype_metatable.hpp @@ -31,16 +31,36 @@ namespace sol { namespace usertype_detail { - template + inline int call_indexing_object(lua_State* L, object& f) { + int before = lua_gettop(L); + f.push(); + for (int i = 1; i <= before; ++i) { + lua_pushvalue(L, i); + } + lua_callk(L, before, LUA_MULTRET, 0, nullptr); + int after = lua_gettop(L); + return after - before; + } + + template inline int simple_core_indexing_call(lua_State* L) { - simple_map& sm = toplevel ? stack::get>(L, upvalue_index(1)) : stack::pop>(L); + simple_map& sm = toplevel + ? stack::get>(L, upvalue_index(simple_metatable_index)) + : stack::pop>(L); variable_map& variables = sm.variables; function_map& functions = sm.functions; static const int keyidx = -2 + static_cast(is_index); if (toplevel) { if (stack::get(L, keyidx) != type::string) { - lua_CFunction indexingfunc = is_index ? stack::get(L, upvalue_index(2)) : stack::get(L, upvalue_index(3)); - return indexingfunc(L); + if (has_indexing) { + object& indexingfunc = is_index + ? sm.index + : sm.newindex; + return call_indexing_object(L, indexingfunc); + } + else { + return usertype_detail::indexing_fail(L); + } } } string_detail::string_shim accessor = stack::get(L, keyidx); @@ -60,11 +80,20 @@ namespace sol { return stack::push(L, func); } else { - lua_CFunction indexingfunc = is_index ? stack::get(L, upvalue_index(2)) : stack::get(L, upvalue_index(3)); - return indexingfunc(L); + if (has_indexing) { + object& indexingfunc = is_index + ? sm.index + : sm.newindex; + return call_indexing_object(L, indexingfunc); + } + else { + return is_index + ? usertype_detail::indexing_fail(L) + : usertype_detail::metatable_newindex(L); + } } } - // Check table storage first for a method that works + /* Check table storage first for a method that works luaL_getmetatable(L, sm.metakey); if (type_of(L, -1) != type::lua_nil) { stack::get_field(L, accessor.c_str(), lua_gettop(L)); @@ -76,7 +105,8 @@ namespace sol { lua_pop(L, 1); } lua_pop(L, 1); - + */ + int ret = 0; bool found = false; // Otherwise, we need to do propagating calls through the bases @@ -90,26 +120,37 @@ namespace sol { return ret; } if (toplevel) { - lua_CFunction indexingfunc = is_index ? stack::get(L, upvalue_index(2)) : stack::get(L, upvalue_index(3)); - return indexingfunc(L); + if (has_indexing) { + object& indexingfunc = is_index + ? sm.index + : sm.newindex; + return call_indexing_object(L, indexingfunc); + } + else { + return usertype_detail::metatable_newindex(L); + } } return -1; } + template inline int simple_real_index_call(lua_State* L) { - return simple_core_indexing_call(L); + return simple_core_indexing_call(L); } + template inline int simple_real_new_index_call(lua_State* L) { - return simple_core_indexing_call(L); + return simple_core_indexing_call(L); } + template inline int simple_index_call(lua_State* L) { - return detail::static_trampoline<(&simple_real_index_call)>(L); + return detail::static_trampoline<(&simple_real_index_call)>(L); } + template inline int simple_new_index_call(lua_State* L) { - return detail::static_trampoline<(&simple_real_new_index_call)>(L); + return detail::static_trampoline<(&simple_real_new_index_call)>(L); } } @@ -121,8 +162,8 @@ namespace sol { usertype_detail::function_map registrations; usertype_detail::variable_map varmap; object callconstructfunc; - lua_CFunction indexfunc; - lua_CFunction newindexfunc; + object indexfunc; + object newindexfunc; lua_CFunction indexbase; lua_CFunction newindexbase; usertype_detail::base_walk indexbaseclasspropogation; @@ -135,6 +176,15 @@ namespace sol { template void insert(N&& n, object&& o) { std::string key = usertype_detail::make_string(std::forward(n)); + int is_indexer = static_cast(usertype_detail::is_indexer(n)); + if (is_indexer == 1) { + indexfunc = o; + mustindex = true; + } + else if (is_indexer == 2) { + newindexfunc = o; + mustindex = true; + } auto hint = registrations.find(key); if (hint == registrations.cend()) { registrations.emplace_hint(hint, std::move(key), std::move(o)); @@ -261,8 +311,8 @@ namespace sol { template simple_usertype_metatable(usertype_detail::verified_tag, std::index_sequence, lua_State* L, Tuple&& args) : callconstructfunc(lua_nil), - indexfunc(&usertype_detail::indexing_fail), newindexfunc(&usertype_detail::metatable_newindex), - indexbase(&usertype_detail::simple_core_indexing_call), newindexbase(&usertype_detail::simple_core_indexing_call), + indexfunc(lua_nil), newindexfunc(lua_nil), + indexbase(&usertype_detail::simple_core_indexing_call), newindexbase(&usertype_detail::simple_core_indexing_call), indexbaseclasspropogation(usertype_detail::walk_all_bases), newindexbaseclasspropogation(&usertype_detail::walk_all_bases), baseclasscheck(nullptr), baseclasscast(nullptr), mustindex(false), secondarymeta(false) { @@ -331,6 +381,7 @@ namespace sol { const char* gcmetakey = &usertype_traits::gc_table()[0]; stack::push>(L, metatable_key, uniquegcmetakey, &usertype_traits::metatable()[0], umx.indexbaseclasspropogation, umx.newindexbaseclasspropogation, + std::move(umx.indexfunc), std::move(umx.newindexfunc), std::move(umx.varmap), std::move(umx.registrations) ); stack_reference stackvarmap(L, -1); @@ -343,7 +394,11 @@ namespace sol { } static int push(lua_State* L, umt_t&& umx) { + bool hasindex = umx.indexfunc.valid(); + bool hasnewindex = umx.newindexfunc.valid(); auto& varmap = make_cleanup(L, umx); + auto sic = hasindex ? &usertype_detail::simple_index_call : &usertype_detail::simple_index_call; + auto snic = hasnewindex ? &usertype_detail::simple_new_index_call : &usertype_detail::simple_new_index_call; bool hasequals = false; bool hasless = false; bool haslessequals = false; @@ -358,10 +413,10 @@ namespace sol { haslessequals = true; } else if (first == to_string(meta_function::index)) { - umx.indexfunc = second.template as(); + umx.indexfunc = second; } else if (first == to_string(meta_function::new_index)) { - umx.newindexfunc = second.template as(); + umx.newindexfunc = second; } switch (i) { case 0: @@ -382,7 +437,6 @@ namespace sol { stack::set_field(L, first, second, t.stack_index()); }; for (std::size_t i = 0; i < 3; ++i) { - // Pointer types, AKA "references" from C++ const char* metakey = nullptr; switch (i) { case 0: @@ -435,16 +489,14 @@ namespace sol { if (umx.mustindex) { // use indexing function stack::set_field(L, meta_function::index, - make_closure(&usertype_detail::simple_index_call, - make_light(varmap), - umx.indexfunc, - umx.newindexfunc + make_closure(sic, + nullptr, + make_light(varmap) ), t.stack_index()); stack::set_field(L, meta_function::new_index, - make_closure(&usertype_detail::simple_new_index_call, - make_light(varmap), - umx.indexfunc, - umx.newindexfunc + make_closure(snic, + nullptr, + make_light(varmap) ), t.stack_index()); } else { @@ -460,16 +512,14 @@ namespace sol { } if (umx.secondarymeta) { stack::set_field(L, meta_function::index, - make_closure(&usertype_detail::simple_index_call, - make_light(varmap), - umx.indexfunc, - umx.newindexfunc + make_closure(sic, + nullptr, + make_light(varmap) ), metabehind.stack_index()); stack::set_field(L, meta_function::new_index, - make_closure(&usertype_detail::simple_new_index_call, - make_light(varmap), - umx.indexfunc, - umx.newindexfunc + make_closure(snic, + nullptr, + make_light(varmap) ), metabehind.stack_index()); } stack::set_field(L, metatable_key, metabehind, t.stack_index()); @@ -494,17 +544,19 @@ namespace sol { } // use indexing function stack::set_field(L, meta_function::index, - make_closure(&usertype_detail::simple_index_call, + make_closure(sic, + nullptr, make_light(varmap), - umx.indexfunc, - umx.newindexfunc, + nullptr, + nullptr, usertype_detail::toplevel_magic ), metabehind.stack_index()); stack::set_field(L, meta_function::new_index, - make_closure(&usertype_detail::simple_new_index_call, + make_closure(snic, + nullptr, make_light(varmap), - umx.indexfunc, - umx.newindexfunc, + nullptr, + nullptr, usertype_detail::toplevel_magic ), metabehind.stack_index()); stack::set_field(L, metatable_key, metabehind, t.stack_index()); diff --git a/sol/types.hpp b/sol/types.hpp index 8fdff702..85137683 100644 --- a/sol/types.hpp +++ b/sol/types.hpp @@ -185,26 +185,46 @@ namespace sol { struct upvalue_index { int index; - upvalue_index(int idx) : index(lua_upvalueindex(idx)) {} - operator int() const { return index; } + upvalue_index(int idx) : index(lua_upvalueindex(idx)) { + + } + + operator int() const { + return index; + } }; struct raw_index { int index; - raw_index(int i) : index(i) {} - operator int() const { return index; } + raw_index(int i) : index(i) { + + } + + operator int() const { + return index; + } }; struct absolute_index { int index; - absolute_index(lua_State* L, int idx) : index(lua_absindex(L, idx)) {} - operator int() const { return index; } + absolute_index(lua_State* L, int idx) : index(lua_absindex(L, idx)) { + + } + + operator int() const { + return index; + } }; struct ref_index { int index; - ref_index(int idx) : index(idx) {} - operator int() const { return index; } + ref_index(int idx) : index(idx) { + + } + + operator int() const { + return index; + } }; struct lightuserdata_value { diff --git a/sol/usertype_metatable.hpp b/sol/usertype_metatable.hpp index 15d081fc..f46151e0 100644 --- a/sol/usertype_metatable.hpp +++ b/sol/usertype_metatable.hpp @@ -36,6 +36,15 @@ namespace sol { namespace usertype_detail { + const int metatable_index = 2; + const int metatable_core_index = 3; + const int filler_index = 4; + const int magic_index = 5; + + const int simple_metatable_index = 2; + const int index_function_index = 3; + const int newindex_function_index = 4; + typedef void(*base_walk)(lua_State*, bool&, int&, string_detail::string_shim&); typedef int(*member_search)(lua_State*, void*, int); @@ -79,10 +88,15 @@ namespace sol { const char* metakey; variable_map variables; function_map functions; + object index; + object newindex; base_walk indexbaseclasspropogation; base_walk newindexbaseclasspropogation; - simple_map(const char* mkey, base_walk index, base_walk newindex, variable_map&& vars, function_map&& funcs) : metakey(mkey), variables(std::move(vars)), functions(std::move(funcs)), indexbaseclasspropogation(index), newindexbaseclasspropogation(newindex) {} + simple_map(const char* mkey, base_walk index, base_walk newindex, object i, object ni, variable_map&& vars, function_map&& funcs) + : metakey(mkey), variables(std::move(vars)), functions(std::move(funcs)), + index(std::move(i)), newindex(std::move(ni)), + indexbaseclasspropogation(index), newindexbaseclasspropogation(newindex) {} }; } @@ -151,20 +165,32 @@ namespace sol { } }; - inline bool is_indexer(string_detail::string_shim s) { - return s == to_string(meta_function::index) || s == to_string(meta_function::new_index); + inline int is_indexer(string_detail::string_shim s) { + if (s == to_string(meta_function::index)) { + return 1; + } + else if (s == to_string(meta_function::new_index)) { + return 2; + } + return 0; } - inline bool is_indexer(meta_function mf) { - return mf == meta_function::index || mf == meta_function::new_index; + inline int is_indexer(meta_function mf) { + if (mf == meta_function::index) { + return 1; + } + else if (mf == meta_function::new_index) { + return 2; + } + return 0; } - inline bool is_indexer(call_construction) { - return false; + inline int is_indexer(call_construction) { + return 0; } - inline bool is_indexer(base_classes_tag) { - return false; + inline int is_indexer(base_classes_tag) { + return 0; } inline auto make_shim(string_detail::string_shim s) { @@ -206,7 +232,7 @@ namespace sol { }; inline int runtime_object_call(lua_State* L, void*, int runtimetarget) { - usertype_metatable_core& umc = stack::get>(L, upvalue_index(2)); + usertype_metatable_core& umc = stack::get>(L, upvalue_index(metatable_core_index)); std::vector& runtime = umc.runtime; object& runtimeobj = runtime[runtimetarget]; return stack::push(L, runtimeobj); @@ -221,7 +247,7 @@ namespace sol { return luaL_error(L, "sol: attempt to index (get) nil value \"%s\" on userdata (bad (misspelled?) key name or does not exist)", accessor.c_str()); #else int isnum = 0; - lua_Integer magic = lua_tointegerx(L, upvalue_index(4), &isnum); + lua_Integer magic = lua_tointegerx(L, upvalue_index(magic_index), &isnum); if (isnum != 0 && magic == toplevel_magic) { if (lua_getmetatable(L, 1) == 1) { int metatarget = lua_gettop(L); @@ -245,11 +271,11 @@ namespace sol { template inline int metatable_newindex(lua_State* L) { int isnum = 0; - lua_Integer magic = lua_tointegerx(L, upvalue_index(4), &isnum); + lua_Integer magic = lua_tointegerx(L, upvalue_index(magic_index), &isnum); if (isnum != 0 && magic == toplevel_magic) { auto non_indexable = [&L]() { if (is_simple) { - simple_map& sm = stack::get>(L, upvalue_index(1)); + simple_map& sm = stack::get>(L, upvalue_index(simple_metatable_index)); function_map& functions = sm.functions; optional maybeaccessor = stack::get>(L, 2); if (!maybeaccessor) { @@ -265,7 +291,7 @@ namespace sol { } return; } - usertype_metatable_core& umc = stack::get>(L, upvalue_index(2)); + usertype_metatable_core& umc = stack::get>(L, upvalue_index(metatable_core_index)); bool mustindex = umc.mustindex; if (!mustindex) return; @@ -326,7 +352,7 @@ namespace sol { } inline int runtime_new_index(lua_State* L, void*, int runtimetarget) { - usertype_metatable_core& umc = stack::get>(L, upvalue_index(2)); + usertype_metatable_core& umc = stack::get>(L, upvalue_index(metatable_core_index)); std::vector& runtime = umc.runtime; object& runtimeobj = runtime[runtimetarget]; runtimeobj = object(L, 3); @@ -456,7 +482,7 @@ namespace sol { bool contains_index() const { bool idx = false; - (void)detail::swallow{ 0, ((idx |= usertype_detail::is_indexer(std::get(functions))), 0) ... }; + (void)detail::swallow{ 0, ((idx |= (usertype_detail::is_indexer(std::get(functions)) != 0)), 0) ... }; return idx; } @@ -572,7 +598,11 @@ namespace sol { if (is_variable_binding(f.functions))>::value) { return real_call_with(L, f); } - int upvalues = stack::push(L, light(f)); + // set up upvalues + // for a chained call + int upvalues = 0; + upvalues += stack::push(L, nullptr); + upvalues += stack::push(L, light(f)); auto cfunc = &call; return stack::push(L, c_closure(cfunc, upvalues)); } @@ -585,7 +615,9 @@ namespace sol { template static int core_indexing_call(lua_State* L) { - usertype_metatable& f = toplevel ? stack::get>(L, upvalue_index(1)) : stack::pop>(L); + usertype_metatable& f = toplevel + ? stack::get>(L, upvalue_index(usertype_detail::metatable_index)) + : stack::pop>(L); static const int keyidx = -2 + static_cast(is_index); if (toplevel && stack::get(L, keyidx) != type::string) { return is_index ? f.indexfunc(L) : f.newindexfunc(L); @@ -621,7 +653,7 @@ namespace sol { template static int real_call(lua_State* L) { - usertype_metatable& f = stack::get>(L, upvalue_index(1)); + usertype_metatable& f = stack::get>(L, upvalue_index(usertype_detail::metatable_index)); return real_call_with(L, f); } @@ -738,8 +770,10 @@ namespace sol { } luaL_newmetatable(L, metakey); stack_reference t(L, -1); - stack::push(L, make_light(um)); - luaL_setfuncs(L, metaregs, 1); + int upvalues = 0; + upvalues += stack::push(L, nullptr); + upvalues += stack::push(L, make_light(um)); + luaL_setfuncs(L, metaregs, upvalues); if (um.baseclasscheck != nullptr) { stack::set_field(L, detail::base_class_check_key(), um.baseclasscheck, t.stack_index()); @@ -748,14 +782,14 @@ namespace sol { stack::set_field(L, detail::base_class_cast_key(), um.baseclasscast, t.stack_index()); } - stack::set_field(L, detail::base_class_index_propogation_key(), make_closure(um.indexbase, make_light(um), make_light(umc)), t.stack_index()); - stack::set_field(L, detail::base_class_new_index_propogation_key(), make_closure(um.newindexbase, make_light(um), make_light(umc)), t.stack_index()); + stack::set_field(L, detail::base_class_index_propogation_key(), make_closure(um.indexbase, nullptr, make_light(um), make_light(umc)), t.stack_index()); + stack::set_field(L, detail::base_class_new_index_propogation_key(), make_closure(um.newindexbase, nullptr, make_light(um), make_light(umc)), t.stack_index()); if (mustindex) { // Basic index pushing: specialize // index and newindex to give variables and stuff - stack::set_field(L, meta_function::index, make_closure(umt_t::index_call, make_light(um), make_light(umc)), t.stack_index()); - stack::set_field(L, meta_function::new_index, make_closure(umt_t::new_index_call, make_light(um), make_light(umc)), t.stack_index()); + stack::set_field(L, meta_function::index, make_closure(umt_t::index_call, nullptr, make_light(um), make_light(umc)), t.stack_index()); + stack::set_field(L, meta_function::new_index, make_closure(umt_t::new_index_call, nullptr, make_light(um), make_light(umc)), t.stack_index()); } else { // If there's only functions, we can use the fast index version @@ -766,11 +800,11 @@ namespace sol { lua_createtable(L, 0, 3); stack_reference metabehind(L, -1); if (um.callconstructfunc != nullptr) { - stack::set_field(L, meta_function::call_function, make_closure(um.callconstructfunc, make_light(um), make_light(umc)), metabehind.stack_index()); + stack::set_field(L, meta_function::call_function, make_closure(um.callconstructfunc, nullptr, make_light(um), make_light(umc)), metabehind.stack_index()); } if (um.secondarymeta) { - stack::set_field(L, meta_function::index, make_closure(umt_t::index_call, make_light(um), make_light(umc)), metabehind.stack_index()); - stack::set_field(L, meta_function::new_index, make_closure(umt_t::new_index_call, make_light(um), make_light(umc)), metabehind.stack_index()); + stack::set_field(L, meta_function::index, make_closure(umt_t::index_call, nullptr, make_light(um), make_light(umc)), metabehind.stack_index()); + stack::set_field(L, meta_function::new_index, make_closure(umt_t::new_index_call, nullptr, make_light(um), make_light(umc)), metabehind.stack_index()); } stack::set_field(L, metatable_key, metabehind, t.stack_index()); metabehind.pop(); @@ -782,17 +816,19 @@ namespace sol { // Now for the shim-table that actually gets assigned to the name luaL_newmetatable(L, &usertype_traits::user_metatable()[0]); stack_reference t(L, -1); - stack::push(L, make_light(um)); - luaL_setfuncs(L, value_table.data(), 1); + int upvalues = 0; + upvalues += stack::push(L, nullptr); + upvalues += stack::push(L, make_light(um)); + luaL_setfuncs(L, value_table.data(), upvalues); { lua_createtable(L, 0, 3); stack_reference metabehind(L, -1); if (um.callconstructfunc != nullptr) { - stack::set_field(L, meta_function::call_function, make_closure(um.callconstructfunc, make_light(um), make_light(umc)), metabehind.stack_index()); + stack::set_field(L, meta_function::call_function, make_closure(um.callconstructfunc, nullptr, make_light(um), make_light(umc)), metabehind.stack_index()); } - stack::set_field(L, meta_function::index, make_closure(umt_t::index_call, make_light(um), make_light(umc), 0, usertype_detail::toplevel_magic), metabehind.stack_index()); - stack::set_field(L, meta_function::new_index, make_closure(umt_t::new_index_call, make_light(um), make_light(umc), 0, usertype_detail::toplevel_magic), metabehind.stack_index()); + stack::set_field(L, meta_function::index, make_closure(umt_t::index_call, nullptr, make_light(um), make_light(umc), nullptr, usertype_detail::toplevel_magic), metabehind.stack_index()); + stack::set_field(L, meta_function::new_index, make_closure(umt_t::new_index_call, nullptr, make_light(um), make_light(umc), nullptr, usertype_detail::toplevel_magic), metabehind.stack_index()); stack::set_field(L, metatable_key, metabehind, t.stack_index()); metabehind.pop(); diff --git a/test_simple_usertypes.cpp b/test_simple_usertypes.cpp index 5f408c96..c0626d4c 100644 --- a/test_simple_usertypes.cpp +++ b/test_simple_usertypes.cpp @@ -876,3 +876,39 @@ TEST_CASE("simple_usertype/static-properties", "allow for static functions to ge double v2a = lua["v2a"]; REQUIRE(v2a == 60.5); } + +TEST_CASE("simple_usertype/indexing", "make sure simple usertypes can be indexed/new_indexed properly without breaking") { + static int val = 0; + + class indexing_test { + public: + indexing_test() { val = 0; } + + sol::object getter(const std::string& name, sol::this_state _s) { + REQUIRE(name == "a"); + return sol::make_object(_s, 2); + } + + void setter(std::string name, sol::object n) { + REQUIRE(name == "unknown"); + val = n.as(); + } + }; + + sol::state lua; + lua.open_libraries(sol::lib::base); + lua.new_simple_usertype("test", + sol::meta_function::index, &indexing_test::getter, + sol::meta_function::new_index, &indexing_test::setter + ); + + lua.script(R"( + local t = test.new() + v = t.a; + print(v) + t.unknown = 50; + )"); + int v = lua["v"]; + REQUIRE(v == 2); + REQUIRE(val == 50); +}