diff --git a/single/sol/sol.hpp b/single/sol/sol.hpp index a2c35aa2..f2236011 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-10-01 03:03:08.024225 UTC -// This header was generated with sol v2.18.4 (revision 228df26) +// Generated 2017-10-02 21:33:04.936340 UTC +// This header was generated with sol v2.18.4 (revision 73484bf) // https://github.com/ThePhD/sol2 #ifndef SOL_SINGLE_INCLUDE_HPP @@ -6498,28 +6498,343 @@ namespace sol { using unique_destructor = void (*)(void*); - template - inline int unique_destruct(lua_State* L) { - void* memory = lua_touserdata(L, 1); - T** pointerpointer = static_cast(memory); - unique_destructor& dx = *static_cast(static_cast(pointerpointer + 1)); - (dx)(memory); - return 0; + 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 int user_alloc_destruct(lua_State* L) { - void* rawdata = lua_touserdata(L, 1); - T* data = static_cast(rawdata); - std::allocator alloc; - alloc.destroy(data); - return 0; + 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* rawdata = lua_touserdata(L, 1); - T** pdata = static_cast(rawdata); + void* memory = lua_touserdata(L, 1); + memory = align_usertype_pointer(memory); + T** pdata = static_cast(memory); T* data = *pdata; std::allocator alloc{}; alloc.destroy(data); @@ -6527,19 +6842,38 @@ namespace sol { } 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", detail::demangle().data()); + 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; + alloc.destroy(data); + return 0; } template inline void usertype_unique_alloc_destroy(void* memory) { - T** pointerpointer = static_cast(memory); - unique_destructor* dx = static_cast(static_cast(pointerpointer + 1)); - Real* target = static_cast(static_cast(dx + 1)); + memory = align_usertype_unique(memory); + Real* target = static_cast(memory); std::allocator alloc; alloc.destroy(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) { } @@ -7526,8 +7860,8 @@ namespace stack { int metatableindex = lua_gettop(L); if (stack_detail::check_metatable>(L, metatableindex)) { void* memory = lua_touserdata(L, index); - T** pointerpointer = static_cast(memory); - detail::unique_destructor& pdx = *static_cast(static_cast(pointerpointer + 1)); + memory = detail::align_usertype_unique_destructor(memory); + detail::unique_destructor& pdx = *static_cast(memory); bool success = &detail::usertype_unique_alloc_destroy == pdx; if (!success) { handler(L, index, type::userdata, indextype, "value is a userdata but is not the correct unique usertype"); @@ -8246,15 +8580,16 @@ namespace stack { struct getter> { static T* get_no_lua_nil(lua_State* L, int index, record& tracking) { tracking.use(1); - void* rawdata = lua_touserdata(L, index); + void* memory = lua_touserdata(L, index); #ifdef SOL_ENABLE_INTEROP userdata_getter> ug; (void)ug; - auto ugr = ug.get(L, index, rawdata, tracking); + auto ugr = ug.get(L, index, memory, tracking); if (ugr.first) { return ugr.second; } #endif // interop extensibility + 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); @@ -8339,9 +8674,9 @@ namespace stack { static Real& get(lua_State* L, int index, record& tracking) { tracking.use(1); - P** pref = static_cast(lua_touserdata(L, index)); - detail::unique_destructor* fx = static_cast(static_cast(pref + 1)); - Real* mem = static_cast(static_cast(fx + 1)); + void* memory = lua_touserdata(L, index); + memory = detail::align_usertype_unique(memory); + Real* mem = static_cast(memory); return *mem; } }; @@ -8601,12 +8936,9 @@ namespace stack { // data in the first sizeof(T*) bytes, and then however many bytes it takes to // do the actual object. Things that are std::ref or plain T* are stored as // just the sizeof(T*), and nothing else. - T** pointerpointer = static_cast(lua_newuserdata(L, sizeof(T*) + sizeof(T))); - T*& referencereference = *pointerpointer; - T* allocationtarget = reinterpret_cast(pointerpointer + 1); - referencereference = allocationtarget; + T* obj = detail::usertype_allocate(L); std::allocator alloc{}; - alloc.construct(allocationtarget, std::forward(args)...); + alloc.construct(obj, std::forward(args)...); f(); return 1; } @@ -8631,7 +8963,7 @@ namespace stack { static int push_fx(lua_State* L, F&& f, T* obj) { if (obj == nullptr) return stack::push(L, lua_nil); - T** pref = static_cast(lua_newuserdata(L, sizeof(T*))); + T** pref = detail::usertype_allocate_pointer(L); *pref = obj; f(); return 1; @@ -8692,9 +9024,9 @@ namespace stack { template static int push_deep(lua_State* L, Args&&... args) { - P** pref = static_cast(lua_newuserdata(L, sizeof(P*) + sizeof(detail::unique_destructor) + sizeof(Real))); - detail::unique_destructor* fx = static_cast(static_cast(pref + 1)); - Real* mem = static_cast(static_cast(fx + 1)); + P** pref = nullptr; + detail::unique_destructor* fx = nullptr; + Real* mem = detail::usertype_unique_allocate(L, pref, fx); *fx = detail::usertype_unique_alloc_destroy; detail::default_construct::construct(mem, std::forward(args)...); *pref = unique_usertype_traits::get(*mem); @@ -8986,14 +9318,13 @@ namespace stack { template static int push_with(lua_State* L, Key&& name, Args&&... args) { // A dumb pusher - void* rawdata = lua_newuserdata(L, sizeof(T)); - T* data = static_cast(rawdata); + T* data = detail::user_allocate(L); std::allocator alloc; alloc.construct(data, std::forward(args)...); if (with_meta) { - lua_CFunction cdel = detail::user_alloc_destruct; // Make sure we have a plain GC set for this data if (luaL_newmetatable(L, name) != 0) { + lua_CFunction cdel = detail::user_alloc_destruct; lua_pushcclosure(L, cdel, 0); lua_setfield(L, -2, "__gc"); } @@ -9072,6 +9403,33 @@ namespace stack { } }; + template <> + struct pusher { + static int push_sized(lua_State* L, const char* str, std::size_t len) { + pusher p{}; + (void)p; + return p.push_sized(L, str, len); + } + + static int push(lua_State* L, const char* str) { + pusher p{}; + (void)p; + return p.push(L, str); + } + + static int push(lua_State* L, const char* strb, const char* stre) { + pusher p{}; + (void)p; + return p.push(L, strb, stre); + } + + static int push(lua_State* L, const char* str, std::size_t len) { + pusher p{}; + (void)p; + return p.push(L, str, len); + } + }; + template struct pusher { static int push(lua_State* L, const char (&str)[N]) { @@ -9173,6 +9531,27 @@ namespace stack { } }; + template <> + struct pusher { + static int push(lua_State* L, const wchar_t* str) { + pusher p{}; + (void)p; + return p.push(L, str); + } + + static int push(lua_State* L, const wchar_t* strb, const wchar_t* stre) { + pusher p{}; + (void)p; + return p.push(L, strb, stre); + } + + static int push(lua_State* L, const wchar_t* str, std::size_t len) { + pusher p{}; + (void)p; + return p.push(L, str, len); + } + }; + template <> struct pusher { static int push(lua_State* L, const char16_t* u16str) { @@ -9195,6 +9574,27 @@ namespace stack { } }; + template <> + struct pusher { + static int push(lua_State* L, const char16_t* str) { + pusher p{}; + (void)p; + return p.push(L, str); + } + + static int push(lua_State* L, const char16_t* strb, const char16_t* stre) { + pusher p{}; + (void)p; + return p.push(L, strb, stre); + } + + static int push(lua_State* L, const char16_t* str, std::size_t len) { + pusher p{}; + (void)p; + return p.push(L, str, len); + } + }; + template <> struct pusher { static int push(lua_State* L, const char32_t* u32str) { @@ -9217,6 +9617,27 @@ namespace stack { } }; + template <> + struct pusher { + static int push(lua_State* L, const char32_t* str) { + pusher p{}; + (void)p; + return p.push(L, str); + } + + static int push(lua_State* L, const char32_t* strb, const char32_t* stre) { + pusher p{}; + (void)p; + return p.push(L, strb, stre); + } + + static int push(lua_State* L, const char32_t* str, std::size_t len) { + pusher p{}; + (void)p; + return p.push(L, str, len); + } + }; + template struct pusher { static int push(lua_State* L, const wchar_t (&str)[N]) { @@ -10744,10 +11165,7 @@ namespace sol { call_syntax syntax = argcount > 0 ? stack::get_call_syntax(L, &usertype_traits::user_metatable()[0], 1) : call_syntax::dot; argcount -= static_cast(syntax); - T** pointerpointer = reinterpret_cast(lua_newuserdata(L, sizeof(T*) + sizeof(T))); - T*& referencepointer = *pointerpointer; - T* obj = reinterpret_cast(pointerpointer + 1); - referencepointer = obj; + T* obj = detail::usertype_allocate(L); reference userdataref(L, -1); userdataref.pop(); @@ -11046,11 +11464,8 @@ namespace sol { call_syntax syntax = argcount > 0 ? stack::get_call_syntax(L, &usertype_traits::user_metatable()[0], 1) : call_syntax::dot; argcount -= static_cast(syntax); - T** pointerpointer = reinterpret_cast(lua_newuserdata(L, sizeof(T*) + sizeof(T))); + T* obj = detail::usertype_allocate(L); reference userdataref(L, -1); - T*& referencepointer = *pointerpointer; - T* obj = reinterpret_cast(pointerpointer + 1); - referencepointer = obj; construct_match(constructor_match(obj), L, argcount, boost + 1 + static_cast(syntax)); @@ -11074,12 +11489,9 @@ namespace sol { template int operator()(types, index_value, types r, types a, lua_State* L, int, int start, F& f) { const auto& metakey = usertype_traits::metatable(); - T** pointerpointer = reinterpret_cast(lua_newuserdata(L, sizeof(T*) + sizeof(T))); + T* obj = detail::usertype_allocate(L); reference userdataref(L, -1); - T*& referencepointer = *pointerpointer; - T* obj = reinterpret_cast(pointerpointer + 1); - referencepointer = obj; - + auto& func = std::get(f.functions); stack::call_into_lua(r, a, L, boost + start, func, detail::implicit_wrapper(obj));