// sol2 // The MIT License (MIT) // Copyright (c) 2013-2018 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_CALL_HPP #define SOL_CALL_HPP #include "protect.hpp" #include "wrapper.hpp" #include "trampoline.hpp" #include "property.hpp" #include "filters.hpp" #include "stack.hpp" namespace sol { namespace usertype_detail { } // namespace usertype_detail namespace filter_detail { template inline void handle_filter(static_stack_dependencies, lua_State* L, int&) { if (sizeof...(In) == 0) { return; } absolute_index ai(L, I); if (type_of(L, ai) != type::userdata) { return; } lua_createtable(L, static_cast(sizeof...(In)), 0); stack_reference deps(L, -1); auto per_dep = [&L, &deps](int i) { lua_pushvalue(L, i); luaL_ref(L, deps.stack_index()); }; (void)per_dep; (void)detail::swallow{ int(), (per_dep(In), int())... }; lua_setuservalue(L, ai); } template inline void handle_filter(returns_self_with, lua_State* L, int& pushed) { pushed = stack::push(L, raw_index(1)); handle_filter(static_stack_dependencies<-1, In...>(), L, pushed); } inline void handle_filter(const stack_dependencies& sdeps, lua_State* L, int&) { absolute_index ai(L, sdeps.target); if (type_of(L, ai) != type::userdata) { return; } lua_createtable(L, static_cast(sdeps.size()), 0); stack_reference deps(L, -1); for (std::size_t i = 0; i < sdeps.size(); ++i) { lua_pushvalue(L, sdeps.stack_indices[i]); luaL_ref(L, deps.stack_index()); } lua_setuservalue(L, ai); } template >> = meta::enabler> inline void handle_filter(P&& p, lua_State* L, int& pushed) { pushed = std::forward

(p)(L, pushed); } } // namespace filter_detail namespace function_detail { inline int no_construction_error(lua_State* L) { return luaL_error(L, "sol: cannot call this constructor (tagged as non-constructible)"); } } // namespace function_detail namespace call_detail { template inline auto& pick(std::true_type, property_wrapper& f) { return f.read; } template inline auto& pick(std::false_type, property_wrapper& f) { return f.write; } template struct void_call : void_call> {}; template struct void_call> { static void call(Args...) { } }; template struct constructor_match { T* obj; constructor_match(T* o) : obj(o) { } template int operator()(types, index_value, types r, types a, lua_State* L, int, int start) const { detail::default_construct func{}; return stack::call_into_lua(r, a, L, start, func, obj); } }; namespace overload_detail { template inline int overload_match_arity(types<>, std::index_sequence<>, std::index_sequence, Match&&, lua_State* L, int, int, Args&&...) { return luaL_error(L, "sol: no matching function call takes this number of arguments and the specified types"); } template inline int overload_match_arity(types, std::index_sequence, std::index_sequence, Match&& matchfx, lua_State* L, int fxarity, int start, Args&&... args) { typedef lua_bind_traits> traits; typedef meta::tuple_types return_types; typedef typename traits::free_args_list args_list; // compile-time eliminate any functions that we know ahead of time are of improper arity if (!traits::runtime_variadics_t::value && meta::find_in_pack_v, index_value...>::value) { return overload_match_arity(types(), std::index_sequence(), std::index_sequence(), std::forward(matchfx), L, fxarity, start, std::forward(args)...); } if (!traits::runtime_variadics_t::value && traits::free_arity != fxarity) { return overload_match_arity(types(), std::index_sequence(), std::index_sequence(), std::forward(matchfx), L, fxarity, start, std::forward(args)...); } stack::record tracking{}; if (!stack::stack_detail::check_types{}.check(args_list(), L, start, no_panic, tracking)) { return overload_match_arity(types(), std::index_sequence(), std::index_sequence(), std::forward(matchfx), L, fxarity, start, std::forward(args)...); } return matchfx(types(), index_value(), return_types(), args_list(), L, fxarity, start, std::forward(args)...); } template inline int overload_match_arity_single(types<>, std::index_sequence<>, std::index_sequence, Match&& matchfx, lua_State* L, int fxarity, int start, Args&&... args) { return overload_match_arity(types<>(), std::index_sequence<>(), std::index_sequence(), std::forward(matchfx), L, fxarity, start, std::forward(args)...); } template inline int overload_match_arity_single(types, std::index_sequence, std::index_sequence, Match&& matchfx, lua_State* L, int fxarity, int start, Args&&... args) { typedef lua_bind_traits> traits; typedef meta::tuple_types return_types; typedef typename traits::free_args_list args_list; // compile-time eliminate any functions that we know ahead of time are of improper arity if (!traits::runtime_variadics_t::value && meta::find_in_pack_v, index_value...>::value) { return overload_match_arity(types<>(), std::index_sequence<>(), std::index_sequence(), std::forward(matchfx), L, fxarity, start, std::forward(args)...); } if (!traits::runtime_variadics_t::value && traits::free_arity != fxarity) { return overload_match_arity(types<>(), std::index_sequence<>(), std::index_sequence(), std::forward(matchfx), L, fxarity, start, std::forward(args)...); } return matchfx(types(), index_value(), return_types(), args_list(), L, fxarity, start, std::forward(args)...); } template inline int overload_match_arity_single(types, std::index_sequence, std::index_sequence, Match&& matchfx, lua_State* L, int fxarity, int start, Args&&... args) { typedef lua_bind_traits> traits; typedef meta::tuple_types return_types; typedef typename traits::free_args_list args_list; // compile-time eliminate any functions that we know ahead of time are of improper arity if (!traits::runtime_variadics_t::value && meta::find_in_pack_v, index_value...>::value) { return overload_match_arity(types(), std::index_sequence(), std::index_sequence(), std::forward(matchfx), L, fxarity, start, std::forward(args)...); } if (!traits::runtime_variadics_t::value && traits::free_arity != fxarity) { return overload_match_arity(types(), std::index_sequence(), std::index_sequence(), std::forward(matchfx), L, fxarity, start, std::forward(args)...); } stack::record tracking{}; if (!stack::stack_detail::check_types{}.check(args_list(), L, start, no_panic, tracking)) { return overload_match_arity(types(), std::index_sequence(), std::index_sequence(), std::forward(matchfx), L, fxarity, start, std::forward(args)...); } return matchfx(types(), index_value(), return_types(), args_list(), L, fxarity, start, std::forward(args)...); } } // namespace overload_detail template inline int overload_match_arity(Match&& matchfx, lua_State* L, int fxarity, int start, Args&&... args) { return overload_detail::overload_match_arity_single(types(), std::make_index_sequence(), std::index_sequence<>(), std::forward(matchfx), L, fxarity, start, std::forward(args)...); } template inline int overload_match(Match&& matchfx, lua_State* L, int start, Args&&... args) { int fxarity = lua_gettop(L) - (start - 1); return overload_match_arity(std::forward(matchfx), L, fxarity, start, std::forward(args)...); } template inline int construct_match(Match&& matchfx, lua_State* L, int fxarity, int start, Args&&... args) { // use same overload resolution matching as all other parts of the framework return overload_match_arity::call)...>(std::forward(matchfx), L, fxarity, start, std::forward(args)...); } template inline int construct_trampolined(lua_State* L) { static const auto& meta = usertype_traits::metatable(); int argcount = lua_gettop(L); call_syntax syntax = argcount > 0 ? stack::get_call_syntax(L, usertype_traits::user_metatable(), 1) : call_syntax::dot; argcount -= static_cast(syntax); T* obj = detail::usertype_allocate(L); reference userdataref(L, -1); userdataref.pop(); construct_match(constructor_match(obj), L, argcount, 1 + static_cast(syntax)); userdataref.push(); stack::stack_detail::undefined_metatable umf(L, &meta[0]); umf(); return 1; } template inline int construct(lua_State* L) { return detail::static_trampoline<&construct_trampolined>(L); } template struct agnostic_lua_call_wrapper { typedef wrapper> wrap; template static int convertible_call(std::true_type, lua_State* L, Fx&& f, Args&&... args) { typedef typename wrap::traits_type traits_type; typedef typename traits_type::function_pointer_type fp_t; fp_t fx = f; return agnostic_lua_call_wrapper{}.call(L, fx, std::forward(args)...); } template static int convertible_call(std::false_type, lua_State* L, Fx&& f, Args&&... args) { typedef typename wrap::returns_list returns_list; typedef typename wrap::free_args_list args_list; typedef typename wrap::caller caller; return stack::call_into_lua(returns_list(), args_list(), L, boost + 1, caller(), std::forward(f), std::forward(args)...); } template static int call(lua_State* L, Fx&& f, Args&&... args) { typedef typename wrap::traits_type traits_type; typedef typename traits_type::function_pointer_type fp_t; return convertible_call(std::conditional_t>::value, std::is_convertible, fp_t>, std::false_type>(), L, std::forward(f), std::forward(args)...); } }; template struct agnostic_lua_call_wrapper, true, is_variable, checked, boost, clean_stack, C> { template static int call(lua_State* L, F&& f) { typedef is_stack_based> is_stack; if (clean_stack && !is_stack::value) { lua_settop(L, 0); } return stack::push_reference(L, detail::unwrap(f.value)); } }; template struct agnostic_lua_call_wrapper, false, is_variable, checked, boost, clean_stack, C> { template static int call_assign(std::true_type, lua_State* L, V&& f) { detail::unwrap(f.value) = stack::unqualified_get>(L, boost + (is_variable ? 3 : 1)); if (clean_stack) { lua_settop(L, 0); } return 0; } template static int call_assign(std::false_type, lua_State* L, Args&&...) { return luaL_error(L, "sol: cannot write to this variable: copy assignment/constructor not available"); } template static int call_const(std::false_type, lua_State* L, Args&&... args) { typedef meta::unwrapped_t R; return call_assign(std::is_assignable>, R>(), L, std::forward(args)...); } template static int call_const(std::true_type, lua_State* L, Args&&...) { return luaL_error(L, "sol: cannot write to a readonly (const) variable"); } template static int call(lua_State* L, V&& f) { return call_const(std::is_const>(), L, f); } }; template struct agnostic_lua_call_wrapper { static int call(lua_State* L, lua_CFunction_ref f) { return f(L); } }; template struct agnostic_lua_call_wrapper { static int call(lua_State* L, lua_CFunction f) { return f(L); } }; #if defined(SOL_NOEXCEPT_FUNCTION_TYPE) && SOL_NOEXCEPT_FUNCTION_TYPE template struct agnostic_lua_call_wrapper { static int call(lua_State* L, detail::lua_CFunction_noexcept f) { return f(L); } }; #endif // noexcept function types template struct agnostic_lua_call_wrapper { static int call(lua_State* L, const no_prop&) { return luaL_error(L, is_index ? "sol: cannot read from a writeonly property" : "sol: cannot write to a readonly property"); } }; template struct agnostic_lua_call_wrapper { static int call(lua_State* L, const no_construction&) { return function_detail::no_construction_error(L); } }; template struct agnostic_lua_call_wrapper, is_index, is_variable, checked, boost, clean_stack, C> { static int call(lua_State*, const bases&) { // Uh. How did you even call this, lul return 0; } }; template struct agnostic_lua_call_wrapper, is_index, is_variable, checked, boost, clean_stack, C> { static int call(lua_State* L, std::reference_wrapper f) { return agnostic_lua_call_wrapper{}.call(L, f.get()); } }; template struct lua_call_wrapper : agnostic_lua_call_wrapper {}; template struct lua_call_wrapper::value>> { typedef wrapper> wrap; typedef typename wrap::object_type object_type; template static int call(lua_State* L, Fx&& f, object_type& o) { typedef typename wrap::returns_list returns_list; typedef typename wrap::args_list args_list; typedef typename wrap::caller caller; return stack::call_into_lua(returns_list(), args_list(), L, boost + (is_variable ? 3 : 2), caller(), std::forward(f), o); } template static int call(lua_State* L, Fx&& f) { typedef std::conditional_t::value, object_type, T> Ta; #if defined(SOL_SAFE_USERTYPE) && SOL_SAFE_USERTYPE auto maybeo = stack::unqualified_check_get(L, 1); if (!maybeo || maybeo.value() == nullptr) { return luaL_error(L, "sol: received nil for 'self' argument (use ':' for accessing member functions, make sure member variables are preceeded by the actual object with '.' syntax)"); } object_type* o = static_cast(maybeo.value()); return call(L, std::forward(f), *o); #else object_type& o = static_cast(*stack::unqualified_get>(L, 1)); return call(L, std::forward(f), o); #endif // Safety } }; template struct lua_call_wrapper::value>> { typedef lua_bind_traits traits_type; typedef wrapper> wrap; typedef typename wrap::object_type object_type; template static int call_assign(std::true_type, lua_State* L, V&& f, object_type& o) { typedef typename wrap::args_list args_list; typedef typename wrap::caller caller; return stack::call_into_lua(types(), args_list(), L, boost + (is_variable ? 3 : 2), caller(), f, o); } template static int call_assign(std::true_type, lua_State* L, V&& f) { typedef std::conditional_t::value, object_type, T> Ta; #if defined(SOL_SAFE_USERTYPE) && SOL_SAFE_USERTYPE auto maybeo = stack::check_get(L, 1); if (!maybeo || maybeo.value() == nullptr) { if (is_variable) { return luaL_error(L, "sol: received nil for 'self' argument (bad '.' access?)"); } return luaL_error(L, "sol: received nil for 'self' argument (pass 'self' as first argument)"); } object_type* o = static_cast(maybeo.value()); return call_assign(std::true_type(), L, f, *o); #else object_type& o = static_cast(*stack::get>(L, 1)); return call_assign(std::true_type(), L, f, o); #endif // Safety } template static int call_assign(std::false_type, lua_State* L, Args&&...) { return luaL_error(L, "sol: cannot write to this variable: copy assignment/constructor not available"); } template static int call_const(std::false_type, lua_State* L, Args&&... args) { typedef typename traits_type::return_type R; return call_assign(std::is_copy_assignable>(), L, std::forward(args)...); } template static int call_const(std::true_type, lua_State* L, Args&&...) { return luaL_error(L, "sol: cannot write to a readonly (const) variable"); } template static int call(lua_State* L, V&& f) { return call_const(std::is_const(), L, std::forward(f)); } template static int call(lua_State* L, V&& f, object_type& o) { return call_const(std::is_const(), L, std::forward(f), o); } }; template struct lua_call_wrapper::value>> { typedef lua_bind_traits traits_type; typedef wrapper> wrap; typedef typename wrap::object_type object_type; template static int call(lua_State* L, V&& v, object_type& o) { typedef typename wrap::returns_list returns_list; typedef typename wrap::caller caller; F f(std::forward(v)); return stack::call_into_lua(returns_list(), types<>(), L, boost + (is_variable ? 3 : 2), caller(), f, o); } template static int call(lua_State* L, V&& f) { typedef std::conditional_t::value, object_type, T> Ta; #if defined(SOL_SAFE_USERTYPE) && SOL_SAFE_USERTYPE auto maybeo = stack::check_get(L, 1); if (!maybeo || maybeo.value() == nullptr) { if (is_variable) { return luaL_error(L, "sol: 'self' argument is lua_nil (bad '.' access?)"); } return luaL_error(L, "sol: 'self' argument is lua_nil (pass 'self' as first argument)"); } object_type* o = static_cast(maybeo.value()); return call(L, f, *o); #else object_type& o = static_cast(*stack::get>(L, 1)); return call(L, f, o); #endif // Safety } }; template struct lua_call_wrapper, false, is_variable, checked, boost, clean_stack, C> { typedef lua_bind_traits traits_type; typedef wrapper> wrap; typedef typename wrap::object_type object_type; template static int call(lua_State* L, V&&) { return luaL_error(L, "sol: cannot write to a sol::readonly variable"); } template static int call(lua_State* L, V&&, object_type&) { return luaL_error(L, "sol: cannot write to a sol::readonly variable"); } }; template struct lua_call_wrapper, true, is_variable, checked, boost, clean_stack, C> : lua_call_wrapper { }; template struct lua_call_wrapper, is_index, is_variable, checked, boost, clean_stack, C> { typedef constructor_list F; static int call(lua_State* L, F&) { const auto& meta = usertype_traits::metatable(); int argcount = lua_gettop(L); call_syntax syntax = argcount > 0 ? stack::get_call_syntax(L, usertype_traits::user_metatable(), 1) : call_syntax::dot; argcount -= static_cast(syntax); T* obj = detail::usertype_allocate(L); reference userdataref(L, -1); construct_match(constructor_match(obj), L, argcount, boost + 1 + static_cast(syntax)); userdataref.push(); stack::stack_detail::undefined_metatable umf(L, &meta[0]); umf(); return 1; } }; template struct lua_call_wrapper, is_index, is_variable, checked, boost, clean_stack, C> { typedef constructor_wrapper F; struct onmatch { template int operator()(types, index_value, types r, types a, lua_State* L, int, int start, F& f) { const auto& meta = usertype_traits::metatable(); T* obj = detail::usertype_allocate(L); reference userdataref(L, -1); auto& func = std::get(f.functions); stack::call_into_lua(r, a, L, boost + start, func, detail::implicit_wrapper(obj)); userdataref.push(); stack::stack_detail::undefined_metatable umf(L, &meta[0]); umf(); return 1; } }; static int call(lua_State* L, F& f) { call_syntax syntax = stack::get_call_syntax(L, usertype_traits::user_metatable(), 1); int syntaxval = static_cast(syntax); int argcount = lua_gettop(L) - syntaxval; return construct_match>...>(onmatch(), L, argcount, 1 + syntaxval, f); } }; template struct lua_call_wrapper, is_index, is_variable, checked, boost, clean_stack, std::enable_if_t::value>> { typedef destructor_wrapper F; static int call(lua_State* L, const F&) { return detail::usertype_alloc_destruct(L); } }; template struct lua_call_wrapper, is_index, is_variable, checked, boost, clean_stack, std::enable_if_t::value>> { typedef destructor_wrapper F; static int call_void(std::true_type, lua_State* L, const F& f) { typedef meta::bind_traits> bt; typedef typename bt::template arg_at<0> arg0; typedef meta::unqualified_t O; O& obj = stack::get(L); f.fx(detail::implicit_wrapper(obj)); return 0; } static int call_void(std::false_type, lua_State* L, const F& f) { T& obj = stack::get(L); f.fx(detail::implicit_wrapper(obj)); return 0; } static int call(lua_State* L, const F& f) { return call_void(std::is_void(), L, f); } }; template struct lua_call_wrapper, is_index, is_variable, checked, boost, clean_stack, C> { typedef overload_set F; struct on_match { template int operator()(types, index_value, types, types, lua_State* L, int, int, F& fx) { auto& f = std::get(fx.functions); return lua_call_wrapper{}.call(L, f); } }; static int call(lua_State* L, F& fx) { return overload_match_arity(on_match(), L, lua_gettop(L), 1, fx); } }; template struct lua_call_wrapper, is_index, is_variable, checked, boost, clean_stack, C> { typedef factory_wrapper F; struct on_match { template int operator()(types, index_value, types, types, lua_State* L, int, int, F& fx) { auto& f = std::get(fx.functions); return lua_call_wrapper{}.call(L, f); } }; static int call(lua_State* L, F& fx) { return overload_match_arity(on_match(), L, lua_gettop(L) - boost, 1 + boost, fx); } }; template struct lua_call_wrapper, is_index, is_variable, checked, boost, clean_stack, C> { typedef std::conditional_t P; typedef meta::unqualified_t

U; typedef wrapper wrap; typedef lua_bind_traits traits_type; typedef meta::unqualified_t> object_type; template static int self_call(std::true_type, lua_State* L, F&& f) { // The type being void means we don't have any arguments, so it might be a free functions? typedef typename traits_type::free_args_list args_list; typedef typename wrap::returns_list returns_list; typedef typename wrap::caller caller; return stack::call_into_lua(returns_list(), args_list(), L, boost + (is_variable ? 3 : 2), caller(), f); } template static int self_call(std::false_type, lua_State* L, F&& f) { typedef meta::pop_front_type_t args_list; typedef T Ta; typedef std::remove_pointer_t Oa; #if defined(SOL_SAFE_USERTYPE) && SOL_SAFE_USERTYPE auto maybeo = stack::check_get(L, 1); if (!maybeo || maybeo.value() == nullptr) { if (is_variable) { return luaL_error(L, "sol: 'self' argument is lua_nil (bad '.' access?)"); } return luaL_error(L, "sol: 'self' argument is lua_nil (pass 'self' as first argument)"); } Oa* o = static_cast(maybeo.value()); #else Oa* o = static_cast(stack::get>(L, 1)); #endif // Safety typedef typename wrap::returns_list returns_list; typedef typename wrap::caller caller; return stack::call_into_lua(returns_list(), args_list(), L, boost + (is_variable ? 3 : 2), caller(), f, detail::implicit_wrapper(*o)); } template static int defer_call(std::false_type, lua_State* L, F&& f, Args&&... args) { return self_call(meta::any, meta::boolean>::value != type::userdata>>(), L, pick(meta::boolean(), f), std::forward(args)...); } template static int defer_call(std::true_type, lua_State* L, F&& f, Args&&... args) { auto& p = pick(meta::boolean(), std::forward(f)); return lua_call_wrapper, is_index, is_variable, checked, boost, clean_stack>{}.call(L, p, std::forward(args)...); } template static int call(lua_State* L, F&& f, Args&&... args) { typedef meta::any< std::is_void, std::is_same, meta::is_specialization_of, meta::is_specialization_of, meta::is_specialization_of, std::is_member_pointer> is_specialized; return defer_call(is_specialized(), L, std::forward(f), std::forward(args)...); } }; template struct lua_call_wrapper, is_index, is_variable, checked, boost, clean_stack, C> { typedef protect_t F; template static int call(lua_State* L, F& fx, Args&&... args) { return lua_call_wrapper{}.call(L, fx.value, std::forward(args)...); } }; template struct lua_call_wrapper, is_index, is_variable, checked, boost, clean_stack, C> { typedef filter_wrapper P; template static int call(std::index_sequence, lua_State* L, P& fx) { int pushed = lua_call_wrapper{}.call(L, fx.value); (void)detail::swallow{ int(), (filter_detail::handle_filter(std::get(fx.filters), L, pushed), int())... }; return pushed; } static int call(lua_State* L, P& fx) { typedef typename P::indices indices; return call(indices(), L, fx); } }; template struct lua_call_wrapper, is_index, is_variable, checked, boost, clean_stack, C> { template static int call(lua_State* L, F&& f) { return lua_call_wrapper, is_index, is_variable, checked, boost, clean_stack>{}.call(L, std::get<0>(f.arguments)); } }; template inline int call_wrapped(lua_State* L, Fx&& fx, Args&&... args) { return lua_call_wrapper, is_index, is_variable, checked, boost, clean_stack>{}.call(L, std::forward(fx), std::forward(args)...); } template inline int call_user(lua_State* L) { auto& fx = stack::unqualified_get>(L, upvalue_index(start)); return call_wrapped(L, fx); } template struct is_var_bind : std::false_type {}; template struct is_var_bind::value>> : std::true_type {}; template <> struct is_var_bind : std::true_type {}; template struct is_var_bind> : std::true_type {}; template struct is_var_bind> : std::true_type {}; template struct is_var_bind> : is_var_bind> {}; template struct is_var_bind> : is_var_bind> {}; } // namespace call_detail template struct is_variable_binding : call_detail::is_var_bind> {}; template struct is_function_binding : meta::neg> {}; } // namespace sol #endif // SOL_CALL_HPP