diff --git a/docs/source/mentions.rst b/docs/source/mentions.rst index 15f0d466..03f1ba05 100644 --- a/docs/source/mentions.rst +++ b/docs/source/mentions.rst @@ -24,6 +24,7 @@ Okay, so the features don't convince you, the documentation doesn't convince you +----------+---------+ * In `Perforce`_ (later versions of the code using sol2 more directly can be requested by e-mailing support@perforce.com !) +* For Sandboxing `Fun`_! * In `High Performance Computing research`_ - `Published research, too!`_ * The `Multiple Arcade Machine Emulator (MAME)`_ project switched from using LuaBridge to sol3! @@ -39,7 +40,7 @@ Okay, so the features don't convince you, the documentation doesn't convince you * (Twitter) Twitter has some people that link it: - The image above, `tweeted out by eevee`_ - Eevee: `"I heartily recommend sol3"`_ - - Elias Daler: `"sol3 saved my life."`_ + - Elias Daler: `"sol2 is making usertypes in Lua so awesome <3"`_ - Racod's Lair: `"from outdated LuaBridge to superior #sol3"`_ * (Reddit) Posts on reddit about it! - `sol2's initial reddit release`_ @@ -52,12 +53,14 @@ Are you using sol3 for something neat? Want it to be featured here or think it's .. _tell me about your uses!: https://github.com/ThePhD/sol2/issues/189 .. _eevee: https://twitter.com/eevee .. _eevee's blog: https://eev.ee/dev/2016/08/07/weekly-roundup-three-big-things/ +.. _Fun: https://blog.rubenwardy.com/2020/07/26/sol3-script-sandbox/ .. _Jason Turner's presentation: https://github.com/lefticus/presentations/blob/master/WhyAndHowToAddScripting.md .. _Elias Daler's blog: https://eliasdaler.github.io/cppcast#read-more .. _CppCast: http://cppcast.com/2016/07/elias-daler/ .. _tweeted out by eevee: https://twitter.com/eevee/status/762039984085798913 .. _"I heartily recommend sol3": https://twitter.com/eevee/status/762040086540144644 .. _"from outdated LuaBridge to superior #sol3": https://twitter.com/racodslair/status/754031870640267264 +.. _"sol2 is making usertypes in Lua so awesome <3": https://twitter.com/EliasDaler/status/778708299029946368 .. _sol2's initial reddit release: https://www.reddit.com/r/cpp/comments/4a8gy7/sol2_lua_c_binding_framework/ .. _Benchmarking Discussing: https://www.reddit.com/r/cpp/comments/4x82hd/plain_c_versus_lua_libraries_benchmarking_speed/ .. _"After spending hours with sol2, it wins. Amazing lib.": https://twitter.com/EliasDaler/status/739215685264494593 diff --git a/docs/source/safety.rst b/docs/source/safety.rst index 78993365..d0745f7a 100644 --- a/docs/source/safety.rst +++ b/docs/source/safety.rst @@ -89,6 +89,14 @@ Feature Config * Includes ```` and prints all exceptions and errors to ``std::cerr``, for you to see * **Not** turned on by default under any settings: *this MUST be turned on manually* +``SOL_GET_FUNCTION_POINTERS_UNSAFE`` triggers the following change: + * Allows function pointers serialized into Lua as a callable to be retrieved back from Lua in a semi-proper manner + * **This is under NO circumstances type safe** + - It **WILL** break ``sol::overload`` type checking and will not discriminate properly between function types + - It **WILL** happily let you retrieve an ``int(*)(int, int int)`` from a ``void(*)()`` function pointer, and shatter your runtime if you call it + * This is an **advanced, experimental feature** for experts only and requires the user has **perfect type safety** in both C++ and Lua + * **Not** turned on by default under any settings: *this MUST be turned on manually* + ``SOL_CONTAINERS_START`` triggers the following change: * If defined and **is an integral value**, is used to adjust the container start value * Applies to C++ containers **only** (not Lua tables or algorithms) diff --git a/include/sol/function_types.hpp b/include/sol/function_types.hpp index e9d0827f..279a92ce 100644 --- a/include/sol/function_types.hpp +++ b/include/sol/function_types.hpp @@ -39,7 +39,7 @@ namespace sol { using type = T; }; - struct call_indicator {}; + struct call_indicator { }; template int lua_c_wrapper(lua_State* L) { @@ -65,16 +65,15 @@ namespace sol { } } - struct c_function_invocation {}; + struct c_function_invocation { }; template void select(lua_State* L, Fx&& fx, Args&&... args); template void select_set_fx(lua_State* L, Args&&... args) { - lua_CFunction freefunc = no_trampoline ? - detail::static_trampoline, 2, is_yielding>> - : function_detail::call, 2, is_yielding>; + lua_CFunction freefunc = no_trampoline ? detail::static_trampoline, 2, is_yielding>> + : function_detail::call, 2, is_yielding>; int upvalues = 0; upvalues += stack::push(L, nullptr); @@ -680,7 +679,56 @@ namespace sol { } } }; - } // namespace stack + + namespace stack_detail { + template + bool check_function_pointer(lua_State* L, int index, Handler&& handler, record& tracking) noexcept { +#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE_I_) + tracking.use(1); + bool success = lua_iscfunction(L, index) == 1; + if (success) { + // there must be at LEAST 2 upvalues; otherwise, we didn't serialize it. + const char* upvalue_name = lua_getupvalue(L, index, 2); + lua_pop(L, 1); + success = upvalue_name != nullptr; + } + if (!success) { + // expected type, actual type + handler( + L, index, type::function, type_of(L, index), "type must be a Lua C Function gotten from a function pointer serialized by sol2"); + } + return success; +#else + return false; +#endif + } + + template + Function* get_function_pointer(lua_State* L, int index, record& tracking) noexcept { +#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE_I_) + tracking.use(1); + auto udata = stack::stack_detail::get_as_upvalues_using_function(L, index); + Function* fx = udata.first; + return fx; +#else + static_assert(meta::meta_detail::always_true::value, +#if SOL_IS_DEFAULT_OFF(SOL_GET_FUNCTION_POINTER_UNSAFE_I_) + "You are attempting to retrieve a function pointer type. " + "This is inherently unsafe in sol2. In order to do this, you must turn on the " + "SOL_GET_FUNCTION_POINTER_UNSAFE configuration macro, as detailed in the documentation. " + "Please be careful!" +#else + "You are attempting to retrieve a function pointer type. " + "You explicitly turned off the ability to do this by defining " + "SOL_GET_FUNCTION_POINTER_UNSAFE or similar to be off. " + "Please reconsider this!" +#endif + ); + return nullptr; +#endif + } + } // namespace stack_detail + } // namespace stack } // namespace sol #endif // SOL_FUNCTION_TYPES_HPP diff --git a/include/sol/stack.hpp b/include/sol/stack.hpp index c7ee5c3c..8748a8cb 100644 --- a/include/sol/stack.hpp +++ b/include/sol/stack.hpp @@ -101,8 +101,9 @@ namespace sol { data_t data { {} }; std::memcpy(&data[0], std::addressof(item), itemsize); int pushcount = 0; - for (auto&& v : data) { - pushcount += push(L, lightuserdata_value(v)); + for (const auto& v : data) { + lua_pushlightuserdata(L, v); + pushcount += 1; } return pushcount; } @@ -113,11 +114,33 @@ namespace sol { typedef std::array data_t; data_t voiddata { {} }; for (std::size_t i = 0, d = 0; d < sizeof(T); ++i, d += sizeof(void*)) { - voiddata[i] = get(L, upvalue_index(index++)); + voiddata[i] = lua_touserdata(L, upvalue_index(index++)); } return std::pair(*reinterpret_cast(static_cast(voiddata.data())), index); } + template + inline std::pair get_as_upvalues_using_function(lua_State* L, int function_index = -1) { + static const std::size_t data_t_count = (sizeof(T) + (sizeof(void*) - 1)) / sizeof(void*); + typedef std::array data_t; + function_index = lua_absindex(L, function_index); + int index = 0; + data_t voiddata { {} }; + for (std::size_t d = 0; d < sizeof(T); d += sizeof(void*)) { + // first upvalue is nullptr to respect environment shenanigans + // So +2 instead of +1 + const char* upvalue_name = lua_getupvalue(L, function_index, index + 2); + if (upvalue_name == nullptr) { + // We should freak out here... + break; + } + voiddata[index] = lua_touserdata(L, -1); + ++index; + } + lua_pop(L, index); + return std::pair(*reinterpret_cast(static_cast(voiddata.data())), index); + } + template static decltype(auto) eval(types<>, std::index_sequence<>, lua_State*, int, record&, Fx&& fx, Args&&... args) { return std::forward(fx)(std::forward(args)...); diff --git a/include/sol/stack_check_unqualified.hpp b/include/sol/stack_check_unqualified.hpp index c5959089..9342f364 100644 --- a/include/sol/stack_check_unqualified.hpp +++ b/include/sol/stack_check_unqualified.hpp @@ -348,6 +348,11 @@ namespace sol { namespace stack { } return stack::unqualified_check(L, index, no_panic, tracking); } +#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE_I_) + else if constexpr (std::is_function_v || (std::is_pointer_v && std::is_function_v>)) { + return stack_detail::check_function_pointer>(L, index, std::forward(handler), tracking); + } +#endif else if constexpr (expected == type::userdata) { if constexpr (meta::any_same_v || meta::is_specialization_of_v) { tracking.use(1); diff --git a/include/sol/stack_core.hpp b/include/sol/stack_core.hpp index ebe8dbe6..97c8b3ca 100644 --- a/include/sol/stack_core.hpp +++ b/include/sol/stack_core.hpp @@ -164,7 +164,7 @@ namespace sol { #if SOL_IS_OFF(SOL_ALIGN_MEMORY_I_) false #else - (std::alignment_of::value > 1) + (std::alignment_of_v > 1) #endif > use_align; @@ -178,7 +178,7 @@ namespace sol { return ptr; } std::size_t space = (std::numeric_limits::max)(); - return align(std::alignment_of::value, sizeof(T), ptr, space); + return align(std::alignment_of_v, sizeof(T), ptr, space); } template @@ -187,7 +187,7 @@ namespace sol { #if SOL_IS_OFF(SOL_ALIGN_MEMORY_I_) false #else - (std::alignment_of::value > 1) + (std::alignment_of_v > 1) #endif > use_align; @@ -195,7 +195,7 @@ namespace sol { return ptr; } std::size_t space = (std::numeric_limits::max)(); - return align(std::alignment_of::value, sizeof(T), ptr, space); + return align(std::alignment_of_v, sizeof(T), ptr, space); } template @@ -297,7 +297,7 @@ namespace sol { #if SOL_IS_OFF(SOL_ALIGN_MEMORY_I_) false #else - (std::alignment_of::value > 1 || std::alignment_of::value > 1) + (std::alignment_of::value > 1 || std::alignment_of_v > 1) #endif > use_align; @@ -436,7 +436,7 @@ namespace sol { #if SOL_IS_OFF(SOL_ALIGN_MEMORY_I_) false #else - (std::alignment_of::value > 1) + (std::alignment_of_v > 1) #endif > use_align; @@ -450,13 +450,13 @@ namespace sol { 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); + void* adjusted = align(std::alignment_of_v, 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); + adjusted = align(std::alignment_of_v, sizeof(T), unadjusted, allocated_size); if (adjusted == nullptr) { lua_pop(L, 1); luaL_error(L, "cannot properly align memory for '%s'", detail::demangle().data()); @@ -630,14 +630,21 @@ namespace sol { int last; int used; - record() : last(), used() { + record() noexcept : last(), used() { } - void use(int count) { + void use(int count) noexcept { last = count; used += count; } }; + namespace stack_detail { + template + Function* get_function_pointer(lua_State*, int, record&) noexcept; + template + bool check_function_pointer(lua_State* L, int index, Handler&& handler, record& tracking) noexcept; + } // namespace stack_detail + } // namespace stack namespace meta { namespace meta_detail { diff --git a/include/sol/stack_get_unqualified.hpp b/include/sol/stack_get_unqualified.hpp index 2e2e1833..1cde721f 100644 --- a/include/sol/stack_get_unqualified.hpp +++ b/include/sol/stack_get_unqualified.hpp @@ -163,6 +163,11 @@ namespace sol { namespace stack { luaL_Stream* pstream = static_cast(lua_touserdata(L, index)); return *pstream; } +#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE_I_) + else if constexpr (std::is_function_v || (std::is_pointer_v && std::is_function_v>)) { + return stack_detail::get_function_pointer>(L, index, tracking); + } +#endif else { return stack_detail::unchecked_unqualified_get>(L, index, tracking); } @@ -949,10 +954,22 @@ namespace sol { namespace stack { template struct unqualified_getter { static T* get(lua_State* L, int index, record& tracking) { +#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE_I_) + if constexpr (std::is_function_v) { + return stack_detail::get_function_pointer(L, index, tracking); + } + else { + unqualified_getter> g; + // Avoid VC++ warning + (void)g; + return g.get(L, index, tracking); + } +#else unqualified_getter> g; // Avoid VC++ warning (void)g; return g.get(L, index, tracking); +#endif } }; diff --git a/include/sol/types.hpp b/include/sol/types.hpp index 889aafc2..229ab11b 100644 --- a/include/sol/types.hpp +++ b/include/sol/types.hpp @@ -1077,13 +1077,21 @@ namespace sol { template <> struct lua_type_of : std::integral_constant { }; +#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE_I_) + template + struct lua_type_of : std::integral_constant ? type::function : type::userdata> { }; +#else template struct lua_type_of : std::integral_constant { }; +#endif template struct lua_type_of || std::is_same_v || std::is_same_v>> : std::integral_constant { }; + template + struct lua_type_of>> : std::integral_constant { }; + template struct lua_type_of>> : std::integral_constant { }; diff --git a/include/sol/version.hpp b/include/sol/version.hpp index 8ca5dc81..8224efe2 100644 --- a/include/sol/version.hpp +++ b/include/sol/version.hpp @@ -563,6 +563,16 @@ #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_DEFAULT_ON #endif +#if defined(SOL_GET_FUNCTION_POINTER_UNSAFE) + #if (SOL_GET_FUNCTION_POINTER_UNSAFE != 0) + #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_ON + #else + #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_OFF + #endif +#else + #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_DEFAULT_OFF +#endif + #if SOL_IS_ON(SOL_COMPILER_FRONTEND_MINGW_I_) && defined(__GNUC__) && (__GNUC__ < 6) // MinGW is off its rocker in some places... #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_ON diff --git a/single/include/sol/config.hpp b/single/include/sol/config.hpp index 6565a867..c69d27b2 100644 --- a/single/include/sol/config.hpp +++ b/single/include/sol/config.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 2020-09-26 10:47:52.233754 UTC -// This header was generated with sol v3.2.1 (revision 000fa31) +// Generated 2020-09-26 13:38:14.149661 UTC +// This header was generated with sol v3.2.1 (revision 1fb483d) // https://github.com/ThePhD/sol2 #ifndef SOL_SINGLE_CONFIG_HPP diff --git a/single/include/sol/forward.hpp b/single/include/sol/forward.hpp index 7fbd6f8c..547462b1 100644 --- a/single/include/sol/forward.hpp +++ b/single/include/sol/forward.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 2020-09-26 10:47:52.207756 UTC -// This header was generated with sol v3.2.1 (revision 000fa31) +// Generated 2020-09-26 13:38:14.123663 UTC +// This header was generated with sol v3.2.1 (revision 1fb483d) // https://github.com/ThePhD/sol2 #ifndef SOL_SINGLE_INCLUDE_FORWARD_HPP @@ -569,6 +569,16 @@ #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_DEFAULT_ON #endif +#if defined(SOL_GET_FUNCTION_POINTER_UNSAFE) + #if (SOL_GET_FUNCTION_POINTER_UNSAFE != 0) + #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_ON + #else + #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_OFF + #endif +#else + #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_DEFAULT_OFF +#endif + #if SOL_IS_ON(SOL_COMPILER_FRONTEND_MINGW_I_) && defined(__GNUC__) && (__GNUC__ < 6) // MinGW is off its rocker in some places... #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_ON diff --git a/single/include/sol/sol.hpp b/single/include/sol/sol.hpp index 2596c1b2..a3234161 100644 --- a/single/include/sol/sol.hpp +++ b/single/include/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 2020-09-26 10:47:51.277755 UTC -// This header was generated with sol v3.2.1 (revision 000fa31) +// Generated 2020-09-26 13:38:13.096661 UTC +// This header was generated with sol v3.2.1 (revision 1fb483d) // https://github.com/ThePhD/sol2 #ifndef SOL_SINGLE_INCLUDE_HPP @@ -569,6 +569,16 @@ #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_DEFAULT_ON #endif +#if defined(SOL_GET_FUNCTION_POINTER_UNSAFE) + #if (SOL_GET_FUNCTION_POINTER_UNSAFE != 0) + #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_ON + #else + #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_OFF + #endif +#else + #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_DEFAULT_OFF +#endif + #if SOL_IS_ON(SOL_COMPILER_FRONTEND_MINGW_I_) && defined(__GNUC__) && (__GNUC__ < 6) // MinGW is off its rocker in some places... #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_ON @@ -7588,13 +7598,21 @@ namespace sol { template <> struct lua_type_of : std::integral_constant { }; +#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE_I_) + template + struct lua_type_of : std::integral_constant ? type::function : type::userdata> { }; +#else template struct lua_type_of : std::integral_constant { }; +#endif template struct lua_type_of || std::is_same_v || std::is_same_v>> : std::integral_constant { }; + template + struct lua_type_of>> : std::integral_constant { }; + template struct lua_type_of>> : std::integral_constant { }; @@ -9849,7 +9867,7 @@ namespace sol { #if SOL_IS_OFF(SOL_ALIGN_MEMORY_I_) false #else - (std::alignment_of::value > 1) + (std::alignment_of_v > 1) #endif > use_align; @@ -9863,7 +9881,7 @@ namespace sol { return ptr; } std::size_t space = (std::numeric_limits::max)(); - return align(std::alignment_of::value, sizeof(T), ptr, space); + return align(std::alignment_of_v, sizeof(T), ptr, space); } template @@ -9872,7 +9890,7 @@ namespace sol { #if SOL_IS_OFF(SOL_ALIGN_MEMORY_I_) false #else - (std::alignment_of::value > 1) + (std::alignment_of_v > 1) #endif > use_align; @@ -9880,7 +9898,7 @@ namespace sol { return ptr; } std::size_t space = (std::numeric_limits::max)(); - return align(std::alignment_of::value, sizeof(T), ptr, space); + return align(std::alignment_of_v, sizeof(T), ptr, space); } template @@ -9982,7 +10000,7 @@ namespace sol { #if SOL_IS_OFF(SOL_ALIGN_MEMORY_I_) false #else - (std::alignment_of::value > 1 || std::alignment_of::value > 1) + (std::alignment_of::value > 1 || std::alignment_of_v > 1) #endif > use_align; @@ -10121,7 +10139,7 @@ namespace sol { #if SOL_IS_OFF(SOL_ALIGN_MEMORY_I_) false #else - (std::alignment_of::value > 1) + (std::alignment_of_v > 1) #endif > use_align; @@ -10135,13 +10153,13 @@ namespace sol { 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); + void* adjusted = align(std::alignment_of_v, 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); + adjusted = align(std::alignment_of_v, sizeof(T), unadjusted, allocated_size); if (adjusted == nullptr) { lua_pop(L, 1); luaL_error(L, "cannot properly align memory for '%s'", detail::demangle().data()); @@ -10315,14 +10333,21 @@ namespace sol { int last; int used; - record() : last(), used() { + record() noexcept : last(), used() { } - void use(int count) { + void use(int count) noexcept { last = count; used += count; } }; + namespace stack_detail { + template + Function* get_function_pointer(lua_State*, int, record&) noexcept; + template + bool check_function_pointer(lua_State* L, int index, Handler&& handler, record& tracking) noexcept; + } // namespace stack_detail + } // namespace stack namespace meta { namespace meta_detail { @@ -11457,6 +11482,11 @@ namespace sol { namespace stack { } return stack::unqualified_check(L, index, no_panic, tracking); } +#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE_I_) + else if constexpr (std::is_function_v || (std::is_pointer_v && std::is_function_v>)) { + return stack_detail::check_function_pointer>(L, index, std::forward(handler), tracking); + } +#endif else if constexpr (expected == type::userdata) { if constexpr (meta::any_same_v || meta::is_specialization_of_v) { tracking.use(1); @@ -12265,6 +12295,11 @@ namespace sol { namespace stack { luaL_Stream* pstream = static_cast(lua_touserdata(L, index)); return *pstream; } +#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE_I_) + else if constexpr (std::is_function_v || (std::is_pointer_v && std::is_function_v>)) { + return stack_detail::get_function_pointer>(L, index, tracking); + } +#endif else { return stack_detail::unchecked_unqualified_get>(L, index, tracking); } @@ -13051,10 +13086,22 @@ namespace sol { namespace stack { template struct unqualified_getter { static T* get(lua_State* L, int index, record& tracking) { +#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE_I_) + if constexpr (std::is_function_v) { + return stack_detail::get_function_pointer(L, index, tracking); + } + else { + unqualified_getter> g; + // Avoid VC++ warning + (void)g; + return g.get(L, index, tracking); + } +#else unqualified_getter> g; // Avoid VC++ warning (void)g; return g.get(L, index, tracking); +#endif } }; @@ -14936,8 +14983,9 @@ namespace sol { data_t data { {} }; std::memcpy(&data[0], std::addressof(item), itemsize); int pushcount = 0; - for (auto&& v : data) { - pushcount += push(L, lightuserdata_value(v)); + for (const auto& v : data) { + lua_pushlightuserdata(L, v); + pushcount += 1; } return pushcount; } @@ -14948,11 +14996,33 @@ namespace sol { typedef std::array data_t; data_t voiddata { {} }; for (std::size_t i = 0, d = 0; d < sizeof(T); ++i, d += sizeof(void*)) { - voiddata[i] = get(L, upvalue_index(index++)); + voiddata[i] = lua_touserdata(L, upvalue_index(index++)); } return std::pair(*reinterpret_cast(static_cast(voiddata.data())), index); } + template + inline std::pair get_as_upvalues_using_function(lua_State* L, int function_index = -1) { + static const std::size_t data_t_count = (sizeof(T) + (sizeof(void*) - 1)) / sizeof(void*); + typedef std::array data_t; + function_index = lua_absindex(L, function_index); + int index = 0; + data_t voiddata { {} }; + for (std::size_t d = 0; d < sizeof(T); d += sizeof(void*)) { + // first upvalue is nullptr to respect environment shenanigans + // So +2 instead of +1 + const char* upvalue_name = lua_getupvalue(L, function_index, index + 2); + if (upvalue_name == nullptr) { + // We should freak out here... + break; + } + voiddata[index] = lua_touserdata(L, -1); + ++index; + } + lua_pop(L, index); + return std::pair(*reinterpret_cast(static_cast(voiddata.data())), index); + } + template static decltype(auto) eval(types<>, std::index_sequence<>, lua_State*, int, record&, Fx&& fx, Args&&... args) { return std::forward(fx)(std::forward(args)...); @@ -18120,7 +18190,7 @@ namespace sol { using type = T; }; - struct call_indicator {}; + struct call_indicator { }; template int lua_c_wrapper(lua_State* L) { @@ -18146,16 +18216,15 @@ namespace sol { } } - struct c_function_invocation {}; + struct c_function_invocation { }; template void select(lua_State* L, Fx&& fx, Args&&... args); template void select_set_fx(lua_State* L, Args&&... args) { - lua_CFunction freefunc = no_trampoline ? - detail::static_trampoline, 2, is_yielding>> - : function_detail::call, 2, is_yielding>; + lua_CFunction freefunc = no_trampoline ? detail::static_trampoline, 2, is_yielding>> + : function_detail::call, 2, is_yielding>; int upvalues = 0; upvalues += stack::push(L, nullptr); @@ -18761,7 +18830,56 @@ namespace sol { } } }; - } // namespace stack + + namespace stack_detail { + template + bool check_function_pointer(lua_State* L, int index, Handler&& handler, record& tracking) noexcept { +#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE_I_) + tracking.use(1); + bool success = lua_iscfunction(L, index) == 1; + if (success) { + // there must be at LEAST 2 upvalues; otherwise, we didn't serialize it. + const char* upvalue_name = lua_getupvalue(L, index, 2); + lua_pop(L, 1); + success = upvalue_name != nullptr; + } + if (!success) { + // expected type, actual type + handler( + L, index, type::function, type_of(L, index), "type must be a Lua C Function gotten from a function pointer serialized by sol2"); + } + return success; +#else + return false; +#endif + } + + template + Function* get_function_pointer(lua_State* L, int index, record& tracking) noexcept { +#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE_I_) + tracking.use(1); + auto udata = stack::stack_detail::get_as_upvalues_using_function(L, index); + Function* fx = udata.first; + return fx; +#else + static_assert(meta::meta_detail::always_true::value, +#if SOL_IS_DEFAULT_OFF(SOL_GET_FUNCTION_POINTER_UNSAFE_I_) + "You are attempting to retrieve a function pointer type. " + "This is inherently unsafe in sol2. In order to do this, you must turn on the " + "SOL_GET_FUNCTION_POINTER_UNSAFE configuration macro, as detailed in the documentation. " + "Please be careful!" +#else + "You are attempting to retrieve a function pointer type. " + "You explicitly turned off the ability to do this by defining " + "SOL_GET_FUNCTION_POINTER_UNSAFE or similar to be off. " + "Please reconsider this!" +#endif + ); + return nullptr; +#endif + } + } // namespace stack_detail + } // namespace stack } // namespace sol // end of sol/function_types.hpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index eafdbb43..2aa03422 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -24,4 +24,5 @@ add_subdirectory(runtime_tests) add_subdirectory(compile_tests) +add_subdirectory(config_tests) add_subdirectory(regression_tests) diff --git a/tests/config_tests/CMakeLists.txt b/tests/config_tests/CMakeLists.txt new file mode 100644 index 00000000..46760e30 --- /dev/null +++ b/tests/config_tests/CMakeLists.txt @@ -0,0 +1,25 @@ +# # # # sol3 +# The MIT License (MIT) +# +# Copyright (c) 2013-2020 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. + +# # # # sol3 tests + +add_subdirectory(function_pointers) diff --git a/tests/config_tests/function_pointers/CMakeLists.txt b/tests/config_tests/function_pointers/CMakeLists.txt new file mode 100644 index 00000000..ff6186f5 --- /dev/null +++ b/tests/config_tests/function_pointers/CMakeLists.txt @@ -0,0 +1,103 @@ +# # # # sol3 +# The MIT License (MIT) +# +# Copyright (c) 2013-2020 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. + +# # # # sol3 tests - simple regression tests + +file(GLOB test_sources source/*.cpp) +source_group(sources FILES ${test_sources}) + +function(CREATE_TEST test_target_name test_name target_sol) + add_executable(${test_target_name} ${test_sources}) + set_target_properties(${test_target_name} + PROPERTIES + OUTPUT_NAME ${test_name} + EXPORT_NAME sol2::${test_name}) + target_link_libraries(${test_target_name} + PUBLIC Threads::Threads ${LUA_LIBRARIES} ${target_sol}) + target_compile_definitions(${test_target_name} + PRIVATE SOL_GET_FUNCTION_POINTER_UNSAFE=1 SOL_ALL_SAFETIES_ON=1) + target_include_directories(${test_target_name} + PRIVATE ../../../examples/include) + + if (MSVC) + if (NOT CMAKE_COMPILER_ID MATCHES "Clang") + target_compile_options(${test_target_name} + PRIVATE /bigobj /W4) + endif() + else() + target_compile_options(${test_target_name} + PRIVATE -std=c++1z -pthread + -Wno-unknown-warning -Wno-unknown-warning-option + -Wall -Wpedantic -Werror -pedantic -pedantic-errors + -Wno-noexcept-type) + + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + # For another day, when C++ is not so crap + # and we have time to audit the entire lib + # for all uses of `detail::swallow`... + #target_compile_options(${test_target_name} + # PRIVATE -Wcomma) + endif() + + if (IS_X86) + if(MINGW) + set_target_properties(${test_target_name} + PROPERTIES + LINK_FLAGS -static-libstdc++) + endif() + endif() + endif() + if (MSVC) + target_compile_options(${test_target_name} + PRIVATE /EHsc /std:c++latest) + target_compile_definitions(${test_target_name} + PRIVATE UNICODE _UNICODE + _CRT_SECURE_NO_WARNINGS _CRT_SECURE_NO_DEPRECATE) + else() + target_compile_options(${test_target_name} + PRIVATE -std=c++1z -Wno-unknown-warning -Wno-unknown-warning-option + -Wall -Wextra -Wpedantic -pedantic -pedantic-errors) + endif() + + if (SOL2_CI) + target_compile_definitions(${test_target_name} + PRIVATE SOL2_CI) + endif() + + if (CMAKE_DL_LIBS) + target_link_libraries(${test_target_name} + PRIVATE ${CMAKE_DL_LIBS}) + endif() + + add_test(NAME ${test_name} COMMAND ${test_target_name}) + install(TARGETS ${test_target_name} RUNTIME DESTINATION bin) +endfunction(CREATE_TEST) + +if (SOL2_TESTS) + CREATE_TEST(config_function_pointers_tests "config_function_pointers_tests" sol2::sol2) +endif() +if (SOL2_TESTS_SINGLE) + CREATE_TEST(config_function_pointers_tests_single "config_function_pointers_tests.single" sol2::sol2_single) +endif() +if (SOL2_TESTS_SINGLE_GENERATED) + CREATE_TEST(config_function_pointers_tests_generated_single "config_function_pointers_tests.single.generated" sol2::sol2_single_generated) +endif() diff --git a/tests/config_tests/function_pointers/source/main.cpp b/tests/config_tests/function_pointers/source/main.cpp new file mode 100644 index 00000000..a2d3c0d5 --- /dev/null +++ b/tests/config_tests/function_pointers/source/main.cpp @@ -0,0 +1,74 @@ +#include + +#include + +#include + +inline constexpr int magic_value = 24; + +using zero_arg_type = int (*)(); +using one_arg_type = int (*)(int); +using callback_type = int (*)(one_arg_type); + +int free_function(int value) { + return value + 1; +} + +int callback(one_arg_type f) { + return f(magic_value) + 1; +} + +int main() { + constexpr int expected_value = magic_value; + constexpr int expected_free_function_value = magic_value + 1; + constexpr int expected_callback_value = magic_value + 1 + 1; + + sol::state lua; + lua.open_libraries(sol::lib::base); + + auto lambda = []() { return magic_value; }; + auto lambda_ptr = static_cast(lambda); + + lua["magic_value"] = magic_value; + lua["expected_value"] = expected_value; + lua["expected_free_function_value"] = expected_free_function_value; + lua["expected_callback_value"] = expected_callback_value; + + lua["lambda"] = sol::as_function_reference(lambda); + lua["lambda_ptr"] = lambda_ptr; + lua["free_function"] = &free_function; + lua["callback"] = &callback; + + zero_arg_type lambda_f = lua["lambda"]; + zero_arg_type lambda_ptr_f = lua["lambda_ptr"]; + one_arg_type free_function_f = lua["free_function"]; + callback_type callback_f = lua["callback"]; + sol::function lua_callback_f = lua["callback"]; + + int lambda_f_result = lambda_f(); + int lambda_ptr_f_result = lambda_ptr_f(); + int free_function_f_result = free_function_f(magic_value); + int callback_f_result = callback_f(&free_function); + int lua_callback_f_result = lua_callback_f(&free_function); + c_assert(lambda_f_result == expected_value); + c_assert(lambda_ptr_f_result == expected_value); + c_assert(free_function_f_result == expected_free_function_value); + c_assert(callback_f_result == expected_callback_value); + c_assert(lua_callback_f_result == expected_callback_value); + + const char code[] = R"( +assert(lambda() == expected_value) +assert(lambda_ptr() == expected_value) +assert(free_function(magic_value) == expected_free_function_value) +assert(callback(free_function) == expected_callback_value) + )"; + + sol::optional err = lua.safe_script(code, sol::script_pass_on_error); + if (err.has_value()) { + std::cerr << err.value().what() << std::endl; + return 1; + } + c_assert(!err.has_value()); + + return 0; +}