// sol3 // The MIT License (MIT) // Copyright (c) 2013-2018 Rapptz, ThePhD and contributors // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of // the Software, and to permit persons to whom the Software is furnished to do so, // subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifndef SOL_STACK_UNQUALIFIED_GET_HPP #define SOL_STACK_UNQUALIFIED_GET_HPP #include "stack_core.hpp" #include "usertype_traits.hpp" #include "inheritance.hpp" #include "overload.hpp" #include "error.hpp" #include "unicode.hpp" #include #include #include #include #include #if defined(SOL_CXX17_FEATURES) && SOL_CXX17_FEATURES #include #if defined(SOL_STD_VARIANT) && SOL_STD_VARIANT #include #endif // Apple clang screwed up #endif // C++17 namespace sol { namespace stack { namespace stack_detail { template struct count_code_units_utf { std::size_t needed_size; count_code_units_utf() : needed_size(0) { } void operator()(const unicode::encoded_result er) { needed_size += er.code_units_size; } }; template struct copy_code_units_utf { Ch* target_; copy_code_units_utf(Ch* target) : target_(target) { } void operator()(const unicode::encoded_result er) { std::memcpy(target_, er.code_units.data(), er.code_units_size * sizeof(ErCh)); target_ += er.code_units_size; } }; template inline void convert(const char* strb, const char* stre, F&& f) { char32_t cp = 0; for (const char* strtarget = strb; strtarget < stre;) { auto dr = unicode::utf8_to_code_point(strtarget, stre); if (dr.error != unicode::error_code::ok) { cp = unicode::unicode_detail::replacement; ++strtarget; } else { cp = dr.codepoint; strtarget = dr.next; } if constexpr(std::is_same_v) { auto er = unicode::code_point_to_utf32(cp); f(er); } else { auto er = unicode::code_point_to_utf16(cp); f(er); } } } template inline S get_into(lua_State* L, int index, record& tracking) { using Ch = typename S::value_type; tracking.use(1); size_t len; auto utf8p = lua_tolstring(L, index, &len); if (len < 1) return S(); const char* strb = utf8p; const char* stre = utf8p + len; stack_detail::count_code_units_utf count_units; convert(strb, stre, count_units); S r(count_units.needed_size, static_cast(0)); r.resize(count_units.needed_size); Ch* target = &r[0]; stack_detail::copy_code_units_utf copy_units(target); convert(strb, stre, copy_units); return r; } } // namespace stack_detail template struct unqualified_getter { static T& get(lua_State* L, int index, record& tracking) { return unqualified_getter>{}.get(L, index, tracking); } }; template struct qualified_getter { static decltype(auto) get(lua_State* L, int index, record& tracking) { return stack::unqualified_get(L, index, tracking); } }; template struct unqualified_interop_getter { using T = stack_detail::strip_extensible_t; static std::pair get(lua_State*, int, void*, record&) { return { false, nullptr }; } }; template struct qualified_interop_getter { static decltype(auto) get(lua_State* L, int index, void* unadjusted_pointer, record& tracking) { return stack_detail::unqualified_interop_get(L, index, unadjusted_pointer, tracking); } }; template struct unqualified_getter::value>> { static T get(lua_State* L, int index, record& tracking) { tracking.use(1); return static_cast(lua_tonumber(L, index)); } }; template struct unqualified_getter::value>> { static T get(lua_State* L, int index, record& tracking) { tracking.use(1); #if SOL_LUA_VERSION >= 503 if (lua_isinteger(L, index) != 0) { return static_cast(lua_tointeger(L, index)); } #endif return static_cast(llround(lua_tonumber(L, index))); } }; template struct unqualified_getter::value>> { static T get(lua_State* L, int index, record& tracking) { tracking.use(1); return static_cast(lua_tointegerx(L, index, nullptr)); } }; template struct unqualified_getter> { using Tu = meta::unqualified_t; template static void push_back_at_end(std::true_type, types, lua_State* L, T& arr, std::size_t) { arr.push_back(stack::get(L, -lua_size::value)); } template static void push_back_at_end(std::false_type, types t, lua_State* L, T& arr, std::size_t idx) { insert_at_end(meta::has_insert(), t, L, arr, idx); } template static void insert_at_end(std::true_type, types, lua_State* L, T& arr, std::size_t) { using std::cend; arr.insert(cend(arr), stack::get(L, -lua_size::value)); } template static void insert_at_end(std::false_type, types, lua_State* L, T& arr, std::size_t idx) { arr[idx] = stack::get(L, -lua_size::value); } static bool max_size_check(std::false_type, T&, std::size_t) { return false; } static bool max_size_check(std::true_type, T& arr, std::size_t idx) { return idx >= arr.max_size(); } static T get(lua_State* L, int relindex, record& tracking) { return get(meta::is_associative(), L, relindex, tracking); } static T get(std::false_type, lua_State* L, int relindex, record& tracking) { typedef typename Tu::value_type V; return get(types(), L, relindex, tracking); } template static T get(types t, lua_State* L, int relindex, record& tracking) { tracking.use(1); // the W4 flag is really great, // so great that it can tell my for loops (twice nested) // below never actually terminate // without hitting where the gotos have infested // so now I would get the error W4XXX unreachable // me that the return arr at the end of this function // which is fair until other compilers complain // that there isn't a return and that based on // SOME MAGICAL FORCE // control flow falls off the end of a non-void function // so it needs to be there for the compilers that are // too flimsy to analyze the basic blocks... // (I'm sure I should file a bug but those compilers are already // in the wild; it doesn't matter if I fix them, // someone else is still going to get some old-ass compiler // and then bother me about the unclean build for the 30th // time) // "Why not an IIFE?" // Because additional lambdas / functions which serve as // capture-all-and-then-invoke bloat binary sizes // by an actually detectable amount // (one user uses sol2 pretty heavily and 22 MB of binary size // was saved by reducing reliance on lambdas in templates) // This would really be solved by having break N; // be a real, proper thing... // but instead, we have to use labels and gotos // and earn the universal vitriol of the dogmatic // programming community // all in all: W4 is great!~ int index = lua_absindex(L, relindex); T arr; std::size_t idx = 0; #if SOL_LUA_VERSION >= 503 // This method is HIGHLY performant over regular table iteration // thanks to the Lua API changes in 5.3 // Questionable in 5.4 for (lua_Integer i = 0;; i += lua_size::value) { if (max_size_check(meta::has_max_size(), arr, idx)) { // see above comment goto done; } bool isnil = false; for (int vi = 0; vi < lua_size::value; ++vi) { #if defined(LUA_NILINTABLE) && LUA_NILINTABLE && SOL_LUA_VERSION >= 600 #if defined(SOL_SAFE_STACK_CHECK) && SOL_SAFE_STACK_CHECK luaL_checkstack(L, 1, detail::not_enough_stack_space_generic); #endif // make sure stack doesn't overflow lua_pushinteger(L, static_cast(i + vi)); if (lua_keyin(L, index) == 0) { // it's time to stop isnil = true; } else { // we have a key, have to get the value lua_geti(L, index, i + vi); } #else type vt = static_cast(lua_geti(L, index, i + vi)); isnil = vt == type::none || vt == type::lua_nil; #endif if (isnil) { if (i == 0) { break; } #if defined(LUA_NILINTABLE) && LUA_NILINTABLE && SOL_LUA_VERSION >= 600 lua_pop(L, vi); #else lua_pop(L, (vi + 1)); #endif // see above comment goto done; } } if (isnil) { #if defined(LUA_NILINTABLE) && LUA_NILINTABLE && SOL_LUA_VERSION >= 600 #else lua_pop(L, lua_size::value); #endif continue; } push_back_at_end(meta::has_push_back(), t, L, arr, idx); ++idx; lua_pop(L, lua_size::value); } #else // Zzzz slower but necessary thanks to the lower version API and missing functions qq for (lua_Integer i = 0;; i += lua_size::value, lua_pop(L, lua_size::value)) { if (idx >= arr.max_size()) { // see above comment goto done; } #if defined(SOL_SAFE_STACK_CHECK) && SOL_SAFE_STACK_CHECK luaL_checkstack(L, 2, detail::not_enough_stack_space_generic); #endif // make sure stack doesn't overflow bool isnil = false; for (int vi = 0; vi < lua_size::value; ++vi) { lua_pushinteger(L, i); lua_gettable(L, index); type vt = type_of(L, -1); isnil = vt == type::lua_nil; if (isnil) { if (i == 0) { break; } lua_pop(L, (vi + 1)); // see above comment goto done; } } if (isnil) continue; push_back_at_end(meta::has_push_back(), t, L, arr, idx); ++idx; } #endif done: return arr; } static T get(std::true_type, lua_State* L, int index, record& tracking) { typedef typename Tu::value_type P; typedef typename P::first_type K; typedef typename P::second_type V; return get(types(), L, index, tracking); } template static T get(types, lua_State* L, int relindex, record& tracking) { tracking.use(1); #if defined(SOL_SAFE_STACK_CHECK) && SOL_SAFE_STACK_CHECK luaL_checkstack(L, 3, detail::not_enough_stack_space_generic); #endif // make sure stack doesn't overflow T associative; int index = lua_absindex(L, relindex); lua_pushnil(L); while (lua_next(L, index) != 0) { decltype(auto) key = stack::check_get(L, -2); if (!key) { lua_pop(L, 1); continue; } associative.emplace(std::forward(*key), stack::get(L, -1)); lua_pop(L, 1); } return associative; } }; template struct unqualified_getter>> { typedef std::forward_list C; static C get(lua_State* L, int relindex, record& tracking) { return get(meta::has_key_value_pair(), L, relindex, tracking); } static C get(std::true_type, lua_State* L, int index, record& tracking) { typedef typename T::value_type P; typedef typename P::first_type K; typedef typename P::second_type V; return get(types(), L, index, tracking); } static C get(std::false_type, lua_State* L, int relindex, record& tracking) { typedef typename C::value_type V; return get(types(), L, relindex, tracking); } template static C get(types, lua_State* L, int relindex, record& tracking) { tracking.use(1); #if defined(SOL_SAFE_STACK_CHECK) && SOL_SAFE_STACK_CHECK luaL_checkstack(L, 3, detail::not_enough_stack_space_generic); #endif // make sure stack doesn't overflow int index = lua_absindex(L, relindex); C arr; auto at = arr.cbefore_begin(); std::size_t idx = 0; #if SOL_LUA_VERSION >= 503 // This method is HIGHLY performant over regular table iteration thanks to the Lua API changes in 5.3 for (lua_Integer i = 0;; i += lua_size::value, lua_pop(L, lua_size::value)) { if (idx >= arr.max_size()) { goto done; } bool isnil = false; for (int vi = 0; vi < lua_size::value; ++vi) { type t = static_cast(lua_geti(L, index, i + vi)); isnil = t == type::lua_nil; if (isnil) { if (i == 0) { break; } lua_pop(L, (vi + 1)); goto done; } } if (isnil) continue; at = arr.insert_after(at, stack::get(L, -lua_size::value)); ++idx; } #else // Zzzz slower but necessary thanks to the lower version API and missing functions qq for (lua_Integer i = 0;; i += lua_size::value, lua_pop(L, lua_size::value)) { if (idx >= arr.max_size()) { goto done; } bool isnil = false; for (int vi = 0; vi < lua_size::value; ++vi) { lua_pushinteger(L, i); lua_gettable(L, index); type t = type_of(L, -1); isnil = t == type::lua_nil; if (isnil) { if (i == 0) { break; } lua_pop(L, (vi + 1)); goto done; } } if (isnil) continue; at = arr.insert_after(at, stack::get(L, -lua_size::value)); ++idx; } #endif done: return arr; } template static C get(types, lua_State* L, int relindex, record& tracking) { tracking.use(1); #if defined(SOL_SAFE_STACK_CHECK) && SOL_SAFE_STACK_CHECK luaL_checkstack(L, 3, detail::not_enough_stack_space_generic); #endif // make sure stack doesn't overflow C associative; auto at = associative.cbefore_begin(); int index = lua_absindex(L, relindex); lua_pushnil(L); while (lua_next(L, index) != 0) { decltype(auto) key = stack::check_get(L, -2); if (!key) { lua_pop(L, 1); continue; } at = associative.emplace_after(at, std::forward(*key), stack::get(L, -1)); lua_pop(L, 1); } return associative; } }; template struct unqualified_getter, std::enable_if_t::value>> { static T get(lua_State* L, int index, record& tracking) { unqualified_getter g; // VC++ has a bad warning here: shut it up (void)g; return g.get(L, index, tracking); } }; template struct unqualified_getter, std::enable_if_t::value>> { using Tu = meta::unqualified_t; static T get(lua_State* L, int index, record& tracking) { if constexpr(meta::is_associative::value) { typedef typename T::value_type P; typedef typename P::first_type K; typedef typename P::second_type V; unqualified_getter> g; // VC++ has a bad warning here: shut it up (void)g; return g.get(types>(), L, index, tracking); } else { typedef typename T::value_type V; unqualified_getter> g; // VC++ has a bad warning here: shut it up (void)g; return g.get(types>(), L, index, tracking); } } }; template struct unqualified_getter::value>> { static T get(lua_State* L, int index, record& tracking) { tracking.use(1); return T(L, index); } }; template <> struct unqualified_getter { static userdata_value get(lua_State* L, int index, record& tracking) { tracking.use(1); return userdata_value(lua_touserdata(L, index)); } }; template <> struct unqualified_getter { static lightuserdata_value get(lua_State* L, int index, record& tracking) { tracking.use(1); return lightuserdata_value(lua_touserdata(L, index)); } }; template struct unqualified_getter> { static light get(lua_State* L, int index, record& tracking) { tracking.use(1); void* memory = lua_touserdata(L, index); return light(static_cast(memory)); } }; template struct unqualified_getter> { static std::add_lvalue_reference_t get(lua_State* L, int index, record& tracking) { tracking.use(1); void* memory = lua_touserdata(L, index); memory = detail::align_user(memory); return *static_cast*>(memory); } }; template struct unqualified_getter> { static T* get(lua_State* L, int index, record& tracking) { tracking.use(1); void* memory = lua_touserdata(L, index); memory = detail::align_user(memory); return static_cast(memory); } }; template <> struct unqualified_getter { static type get(lua_State* L, int index, record& tracking) { tracking.use(1); return static_cast(lua_type(L, index)); } }; template <> struct unqualified_getter { static bool get(lua_State* L, int index, record& tracking) { tracking.use(1); return lua_toboolean(L, index) != 0; } }; template <> struct unqualified_getter { static std::string get(lua_State* L, int index, record& tracking) { tracking.use(1); std::size_t len; auto str = lua_tolstring(L, index, &len); return std::string(str, len); } }; template <> struct unqualified_getter { static const char* get(lua_State* L, int index, record& tracking) { tracking.use(1); size_t sz; return lua_tolstring(L, index, &sz); } }; template <> struct unqualified_getter { static char get(lua_State* L, int index, record& tracking) { tracking.use(1); size_t len; auto str = lua_tolstring(L, index, &len); return len > 0 ? str[0] : '\0'; } }; template struct unqualified_getter> { static string_view get(lua_State* L, int index, record& tracking) { tracking.use(1); size_t sz; const char* str = lua_tolstring(L, index, &sz); return basic_string_view(str, sz); } }; template struct unqualified_getter> { using S = std::basic_string; static S get(lua_State* L, int index, record& tracking) { using Ch = std::conditional_t; return stack_detail::get_into(L, index, tracking); } }; template struct unqualified_getter> { static std::basic_string get(lua_State* L, int index, record& tracking) { return stack_detail::get_into>(L, index, tracking); } }; template struct unqualified_getter> { static std::basic_string get(lua_State* L, int index, record& tracking) { return stack_detail::get_into>(L, index, tracking); } }; template <> struct unqualified_getter { static char16_t get(lua_State* L, int index, record& tracking) { string_view utf8 = stack::get(L, index, tracking); const char* strb = utf8.data(); const char* stre = utf8.data() + utf8.size(); char32_t cp = 0; auto dr = unicode::utf8_to_code_point(strb, stre); if (dr.error != unicode::error_code::ok) { cp = unicode::unicode_detail::replacement; } else { cp = dr.codepoint; } auto er = unicode::code_point_to_utf16(cp); return er.code_units[0]; } }; template <> struct unqualified_getter { static char32_t get(lua_State* L, int index, record& tracking) { string_view utf8 = stack::get(L, index, tracking); const char* strb = utf8.data(); const char* stre = utf8.data() + utf8.size(); char32_t cp = 0; auto dr = unicode::utf8_to_code_point(strb, stre); if (dr.error != unicode::error_code::ok) { cp = unicode::unicode_detail::replacement; } else { cp = dr.codepoint; } auto er = unicode::code_point_to_utf32(cp); return er.code_units[0]; } }; template <> struct unqualified_getter { static wchar_t get(lua_State* L, int index, record& tracking) { typedef std::conditional_t Ch; unqualified_getter g; (void)g; auto c = g.get(L, index, tracking); return static_cast(c); } }; template <> struct unqualified_getter { static meta_function get(lua_State* L, int index, record& tracking) { tracking.use(1); const char* name = unqualified_getter{}.get(L, index, tracking); const auto& mfnames = meta_function_names(); for (std::size_t i = 0; i < mfnames.size(); ++i) if (mfnames[i] == name) return static_cast(i); return meta_function::construct; } }; template <> struct unqualified_getter { static lua_nil_t get(lua_State*, int, record& tracking) { tracking.use(1); return lua_nil; } }; template <> struct unqualified_getter { static std::nullptr_t get(lua_State*, int, record& tracking) { tracking.use(1); return nullptr; } }; template <> struct unqualified_getter { static nullopt_t get(lua_State*, int, record& tracking) { tracking.use(1); return nullopt; } }; template <> struct unqualified_getter { static this_state get(lua_State* L, int, record& tracking) { tracking.use(0); return this_state(L); } }; template <> struct unqualified_getter { static this_main_state get(lua_State* L, int, record& tracking) { tracking.use(0); return this_main_state(main_thread(L, L)); } }; template <> struct unqualified_getter { static lua_CFunction get(lua_State* L, int index, record& tracking) { tracking.use(1); return lua_tocfunction(L, index); } }; template <> struct unqualified_getter { static c_closure get(lua_State* L, int index, record& tracking) { tracking.use(1); return c_closure(lua_tocfunction(L, index), -1); } }; template <> struct unqualified_getter { static error get(lua_State* L, int index, record& tracking) { tracking.use(1); size_t sz = 0; const char* err = lua_tolstring(L, index, &sz); if (err == nullptr) { return error(detail::direct_error, ""); } return error(detail::direct_error, std::string(err, sz)); } }; template <> struct unqualified_getter { static void* get(lua_State* L, int index, record& tracking) { tracking.use(1); return lua_touserdata(L, index); } }; template <> struct unqualified_getter { static const void* get(lua_State* L, int index, record& tracking) { tracking.use(1); return lua_touserdata(L, index); } }; template struct unqualified_getter> { static T* get_no_lua_nil(lua_State* L, int index, record& tracking) { void* memory = lua_touserdata(L, index); #if defined(SOL_ENABLE_INTEROP) && SOL_ENABLE_INTEROP auto ugr = stack_detail::interop_get(L, index, memory, tracking); if (ugr.first) { return ugr.second; } #endif // interop extensibility tracking.use(1); void* rawdata = detail::align_usertype_pointer(memory); void** pudata = static_cast(rawdata); void* udata = *pudata; return get_no_lua_nil_from(L, udata, index, tracking); } static T* get_no_lua_nil_from(lua_State* L, void* udata, int index, record&) { bool has_derived = derive::value || weak_derive::value; if (has_derived) { if (lua_getmetatable(L, index) == 1) { lua_getfield(L, -1, &detail::base_class_cast_key()[0]); if (type_of(L, -1) != type::lua_nil) { void* basecastdata = lua_touserdata(L, -1); detail::inheritance_cast_function ic = reinterpret_cast(basecastdata); // use the casting function to properly adjust the pointer for the desired T udata = ic(udata, usertype_traits::qualified_name()); } lua_pop(L, 2); } } T* obj = static_cast(udata); return obj; } static T& get(lua_State* L, int index, record& tracking) { return *get_no_lua_nil(L, index, tracking); } }; template struct unqualified_getter> { static T* get(lua_State* L, int index, record& tracking) { type t = type_of(L, index); if (t == type::lua_nil) { tracking.use(1); return nullptr; } unqualified_getter> g; // Avoid VC++ warning (void)g; return g.get_no_lua_nil(L, index, tracking); } }; template struct unqualified_getter> { static T* get(lua_State* L, int index, record& tracking) { unqualified_getter> g; // Avoid VC++ warning (void)g; return g.get_no_lua_nil(L, index, tracking); } }; template struct unqualified_getter { static T& get(lua_State* L, int index, record& tracking) { unqualified_getter> g; // Avoid VC++ warning (void)g; return g.get(L, index, tracking); } }; template struct unqualified_getter> { static T& get(lua_State* L, int index, record& tracking) { unqualified_getter g; // Avoid VC++ warning (void)g; return g.get(L, index, tracking); } }; template struct unqualified_getter { static T* get(lua_State* L, int index, record& tracking) { unqualified_getter> g; // Avoid VC++ warning (void)g; return g.get(L, index, tracking); } }; template struct unqualified_getter::value>> { typedef typename unique_usertype_traits::type P; typedef typename unique_usertype_traits::actual_type Real; static Real& get(lua_State* L, int index, record& tracking) { tracking.use(1); void* memory = lua_touserdata(L, index); memory = detail::align_usertype_unique(memory); Real* mem = static_cast(memory); return *mem; } }; template struct unqualified_getter> { typedef std::tuple(nullptr, 0))...> R; template static R apply(std::index_sequence<>, lua_State*, int, record&, Args&&... args) { // Fuck you too, VC++ return R{ std::forward(args)... }; } template static R apply(std::index_sequence, lua_State* L, int index, record& tracking, Args&&... args) { // Fuck you too, VC++ typedef std::tuple_element_t> T; return apply(std::index_sequence(), L, index, tracking, std::forward(args)..., stack::get(L, index + tracking.used, tracking)); } static R get(lua_State* L, int index, record& tracking) { return apply(std::make_index_sequence(), L, index, tracking); } }; template struct unqualified_getter> { static decltype(auto) get(lua_State* L, int index, record& tracking) { return std::pair(L, index)), decltype(stack::get(L, index))>{ stack::get(L, index, tracking), stack::get(L, index + tracking.used, tracking) }; } }; #if defined(SOL_CXX17_FEATURES) && SOL_CXX17_FEATURES #if defined(SOL_STD_VARIANT) && SOL_STD_VARIANT template struct unqualified_getter> { using V = std::variant; static V get_one(std::integral_constant, lua_State* L, int index, record& tracking) { (void)L; (void)index; (void)tracking; if constexpr (std::variant_size_v == 0) { return V(); } else { //using T = std::variant_alternative_t<0, V>; std::abort(); //return V(std::in_place_index<0>, stack::get(L, index, tracking)); } } template static V get_one(std::integral_constant, lua_State* L, int index, record& tracking) { typedef std::variant_alternative_t T; record temp_tracking = tracking; if (stack::check(L, index, no_panic, temp_tracking)) { tracking = temp_tracking; return V(std::in_place_index, stack::get(L, index)); } return get_one(std::integral_constant(), L, index, tracking); } static V get(lua_State* L, int index, record& tracking) { return get_one(std::integral_constant>(), L, index, tracking); } }; #endif // SOL_STD_VARIANT #endif // SOL_CXX17_FEATURES }} // namespace sol::stack #endif // SOL_STACK_UNQUALIFIED_GET_HPP