// The MIT License (MIT) // Copyright (c) 2013-2016 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 "property.hpp" #include "stack.hpp" namespace sol { 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; 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 (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 (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 (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)...); } } // 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(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()[0], 1) : call_syntax::dot; argcount -= static_cast(syntax); T** pointerpointer = reinterpret_cast(lua_newuserdata(L, sizeof(T*) + sizeof(T))); T*& referencepointer = *pointerpointer; T* obj = reinterpret_cast(pointerpointer + 1); referencepointer = obj; reference userdataref(L, -1); userdataref.pop(); construct_match(constructor_match(obj), L, argcount, 1 + static_cast(syntax)); userdataref.push(); luaL_getmetatable(L, &meta[0]); if (type_of(L, -1) == type::lua_nil) { lua_pop(L, 1); return luaL_error(L, "sol: unable to get usertype metatable"); } lua_setmetatable(L, -2); return 1; } template struct agnostic_lua_call_wrapper { template static int call(lua_State* L, Fx&& f, Args&&... args) { typedef wrapper> wrap; 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 struct agnostic_lua_call_wrapper, true, is_variable, checked, boost, C> { template static int call(lua_State* L, F&& f) { return stack::push_reference(L, detail::unwrap(f.value)); } }; template struct agnostic_lua_call_wrapper, false, is_variable, checked, boost, C> { template static int call_assign(std::true_type, lua_State* L, V&& f) { detail::unwrap(f.value) = stack::get>(L, boost + (is_variable ? 3 : 1)); 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_r_CFunction f) { return f(L); } }; template struct agnostic_lua_call_wrapper { static int call(lua_State* L, lua_CFunction f) { return f(L); } }; 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 luaL_error(L, "sol: cannot call this constructor (tagged as non-constructible)"); } }; template struct agnostic_lua_call_wrapper, is_index, is_variable, checked, boost, C> { static int call(lua_State*, const bases&) { // Uh. How did you even call this, lul return 0; } }; 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; #ifdef SOL_SAFE_USERTYPE auto maybeo = stack::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::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; #ifdef 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_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 static int call(lua_State* L, V&& f, object_type& o) { return call_const(std::is_const(), L, 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&& f, object_type& o) { typedef typename wrap::returns_list returns_list; typedef typename wrap::caller caller; 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; #ifdef 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, is_index, is_variable, checked, boost, C> { typedef constructor_list F; static int call(lua_State* L, F&) { const auto& metakey = usertype_traits::metatable(); int argcount = lua_gettop(L); call_syntax syntax = argcount > 0 ? stack::get_call_syntax(L, &usertype_traits::user_metatable()[0], 1) : call_syntax::dot; argcount -= static_cast(syntax); T** pointerpointer = reinterpret_cast(lua_newuserdata(L, sizeof(T*) + sizeof(T))); reference userdataref(L, -1); T*& referencepointer = *pointerpointer; T* obj = reinterpret_cast(pointerpointer + 1); referencepointer = obj; construct_match(constructor_match(obj), L, argcount, boost + 1 + static_cast(syntax)); userdataref.push(); luaL_getmetatable(L, &metakey[0]); if (type_of(L, -1) == type::lua_nil) { lua_pop(L, 1); return luaL_error(L, "sol: unable to get usertype metatable"); } lua_setmetatable(L, -2); return 1; } }; template struct lua_call_wrapper, is_index, is_variable, checked, boost, 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& metakey = usertype_traits::metatable(); T** pointerpointer = reinterpret_cast(lua_newuserdata(L, sizeof(T*) + sizeof(T))); reference userdataref(L, -1); T*& referencepointer = *pointerpointer; T* obj = reinterpret_cast(pointerpointer + 1); referencepointer = obj; auto& func = std::get(f.functions); stack::call_into_lua(r, a, L, boost + start, func, detail::implicit_wrapper(obj)); userdataref.push(); luaL_getmetatable(L, &metakey[0]); if (type_of(L, -1) == type::lua_nil) { lua_pop(L, 1); std::string err = "sol: unable to get usertype metatable for "; err += usertype_traits::name(); return luaL_error(L, err.c_str()); } lua_setmetatable(L, -2); return 1; } }; static int call(lua_State* L, F& f) { call_syntax syntax = stack::get_call_syntax(L, &usertype_traits::user_metatable()[0], 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, std::enable_if_t::value>> { typedef destructor_wrapper F; static int call(lua_State* L, const F&) { return detail::usertype_alloc_destroy(L); } }; template struct lua_call_wrapper, is_index, is_variable, checked, boost, std::enable_if_t::value>> { typedef destructor_wrapper F; static int call(lua_State* L, const F& f) { T& obj = stack::get(L); f.fx(detail::implicit_wrapper(obj)); return 0; } }; template struct lua_call_wrapper, is_index, is_variable, checked, boost, 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, 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), 1, fx); } }; template struct lua_call_wrapper, is_index, is_variable, checked, boost, C> { typedef std::conditional_t P; typedef meta::unqualified_t

U; typedef lua_bind_traits traits_type; template static int self_call(lua_State* L, F&& f) { typedef wrapper wrap; typedef meta::unqualified_t> object_type; typedef meta::pop_front_type_t args_list; typedef T Ta; #ifdef 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()); #else object_type* 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, *o); } template static int defer_call(std::false_type, lua_State* L, F&& f, Args&&... args) { return self_call(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>{}.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, 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, C> { template static int call(lua_State* L, F&& f) { return lua_call_wrapper, is_index, is_variable, stack::stack_detail::default_check_arguments, boost>{}.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, stack::stack_detail::default_check_arguments, boost>{}.call(L, std::forward(fx), std::forward(args)...); } template inline int call_user(lua_State* L) { auto& fx = stack::get>(L, upvalue_index(1)); 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 {}; } // call_detail template struct is_variable_binding : call_detail::is_var_bind> {}; template struct is_function_binding : meta::neg> {}; } // sol #endif // SOL_CALL_HPP