// The MIT License (MIT) // Copyright (c) 2013-2017 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 "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 namespace sol { namespace detail { struct as_reference_tag {}; template struct as_pointer_tag {}; template struct as_value_tag {}; template struct as_table_tag {}; using unique_destructor = void (*)(void*); 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); } template inline std::size_t aligned_space_for(void* alignment = nullptr) { char* start = static_cast(alignment); auto specific_align = [&alignment](std::size_t a, std::size_t s) { std::size_t space = (std::numeric_limits::max)(); alignment = align(a, s, alignment, space); alignment = static_cast(static_cast(alignment) + s); }; (void)detail::swallow{ int{}, (specific_align(std::alignment_of::value, sizeof(Args)), int{})... }; return static_cast(alignment) - start; } inline void* align_usertype_pointer(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(void*), ptr, space); } inline void* align_usertype_unique_destructor(void* ptr) { typedef std::integral_constant::value > 1) #endif > use_align; if (!use_align::value) { return static_cast(static_cast(ptr) + 1); } ptr = align_usertype_pointer(ptr); ptr = static_cast(static_cast(ptr) + sizeof(void*)); std::size_t space = (std::numeric_limits::max)(); return align(std::alignment_of::value, sizeof(unique_destructor), ptr, space); } template inline void* align_usertype_unique(void* ptr) { typedef std::integral_constant::value > 1) #endif > use_align; if (!pre_aligned) { ptr = align_usertype_unique_destructor(ptr); 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(T), ptr, space); } template inline 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 inline 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); } template inline 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; auto attempt_alloc = [](lua_State* L, std::size_t allocated_size, void*& pointer_adjusted, void*& data_adjusted) -> bool { void* adjusted = lua_newuserdata(L, allocated_size); pointer_adjusted = align(std::alignment_of::value, sizeof(T*), 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 -= sizeof(T*); adjusted = static_cast(static_cast(pointer_adjusted) + sizeof(T*)); data_adjusted = align(std::alignment_of::value, sizeof(T), adjusted, allocated_size); if (data_adjusted == nullptr) { lua_pop(L, 1); return false; } return true; }; bool result = attempt_alloc(L, 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, 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 inline Real* usertype_unique_allocate(lua_State* L, T**& pref, unique_destructor*& dx) { typedef std::integral_constant::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(Real))); dx = static_cast(static_cast(pref + 1)); Real* mem = static_cast(static_cast(dx + 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* data_adjusted; auto attempt_alloc = [](lua_State* L, std::size_t allocated_size, void*& pointer_adjusted, void*& dx_adjusted, void*& data_adjusted) -> bool { void* adjusted = lua_newuserdata(L, allocated_size); pointer_adjusted = align(std::alignment_of::value, sizeof(T*), adjusted, allocated_size); if (pointer_adjusted == nullptr) { lua_pop(L, 1); return false; } allocated_size -= sizeof(T*); adjusted = static_cast(static_cast(pointer_adjusted) + sizeof(T*)); dx_adjusted = align(std::alignment_of::value, 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)); data_adjusted = align(std::alignment_of::value, sizeof(Real), adjusted, allocated_size); if (data_adjusted == nullptr) { lua_pop(L, 1); return false; } return true; }; bool result = attempt_alloc(L, initial_size, pointer_adjusted, dx_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; data_adjusted = nullptr; result = attempt_alloc(L, misaligned_size, pointer_adjusted, dx_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); Real* mem = static_cast(data_adjusted); return mem; } template inline 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 inline 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 inline 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 = static_cast(static_cast(memory) + sizeof(unique_destructor)); (dx)(memory); return 0; } template inline 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 inline 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 inline 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& arr, std::size_t hint) { arr.reserve(hint); } template void reserve(std::basic_string& arr, std::size_t hint) { arr.reserve(hint); } } // namespace detail namespace stack { template struct extensible {}; template struct field_getter; template struct probe_field_getter; template struct field_setter; template struct getter; template struct userdata_getter; template struct popper; template struct pusher; template ::value, typename = void> struct checker; template struct userdata_checker; template struct 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_detail { 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 struct strip_extensible { typedef T type; }; template struct strip_extensible> { typedef T type; }; template using strip_extensible_t = typename strip_extensible::type; template static int get_size_hint(const 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 inline decltype(auto) unchecked_get(lua_State* L, int index, record& tracking) { getter> g{}; (void)g; return g.get(L, index, tracking); } template inline int push_reference(lua_State* L, Arg&& arg, Args&&... args) { typedef meta::all< std::is_lvalue_reference, meta::neg>, meta::neg>>, meta::neg>>> use_reference_tag; return pusher>>{}.push(L, std::forward(arg), std::forward(args)...); } template bool check_usertype(std::false_type, lua_State* L, int index, type indextype, Handler&& handler, record& tracking) { typedef meta::unqualified_t Tu; typedef detail::as_value_tag detail_t; return checker{}.check(types>(), L, index, indextype, std::forward(handler), tracking); } template bool check_usertype(std::true_type, lua_State* L, int index, type indextype, Handler&& handler, record& tracking) { typedef meta::unqualified_t>> Tu; typedef detail::as_pointer_tag detail_t; return checker{}.check(L, index, indextype, std::forward(handler), tracking); } } // 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); } template inline int push(lua_State* L, T&& t, Args&&... args) { return pusher>{}.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>> inline int push(lua_State* L, Arg&& arg, Args&&... args) { return pusher>{}.push(L, std::forward(arg), std::forward(args)...); } template inline int push_reference(lua_State* L, T&& t, Args&&... args) { return stack_detail::push_reference(L, std::forward(t), std::forward(args)...); } template inline 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 inline 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 inline 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 check(lua_State* L, int index, Handler&& handler, record& tracking) { typedef meta::unqualified_t Tu; 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, Handler&& handler, record& tracking) { type indextype = type_of(L, index); return stack_detail::check_usertype(std::is_pointer(), L, index, indextype, 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 inline decltype(auto) check_get(lua_State* L, int index, Handler&& handler, record& tracking) { typedef meta::unqualified_t Tu; check_getter cg{}; (void)cg; return cg.get(L, index, std::forward(handler), tracking); } template inline decltype(auto) check_get(lua_State* L, int index, Handler&& handler) { record tracking{}; return check_get(L, index, handler, tracking); } template inline 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 { #ifdef SOL_SAFE_GETTER template inline auto tagged_get(types, lua_State* L, int index, record& tracking) -> decltype(stack_detail::unchecked_get(L, index, tracking)) { auto op = check_get(L, index, type_panic_c_str, tracking); return *std::move(op); } template inline decltype(auto) tagged_get(types>, lua_State* L, int index, record& tracking) { return stack_detail::unchecked_get>(L, index, tracking); } #else template inline decltype(auto) tagged_get(types, lua_State* L, int index, record& tracking) { return stack_detail::unchecked_get(L, index, tracking); } #endif template struct check_types { template static 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 static bool check(types<>, lua_State*, int, Handler&&, record&) { return true; } }; template <> struct check_types { template static bool check(types, lua_State*, int, Handler&&, record&) { return true; } }; } // namespace stack_detail template bool multi_check(lua_State* L, int index, Handler&& handler, record& tracking) { return stack_detail::check_types{}.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) { auto handler = no_panic; return multi_check(L, index, handler); } 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, Handler&& handler) { return multi_check(L, index, std::forward(handler)); } template bool multi_check(lua_State* L, int index) { return multi_check(L, index); } template inline decltype(auto) get_usertype(lua_State* L, int index, record& tracking) { #ifdef SOL_SAFE_GETTER return stack_detail::tagged_get(types::value, detail::as_pointer_tag>, detail::as_value_tag>>(), L, index, tracking); #else return stack_detail::unchecked_get::value, detail::as_pointer_tag>, detail::as_value_tag>>(L, index, tracking); #endif } template inline decltype(auto) get_usertype(lua_State* L, int index = -lua_size>::value) { record tracking{}; return get_usertype(L, index, tracking); } template inline decltype(auto) get(lua_State* L, int index, record& tracking) { return stack_detail::tagged_get(types(), L, index, tracking); } template inline decltype(auto) get(lua_State* L, int index = -lua_size>::value) { record tracking{}; return get(L, index, tracking); } template inline 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, global, raw>{}.get(L, std::forward(key)); } template probe probe_get_field(lua_State* L, Key&& key, int tableindex) { return probe_field_getter, 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); } } // namespace stack } // namespace sol #endif // SOL_STACK_CORE_HPP