From 54bcda140c2bf9372c192fcf35091fc55319db26 Mon Sep 17 00:00:00 2001 From: ThePhD Date: Thu, 31 Aug 2017 20:47:09 -0400 Subject: [PATCH] upgrade checkers and handlers to take a semi-optional "reason" type (const char* c-string desired) add constructor_handler in same vein of handlers add argument_handler in same vein of handlers rewrite env_t checkers --- docs/source/api/stack.rst | 9 +- single/sol/sol.hpp | 321 ++++++++++++++++++------------ sol/coroutine.hpp | 6 +- sol/environment.hpp | 15 +- sol/error_handler.hpp | 99 +++++++++ sol/load_result.hpp | 4 +- sol/protected_function.hpp | 43 ++-- sol/protected_function_result.hpp | 4 +- sol/stack.hpp | 6 +- sol/stack_check.hpp | 68 +++---- sol/stack_check_get.hpp | 10 +- sol/stack_core.hpp | 3 +- sol/stack_push.hpp | 9 +- sol/state_view.hpp | 11 +- sol/table_core.hpp | 9 +- sol/thread.hpp | 2 +- sol/types.hpp | 31 --- sol/unsafe_function.hpp | 9 +- sol/userdata.hpp | 12 +- test_functions.cpp | 108 +++++----- test_operators.cpp | 2 +- 21 files changed, 482 insertions(+), 299 deletions(-) create mode 100644 sol/error_handler.hpp diff --git a/docs/source/api/stack.rst b/docs/source/api/stack.rst index 0842ce4b..06af4c20 100644 --- a/docs/source/api/stack.rst +++ b/docs/source/api/stack.rst @@ -55,7 +55,7 @@ members :caption: function: call_lua :name: stack-call-lua - template + template inline int call_lua(lua_State* L, int start, Fx&& fx, FxArgs&&... fxargs); This function is helpful for when you bind to a raw C function but need sol's abstractions to save you the agony of setting up arguments and know how `calling C functions works`_. The ``start`` parameter tells the function where to start pulling arguments from. The parameter ``fx`` is what's supposed to be called. Extra arguments are passed to the function directly. There are intermediate versions of this (``sol::stack::call_into_lua`` and similar) for more advanced users, but they are not documented as they are subject to change to improve performance or adjust the API accordingly in later iterations of sol2. Use the more advanced versions at your own peril. @@ -97,7 +97,7 @@ Checks if the object at ``index`` is of type ``T``. If it is not, it will call t template auto check_get( lua_State* L, int index, Handler&& handler, record& tracking ) -Retrieves the value of the object at ``index`` in the stack, but does so safely. It returns an ``optional``, where ``U`` in this case is the return type deduced from ``stack::get``. This allows a person to properly check if the type they're getting is what they actually want, and gracefully handle errors when working with the stack if they so choose to. You can define ``SOL_CHECK_ARGUMENTS`` to turn on additional :doc:`safety<../safety>`, in which ``stack::get`` will default to calling this version of the function with a handler of ``type_panic`` to strongly alert for errors and help you track bugs if you suspect something might be going wrong in your system. +Retrieves the value of the object at ``index`` in the stack, but does so safely. It returns an ``optional``, where ``U`` in this case is the return type deduced from ``stack::get``. This allows a person to properly check if the type they're getting is what they actually want, and gracefully handle errors when working with the stack if they so choose to. You can define ``SOL_CHECK_ARGUMENTS`` to turn on additional :doc:`safety<../safety>`, in which ``stack::get`` will default to calling this version of the function with some variant on a handler of ``sol::type_panic_string`` to strongly alert for errors and help you track bugs if you suspect something might be going wrong in your system. .. code-block:: cpp :caption: function: push @@ -237,8 +237,9 @@ This is an SFINAE-friendly struct that is meant to expose static function ``push // if the object in the Lua stack at index is a T, return true if ( ... ) return true; // otherwise, call the handler function, - // with the required 4 arguments, then return false - handler(L, index, expected, indextype); + // with the required 5 arguments, then return false + // + handler(L, index, expected, indextype, "optional message"); return false; } }; diff --git a/single/sol/sol.hpp b/single/sol/sol.hpp index ecfbeb6e..287758b8 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-08-30 20:09:44.279724 UTC -// This header was generated with sol v2.18.1 (revision dea0ec0) +// Generated 2017-09-01 00:38:40.996447 UTC +// This header was generated with sol v2.18.1 (revision 3549bfa) // https://github.com/ThePhD/sol2 #ifndef SOL_SINGLE_INCLUDE_HPP @@ -4568,37 +4568,6 @@ namespace sol { return static_cast(lua_type(L, index)); } - inline int type_panic(lua_State* L, int index, type expected, type actual) noexcept(false) { - return luaL_error(L, "stack index %d, expected %s, received %s", index, - expected == type::poly ? "anything" : lua_typename(L, static_cast(expected)), - actual == type::poly ? "anything" : lua_typename(L, static_cast(actual)) - ); - } - - // Specify this function as the handler for lua::check if you know there's nothing wrong - inline int no_panic(lua_State*, int, type, type) noexcept { - return 0; - } - - inline void type_error(lua_State* L, int expected, int actual) noexcept(false) { - luaL_error(L, "expected %s, received %s", lua_typename(L, expected), lua_typename(L, actual)); - } - - inline void type_error(lua_State* L, type expected, type actual) noexcept(false) { - type_error(L, static_cast(expected), static_cast(actual)); - } - - inline void type_assert(lua_State* L, int index, type expected, type actual) noexcept(false) { - if (expected != type::poly && expected != actual) { - type_panic(L, index, expected, actual); - } - } - - inline void type_assert(lua_State* L, int index, type expected) { - type actual = type_of(L, index); - type_assert(L, index, expected, actual); - } - inline std::string type_name(lua_State* L, type t) { return lua_typename(L, static_cast(t)); } @@ -5045,6 +5014,82 @@ namespace sol { // end of sol/types.hpp +// beginning of sol/error_handler.hpp + +namespace sol { + + inline int type_panic_string(lua_State* L, int index, type expected, type actual, const std::string& message = "") noexcept(false) { + const char* err = message.empty() ? "stack index %d, expected %s, received %s" : "stack index %d, expected %s, received %s with message %s"; + return luaL_error(L, err, index, + expected == type::poly ? "anything" : lua_typename(L, static_cast(expected)), + actual == type::poly ? "anything" : lua_typename(L, static_cast(actual)), + message.c_str() + ); + } + + inline int type_panic_c_str(lua_State* L, int index, type expected, type actual, const char* message = nullptr) noexcept(false) { + const char* err = message == nullptr || (std::char_traits::length(message) == 0) ? "stack index %d, expected %s, received %s" : "stack index %d, expected %s, received %s with message %s"; + return luaL_error(L, err, index, + expected == type::poly ? "anything" : lua_typename(L, static_cast(expected)), + actual == type::poly ? "anything" : lua_typename(L, static_cast(actual)), + message + ); + } + + struct type_panic_t { + int operator()(lua_State* L, int index, type expected, type actual) const noexcept(false) { + return type_panic_c_str(L, index, expected, actual, nullptr); + } + int operator()(lua_State* L, int index, type expected, type actual, const char* message) const noexcept(false) { + return type_panic_c_str(L, index, expected, actual, message); + } + int operator()(lua_State* L, int index, type expected, type actual, const std::string& message) const noexcept(false) { + return type_panic_string(L, index, expected, actual, message); + } + }; + + const type_panic_t type_panic = {}; + + struct constructor_handler { + int operator()(lua_State* L, int index, type expected, type actual, const std::string& message) noexcept(false) { + return type_panic_string(L, index, expected, actual, message + " (type check failed in constructor)"); + } + }; + + struct argument_handler { + int operator()(lua_State* L, int index, type expected, type actual, const std::string& message) noexcept(false) { + return type_panic_string(L, index, expected, actual, message + " (bad argument to variable or function call)"); + } + }; + + // Specify this function as the handler for lua::check if you know there's nothing wrong + inline int no_panic(lua_State*, int, type, type, const char* = nullptr) noexcept { + return 0; + } + + inline void type_error(lua_State* L, int expected, int actual) noexcept(false) { + luaL_error(L, "expected %s, received %s", lua_typename(L, expected), lua_typename(L, actual)); + } + + inline void type_error(lua_State* L, type expected, type actual) noexcept(false) { + type_error(L, static_cast(expected), static_cast(actual)); + } + + inline void type_assert(lua_State* L, int index, type expected, type actual) noexcept(false) { + if (expected != type::poly && expected != actual) { + type_panic_c_str(L, index, expected, actual, nullptr); + } + } + + inline void type_assert(lua_State* L, int index, type expected) { + type actual = type_of(L, index); + type_assert(L, index, expected, actual); + } + +} // sol + +// end of sol/error_handler.hpp + // beginning of sol/reference.hpp // beginning of sol/stack_reference.hpp @@ -5893,7 +5938,7 @@ namespace sol { #ifdef SOL_CHECK_ARGUMENTS 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, tracking); + auto op = check_get(L, index, type_panic_c_str, tracking); return *std::move(op); } #else @@ -6208,7 +6253,7 @@ namespace sol { bool success = check_func(L, index) == 1; if (!success) { // expected type, actual type - handler(L, index, expected, type_of(L, index)); + handler(L, index, expected, type_of(L, index), ""); } return success; } @@ -6223,8 +6268,8 @@ namespace sol { const type indextype = type_of(L, index); bool success = expected == indextype; if (!success) { - // expected type, actual type - handler(L, index, expected, indextype); + // expected type, actual type, message + handler(L, index, expected, indextype, ""); } return success; } @@ -6249,7 +6294,7 @@ namespace sol { #endif // If numbers are enabled, use the imprecise check if (!success) { // expected type, actual type - handler(L, index, type::number, type_of(L, index)); + handler(L, index, type::number, type_of(L, index), "not a numeric type"); } return success; #else @@ -6258,7 +6303,7 @@ namespace sol { type t = type_of(L, index); if (t != type::number) { // expected type, actual type - handler(L, index, type::number, t); + handler(L, index, type::number, t, "not a numeric type"); return false; } #endif // Do not allow strings to be numbers @@ -6268,9 +6313,9 @@ namespace sol { if (!success) { // expected type, actual type #ifndef SOL_STRINGS_ARE_NUMBERS - handler(L, index, type::number, t); + handler(L, index, type::number, t, "not a numeric type"); #else - handler(L, index, type::number, type_of(L, index)); + handler(L, index, type::number, type_of(L, index), "not a numeric type or numeric string"); #endif } return success; @@ -6283,19 +6328,19 @@ namespace sol { template static bool check(lua_State* L, int index, Handler&& handler, record& tracking) { tracking.use(1); -#ifdef SOL_STRINGS_ARE_NUMBERS +#ifndef SOL_STRINGS_ARE_NUMBERS type t = type_of(L, index); bool success = t == type::number; if (!success) { // expected type, actual type - handler(L, index, type::number, t); + handler(L, index, type::number, t, "not a numeric type"); } return success; #else bool success = lua_isnumber(L, index) == 1; if (!success) { // expected type, actual type - handler(L, index, type::number, type_of(L, index)); + handler(L, index, type::number, type_of(L, index), "not a numeric type or numeric string"); } return success; #endif @@ -6315,7 +6360,7 @@ namespace sol { success = lua_isnone(L, index); if (!success) { // expected type, actual type - handler(L, index, expected, type_of(L, index)); + handler(L, index, expected, type_of(L, index), ""); } return success; } @@ -6368,7 +6413,7 @@ namespace sol { bool success = !lua_isnone(L, index); if (!success) { // expected type, actual type - handler(L, index, type::none, type_of(L, index)); + handler(L, index, type::none, type_of(L, index), ""); } return success; } @@ -6383,7 +6428,7 @@ namespace sol { bool success = t == type::userdata || t == type::lightuserdata; if (!success) { // expected type, actual type - handler(L, index, type::lightuserdata, t); + handler(L, index, type::lightuserdata, t, ""); } return success; } @@ -6398,7 +6443,7 @@ namespace sol { bool success = t == type::userdata; if (!success) { // expected type, actual type - handler(L, index, type::userdata, t); + handler(L, index, type::userdata, t, ""); } return success; } @@ -6436,25 +6481,25 @@ namespace sol { return true; } if (t != type::userdata && t != type::table) { - handler(L, index, type::function, t); + handler(L, index, type::function, t, "must be a function or table or a userdata"); return false; } // Do advanced check for call-style userdata? static const auto& callkey = to_string(meta_function::call); if (lua_getmetatable(L, index) == 0) { // No metatable, no __call key possible - handler(L, index, type::function, t); + handler(L, index, type::function, t, "value is not a function and does not have overriden metatable"); return false; } if (lua_isnoneornil(L, -1)) { lua_pop(L, 1); - handler(L, index, type::function, t); + handler(L, index, type::function, t, "value is not a function and does not have valid metatable"); return false; } lua_getfield(L, -1, &callkey[0]); if (lua_isnoneornil(L, -1)) { lua_pop(L, 2); - handler(L, index, type::function, t); + handler(L, index, type::function, t, "value's metatable does not have __call overridden in metatable, cannot call this type"); return false; } // has call, is definitely a function @@ -6473,7 +6518,7 @@ namespace sol { return true; } if (t != type::userdata) { - handler(L, index, type::table, t); + handler(L, index, type::table, t, "value is not a table or a userdata that can behave like one"); return false; } return true; @@ -6495,7 +6540,7 @@ namespace sol { } if (t != type::userdata) { lua_pop(L, 1); - handler(L, index, expected, t); + handler(L, index, expected, t, "value does not have a valid metatable"); return false; } return true; @@ -6507,19 +6552,11 @@ namespace sol { template static bool check(lua_State* L, int index, Handler&& handler, record& tracking) { tracking.use(1); - if (lua_getmetatable(L, index) == 0) { + type t = type_of(L, index); + if (t == type::table || t == type::none || t == type::nil || t == type::userdata) { return true; } - type t = type_of(L, -1); - if (t == type::table || t == type::none || t == type::nil) { - lua_pop(L, 1); - return true; - } - if (t != type::userdata) { - lua_pop(L, 1); - handler(L, index, type::table, t); - return false; - } + handler(L, index, type::table, t, "value cannot not have a valid environment"); return true; } }; @@ -6539,7 +6576,7 @@ namespace sol { } if (t != type::userdata) { lua_pop(L, 1); - handler(L, index, type::table, t); + handler(L, index, type::table, t, "value does not have a valid metatable"); return false; } return true; @@ -6552,7 +6589,7 @@ namespace sol { static bool check(types, lua_State* L, type indextype, int index, Handler&& handler, record& tracking) { tracking.use(1); if (indextype != type::userdata) { - handler(L, index, type::userdata, indextype); + handler(L, index, type::userdata, indextype, "value is not a valid userdata"); return false; } if (meta::any, std::is_same, std::is_same, std::is_same>::value) @@ -6582,7 +6619,7 @@ namespace sol { } if (!success) { lua_pop(L, 1); - handler(L, index, type::userdata, indextype); + handler(L, index, type::userdata, indextype, "value is not a valid sol userdata of any kind"); return false; } lua_pop(L, 1); @@ -6621,7 +6658,7 @@ namespace sol { const type indextype = type_of(L, index); tracking.use(1); if (indextype != type::userdata) { - handler(L, index, type::userdata, indextype); + handler(L, index, type::userdata, indextype, "value is not a userdata"); return false; } if (lua_getmetatable(L, index) == 0) { @@ -6634,12 +6671,12 @@ namespace sol { detail::unique_destructor& pdx = *static_cast(static_cast(pointerpointer + 1)); bool success = &detail::usertype_unique_alloc_destroy == pdx; if (!success) { - handler(L, index, type::userdata, indextype); + handler(L, index, type::userdata, indextype, "value is a userdata but is not the correct unique usertype"); } return success; } lua_pop(L, 1); - handler(L, index, type::userdata, indextype); + handler(L, index, type::userdata, indextype, "unrecognized userdata (not pushed by sol?)"); return false; } }; @@ -6698,7 +6735,7 @@ namespace sol { return true; } tracking.use(1); - handler(L, index, type::poly, type_of(L, index)); + handler(L, index, type::poly, type_of(L, index), "value does not fit any type present in the variant"); return false; } @@ -7538,7 +7575,7 @@ namespace sol { int isnum = 0; const lua_Number value = lua_tonumberx(L, index, &isnum); if (isnum != 0) { -#if 1 // defined(SOL_CHECK_ARGUMENTS) && !defined(SOL_NO_CHECK_NUMBER_PRECISION) +#if defined(SOL_CHECK_ARGUMENTS) && !defined(SOL_NO_CHECK_NUMBER_PRECISION) const auto integer_value = llround(value); if (static_cast(integer_value) == value) { tracking.use(1); @@ -7551,7 +7588,7 @@ namespace sol { } const type t = type_of(L, index); tracking.use(static_cast(t != type::none)); - handler(L, index, type::number, t); + handler(L, index, type::number, t, "not an integer"); return nullopt; } }; @@ -7565,7 +7602,7 @@ namespace sol { if (isnum == 0) { type t = type_of(L, index); tracking.use(static_cast(t != type::none)); - handler(L, index, type::number, t); + handler(L, index, type::number, t, "not a valid enumeration value"); return nullopt; } tracking.use(1); @@ -7582,7 +7619,7 @@ namespace sol { if (isnum == 0) { type t = type_of(L, index); tracking.use(static_cast(t != type::none)); - handler(L, index, type::number, t); + handler(L, index, type::number, t, "not a valid floating point number"); return nullopt; } tracking.use(1); @@ -7614,7 +7651,7 @@ namespace sol { typedef std::variant_alternative_t<0, V> T; // This should never be reached... // please check your code and understand what you did to bring yourself here - handler(L, index, type::poly, type_of(L, index)); + handler(L, index, type::poly, type_of(L, index), "this variant code should never be reached: if it has, you have done something so terribly wrong"); return nullopt; } @@ -7837,10 +7874,13 @@ namespace sol { #endif #if defined(SOL_CHECK_ARGUMENTS) && !defined(SOL_NO_CHECK_NUMBER_PRECISION) if (static_cast(llround(static_cast(value))) != value) { -#ifndef SOL_NO_EXCEPTIONS - throw sol::error("The integer will be misrepresented in lua."); +#ifdef SOL_NO_EXCEPTIONS + // Is this really worth it? + assert(false && "integer value will be misrepresented in lua"); + lua_pushnumber(L, static_cast(value)); + return 1; #else - assert(false && "The integer will be misrepresented in lua."); + throw error(detail::direct_error, "integer value will be misrepresented in lua"); #endif } #endif @@ -8910,7 +8950,8 @@ namespace sol { #ifndef _MSC_VER static_assert(meta::all...>::value, "One of the arguments being bound is a move-only type, and it is not being taken by reference: this will break your code. Please take a reference and std::move it manually if this was your intention."); #endif // This compiler make me so fucking sad - multi_check(L, start, type_panic); + argument_handler handler{}; + multi_check(L, start, handler); record tracking{}; return evaluator{}.eval(ta, tai, L, start, tracking, std::forward(fx), std::forward(args)...); } @@ -8920,7 +8961,8 @@ namespace sol { #ifndef _MSC_VER static_assert(meta::all...>::value, "One of the arguments being bound is a move-only type, and it is not being taken by reference: this will break your code. Please take a reference and std::move it manually if this was your intention."); #endif // This compiler make me so fucking sad - multi_check(L, start, type_panic); + argument_handler handler{}; + multi_check(L, start, handler); record tracking{}; evaluator{}.eval(ta, tai, L, start, tracking, std::forward(fx), std::forward(args)...); } @@ -11394,7 +11436,8 @@ namespace sol { #ifdef SOL_CHECK_ARGUMENTS if (!is_function>::value) { auto pp = stack::push_pop(*this); - stack::check(lua_state(), -1, type_panic); + constructor_handler handler{}; + stack::check(lua_state(), -1, handler); } #endif // Safety } @@ -11408,13 +11451,15 @@ namespace sol { basic_function(lua_State* L, T&& r) : basic_function(L, sol::ref_index(r.registry_index())) {} basic_function(lua_State* L, int index = -1) : base_t(L, index) { #ifdef SOL_CHECK_ARGUMENTS - stack::check(L, index, type_panic); + constructor_handler handler{}; + stack::check(L, index, handler); #endif // Safety } basic_function(lua_State* L, ref_index index) : base_t(L, index) { #ifdef SOL_CHECK_ARGUMENTS auto pp = stack::push_pop(*this); - stack::check(L, -1, type_panic); + constructor_handler handler{}; + stack::check(L, -1, handler); #endif // Safety } @@ -11466,7 +11511,7 @@ namespace sol { decltype(auto) tagged_get(types) const { #ifdef SOL_CHECK_ARGUMENTS if (!valid()) { - type_panic(L, index, type_of(L, index), type::none); + type_panic_c_str(L, index, type_of(L, index), type::none); } #endif // Check Argument Safety return stack::get(L, index); @@ -11482,7 +11527,7 @@ namespace sol { error tagged_get(types) const { #ifdef SOL_CHECK_ARGUMENTS if (valid()) { - type_panic(L, index, type_of(L, index), type::none); + type_panic_c_str(L, index, type_of(L, index), type::none); } #endif // Check Argument Safety return error(detail::direct_error, stack::get(L, index)); @@ -11573,29 +11618,29 @@ namespace sol { } template - struct handler { + struct protected_handler { typedef std::is_base_of is_stack; const target_t& target; int stackindex; - handler(std::false_type, const target_t& target) : target(target), stackindex(0) { + protected_handler(std::false_type, const target_t& target) : target(target), stackindex(0) { if (b) { stackindex = lua_gettop(target.lua_state()) + 1; target.push(); } } - handler(std::true_type, const target_t& target) : target(target), stackindex(0) { + protected_handler(std::true_type, const target_t& target) : target(target), stackindex(0) { if (b) { stackindex = target.stack_index(); } } - handler(const target_t& target) : handler(is_stack(), target) {} + protected_handler(const target_t& target) : protected_handler(is_stack(), target) {} bool valid() const noexcept { return b; } - ~handler() { + ~protected_handler() { if (!is_stack::value && stackindex != 0) { lua_remove(target.lua_state(), stackindex); } @@ -11638,29 +11683,29 @@ namespace sol { private: template - call_status luacall(std::ptrdiff_t argcount, std::ptrdiff_t resultcount, detail::handler& h) const { + call_status luacall(std::ptrdiff_t argcount, std::ptrdiff_t resultcount, detail::protected_handler& h) const { return static_cast(lua_pcallk(lua_state(), static_cast(argcount), static_cast(resultcount), h.stackindex, 0, nullptr)); } template - auto invoke(types, std::index_sequence, std::ptrdiff_t n, detail::handler& h) const { + auto invoke(types, std::index_sequence, std::ptrdiff_t n, detail::protected_handler& h) const { luacall(n, sizeof...(Ret), h); return stack::pop>(lua_state()); } template - Ret invoke(types, std::index_sequence, std::ptrdiff_t n, detail::handler& h) const { + Ret invoke(types, std::index_sequence, std::ptrdiff_t n, detail::protected_handler& h) const { luacall(n, 1, h); return stack::pop(lua_state()); } template - void invoke(types, std::index_sequence, std::ptrdiff_t n, detail::handler& h) const { + void invoke(types, std::index_sequence, std::ptrdiff_t n, detail::protected_handler& h) const { luacall(n, 0, h); } template - protected_function_result invoke(types<>, std::index_sequence<>, std::ptrdiff_t n, detail::handler& h) const { + protected_function_result invoke(types<>, std::index_sequence<>, std::ptrdiff_t n, detail::protected_handler& h) const { int stacksize = lua_gettop(lua_state()); int poststacksize = stacksize; int firstreturn = 1; @@ -11722,7 +11767,8 @@ namespace sol { #ifdef SOL_CHECK_ARGUMENTS if (!is_function>::value) { auto pp = stack::push_pop(*this); - stack::check(lua_state(), -1, type_panic); + constructor_handler handler{}; + stack::check(lua_state(), -1, handler); } #endif // Safety } @@ -11757,26 +11803,30 @@ namespace sol { basic_protected_function(lua_State* L, int index = -1) : basic_protected_function(L, index, get_default_handler(L)) {} basic_protected_function(lua_State* L, int index, handler_t eh) : base_t(L, index), error_handler(std::move(eh)) { #ifdef SOL_CHECK_ARGUMENTS - stack::check(L, index, type_panic); + constructor_handler handler{}; + stack::check(L, index, handler); #endif // Safety } basic_protected_function(lua_State* L, absolute_index index) : basic_protected_function(L, index, get_default_handler(L)) {} basic_protected_function(lua_State* L, absolute_index index, handler_t eh) : base_t(L, index), error_handler(std::move(eh)) { #ifdef SOL_CHECK_ARGUMENTS - stack::check(L, index, type_panic); + constructor_handler handler{}; + stack::check(L, index, handler); #endif // Safety } basic_protected_function(lua_State* L, raw_index index) : basic_protected_function(L, index, get_default_handler(L)) {} basic_protected_function(lua_State* L, raw_index index, handler_t eh) : base_t(L, index), error_handler(std::move(eh)) { #ifdef SOL_CHECK_ARGUMENTS - stack::check(L, index, type_panic); + constructor_handler handler{}; + stack::check(L, index, handler); #endif // Safety } basic_protected_function(lua_State* L, ref_index index) : basic_protected_function(L, index, get_default_handler(L)) {} basic_protected_function(lua_State* L, ref_index index, handler_t eh) : base_t(L, index), error_handler(std::move(eh)) { #ifdef SOL_CHECK_ARGUMENTS auto pp = stack::push_pop(*this); - stack::check(L, -1, type_panic); + constructor_handler handler{}; + stack::check(L, -1, handler); #endif // Safety } @@ -11795,13 +11845,13 @@ namespace sol { if (!aligned) { // we do not expect the function to already be on the stack: push it if (error_handler.valid()) { - detail::handler h(error_handler); + detail::protected_handler h(error_handler); base_t::push(); int pushcount = stack::multi_push_reference(lua_state(), std::forward(args)...); return invoke(types(), std::make_index_sequence(), pushcount, h); } else { - detail::handler h(error_handler); + detail::protected_handler h(error_handler); base_t::push(); int pushcount = stack::multi_push_reference(lua_state(), std::forward(args)...); return invoke(types(), std::make_index_sequence(), pushcount, h); @@ -11817,7 +11867,7 @@ namespace sol { // so, we need to remove the function at the top and then dump the handler out ourselves base_t::push(); } - detail::handler h(error_handler); + detail::protected_handler h(error_handler); if (!is_stack_handler::value) { lua_replace(lua_state(), -3); h.stackindex = lua_absindex(lua_state(), -2); @@ -11826,7 +11876,7 @@ namespace sol { return invoke(types(), std::make_index_sequence(), pushcount, h); } else { - detail::handler h(error_handler); + detail::protected_handler h(error_handler); int pushcount = stack::multi_push_reference(lua_state(), std::forward(args)...); return invoke(types(), std::make_index_sequence(), pushcount, h); } @@ -12215,13 +12265,15 @@ namespace sol { basic_userdata(lua_State* L, T&& r) : basic_userdata(L, sol::ref_index(r.registry_index())) {} basic_userdata(lua_State* L, int index = -1) : base_t(detail::no_safety, L, index) { #ifdef SOL_CHECK_ARGUMENTS - stack::check(L, index, type_panic); + constructor_handler handler{}; + stack::check(L, index, handler); #endif // Safety } basic_userdata(lua_State* L, ref_index index) : base_t(detail::no_safety, L, index) { #ifdef SOL_CHECK_ARGUMENTS auto pp = stack::push_pop(*this); - stack::check(L, index, type_panic); + constructor_handler handler{}; + stack::check(L, index, handler); #endif // Safety } }; @@ -12252,13 +12304,15 @@ namespace sol { basic_lightuserdata(lua_State* L, T&& r) : basic_lightuserdata(L, sol::ref_index(r.registry_index())) {} basic_lightuserdata(lua_State* L, int index = -1) : base_t(L, index) { #ifdef SOL_CHECK_ARGUMENTS - stack::check(L, index, type_panic); + constructor_handler handler{}; + stack::check(L, index, handler); #endif // Safety } basic_lightuserdata(lua_State* L, ref_index index) : base_t(L, index) { #ifdef SOL_CHECK_ARGUMENTS auto pp = stack::push_pop(*this); - stack::check(L, index, type_panic); + constructor_handler handler{}; + stack::check(L, index, handler); #endif // Safety } }; @@ -16096,13 +16150,15 @@ namespace sol { } basic_table_core(lua_State* L, int index = -1) : basic_table_core(detail::no_safety, L, index) { #ifdef SOL_CHECK_ARGUMENTS - stack::check(L, index, type_panic); + constructor_handler handler{}; + stack::check(L, index, handler); #endif // Safety } basic_table_core(lua_State* L, ref_index index) : basic_table_core(detail::no_safety, L, index) { #ifdef SOL_CHECK_ARGUMENTS auto pp = stack::push_pop(*this); - stack::check(L, -1, type_panic); + constructor_handler handler{}; + stack::check(L, -1, handler); #endif // Safety } template , basic_table_core>>, meta::neg>, std::is_base_of>> = meta::enabler> @@ -16110,7 +16166,8 @@ namespace sol { #ifdef SOL_CHECK_ARGUMENTS if (!is_table>::value) { auto pp = stack::push_pop(*this); - stack::check(base_t::lua_state(), -1, type_panic); + constructor_handler handler{}; + stack::check(base_t::lua_state(), -1, handler); } #endif // Safety } @@ -16449,25 +16506,29 @@ namespace sol { basic_environment(env_t, const stack_reference& extraction_target) : base_t(detail::no_safety, extraction_target.lua_state(), (stack::push_environment_of(extraction_target), -1)) { #ifdef SOL_CHECK_ARGUMENTS - stack::check(this->lua_state(), -1, type_panic); + constructor_handler handler{}; + stack::check(this->lua_state(), -1, handler); #endif // Safety lua_pop(this->lua_state(), 2); } basic_environment(env_t, const reference& extraction_target) : base_t(detail::no_safety, extraction_target.lua_state(), (stack::push_environment_of(extraction_target), -1)) { #ifdef SOL_CHECK_ARGUMENTS - stack::check(this->lua_state(), -1, type_panic); + constructor_handler handler{}; + stack::check(this->lua_state(), -1, handler); #endif // Safety lua_pop(this->lua_state(), 2); } basic_environment(lua_State* L, int index = -1) : base_t(detail::no_safety, L, index) { #ifdef SOL_CHECK_ARGUMENTS - stack::check(L, index, type_panic); + constructor_handler handler{}; + stack::check(L, index, handler); #endif // Safety } basic_environment(lua_State* L, ref_index index) : base_t(detail::no_safety, L, index) { #ifdef SOL_CHECK_ARGUMENTS auto pp = stack::push_pop(*this); - stack::check(L, -1, type_panic); + constructor_handler handler{}; + stack::check(L, -1, handler); #endif // Safety } template , basic_environment>>, meta::neg>, std::is_base_of>> = meta::enabler> @@ -16475,7 +16536,8 @@ namespace sol { #ifdef SOL_CHECK_ARGUMENTS if (!is_environment>::value) { auto pp = stack::push_pop(*this); - stack::check(lua_state(), -1, type_panic); + constructor_handler handler{}; + stack::check(lua_state(), -1, handler); } #endif // Safety } @@ -16607,7 +16669,7 @@ namespace sol { decltype(auto) tagged_get(types) const { #ifdef SOL_CHECK_ARGUMENTS if (!valid()) { - type_panic(L, index, type_of(L, index), type::none); + type_panic_c_str(L, index, type_of(L, index), type::none, ""); } #endif // Check Argument Safety return stack::get(L, index); @@ -16623,7 +16685,7 @@ namespace sol { error tagged_get(types) const { #ifdef SOL_CHECK_ARGUMENTS if (valid()) { - type_panic(L, index, type_of(L, index), type::none); + type_panic_c_str(L, index, type_of(L, index), type::none); } #endif // Check Argument Safety return error(detail::direct_error, stack::get(L, index)); @@ -17104,6 +17166,15 @@ namespace sol { return safe_script_file(filename, env, script_default_on_error, mode); } +#ifdef SOL_SAFE_FUNCTIONS + protected_function_result script(const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + return safe_script(code, chunkname, mode); + } + + protected_function_result script_file(const std::string& filename, load_mode mode = load_mode::any) { + return safe_script_file(filename, mode); + } +#else function_result script(const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { return unsafe_script(code, chunkname, mode); } @@ -17111,7 +17182,7 @@ namespace sol { function_result script_file(const std::string& filename, load_mode mode = load_mode::any) { return unsafe_script_file(filename, mode); } - +#endif load_result load(const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { char basechunkname[17] = {}; const char* chunknametarget = detail::make_chunk_name(code, chunkname, basechunkname); @@ -17475,7 +17546,7 @@ namespace sol { optional get(lua_State* L, int index, Handler&& handler, record& tracking) { lua_thread_state lts{ lua_tothread(L, index) }; if (lts.L == nullptr) { - handler(L, index, type::thread, type_of(L, index)); + handler(L, index, type::thread, type_of(L, index), "value does is not a valid thread type"); return nullopt; } tracking.use(1); @@ -17628,13 +17699,15 @@ namespace sol { coroutine(lua_State* L, T&& r) : coroutine(L, sol::ref_index(r.registry_index())) {} coroutine(lua_State* L, int index = -1) : reference(L, index) { #ifdef SOL_CHECK_ARGUMENTS - stack::check(L, index, type_panic); + constructor_handler handler{}; + stack::check(L, index, handler); #endif // Safety } coroutine(lua_State* L, ref_index index) : reference(L, index) { #ifdef SOL_CHECK_ARGUMENTS auto pp = stack::push_pop(*this); - stack::check(L, -1, type_panic); + constructor_handler handler{}; + stack::check(L, -1, handler); #endif // Safety } diff --git a/sol/coroutine.hpp b/sol/coroutine.hpp index 17f06b96..ddf97bc4 100644 --- a/sol/coroutine.hpp +++ b/sol/coroutine.hpp @@ -84,13 +84,15 @@ namespace sol { coroutine(lua_State* L, T&& r) : coroutine(L, sol::ref_index(r.registry_index())) {} coroutine(lua_State* L, int index = -1) : reference(L, index) { #ifdef SOL_CHECK_ARGUMENTS - stack::check(L, index, type_panic); + constructor_handler handler{}; + stack::check(L, index, handler); #endif // Safety } coroutine(lua_State* L, ref_index index) : reference(L, index) { #ifdef SOL_CHECK_ARGUMENTS auto pp = stack::push_pop(*this); - stack::check(L, -1, type_panic); + constructor_handler handler{}; + stack::check(L, -1, handler); #endif // Safety } diff --git a/sol/environment.hpp b/sol/environment.hpp index 9a8576ce..08b4e87d 100644 --- a/sol/environment.hpp +++ b/sol/environment.hpp @@ -52,25 +52,29 @@ namespace sol { basic_environment(env_t, const stack_reference& extraction_target) : base_t(detail::no_safety, extraction_target.lua_state(), (stack::push_environment_of(extraction_target), -1)) { #ifdef SOL_CHECK_ARGUMENTS - stack::check(this->lua_state(), -1, type_panic); + constructor_handler handler{}; + stack::check(this->lua_state(), -1, handler); #endif // Safety lua_pop(this->lua_state(), 2); } basic_environment(env_t, const reference& extraction_target) : base_t(detail::no_safety, extraction_target.lua_state(), (stack::push_environment_of(extraction_target), -1)) { #ifdef SOL_CHECK_ARGUMENTS - stack::check(this->lua_state(), -1, type_panic); + constructor_handler handler{}; + stack::check(this->lua_state(), -1, handler); #endif // Safety lua_pop(this->lua_state(), 2); } basic_environment(lua_State* L, int index = -1) : base_t(detail::no_safety, L, index) { #ifdef SOL_CHECK_ARGUMENTS - stack::check(L, index, type_panic); + constructor_handler handler{}; + stack::check(L, index, handler); #endif // Safety } basic_environment(lua_State* L, ref_index index) : base_t(detail::no_safety, L, index) { #ifdef SOL_CHECK_ARGUMENTS auto pp = stack::push_pop(*this); - stack::check(L, -1, type_panic); + constructor_handler handler{}; + stack::check(L, -1, handler); #endif // Safety } template , basic_environment>>, meta::neg>, std::is_base_of>> = meta::enabler> @@ -78,7 +82,8 @@ namespace sol { #ifdef SOL_CHECK_ARGUMENTS if (!is_environment>::value) { auto pp = stack::push_pop(*this); - stack::check(lua_state(), -1, type_panic); + constructor_handler handler{}; + stack::check(lua_state(), -1, handler); } #endif // Safety } diff --git a/sol/error_handler.hpp b/sol/error_handler.hpp new file mode 100644 index 00000000..aa504d8d --- /dev/null +++ b/sol/error_handler.hpp @@ -0,0 +1,99 @@ +// 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_ERROR_HANDLER_HPP +#define SOL_ERROR_HANDLER_HPP + +#include "types.hpp" + +namespace sol { + + inline int type_panic_string(lua_State* L, int index, type expected, type actual, const std::string& message = "") noexcept(false) { + const char* err = message.empty() ? "stack index %d, expected %s, received %s" : "stack index %d, expected %s, received %s with message %s"; + return luaL_error(L, err, index, + expected == type::poly ? "anything" : lua_typename(L, static_cast(expected)), + actual == type::poly ? "anything" : lua_typename(L, static_cast(actual)), + message.c_str() + ); + } + + inline int type_panic_c_str(lua_State* L, int index, type expected, type actual, const char* message = nullptr) noexcept(false) { + const char* err = message == nullptr || (std::char_traits::length(message) == 0) ? "stack index %d, expected %s, received %s" : "stack index %d, expected %s, received %s with message %s"; + return luaL_error(L, err, index, + expected == type::poly ? "anything" : lua_typename(L, static_cast(expected)), + actual == type::poly ? "anything" : lua_typename(L, static_cast(actual)), + message + ); + } + + struct type_panic_t { + int operator()(lua_State* L, int index, type expected, type actual) const noexcept(false) { + return type_panic_c_str(L, index, expected, actual, nullptr); + } + int operator()(lua_State* L, int index, type expected, type actual, const char* message) const noexcept(false) { + return type_panic_c_str(L, index, expected, actual, message); + } + int operator()(lua_State* L, int index, type expected, type actual, const std::string& message) const noexcept(false) { + return type_panic_string(L, index, expected, actual, message); + } + }; + + const type_panic_t type_panic = {}; + + struct constructor_handler { + int operator()(lua_State* L, int index, type expected, type actual, const std::string& message) const noexcept(false) { + return type_panic_string(L, index, expected, actual, message + " (type check failed in constructor)"); + } + }; + + struct argument_handler { + int operator()(lua_State* L, int index, type expected, type actual, const std::string& message) const noexcept(false) { + return type_panic_string(L, index, expected, actual, message + " (bad argument to variable or function call)"); + } + }; + + // Specify this function as the handler for lua::check if you know there's nothing wrong + inline int no_panic(lua_State*, int, type, type, const char* = nullptr) noexcept { + return 0; + } + + inline void type_error(lua_State* L, int expected, int actual) noexcept(false) { + luaL_error(L, "expected %s, received %s", lua_typename(L, expected), lua_typename(L, actual)); + } + + inline void type_error(lua_State* L, type expected, type actual) noexcept(false) { + type_error(L, static_cast(expected), static_cast(actual)); + } + + inline void type_assert(lua_State* L, int index, type expected, type actual) noexcept(false) { + if (expected != type::poly && expected != actual) { + type_panic_c_str(L, index, expected, actual, nullptr); + } + } + + inline void type_assert(lua_State* L, int index, type expected) { + type actual = type_of(L, index); + type_assert(L, index, expected, actual); + } + +} // sol + +#endif // SOL_ERROR_HANDLER_HPP diff --git a/sol/load_result.hpp b/sol/load_result.hpp index becc01a8..564725e7 100644 --- a/sol/load_result.hpp +++ b/sol/load_result.hpp @@ -48,7 +48,7 @@ namespace sol { decltype(auto) tagged_get(types) const { #ifdef SOL_CHECK_ARGUMENTS if (!valid()) { - type_panic(L, index, type_of(L, index), type::none); + type_panic_c_str(L, index, type_of(L, index), type::none, ""); } #endif // Check Argument Safety return stack::get(L, index); @@ -64,7 +64,7 @@ namespace sol { error tagged_get(types) const { #ifdef SOL_CHECK_ARGUMENTS if (valid()) { - type_panic(L, index, type_of(L, index), type::none); + type_panic_c_str(L, index, type_of(L, index), type::none); } #endif // Check Argument Safety return error(detail::direct_error, stack::get(L, index)); diff --git a/sol/protected_function.hpp b/sol/protected_function.hpp index fd337ea3..bf24b88d 100644 --- a/sol/protected_function.hpp +++ b/sol/protected_function.hpp @@ -37,29 +37,29 @@ namespace sol { } template - struct handler { + struct protected_handler { typedef std::is_base_of is_stack; const target_t& target; int stackindex; - handler(std::false_type, const target_t& target) : target(target), stackindex(0) { + protected_handler(std::false_type, const target_t& target) : target(target), stackindex(0) { if (b) { stackindex = lua_gettop(target.lua_state()) + 1; target.push(); } } - handler(std::true_type, const target_t& target) : target(target), stackindex(0) { + protected_handler(std::true_type, const target_t& target) : target(target), stackindex(0) { if (b) { stackindex = target.stack_index(); } } - handler(const target_t& target) : handler(is_stack(), target) {} + protected_handler(const target_t& target) : protected_handler(is_stack(), target) {} bool valid() const noexcept { return b; } - ~handler() { + ~protected_handler() { if (!is_stack::value && stackindex != 0) { lua_remove(target.lua_state(), stackindex); } @@ -102,29 +102,29 @@ namespace sol { private: template - call_status luacall(std::ptrdiff_t argcount, std::ptrdiff_t resultcount, detail::handler& h) const { + call_status luacall(std::ptrdiff_t argcount, std::ptrdiff_t resultcount, detail::protected_handler& h) const { return static_cast(lua_pcallk(lua_state(), static_cast(argcount), static_cast(resultcount), h.stackindex, 0, nullptr)); } template - auto invoke(types, std::index_sequence, std::ptrdiff_t n, detail::handler& h) const { + auto invoke(types, std::index_sequence, std::ptrdiff_t n, detail::protected_handler& h) const { luacall(n, sizeof...(Ret), h); return stack::pop>(lua_state()); } template - Ret invoke(types, std::index_sequence, std::ptrdiff_t n, detail::handler& h) const { + Ret invoke(types, std::index_sequence, std::ptrdiff_t n, detail::protected_handler& h) const { luacall(n, 1, h); return stack::pop(lua_state()); } template - void invoke(types, std::index_sequence, std::ptrdiff_t n, detail::handler& h) const { + void invoke(types, std::index_sequence, std::ptrdiff_t n, detail::protected_handler& h) const { luacall(n, 0, h); } template - protected_function_result invoke(types<>, std::index_sequence<>, std::ptrdiff_t n, detail::handler& h) const { + protected_function_result invoke(types<>, std::index_sequence<>, std::ptrdiff_t n, detail::protected_handler& h) const { int stacksize = lua_gettop(lua_state()); int poststacksize = stacksize; int firstreturn = 1; @@ -186,7 +186,8 @@ namespace sol { #ifdef SOL_CHECK_ARGUMENTS if (!is_function>::value) { auto pp = stack::push_pop(*this); - stack::check(lua_state(), -1, type_panic); + constructor_handler handler{}; + stack::check(lua_state(), -1, handler); } #endif // Safety } @@ -221,26 +222,30 @@ namespace sol { basic_protected_function(lua_State* L, int index = -1) : basic_protected_function(L, index, get_default_handler(L)) {} basic_protected_function(lua_State* L, int index, handler_t eh) : base_t(L, index), error_handler(std::move(eh)) { #ifdef SOL_CHECK_ARGUMENTS - stack::check(L, index, type_panic); + constructor_handler handler{}; + stack::check(L, index, handler); #endif // Safety } basic_protected_function(lua_State* L, absolute_index index) : basic_protected_function(L, index, get_default_handler(L)) {} basic_protected_function(lua_State* L, absolute_index index, handler_t eh) : base_t(L, index), error_handler(std::move(eh)) { #ifdef SOL_CHECK_ARGUMENTS - stack::check(L, index, type_panic); + constructor_handler handler{}; + stack::check(L, index, handler); #endif // Safety } basic_protected_function(lua_State* L, raw_index index) : basic_protected_function(L, index, get_default_handler(L)) {} basic_protected_function(lua_State* L, raw_index index, handler_t eh) : base_t(L, index), error_handler(std::move(eh)) { #ifdef SOL_CHECK_ARGUMENTS - stack::check(L, index, type_panic); + constructor_handler handler{}; + stack::check(L, index, handler); #endif // Safety } basic_protected_function(lua_State* L, ref_index index) : basic_protected_function(L, index, get_default_handler(L)) {} basic_protected_function(lua_State* L, ref_index index, handler_t eh) : base_t(L, index), error_handler(std::move(eh)) { #ifdef SOL_CHECK_ARGUMENTS auto pp = stack::push_pop(*this); - stack::check(L, -1, type_panic); + constructor_handler handler{}; + stack::check(L, -1, handler); #endif // Safety } @@ -259,13 +264,13 @@ namespace sol { if (!aligned) { // we do not expect the function to already be on the stack: push it if (error_handler.valid()) { - detail::handler h(error_handler); + detail::protected_handler h(error_handler); base_t::push(); int pushcount = stack::multi_push_reference(lua_state(), std::forward(args)...); return invoke(types(), std::make_index_sequence(), pushcount, h); } else { - detail::handler h(error_handler); + detail::protected_handler h(error_handler); base_t::push(); int pushcount = stack::multi_push_reference(lua_state(), std::forward(args)...); return invoke(types(), std::make_index_sequence(), pushcount, h); @@ -281,7 +286,7 @@ namespace sol { // so, we need to remove the function at the top and then dump the handler out ourselves base_t::push(); } - detail::handler h(error_handler); + detail::protected_handler h(error_handler); if (!is_stack_handler::value) { lua_replace(lua_state(), -3); h.stackindex = lua_absindex(lua_state(), -2); @@ -290,7 +295,7 @@ namespace sol { return invoke(types(), std::make_index_sequence(), pushcount, h); } else { - detail::handler h(error_handler); + detail::protected_handler h(error_handler); int pushcount = stack::multi_push_reference(lua_state(), std::forward(args)...); return invoke(types(), std::make_index_sequence(), pushcount, h); } diff --git a/sol/protected_function_result.hpp b/sol/protected_function_result.hpp index 0e6d7077..4639ff9e 100644 --- a/sol/protected_function_result.hpp +++ b/sol/protected_function_result.hpp @@ -49,7 +49,7 @@ namespace sol { decltype(auto) tagged_get(types) const { #ifdef SOL_CHECK_ARGUMENTS if (!valid()) { - type_panic(L, index, type_of(L, index), type::none); + type_panic_c_str(L, index, type_of(L, index), type::none); } #endif // Check Argument Safety return stack::get(L, index); @@ -65,7 +65,7 @@ namespace sol { error tagged_get(types) const { #ifdef SOL_CHECK_ARGUMENTS if (valid()) { - type_panic(L, index, type_of(L, index), type::none); + type_panic_c_str(L, index, type_of(L, index), type::none); } #endif // Check Argument Safety return error(detail::direct_error, stack::get(L, index)); diff --git a/sol/stack.hpp b/sol/stack.hpp index 93110e34..47e5dcb7 100644 --- a/sol/stack.hpp +++ b/sol/stack.hpp @@ -113,7 +113,8 @@ namespace sol { #ifndef _MSC_VER static_assert(meta::all...>::value, "One of the arguments being bound is a move-only type, and it is not being taken by reference: this will break your code. Please take a reference and std::move it manually if this was your intention."); #endif // This compiler make me so fucking sad - multi_check(L, start, type_panic); + argument_handler handler{}; + multi_check(L, start, handler); record tracking{}; return evaluator{}.eval(ta, tai, L, start, tracking, std::forward(fx), std::forward(args)...); } @@ -123,7 +124,8 @@ namespace sol { #ifndef _MSC_VER static_assert(meta::all...>::value, "One of the arguments being bound is a move-only type, and it is not being taken by reference: this will break your code. Please take a reference and std::move it manually if this was your intention."); #endif // This compiler make me so fucking sad - multi_check(L, start, type_panic); + argument_handler handler{}; + multi_check(L, start, handler); record tracking{}; evaluator{}.eval(ta, tai, L, start, tracking, std::forward(fx), std::forward(args)...); } diff --git a/sol/stack_check.hpp b/sol/stack_check.hpp index ee0c8ce8..e7dbbb24 100644 --- a/sol/stack_check.hpp +++ b/sol/stack_check.hpp @@ -59,7 +59,7 @@ namespace sol { bool success = check_func(L, index) == 1; if (!success) { // expected type, actual type - handler(L, index, expected, type_of(L, index)); + handler(L, index, expected, type_of(L, index), ""); } return success; } @@ -74,8 +74,8 @@ namespace sol { const type indextype = type_of(L, index); bool success = expected == indextype; if (!success) { - // expected type, actual type - handler(L, index, expected, indextype); + // expected type, actual type, message + handler(L, index, expected, indextype, ""); } return success; } @@ -100,7 +100,7 @@ namespace sol { #endif // If numbers are enabled, use the imprecise check if (!success) { // expected type, actual type - handler(L, index, type::number, type_of(L, index)); + handler(L, index, type::number, type_of(L, index), "not a numeric type"); } return success; #else @@ -109,7 +109,7 @@ namespace sol { type t = type_of(L, index); if (t != type::number) { // expected type, actual type - handler(L, index, type::number, t); + handler(L, index, type::number, t, "not a numeric type"); return false; } #endif // Do not allow strings to be numbers @@ -119,9 +119,9 @@ namespace sol { if (!success) { // expected type, actual type #ifndef SOL_STRINGS_ARE_NUMBERS - handler(L, index, type::number, t); + handler(L, index, type::number, t, "not a numeric type"); #else - handler(L, index, type::number, type_of(L, index)); + handler(L, index, type::number, type_of(L, index), "not a numeric type or numeric string"); #endif } return success; @@ -134,19 +134,19 @@ namespace sol { template static bool check(lua_State* L, int index, Handler&& handler, record& tracking) { tracking.use(1); -#ifdef SOL_STRINGS_ARE_NUMBERS +#ifndef SOL_STRINGS_ARE_NUMBERS type t = type_of(L, index); bool success = t == type::number; if (!success) { // expected type, actual type - handler(L, index, type::number, t); + handler(L, index, type::number, t, "not a numeric type"); } return success; #else bool success = lua_isnumber(L, index) == 1; if (!success) { // expected type, actual type - handler(L, index, type::number, type_of(L, index)); + handler(L, index, type::number, type_of(L, index), "not a numeric type or numeric string"); } return success; #endif @@ -166,7 +166,7 @@ namespace sol { success = lua_isnone(L, index); if (!success) { // expected type, actual type - handler(L, index, expected, type_of(L, index)); + handler(L, index, expected, type_of(L, index), ""); } return success; } @@ -219,7 +219,7 @@ namespace sol { bool success = !lua_isnone(L, index); if (!success) { // expected type, actual type - handler(L, index, type::none, type_of(L, index)); + handler(L, index, type::none, type_of(L, index), ""); } return success; } @@ -234,7 +234,7 @@ namespace sol { bool success = t == type::userdata || t == type::lightuserdata; if (!success) { // expected type, actual type - handler(L, index, type::lightuserdata, t); + handler(L, index, type::lightuserdata, t, ""); } return success; } @@ -249,7 +249,7 @@ namespace sol { bool success = t == type::userdata; if (!success) { // expected type, actual type - handler(L, index, type::userdata, t); + handler(L, index, type::userdata, t, ""); } return success; } @@ -287,25 +287,25 @@ namespace sol { return true; } if (t != type::userdata && t != type::table) { - handler(L, index, type::function, t); + handler(L, index, type::function, t, "must be a function or table or a userdata"); return false; } // Do advanced check for call-style userdata? static const auto& callkey = to_string(meta_function::call); if (lua_getmetatable(L, index) == 0) { // No metatable, no __call key possible - handler(L, index, type::function, t); + handler(L, index, type::function, t, "value is not a function and does not have overriden metatable"); return false; } if (lua_isnoneornil(L, -1)) { lua_pop(L, 1); - handler(L, index, type::function, t); + handler(L, index, type::function, t, "value is not a function and does not have valid metatable"); return false; } lua_getfield(L, -1, &callkey[0]); if (lua_isnoneornil(L, -1)) { lua_pop(L, 2); - handler(L, index, type::function, t); + handler(L, index, type::function, t, "value's metatable does not have __call overridden in metatable, cannot call this type"); return false; } // has call, is definitely a function @@ -324,7 +324,7 @@ namespace sol { return true; } if (t != type::userdata) { - handler(L, index, type::table, t); + handler(L, index, type::table, t, "value is not a table or a userdata that can behave like one"); return false; } return true; @@ -346,7 +346,7 @@ namespace sol { } if (t != type::userdata) { lua_pop(L, 1); - handler(L, index, expected, t); + handler(L, index, expected, t, "value does not have a valid metatable"); return false; } return true; @@ -358,19 +358,11 @@ namespace sol { template static bool check(lua_State* L, int index, Handler&& handler, record& tracking) { tracking.use(1); - if (lua_getmetatable(L, index) == 0) { + type t = type_of(L, index); + if (t == type::table || t == type::none || t == type::nil || t == type::userdata) { return true; } - type t = type_of(L, -1); - if (t == type::table || t == type::none || t == type::nil) { - lua_pop(L, 1); - return true; - } - if (t != type::userdata) { - lua_pop(L, 1); - handler(L, index, type::table, t); - return false; - } + handler(L, index, type::table, t, "value cannot not have a valid environment"); return true; } }; @@ -390,7 +382,7 @@ namespace sol { } if (t != type::userdata) { lua_pop(L, 1); - handler(L, index, type::table, t); + handler(L, index, type::table, t, "value does not have a valid metatable"); return false; } return true; @@ -403,7 +395,7 @@ namespace sol { static bool check(types, lua_State* L, type indextype, int index, Handler&& handler, record& tracking) { tracking.use(1); if (indextype != type::userdata) { - handler(L, index, type::userdata, indextype); + handler(L, index, type::userdata, indextype, "value is not a valid userdata"); return false; } if (meta::any, std::is_same, std::is_same, std::is_same>::value) @@ -433,7 +425,7 @@ namespace sol { } if (!success) { lua_pop(L, 1); - handler(L, index, type::userdata, indextype); + handler(L, index, type::userdata, indextype, "value is not a valid sol userdata of any kind"); return false; } lua_pop(L, 1); @@ -472,7 +464,7 @@ namespace sol { const type indextype = type_of(L, index); tracking.use(1); if (indextype != type::userdata) { - handler(L, index, type::userdata, indextype); + handler(L, index, type::userdata, indextype, "value is not a userdata"); return false; } if (lua_getmetatable(L, index) == 0) { @@ -485,12 +477,12 @@ namespace sol { detail::unique_destructor& pdx = *static_cast(static_cast(pointerpointer + 1)); bool success = &detail::usertype_unique_alloc_destroy == pdx; if (!success) { - handler(L, index, type::userdata, indextype); + handler(L, index, type::userdata, indextype, "value is a userdata but is not the correct unique usertype"); } return success; } lua_pop(L, 1); - handler(L, index, type::userdata, indextype); + handler(L, index, type::userdata, indextype, "unrecognized userdata (not pushed by sol?)"); return false; } }; @@ -549,7 +541,7 @@ namespace sol { return true; } tracking.use(1); - handler(L, index, type::poly, type_of(L, index)); + handler(L, index, type::poly, type_of(L, index), "value does not fit any type present in the variant"); return false; } diff --git a/sol/stack_check_get.hpp b/sol/stack_check_get.hpp index 8f6aa7bc..da4da9f7 100644 --- a/sol/stack_check_get.hpp +++ b/sol/stack_check_get.hpp @@ -66,7 +66,7 @@ namespace sol { int isnum = 0; const lua_Number value = lua_tonumberx(L, index, &isnum); if (isnum != 0) { -#if 1 // defined(SOL_CHECK_ARGUMENTS) && !defined(SOL_NO_CHECK_NUMBER_PRECISION) +#if defined(SOL_CHECK_ARGUMENTS) && !defined(SOL_NO_CHECK_NUMBER_PRECISION) const auto integer_value = llround(value); if (static_cast(integer_value) == value) { tracking.use(1); @@ -79,7 +79,7 @@ namespace sol { } const type t = type_of(L, index); tracking.use(static_cast(t != type::none)); - handler(L, index, type::number, t); + handler(L, index, type::number, t, "not an integer"); return nullopt; } }; @@ -93,7 +93,7 @@ namespace sol { if (isnum == 0) { type t = type_of(L, index); tracking.use(static_cast(t != type::none)); - handler(L, index, type::number, t); + handler(L, index, type::number, t, "not a valid enumeration value"); return nullopt; } tracking.use(1); @@ -110,7 +110,7 @@ namespace sol { if (isnum == 0) { type t = type_of(L, index); tracking.use(static_cast(t != type::none)); - handler(L, index, type::number, t); + handler(L, index, type::number, t, "not a valid floating point number"); return nullopt; } tracking.use(1); @@ -142,7 +142,7 @@ namespace sol { typedef std::variant_alternative_t<0, V> T; // This should never be reached... // please check your code and understand what you did to bring yourself here - handler(L, index, type::poly, type_of(L, index)); + handler(L, index, type::poly, type_of(L, index), "this variant code should never be reached: if it has, you have done something so terribly wrong"); return nullopt; } diff --git a/sol/stack_core.hpp b/sol/stack_core.hpp index f87cde83..950c96b2 100644 --- a/sol/stack_core.hpp +++ b/sol/stack_core.hpp @@ -23,6 +23,7 @@ #define SOL_STACK_CORE_HPP #include "types.hpp" +#include "error_handler.hpp" #include "reference.hpp" #include "stack_reference.hpp" #include "tuple.hpp" @@ -299,7 +300,7 @@ namespace sol { #ifdef SOL_CHECK_ARGUMENTS 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, tracking); + auto op = check_get(L, index, type_panic_c_str, tracking); return *std::move(op); } #else diff --git a/sol/stack_push.hpp b/sol/stack_push.hpp index 9f6f47c2..b16cc72a 100644 --- a/sol/stack_push.hpp +++ b/sol/stack_push.hpp @@ -226,10 +226,13 @@ namespace sol { #endif #if defined(SOL_CHECK_ARGUMENTS) && !defined(SOL_NO_CHECK_NUMBER_PRECISION) if (static_cast(llround(static_cast(value))) != value) { -#ifndef SOL_NO_EXCEPTIONS - throw sol::error("The integer will be misrepresented in lua."); +#ifdef SOL_NO_EXCEPTIONS + // Is this really worth it? + assert(false && "integer value will be misrepresented in lua"); + lua_pushnumber(L, static_cast(value)); + return 1; #else - assert(false && "The integer will be misrepresented in lua."); + throw error(detail::direct_error, "integer value will be misrepresented in lua"); #endif } #endif diff --git a/sol/state_view.hpp b/sol/state_view.hpp index 4f69568b..ccd03084 100644 --- a/sol/state_view.hpp +++ b/sol/state_view.hpp @@ -435,6 +435,15 @@ namespace sol { return safe_script_file(filename, env, script_default_on_error, mode); } +#ifdef SOL_SAFE_FUNCTIONS + protected_function_result script(const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + return safe_script(code, chunkname, mode); + } + + protected_function_result script_file(const std::string& filename, load_mode mode = load_mode::any) { + return safe_script_file(filename, mode); + } +#else function_result script(const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { return unsafe_script(code, chunkname, mode); } @@ -442,7 +451,7 @@ namespace sol { function_result script_file(const std::string& filename, load_mode mode = load_mode::any) { return unsafe_script_file(filename, mode); } - +#endif load_result load(const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { char basechunkname[17] = {}; const char* chunknametarget = detail::make_chunk_name(code, chunkname, basechunkname); diff --git a/sol/table_core.hpp b/sol/table_core.hpp index f9cbc552..2077bf35 100644 --- a/sol/table_core.hpp +++ b/sol/table_core.hpp @@ -185,13 +185,15 @@ namespace sol { } basic_table_core(lua_State* L, int index = -1) : basic_table_core(detail::no_safety, L, index) { #ifdef SOL_CHECK_ARGUMENTS - stack::check(L, index, type_panic); + constructor_handler handler{}; + stack::check(L, index, handler); #endif // Safety } basic_table_core(lua_State* L, ref_index index) : basic_table_core(detail::no_safety, L, index) { #ifdef SOL_CHECK_ARGUMENTS auto pp = stack::push_pop(*this); - stack::check(L, -1, type_panic); + constructor_handler handler{}; + stack::check(L, -1, handler); #endif // Safety } template , basic_table_core>>, meta::neg>, std::is_base_of>> = meta::enabler> @@ -199,7 +201,8 @@ namespace sol { #ifdef SOL_CHECK_ARGUMENTS if (!is_table>::value) { auto pp = stack::push_pop(*this); - stack::check(base_t::lua_state(), -1, type_panic); + constructor_handler handler{}; + stack::check(base_t::lua_state(), -1, handler); } #endif // Safety } diff --git a/sol/thread.hpp b/sol/thread.hpp index 5050bbf8..d5fe41ed 100644 --- a/sol/thread.hpp +++ b/sol/thread.hpp @@ -61,7 +61,7 @@ namespace sol { optional get(lua_State* L, int index, Handler&& handler, record& tracking) { lua_thread_state lts{ lua_tothread(L, index) }; if (lts.L == nullptr) { - handler(L, index, type::thread, type_of(L, index)); + handler(L, index, type::thread, type_of(L, index), "value does is not a valid thread type"); return nullopt; } tracking.use(1); diff --git a/sol/types.hpp b/sol/types.hpp index 9acb075a..1cd2d059 100644 --- a/sol/types.hpp +++ b/sol/types.hpp @@ -667,37 +667,6 @@ namespace sol { return static_cast(lua_type(L, index)); } - inline int type_panic(lua_State* L, int index, type expected, type actual) noexcept(false) { - return luaL_error(L, "stack index %d, expected %s, received %s", index, - expected == type::poly ? "anything" : lua_typename(L, static_cast(expected)), - actual == type::poly ? "anything" : lua_typename(L, static_cast(actual)) - ); - } - - // Specify this function as the handler for lua::check if you know there's nothing wrong - inline int no_panic(lua_State*, int, type, type) noexcept { - return 0; - } - - inline void type_error(lua_State* L, int expected, int actual) noexcept(false) { - luaL_error(L, "expected %s, received %s", lua_typename(L, expected), lua_typename(L, actual)); - } - - inline void type_error(lua_State* L, type expected, type actual) noexcept(false) { - type_error(L, static_cast(expected), static_cast(actual)); - } - - inline void type_assert(lua_State* L, int index, type expected, type actual) noexcept(false) { - if (expected != type::poly && expected != actual) { - type_panic(L, index, expected, actual); - } - } - - inline void type_assert(lua_State* L, int index, type expected) { - type actual = type_of(L, index); - type_assert(L, index, expected, actual); - } - inline std::string type_name(lua_State* L, type t) { return lua_typename(L, static_cast(t)); } diff --git a/sol/unsafe_function.hpp b/sol/unsafe_function.hpp index c87658d9..c29480f0 100644 --- a/sol/unsafe_function.hpp +++ b/sol/unsafe_function.hpp @@ -71,7 +71,8 @@ namespace sol { #ifdef SOL_CHECK_ARGUMENTS if (!is_function>::value) { auto pp = stack::push_pop(*this); - stack::check(lua_state(), -1, type_panic); + constructor_handler handler{}; + stack::check(lua_state(), -1, handler); } #endif // Safety } @@ -85,13 +86,15 @@ namespace sol { basic_function(lua_State* L, T&& r) : basic_function(L, sol::ref_index(r.registry_index())) {} basic_function(lua_State* L, int index = -1) : base_t(L, index) { #ifdef SOL_CHECK_ARGUMENTS - stack::check(L, index, type_panic); + constructor_handler handler{}; + stack::check(L, index, handler); #endif // Safety } basic_function(lua_State* L, ref_index index) : base_t(L, index) { #ifdef SOL_CHECK_ARGUMENTS auto pp = stack::push_pop(*this); - stack::check(L, -1, type_panic); + constructor_handler handler{}; + stack::check(L, -1, handler); #endif // Safety } diff --git a/sol/userdata.hpp b/sol/userdata.hpp index 6251fa35..bf82f80e 100644 --- a/sol/userdata.hpp +++ b/sol/userdata.hpp @@ -52,13 +52,15 @@ namespace sol { basic_userdata(lua_State* L, T&& r) : basic_userdata(L, sol::ref_index(r.registry_index())) {} basic_userdata(lua_State* L, int index = -1) : base_t(detail::no_safety, L, index) { #ifdef SOL_CHECK_ARGUMENTS - stack::check(L, index, type_panic); + constructor_handler handler{}; + stack::check(L, index, handler); #endif // Safety } basic_userdata(lua_State* L, ref_index index) : base_t(detail::no_safety, L, index) { #ifdef SOL_CHECK_ARGUMENTS auto pp = stack::push_pop(*this); - stack::check(L, index, type_panic); + constructor_handler handler{}; + stack::check(L, index, handler); #endif // Safety } }; @@ -89,13 +91,15 @@ namespace sol { basic_lightuserdata(lua_State* L, T&& r) : basic_lightuserdata(L, sol::ref_index(r.registry_index())) {} basic_lightuserdata(lua_State* L, int index = -1) : base_t(L, index) { #ifdef SOL_CHECK_ARGUMENTS - stack::check(L, index, type_panic); + constructor_handler handler{}; + stack::check(L, index, handler); #endif // Safety } basic_lightuserdata(lua_State* L, ref_index index) : base_t(L, index) { #ifdef SOL_CHECK_ARGUMENTS auto pp = stack::push_pop(*this); - stack::check(L, index, type_panic); + constructor_handler handler{}; + stack::check(L, index, handler); #endif // Safety } }; diff --git a/test_functions.cpp b/test_functions.cpp index 23a78e5b..9ecd920f 100644 --- a/test_functions.cpp +++ b/test_functions.cpp @@ -1164,82 +1164,94 @@ TEST_CASE("functions/pointer nullptr + nil", "ensure specific semantics for hand REQUIRE_FALSE(result.valid()); } SECTION("throw ref") { - REQUIRE_THROWS([&]() { + { sol::state lua; lua["v1"] = sptr; - nil_test& v1 = lua["v1"]; - (void)(&v1 == sptr.get()); - }()); - REQUIRE_THROWS([&]() { + sol::object o = lua["v1"]; + bool isp = o.is(); + REQUIRE_FALSE(isp); + } + { sol::state lua; lua["v2"] = std::unique_ptr(); - nil_test& v2 = lua["v2"]; - (void)(&v2 == uptr.get()); - }()); - REQUIRE_THROWS([&]() { + sol::object o = lua["v2"]; + bool isp = o.is(); + REQUIRE_FALSE(isp); + } + { sol::state lua; lua["v3"] = rptr; - nil_test& v3 = lua["v3"]; - (void)(&v3 == rptr); - }()); - REQUIRE_THROWS([&]() { + sol::object o = lua["v3"]; + bool isp = o.is(); + REQUIRE_FALSE(isp); + } + { sol::state lua; lua["v4"] = vptr; - nil_test& v4 = lua["v4"]; - (void)(&v4 == vptr); - }()); + sol::object o = lua["v4"]; + bool isp = o.is(); + REQUIRE_FALSE(isp); + } } SECTION("throw unique") { - REQUIRE_THROWS([&]() { + { sol::state lua; lua["v1"] = sptr; - std::unique_ptr& v1 = lua["v1"]; - (void)(v1.get() == sptr.get()); - }()); - REQUIRE_THROWS([&]() { + sol::object o = lua["v1"]; + bool isp = o.is>(); + REQUIRE_FALSE(isp); + } + { sol::state lua; lua["v2"] = std::unique_ptr(); - std::unique_ptr& v2 = lua["v2"]; - (void)(v2.get() == uptr.get()); - }()); - REQUIRE_THROWS([&]() { + sol::object o = lua["v2"]; + bool isp = o.is>(); + REQUIRE_FALSE(isp); + } + { sol::state lua; lua["v3"] = rptr; - std::unique_ptr& v3 = lua["v3"]; - (void)(v3.get() == rptr); - }()); - REQUIRE_THROWS([&]() { + sol::object o = lua["v3"]; + bool isp = o.is>(); + REQUIRE_FALSE(isp); + }; + { sol::state lua; lua["v4"] = vptr; - std::unique_ptr& v4 = lua["v4"]; - (void)(v4.get() == vptr); - }()); + sol::object o = lua["v4"]; + bool isp = o.is>(); + REQUIRE_FALSE(isp); + }; } SECTION("throw shared") { - REQUIRE_THROWS([&]() { + { sol::state lua; lua["v1"] = sptr; - std::shared_ptr& v1 = lua["v1"]; - (void)(v1.get() == sptr.get()); - }()); - REQUIRE_THROWS([&]() { + sol::object o = lua["v1"]; + bool isp = o.is>(); + REQUIRE_FALSE(isp); + } + { sol::state lua; lua["v2"] = std::unique_ptr(); - std::shared_ptr& v2 = lua["v2"]; - (void)(v2.get() == uptr.get()); - }()); - REQUIRE_THROWS([&]() { + sol::object o = lua["v2"]; + bool isp = o.is>(); + REQUIRE_FALSE(isp); + } + { sol::state lua; lua["v3"] = rptr; - std::shared_ptr& v3 = lua["v3"]; - (void)(v3.get() == rptr); - }()); - REQUIRE_THROWS([&]() { + sol::object o = lua["v3"]; + bool isp = o.is>(); + REQUIRE_FALSE(isp); + } + { sol::state lua; lua["v4"] = vptr; - std::shared_ptr& v4 = lua["v4"]; - (void)(v4.get() == vptr); - }()); + sol::object o = lua["v4"]; + bool isp = o.is>(); + REQUIRE_FALSE(isp); + } } } diff --git a/test_operators.cpp b/test_operators.cpp index 210ed77f..849cc598 100644 --- a/test_operators.cpp +++ b/test_operators.cpp @@ -427,7 +427,7 @@ TEST_CASE("operators/container-like", "test that generic begin/end and iterator } } #else - REQUIRE(true); + SUCCEED(""); #endif }