From dc2493e7525bb7633f697ef10f72b72b46222249 Mon Sep 17 00:00:00 2001 From: Andy Belle-Isle Date: Fri, 30 Aug 2019 00:45:36 -0400 Subject: Forget what I said, I just need to change git attributes to mark for vendor --- lib/sol2/include/sol/stack_core.hpp | 1436 +++++++++++++++++++++++++++++++++++ 1 file changed, 1436 insertions(+) create mode 100644 lib/sol2/include/sol/stack_core.hpp (limited to 'lib/sol2/include/sol/stack_core.hpp') diff --git a/lib/sol2/include/sol/stack_core.hpp b/lib/sol2/include/sol/stack_core.hpp new file mode 100644 index 0000000..3b37210 --- /dev/null +++ b/lib/sol2/include/sol/stack_core.hpp @@ -0,0 +1,1436 @@ +// sol3 + +// The MIT License (MIT) + +// Copyright (c) 2013-2019 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_CORE_HPP +#define SOL_STACK_CORE_HPP + +#include "types.hpp" +#include "inheritance.hpp" +#include "error_handler.hpp" +#include "reference.hpp" +#include "stack_reference.hpp" +#include "tuple.hpp" +#include "traits.hpp" +#include "tie.hpp" +#include "stack_guard.hpp" +#include "demangle.hpp" +#include "forward_detail.hpp" + +#include +#include +#include +#include +#include +#include +#if defined(SOL_CXX17_FEATURES) && SOL_CXX17_FEATURES +#include +#endif // C++17 + +namespace sol { + namespace detail { + struct with_function_tag {}; + struct as_reference_tag {}; + template + struct as_pointer_tag {}; + template + struct as_value_tag {}; + template + struct as_table_tag {}; + + using lua_reg_table = luaL_Reg[64]; + + using unique_destructor = void (*)(void*); + using unique_tag = detail::inheritance_unique_cast_function; + + inline void* align(std::size_t alignment, std::size_t size, void*& ptr, std::size_t& space, std::size_t& required_space) { + // this handels arbitrary alignments... + // make this into a power-of-2-only? + // actually can't: this is a C++14-compatible framework, + // power of 2 alignment is C++17 + std::uintptr_t initial = reinterpret_cast(ptr); + std::uintptr_t offby = static_cast(initial % alignment); + std::uintptr_t padding = (alignment - offby) % alignment; + required_space += size + padding; + if (space < required_space) { + return nullptr; + } + ptr = static_cast(static_cast(ptr) + padding); + space -= padding; + return ptr; + } + + inline void* align(std::size_t alignment, std::size_t size, void*& ptr, std::size_t& space) { + std::size_t required_space = 0; + return align(alignment, size, ptr, space, required_space); + } + + inline void align_one(std::size_t a, std::size_t s, void*& target_alignment) { + std::size_t space = (std::numeric_limits::max)(); + target_alignment = align(a, s, target_alignment, space); + target_alignment = static_cast(static_cast(target_alignment) + s); + } + + template + std::size_t aligned_space_for(void* alignment = nullptr) { + char* start = static_cast(alignment); + (void)detail::swallow{ int{}, (align_one(std::alignment_of_v, sizeof(Args), alignment), int{})... }; + return static_cast(alignment) - start; + } + + inline void* align_usertype_pointer(void* ptr) { + using use_align = std::integral_constant::value > 1) +#endif + >; + if (!use_align::value) { + return ptr; + } + std::size_t space = (std::numeric_limits::max)(); + return align(std::alignment_of::value, sizeof(void*), ptr, space); + } + + template + void* align_usertype_unique_destructor(void* ptr) { + using use_align = std::integral_constant::value > 1) +#endif + >; + if (!pre_aligned) { + ptr = align_usertype_pointer(ptr); + } + if (!pre_shifted) { + ptr = static_cast(static_cast(ptr) + sizeof(void*)); + } + if (!use_align::value) { + return static_cast(static_cast(ptr) + 1); + } + std::size_t space = (std::numeric_limits::max)(); + return align(std::alignment_of::value, sizeof(unique_destructor), ptr, space); + } + + template + void* align_usertype_unique_tag(void* ptr) { + using use_align = std::integral_constant::value > 1) +#endif + >; + if (!pre_aligned) { + ptr = align_usertype_unique_destructor(ptr); + } + if (!pre_shifted) { + ptr = static_cast(static_cast(ptr) + sizeof(unique_destructor)); + } + if (!use_align::value) { + return ptr; + } + std::size_t space = (std::numeric_limits::max)(); + return align(std::alignment_of::value, sizeof(unique_tag), ptr, space); + } + + template + void* align_usertype_unique(void* ptr) { + typedef std::integral_constant::value > 1) +#endif + > + use_align; + if (!pre_aligned) { + ptr = align_usertype_unique_tag(ptr); + } + if (!pre_shifted) { + ptr = static_cast(static_cast(ptr) + sizeof(unique_tag)); + } + if (!use_align::value) { + return ptr; + } + std::size_t space = (std::numeric_limits::max)(); + return align(std::alignment_of::value, sizeof(T), ptr, space); + } + + template + void* align_user(void* ptr) { + typedef std::integral_constant::value > 1) +#endif + > + use_align; + if (!use_align::value) { + return ptr; + } + std::size_t space = (std::numeric_limits::max)(); + return align(std::alignment_of::value, sizeof(T), ptr, space); + } + + template + T** usertype_allocate_pointer(lua_State* L) { + typedef std::integral_constant::value > 1) +#endif + > + use_align; + if (!use_align::value) { + T** pointerpointer = static_cast(lua_newuserdata(L, sizeof(T*))); + return pointerpointer; + } + static const std::size_t initial_size = aligned_space_for(nullptr); + static const std::size_t misaligned_size = aligned_space_for(reinterpret_cast(0x1)); + + std::size_t allocated_size = initial_size; + void* unadjusted = lua_newuserdata(L, initial_size); + void* adjusted = align(std::alignment_of::value, sizeof(T*), unadjusted, allocated_size); + if (adjusted == nullptr) { + lua_pop(L, 1); + // what kind of absolute garbage trash allocator are we dealing with? + // whatever, add some padding in the case of MAXIMAL alignment waste... + allocated_size = misaligned_size; + unadjusted = lua_newuserdata(L, allocated_size); + adjusted = align(std::alignment_of::value, sizeof(T*), unadjusted, allocated_size); + if (adjusted == nullptr) { + // trash allocator can burn in hell + lua_pop(L, 1); + // luaL_error(L, "if you are the one that wrote this allocator you should feel bad for doing a + // worse job than malloc/realloc and should go read some books, yeah?"); + luaL_error(L, "cannot properly align memory for '%s'", detail::demangle().data()); + } + } + return static_cast(adjusted); + } + + inline bool attempt_alloc(lua_State* L, std::size_t ptr_align, std::size_t ptr_size, std::size_t value_align, std::size_t value_size, + std::size_t allocated_size, void*& pointer_adjusted, void*& data_adjusted) { + void* adjusted = lua_newuserdata(L, allocated_size); + pointer_adjusted = align(ptr_align, ptr_size, adjusted, allocated_size); + if (pointer_adjusted == nullptr) { + lua_pop(L, 1); + return false; + } + // subtract size of what we're going to allocate there + allocated_size -= ptr_size; + adjusted = static_cast(static_cast(pointer_adjusted) + ptr_size); + data_adjusted = align(value_align, value_size, adjusted, allocated_size); + if (data_adjusted == nullptr) { + lua_pop(L, 1); + return false; + } + return true; + } + + inline bool attempt_alloc_unique(lua_State* L, std::size_t ptr_align, std::size_t ptr_size, std::size_t real_align, std::size_t real_size, + std::size_t allocated_size, void*& pointer_adjusted, void*& dx_adjusted, void*& id_adjusted, void*& data_adjusted) { + void* adjusted = lua_newuserdata(L, allocated_size); + pointer_adjusted = align(ptr_align, ptr_size, adjusted, allocated_size); + if (pointer_adjusted == nullptr) { + lua_pop(L, 1); + return false; + } + allocated_size -= ptr_size; + + adjusted = static_cast(static_cast(pointer_adjusted) + ptr_size); + dx_adjusted = align(std::alignment_of_v, sizeof(unique_destructor), adjusted, allocated_size); + if (dx_adjusted == nullptr) { + lua_pop(L, 1); + return false; + } + allocated_size -= sizeof(unique_destructor); + + adjusted = static_cast(static_cast(dx_adjusted) + sizeof(unique_destructor)); + + id_adjusted = align(std::alignment_of_v, sizeof(unique_tag), adjusted, allocated_size); + if (id_adjusted == nullptr) { + lua_pop(L, 1); + return false; + } + allocated_size -= sizeof(unique_tag); + + adjusted = static_cast(static_cast(id_adjusted) + sizeof(unique_tag)); + data_adjusted = align(real_align, real_size, adjusted, allocated_size); + if (data_adjusted == nullptr) { + lua_pop(L, 1); + return false; + } + return true; + } + + template + T* usertype_allocate(lua_State* L) { + typedef std::integral_constant::value > 1 || std::alignment_of::value > 1) +#endif + > + use_align; + if (!use_align::value) { + T** pointerpointer = static_cast(lua_newuserdata(L, sizeof(T*) + sizeof(T))); + T*& pointerreference = *pointerpointer; + T* allocationtarget = reinterpret_cast(pointerpointer + 1); + pointerreference = allocationtarget; + return allocationtarget; + } + + /* the assumption is that `lua_newuserdata` -- unless someone + passes a specific lua_Alloc that gives us bogus, un-aligned pointers + -- uses malloc, which tends to hand out more or less aligned pointers to memory + (most of the time, anyhow) + + but it's not guaranteed, so we have to do a post-adjustment check and increase padding + + we do this preliminarily with compile-time stuff, to see + if we strike lucky with the allocator and alignment values + + otherwise, we have to re-allocate the userdata and + over-allocate some space for additional padding because + compilers are optimized for aligned reads/writes + (and clang will barf UBsan errors on us for not being aligned) + */ + static const std::size_t initial_size = aligned_space_for(nullptr); + static const std::size_t misaligned_size = aligned_space_for(reinterpret_cast(0x1)); + + void* pointer_adjusted; + void* data_adjusted; + bool result + = attempt_alloc(L, std::alignment_of_v, sizeof(T*), std::alignment_of_v, sizeof(T), initial_size, pointer_adjusted, data_adjusted); + if (!result) { + // we're likely to get something that fails to perform the proper allocation a second time, + // so we use the suggested_new_size bump to help us out here + pointer_adjusted = nullptr; + data_adjusted = nullptr; + result = attempt_alloc( + L, std::alignment_of_v, sizeof(T*), std::alignment_of_v, sizeof(T), misaligned_size, pointer_adjusted, data_adjusted); + if (!result) { + if (pointer_adjusted == nullptr) { + luaL_error(L, "aligned allocation of userdata block (pointer section) for '%s' failed", detail::demangle().c_str()); + } + else { + luaL_error(L, "aligned allocation of userdata block (data section) for '%s' failed", detail::demangle().c_str()); + } + return nullptr; + } + } + + T** pointerpointer = reinterpret_cast(pointer_adjusted); + T*& pointerreference = *pointerpointer; + T* allocationtarget = reinterpret_cast(data_adjusted); + pointerreference = allocationtarget; + return allocationtarget; + } + + template + Real* usertype_unique_allocate(lua_State* L, T**& pref, unique_destructor*& dx, unique_tag*& id) { + typedef std::integral_constant::value > 1 || std::alignment_of::value > 1 || std::alignment_of::value > 1 + || std::alignment_of::value > 1) +#endif + > + use_align; + if (!use_align::value) { + pref = static_cast(lua_newuserdata(L, sizeof(T*) + sizeof(detail::unique_destructor) + sizeof(unique_tag) + sizeof(Real))); + dx = static_cast(static_cast(pref + 1)); + id = static_cast(static_cast(dx + 1)); + Real* mem = static_cast(static_cast(id + 1)); + return mem; + } + + static const std::size_t initial_size = aligned_space_for(nullptr); + static const std::size_t misaligned_size = aligned_space_for(reinterpret_cast(0x1)); + + void* pointer_adjusted; + void* dx_adjusted; + void* id_adjusted; + void* data_adjusted; + bool result = attempt_alloc_unique(L, + std::alignment_of_v, + sizeof(T*), + std::alignment_of_v, + sizeof(Real), + initial_size, + pointer_adjusted, + dx_adjusted, + id_adjusted, + data_adjusted); + if (!result) { + // we're likely to get something that fails to perform the proper allocation a second time, + // so we use the suggested_new_size bump to help us out here + pointer_adjusted = nullptr; + dx_adjusted = nullptr; + id_adjusted = nullptr; + data_adjusted = nullptr; + result = attempt_alloc_unique(L, + std::alignment_of_v, + sizeof(T*), + std::alignment_of_v, + sizeof(Real), + misaligned_size, + pointer_adjusted, + dx_adjusted, + id_adjusted, + data_adjusted); + if (!result) { + if (pointer_adjusted == nullptr) { + luaL_error(L, "aligned allocation of userdata block (pointer section) for '%s' failed", detail::demangle().c_str()); + } + else if (dx_adjusted == nullptr) { + luaL_error(L, "aligned allocation of userdata block (deleter section) for '%s' failed", detail::demangle().c_str()); + } + else { + luaL_error(L, "aligned allocation of userdata block (data section) for '%s' failed", detail::demangle().c_str()); + } + return nullptr; + } + } + + pref = static_cast(pointer_adjusted); + dx = static_cast(dx_adjusted); + id = static_cast(id_adjusted); + Real* mem = static_cast(data_adjusted); + return mem; + } + + template + T* user_allocate(lua_State* L) { + typedef std::integral_constant::value > 1) +#endif + > + use_align; + if (!use_align::value) { + T* pointer = static_cast(lua_newuserdata(L, sizeof(T))); + return pointer; + } + + static const std::size_t initial_size = aligned_space_for(nullptr); + static const std::size_t misaligned_size = aligned_space_for(reinterpret_cast(0x1)); + + std::size_t allocated_size = initial_size; + void* unadjusted = lua_newuserdata(L, allocated_size); + void* adjusted = align(std::alignment_of::value, sizeof(T), unadjusted, allocated_size); + if (adjusted == nullptr) { + lua_pop(L, 1); + // try again, add extra space for alignment padding + allocated_size = misaligned_size; + unadjusted = lua_newuserdata(L, allocated_size); + adjusted = align(std::alignment_of::value, sizeof(T), unadjusted, allocated_size); + if (adjusted == nullptr) { + lua_pop(L, 1); + luaL_error(L, "cannot properly align memory for '%s'", detail::demangle().data()); + } + } + return static_cast(adjusted); + } + + template + int usertype_alloc_destruct(lua_State* L) { + void* memory = lua_touserdata(L, 1); + memory = align_usertype_pointer(memory); + T** pdata = static_cast(memory); + T* data = *pdata; + std::allocator alloc{}; + std::allocator_traits>::destroy(alloc, data); + return 0; + } + + template + int unique_destruct(lua_State* L) { + void* memory = lua_touserdata(L, 1); + memory = align_usertype_unique_destructor(memory); + unique_destructor& dx = *static_cast(memory); + memory = align_usertype_unique_tag(memory); + (dx)(memory); + return 0; + } + + template + int user_alloc_destruct(lua_State* L) { + void* memory = lua_touserdata(L, 1); + memory = align_user(memory); + T* data = static_cast(memory); + std::allocator alloc; + std::allocator_traits>::destroy(alloc, data); + return 0; + } + + template + void usertype_unique_alloc_destroy(void* memory) { + memory = align_usertype_unique(memory); + Real* target = static_cast(memory); + std::allocator alloc; + std::allocator_traits>::destroy(alloc, target); + } + + template + int cannot_destruct(lua_State* L) { + return luaL_error(L, + "cannot call the destructor for '%s': it is either hidden (protected/private) or removed with '= " + "delete' and thusly this type is being destroyed without properly destructing, invoking undefined " + "behavior: please bind a usertype and specify a custom destructor to define the behavior properly", + detail::demangle().data()); + } + + template + void reserve(T&, std::size_t) { + } + + template + void reserve(std::vector& vec, std::size_t hint) { + vec.reserve(hint); + } + + template + void reserve(std::basic_string& str, std::size_t hint) { + str.reserve(hint); + } + + inline bool property_always_true(meta_function) { + return true; + } + + struct properties_enrollment_allowed { + int& times_through; + std::bitset<64>& properties; + automagic_enrollments& enrollments; + + properties_enrollment_allowed(int& times, std::bitset<64>& props, automagic_enrollments& enroll) : times_through(times), properties(props), enrollments(enroll) { + } + + bool operator()(meta_function mf) const { + bool p = properties[static_cast(mf)]; + if (times_through > 0) { + return p; + } + switch (mf) { + case meta_function::length: + return enrollments.length_operator && !p; + case meta_function::pairs: + return enrollments.pairs_operator && !p; + case meta_function::call: + return enrollments.call_operator && !p; + case meta_function::less_than: + return enrollments.less_than_operator && !p; + case meta_function::less_than_or_equal_to: + return enrollments.less_than_or_equal_to_operator && !p; + case meta_function::equal_to: + return enrollments.equal_to_operator && !p; + default: + break; + } + return !p; + } + }; + + struct indexed_insert { + lua_reg_table& l; + int& index; + + indexed_insert(lua_reg_table& cont, int& idx) : l(cont), index(idx) { + } + void operator()(meta_function mf, lua_CFunction f) { + l[index] = luaL_Reg{ to_string(mf).c_str(), f }; + ++index; + } + }; + } // namespace detail + + namespace stack { + + template + struct field_getter; + template + struct probe_field_getter; + + template + struct field_setter; + + template + struct unqualified_getter; + template + struct qualified_getter; + + template + struct qualified_interop_getter; + template + struct unqualified_interop_getter; + + template + struct popper; + + template + struct unqualified_pusher; + + template + struct unqualified_checker; + template + struct qualified_checker; + + template + struct unqualified_check_getter; + template + struct qualified_check_getter; + + struct probe { + bool success; + int levels; + + probe(bool s, int l) : success(s), levels(l) { + } + + operator bool() const { + return success; + }; + }; + + struct record { + int last; + int used; + + record() : last(), used() { + } + void use(int count) { + last = count; + used += count; + } + }; + + } // namespace stack + + namespace meta { namespace meta_detail { + + template + using adl_sol_lua_get_test_t = decltype(sol_lua_get(types(), static_cast(nullptr), -1, std::declval())); + + template + using adl_sol_lua_interop_get_test_t + = decltype(sol_lua_interop_get(types(), static_cast(nullptr), -1, static_cast(nullptr), std::declval())); + + template + using adl_sol_lua_check_test_t = decltype(sol_lua_check(types(), static_cast(nullptr), -1, no_panic, std::declval())); + + template + using adl_sol_lua_interop_check_test_t + = decltype(sol_lua_interop_check(types(), static_cast(nullptr), -1, type::none, no_panic, std::declval())); + + template + using adl_sol_lua_check_get_test_t + = decltype(sol_lua_check_get(types(), static_cast(nullptr), -1, no_panic, std::declval())); + + template + using adl_sol_lua_push_test_t = decltype(sol_lua_push(static_cast(nullptr), std::declval()...)); + + template + using adl_sol_lua_push_exact_test_t = decltype(sol_lua_push(types(), static_cast(nullptr), std::declval()...)); + + template + inline constexpr bool is_adl_sol_lua_get_v = meta::is_detected_v; + + template + inline constexpr bool is_adl_sol_lua_interop_get_v = meta::is_detected_v; + + template + inline constexpr bool is_adl_sol_lua_check_v = meta::is_detected_v; + + template + inline constexpr bool is_adl_sol_lua_interop_check_v = meta::is_detected_v; + + template + inline constexpr bool is_adl_sol_lua_check_get_v = meta::is_detected_v; + + template + inline constexpr bool is_adl_sol_lua_push_v = meta::is_detected_v; + + template + inline constexpr bool is_adl_sol_lua_push_exact_v = meta::is_detected_v; + }} // namespace meta::meta_detail + + + namespace stack { + namespace stack_detail { + constexpr const char* not_enough_stack_space = "not enough space left on Lua stack"; + constexpr const char* not_enough_stack_space_floating = "not enough space left on Lua stack for a floating point number"; + constexpr const char* not_enough_stack_space_integral = "not enough space left on Lua stack for an integral number"; + constexpr const char* not_enough_stack_space_string = "not enough space left on Lua stack for a string"; + constexpr const char* not_enough_stack_space_meta_function_name = "not enough space left on Lua stack for the name of a meta_function"; + constexpr const char* not_enough_stack_space_userdata = "not enough space left on Lua stack to create a sol3 userdata"; + constexpr const char* not_enough_stack_space_generic = "not enough space left on Lua stack to push valuees"; + constexpr const char* not_enough_stack_space_environment = "not enough space left on Lua stack to retrieve environment"; + + template + struct strip { + typedef T type; + }; + template + struct strip> { + typedef T& type; + }; + template + struct strip> { + typedef T& type; + }; + template + struct strip> { + typedef T type; + }; + template + using strip_t = typename strip::type; + + template + static int get_size_hint(C& c) { + return static_cast(c.size()); + } + + template + static int get_size_hint(const std::forward_list&) { + // forward_list makes me sad + return static_cast(32); + } + + template + decltype(auto) unchecked_unqualified_get(lua_State* L, int index, record& tracking) { + using Tu = meta::unqualified_t; + if constexpr (meta::meta_detail::is_adl_sol_lua_get_v) { + return sol_lua_get(types(), L, index, tracking); + } + else { + unqualified_getter g{}; + (void)g; + return g.get(L, index, tracking); + } + } + + template + decltype(auto) unchecked_get(lua_State* L, int index, record& tracking) { + if constexpr (meta::meta_detail::is_adl_sol_lua_get_v) { + return sol_lua_get(types(), L, index, tracking); + } + else { + qualified_getter g{}; + (void)g; + return g.get(L, index, tracking); + } + } + + template + decltype(auto) unqualified_interop_get(lua_State* L, int index, void* unadjusted_pointer, record& tracking) { + using Tu = meta::unqualified_t; + if constexpr (meta::meta_detail::is_adl_sol_lua_interop_get_v) { + return sol_lua_interop_get(types(), L, index, unadjusted_pointer, tracking); + } + else { + (void)L; + (void)index; + (void)unadjusted_pointer; + (void)tracking; + using Ti = stack_detail::strip_t; + return std::pair{ false, nullptr }; + } + } + + template + decltype(auto) interop_get(lua_State* L, int index, void* unadjusted_pointer, record& tracking) { + if constexpr (meta::meta_detail::is_adl_sol_lua_interop_get_v) { + return sol_lua_interop_get(types(), L, index, unadjusted_pointer, tracking); + } + else { + return unqualified_interop_get(L, index, unadjusted_pointer, tracking); + } + } + + template + bool unqualified_interop_check(lua_State* L, int index, type index_type, Handler&& handler, record& tracking) { + using Tu = meta::unqualified_t; + if constexpr (meta::meta_detail::is_adl_sol_lua_interop_check_v) { + return sol_lua_interop_check(types(), L, index, index_type, std::forward(handler), tracking); + } + else { + (void)L; + (void)index; + (void)index_type; + (void)handler; + (void)tracking; + return false; + } + } + + template + bool interop_check(lua_State* L, int index, type index_type, Handler&& handler, record& tracking) { + if constexpr (meta::meta_detail::is_adl_sol_lua_interop_check_v) { + return sol_lua_interop_check(types(), L, index, index_type, std::forward(handler), tracking); + } + else { + return unqualified_interop_check(L, index, index_type, std::forward(handler), tracking); + } + } + + using undefined_method_func = void (*)(stack_reference); + + struct undefined_metatable { + lua_State* L; + const char* key; + undefined_method_func on_new_table; + + undefined_metatable(lua_State* l, const char* k, undefined_method_func umf) : L(l), key(k), on_new_table(umf) { + } + + void operator()() const { + if (luaL_newmetatable(L, key) == 1) { + on_new_table(stack_reference(L, -1)); + } + lua_setmetatable(L, -2); + } + }; + } // namespace stack_detail + + inline bool maybe_indexable(lua_State* L, int index = -1) { + type t = type_of(L, index); + return t == type::userdata || t == type::table; + } + + inline int top(lua_State* L) { + return lua_gettop(L); + } + + inline bool is_main_thread(lua_State* L) { + int ismainthread = lua_pushthread(L); + lua_pop(L, 1); + return ismainthread == 1; + } + + inline void coroutine_create_guard(lua_State* L) { + if (is_main_thread(L)) { + return; + } + int stacksize = lua_gettop(L); + if (stacksize < 1) { + return; + } + if (type_of(L, 1) != type::function) { + return; + } + // well now we're screwed... + // we can clean the stack and pray it doesn't destroy anything? + lua_pop(L, stacksize); + } + + inline void clear(lua_State* L, int table_index) { + lua_pushnil(L); + while (lua_next(L, table_index) != 0) { + // remove value + lua_pop(L, 1); + // duplicate key to protect form rawset + lua_pushvalue(L, -1); + // push new value + lua_pushnil(L); + // table_index%[key] = nil + lua_rawset(L, table_index); + } + } + + inline void clear(reference& r) { + auto pp = push_pop(r); + int stack_index = pp.index_of(r); + clear(r.lua_state(), stack_index); + } + + inline void clear(stack_reference& r) { + clear(r.lua_state(), r.stack_index()); + } + + template + int push(lua_State* L, T&& t, Args&&... args) { + using Tu = meta::unqualified_t; + if constexpr (meta::meta_detail::is_adl_sol_lua_push_exact_v) { + return sol_lua_push(types(), L, std::forward(t), std::forward(args)...); + } + else if constexpr (meta::meta_detail::is_adl_sol_lua_push_exact_v) { + return sol_lua_push(types(), L, std::forward(t), std::forward(args)...); + } + else if constexpr (meta::meta_detail::is_adl_sol_lua_push_v) { + return sol_lua_push(L, std::forward(t), std::forward(args)...); + } + else { + unqualified_pusher p{}; + (void)p; + return p.push(L, std::forward(t), std::forward(args)...); + } + } + + // overload allows to use a pusher of a specific type, but pass in any kind of args + template ::value>> + int push(lua_State* L, Arg&& arg, Args&&... args) { + using Tu = meta::unqualified_t; + if constexpr (meta::meta_detail::is_adl_sol_lua_push_exact_v) { + return sol_lua_push(types(), L, std::forward(arg), std::forward(args)...); + } + else if constexpr (meta::meta_detail::is_adl_sol_lua_push_exact_v) { + return sol_lua_push(types(), L, std::forward(arg), std::forward(args)...); + } + else if constexpr (meta::meta_detail::is_adl_sol_lua_push_v) { + return sol_lua_push(L, std::forward(arg), std::forward(args)...); + } + else { + unqualified_pusher p{}; + (void)p; + return p.push(L, std::forward(arg), std::forward(args)...); + } + } + + namespace stack_detail { + + template + int push_reference(lua_State* L, Arg&& arg, Args&&... args) { + using use_reference_tag = meta::all, + meta::neg>, + meta::neg>>, + meta::neg>>>; + using Tr = meta::conditional_t>; + return stack::push(L, std::forward(arg), std::forward(args)...); + } + + } // namespace stack_detail + + template + int push_reference(lua_State* L, T&& t, Args&&... args) { + return stack_detail::push_reference(L, std::forward(t), std::forward(args)...); + } + + template + int push_reference(lua_State* L, Arg&& arg, Args&&... args) { + return stack_detail::push_reference(L, std::forward(arg), std::forward(args)...); + } + + inline int multi_push(lua_State*) { + // do nothing + return 0; + } + + template + int multi_push(lua_State* L, T&& t, Args&&... args) { + int pushcount = push(L, std::forward(t)); + void(detail::swallow{ (pushcount += stack::push(L, std::forward(args)), 0)... }); + return pushcount; + } + + inline int multi_push_reference(lua_State*) { + // do nothing + return 0; + } + + template + int multi_push_reference(lua_State* L, T&& t, Args&&... args) { + int pushcount = push_reference(L, std::forward(t)); + void(detail::swallow{ (pushcount += stack::push_reference(L, std::forward(args)), 0)... }); + return pushcount; + } + + template + bool unqualified_check(lua_State* L, int index, Handler&& handler, record& tracking) { + using Tu = meta::unqualified_t; + if constexpr (meta::meta_detail::is_adl_sol_lua_check_v) { + return sol_lua_check(types(), L, index, std::forward(handler), tracking); + } + else { + unqualified_checker> c; + // VC++ has a bad warning here: shut it up + (void)c; + return c.check(L, index, std::forward(handler), tracking); + } + } + + template + bool unqualified_check(lua_State* L, int index, Handler&& handler) { + record tracking{}; + return unqualified_check(L, index, std::forward(handler), tracking); + } + + template + bool unqualified_check(lua_State* L, int index = -lua_size>::value) { + auto handler = no_panic; + return unqualified_check(L, index, handler); + } + + template + bool check(lua_State* L, int index, Handler&& handler, record& tracking) { + if constexpr (meta::meta_detail::is_adl_sol_lua_check_v) { + return sol_lua_check(types(), L, index, std::forward(handler), tracking); + } + else { + using Tu = meta::unqualified_t; + qualified_checker> c; + // VC++ has a bad warning here: shut it up + (void)c; + return c.check(L, index, std::forward(handler), tracking); + } + } + + template + bool check(lua_State* L, int index, Handler&& handler) { + record tracking{}; + return check(L, index, std::forward(handler), tracking); + } + + template + bool check(lua_State* L, int index = -lua_size>::value) { + auto handler = no_panic; + return check(L, index, handler); + } + + template + bool check_usertype(lua_State* L, int index, type, Handler&& handler, record& tracking) { + using Tu = meta::unqualified_t; + using detail_t = meta::conditional_t, detail::as_pointer_tag, detail::as_value_tag>; + return check(L, index, std::forward(handler), tracking); + } + + template + bool check_usertype(lua_State* L, int index, Handler&& handler, record& tracking) { + using Tu = meta::unqualified_t; + using detail_t = meta::conditional_t, detail::as_pointer_tag, detail::as_value_tag>; + return check(L, index, std::forward(handler), tracking); + } + + template + bool check_usertype(lua_State* L, int index, Handler&& handler) { + record tracking{}; + return check_usertype(L, index, std::forward(handler), tracking); + } + + template + bool check_usertype(lua_State* L, int index = -lua_size>::value) { + auto handler = no_panic; + return check_usertype(L, index, handler); + } + + template + decltype(auto) unqualified_check_get(lua_State* L, int index, Handler&& handler, record& tracking) { + using Tu = meta::unqualified_t; + if constexpr (meta::meta_detail::is_adl_sol_lua_check_get_v) { + return sol_lua_check_get(types(), L, index, std::forward(handler), tracking); + } + else if constexpr (meta::meta_detail::is_adl_sol_lua_check_get_v) { + return sol_lua_check_get(types(), L, index, std::forward(handler), tracking); + } + else { + unqualified_check_getter cg{}; + (void)cg; + return cg.get(L, index, std::forward(handler), tracking); + } + } + + template + decltype(auto) unqualified_check_get(lua_State* L, int index, Handler&& handler) { + record tracking{}; + return unqualified_check_get(L, index, handler, tracking); + } + + template + decltype(auto) unqualified_check_get(lua_State* L, int index = -lua_size>::value) { + auto handler = no_panic; + return unqualified_check_get(L, index, handler); + } + + template + decltype(auto) check_get(lua_State* L, int index, Handler&& handler, record& tracking) { + if constexpr (meta::meta_detail::is_adl_sol_lua_check_get_v) { + return sol_lua_check_get(types(), L, index, std::forward(handler), tracking); + } + else { + qualified_check_getter cg{}; + (void)cg; + return cg.get(L, index, std::forward(handler), tracking); + } + } + + template + decltype(auto) check_get(lua_State* L, int index, Handler&& handler) { + record tracking{}; + return check_get(L, index, handler, tracking); + } + + template + decltype(auto) check_get(lua_State* L, int index = -lua_size>::value) { + auto handler = no_panic; + return check_get(L, index, handler); + } + + namespace stack_detail { + + template + bool check_types(lua_State*, int, Handler&&, record&) { + return true; + } + + template + bool check_types(lua_State* L, int firstargument, Handler&& handler, record& tracking) { + if (!stack::check(L, firstargument + tracking.used, handler, tracking)) + return false; + return check_types(L, firstargument, std::forward(handler), tracking); + } + + template + bool check_types(types, lua_State* L, int index, Handler&& handler, record& tracking) { + return check_types(L, index, std::forward(handler), tracking); + } + + } // namespace stack_detail + + template + bool multi_check(lua_State* L, int index, Handler&& handler, record& tracking) { + return stack_detail::check_types(L, index, std::forward(handler), tracking); + } + + template + bool multi_check(lua_State* L, int index, Handler&& handler) { + record tracking{}; + return multi_check(L, index, std::forward(handler), tracking); + } + + template + bool multi_check(lua_State* L, int index) { + return multi_check(L, index); + } + + template + auto unqualified_get(lua_State* L, int index, record& tracking) -> decltype(stack_detail::unchecked_unqualified_get(L, index, tracking)) { +#if defined(SOL_SAFE_GETTER) && SOL_SAFE_GETTER + static constexpr bool is_op = meta::is_specialization_of_v +#if defined(SOL_CXX17_FEATURES) && SOL_CXX17_FEATURES + || meta::is_specialization_of_v +#endif + ; + if constexpr (is_op) { + return stack_detail::unchecked_unqualified_get(L, index, tracking); + } + else { + if (is_lua_reference::value) { + return stack_detail::unchecked_unqualified_get(L, index, tracking); + } + auto op = unqualified_check_get(L, index, type_panic_c_str, tracking); + return *std::move(op); + } +#else + return stack_detail::unchecked_unqualified_get(L, index, tracking); +#endif + } + + template + decltype(auto) unqualified_get(lua_State* L, int index = -lua_size>::value) { + record tracking{}; + return unqualified_get(L, index, tracking); + } + + template + auto get(lua_State* L, int index, record& tracking) -> decltype(stack_detail::unchecked_get(L, index, tracking)) { +#if defined(SOL_SAFE_GETTER) && SOL_SAFE_GETTER + static constexpr bool is_op = meta::is_specialization_of_v +#if defined(SOL_CXX17_FEATURES) && SOL_CXX17_FEATURES + || meta::is_specialization_of_v +#endif + ; + if constexpr (is_op) { + return stack_detail::unchecked_get(L, index, tracking); + } + else { + if (is_lua_reference::value) { + return stack_detail::unchecked_get(L, index, tracking); + } + auto op = check_get(L, index, type_panic_c_str, tracking); + return *std::move(op); + } +#else + return stack_detail::unchecked_get(L, index, tracking); +#endif + } + + template + decltype(auto) get(lua_State* L, int index = -lua_size>::value) { + record tracking{}; + return get(L, index, tracking); + } + + template + decltype(auto) get_usertype(lua_State* L, int index, record& tracking) { + using UT = meta::conditional_t::value, detail::as_pointer_tag>, detail::as_value_tag>; + return get(L, index, tracking); + } + + template + decltype(auto) get_usertype(lua_State* L, int index = -lua_size>::value) { + record tracking{}; + return get_usertype(L, index, tracking); + } + + template + decltype(auto) pop(lua_State* L) { + return popper>{}.pop(L); + } + + template + void get_field(lua_State* L, Key&& key) { + field_getter, global, raw>{}.get(L, std::forward(key)); + } + + template + void get_field(lua_State* L, Key&& key, int tableindex) { + field_getter, global, raw>{}.get(L, std::forward(key), tableindex); + } + + template + void raw_get_field(lua_State* L, Key&& key) { + get_field(L, std::forward(key)); + } + + template + void raw_get_field(lua_State* L, Key&& key, int tableindex) { + get_field(L, std::forward(key), tableindex); + } + + template + probe probe_get_field(lua_State* L, Key&& key) { + return probe_field_getter, C, global, raw>{}.get(L, std::forward(key)); + } + + template + probe probe_get_field(lua_State* L, Key&& key, int tableindex) { + return probe_field_getter, C, global, raw>{}.get(L, std::forward(key), tableindex); + } + + template + probe probe_raw_get_field(lua_State* L, Key&& key) { + return probe_get_field(L, std::forward(key)); + } + + template + probe probe_raw_get_field(lua_State* L, Key&& key, int tableindex) { + return probe_get_field(L, std::forward(key), tableindex); + } + + template + void set_field(lua_State* L, Key&& key, Value&& value) { + field_setter, global, raw>{}.set(L, std::forward(key), std::forward(value)); + } + + template + void set_field(lua_State* L, Key&& key, Value&& value, int tableindex) { + field_setter, global, raw>{}.set(L, std::forward(key), std::forward(value), tableindex); + } + + template + void raw_set_field(lua_State* L, Key&& key, Value&& value) { + set_field(L, std::forward(key), std::forward(value)); + } + + template + void raw_set_field(lua_State* L, Key&& key, Value&& value, int tableindex) { + set_field(L, std::forward(key), std::forward(value), tableindex); + } + + template + void modify_unique_usertype_as(const stack_reference& obj, F&& f) { + using u_traits = unique_usertype_traits; + void* raw = lua_touserdata(obj.lua_state(), obj.stack_index()); + void* ptr_memory = detail::align_usertype_pointer(raw); + void* uu_memory = detail::align_usertype_unique(raw); + T& uu = *static_cast(uu_memory); + f(uu); + *static_cast(ptr_memory) = static_cast(u_traits::get(uu)); + } + + template + void modify_unique_usertype(const stack_reference& obj, F&& f) { + using bt = meta::bind_traits>; + using T = typename bt::template arg_at<0>; + using Tu = meta::unqualified_t; + modify_unique_usertype_as(obj, std::forward(f)); + } + + } // namespace stack + + namespace detail { + + template + lua_CFunction make_destructor(std::true_type) { + if constexpr (is_unique_usertype_v) { + return &unique_destruct; + } + else if constexpr (!std::is_pointer_v) { + return &usertype_alloc_destruct; + } + else { + return &cannot_destruct; + } + } + + template + lua_CFunction make_destructor(std::false_type) { + return &cannot_destruct; + } + + template + lua_CFunction make_destructor() { + return make_destructor(std::is_destructible()); + } + + struct no_comp { + template + bool operator()(A&&, B&&) const { + return false; + } + }; + + template + int is_check(lua_State* L) { + return stack::push(L, stack::check(L, 1, &no_panic)); + } + + template + int member_default_to_string(std::true_type, lua_State* L) { + decltype(auto) ts = stack::get(L, 1).to_string(); + return stack::push(L, std::forward(ts)); + } + + template + int member_default_to_string(std::false_type, lua_State* L) { + return luaL_error(L, + "cannot perform to_string on '%s': no 'to_string' overload in namespace, 'to_string' member " + "function, or operator<<(ostream&, ...) present", + detail::demangle().data()); + } + + template + int adl_default_to_string(std::true_type, lua_State* L) { + using namespace std; + decltype(auto) ts = to_string(stack::get(L, 1)); + return stack::push(L, std::forward(ts)); + } + + template + int adl_default_to_string(std::false_type, lua_State* L) { + return member_default_to_string(meta::supports_to_string_member(), L); + } + + template + int oss_default_to_string(std::true_type, lua_State* L) { + std::ostringstream oss; + oss << stack::unqualified_get(L, 1); + return stack::push(L, oss.str()); + } + + template + int oss_default_to_string(std::false_type, lua_State* L) { + return adl_default_to_string(meta::supports_adl_to_string(), L); + } + + template + int default_to_string(lua_State* L) { + return oss_default_to_string(meta::supports_ostream_op(), L); + } + + template + int default_size(lua_State* L) { + decltype(auto) self = stack::unqualified_get(L, 1); + return stack::push(L, self.size()); + } + + template + int comparsion_operator_wrap(lua_State* L) { + if constexpr (std::is_void_v) { + return stack::push(L, false); + } + else { + auto maybel = stack::unqualified_check_get(L, 1); + if (!maybel) { + return stack::push(L, false); + } + auto mayber = stack::unqualified_check_get(L, 2); + if (!mayber) { + return stack::push(L, false); + } + decltype(auto) l = *maybel; + decltype(auto) r = *mayber; + if constexpr (std::is_same_v) { + std::equal_to<> op; + return stack::push(L, op(detail::ptr(l), detail::ptr(r))); + } + else { + if constexpr (std::is_same_v, Op> // clang-format hack + || std::is_same_v, Op> // + || std::is_same_v, Op>) { // + if (detail::ptr(l) == detail::ptr(r)) { + return stack::push(L, true); + } + } + Op op; + return stack::push(L, op(detail::deref(l), detail::deref(r))); + } + } + } + + template + void insert_default_registrations(IFx&& ifx, Fx&& fx); + + template + struct get_is_primitive : is_lua_primitive {}; + + template + struct get_is_primitive + : meta::neg(), nullptr, -1, std::declval()))>> {}; + + template + struct get_is_primitive + : meta::neg>(), nullptr, -1, std::declval()))>> {}; + + template + struct get_is_primitive : get_is_primitive {}; + + } // namespace detail + + template + struct is_proxy_primitive + : detail::get_is_primitive, meta::meta_detail::is_adl_sol_lua_get_v>> {}; + +} // namespace sol + +#endif // SOL_STACK_CORE_HPP -- cgit v1.2.3