diff --git a/libraries/libstratosphere/include/stratosphere/tipc/impl/tipc_autogen_interface_macros.hpp b/libraries/libstratosphere/include/stratosphere/tipc/impl/tipc_autogen_interface_macros.hpp new file mode 100644 index 000000000..ba6008782 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/tipc/impl/tipc_autogen_interface_macros.hpp @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +namespace ams::tipc::impl { + + template + concept HasDefaultServiceCommandProcessor = requires (T &t, const svc::ipc::MessageBuffer &message_buffer) { + { t.ProcessDefaultServiceCommand(message_buffer) } -> std::same_as; + }; + + struct SyncFunctionTraits { + public: + template + static std::tuple GetArgsImpl(R(C::*)(A...)); + }; + + template + using SyncFunctionArgsType = decltype(SyncFunctionTraits::GetArgsImpl(F)); + + #define AMS_TIPC_IMPL_DEFINE_SYNC_METHOD_HOLDER(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \ + struct NAME##ArgumentsFunctionHolder { RETURN f ARGS; }; + + #define AMS_TIPC_IMPL_EXTRACT_SYNC_METHOD_ARGUMENTS(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \ + using NAME##ArgumentsType = ::ams::tipc::impl::SyncFunctionArgsType<&NAME##ArgumentsFunctionHolder::f>; + + #define AMS_TIPC_IMPL_DEFINE_INTERFACE(BASECLASS, CLASSNAME, CMD_MACRO) \ + class CLASSNAME : public BASECLASS { \ + private: \ + CMD_MACRO(CLASSNAME, AMS_TIPC_IMPL_DEFINE_SYNC_METHOD_HOLDER) \ + public: \ + CMD_MACRO(CLASSNAME, AMS_TIPC_IMPL_EXTRACT_SYNC_METHOD_ARGUMENTS) \ + }; + + #define AMS_TIPC_IMPL_DEFINE_CONCEPT_HELPERS(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \ + template \ + concept Is##CLASSNAME##__##NAME##Impl = requires (T &t, Args &&... args) { \ + { t.NAME(std::forward(args)...) } -> std::same_as; \ + }; \ + \ + template \ + struct Is##CLASSNAME##__##NAME##Holder : std::false_type{}; \ + \ + template requires std::same_as, CLASSNAME::NAME##ArgumentsType> \ + struct Is##CLASSNAME##__##NAME##Holder> : std::bool_constant>{}; \ + \ + template \ + static constexpr inline bool Is##CLASSNAME##__##NAME = Is##CLASSNAME##__##NAME##Holder::value; + + #define AMS_TIPC_IMPL_CHECK_CONCEPT_HELPER(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \ + Is##CLASSNAME##__##NAME && + + #define AMS_TIPC_IMPL_DEFINE_CONCEPT(CLASSNAME, CMD_MACRO) \ + CMD_MACRO(CLASSNAME, AMS_TIPC_IMPL_DEFINE_CONCEPT_HELPERS) \ + \ + template \ + concept Is##CLASSNAME = CMD_MACRO(CLASSNAME, AMS_TIPC_IMPL_CHECK_CONCEPT_HELPER) true; + + #define AMS_TIPC_IMPL_PROCESS_METHOD_REQUEST(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \ + if (constexpr u16 TipcCommandId = CMD_ID + 0x10; tag == TipcCommandId) { \ + return this->ProcessMethodById(impl, message_buffer, fw_ver); \ + } + + #define AMS_TIPC_IMPL_PROCESS_METHOD_REQUEST_BY_ID(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \ + if constexpr (constexpr u16 TipcCommandId = CMD_ID + 0x10; CommandId == TipcCommandId) { \ + constexpr bool AlwaysValid = VERSION_MIN == hos::Version_Min && VERSION_MAX == hos::Version_Max; \ + if (AlwaysValid || (VERSION_MIN <= fw_ver && fw_ver <= VERSION_MAX)) { \ + return ::ams::tipc::impl::InvokeServiceCommandImpl(impl, message_buffer); \ + } \ + } + + #define AMS_TIPC_DEFINE_INTERFACE_WITH_DEFAULT_BASE(NAMESPACE, INTERFACE, BASE, CMD_MACRO) \ + namespace NAMESPACE { \ + \ + AMS_TIPC_IMPL_DEFINE_INTERFACE(BASE, INTERFACE, CMD_MACRO) \ + AMS_TIPC_IMPL_DEFINE_CONCEPT(INTERFACE, CMD_MACRO) \ + \ + } \ + \ + namespace ams::tipc::impl { \ + \ + template \ + class ImplTemplateBaseT<::NAMESPACE::INTERFACE, Base, ImplHolder, ImplGetter, Root> : public Base, public ImplHolder { \ + public: \ + template \ + constexpr explicit ImplTemplateBaseT(Args &&...args) : ImplHolder(std::forward(args)...) { /* ... */ } \ + private: \ + template \ + ALWAYS_INLINE Result ProcessDefaultMethod(ImplType *impl, const svc::ipc::MessageBuffer &message_buffer) const { \ + /* Handle a default command. */ \ + if constexpr (HasDefaultServiceCommandProcessor) { \ + return impl->ProcessDefaultServiceCommand(message_buffer); \ + } else { \ + return tipc::ResultInvalidMethod(); \ + } \ + } \ + \ + template \ + ALWAYS_INLINE Result ProcessMethodById(ImplType *impl, const svc::ipc::MessageBuffer &message_buffer, hos::Version fw_ver) const { \ + CMD_MACRO(ImplType, AMS_TIPC_IMPL_PROCESS_METHOD_REQUEST_BY_ID) \ + \ + return this->ProcessDefaultMethod(impl, message_buffer); \ + } \ + public: \ + virtual Result ProcessRequest() override { \ + /* Get the implementation object. */ \ + auto * const impl = ImplGetter::GetImplPointer(static_cast(this)); \ + \ + /* Get the implementation type. */ \ + using ImplType = typename std::remove_reference::type; \ + static_assert(::NAMESPACE::Is##INTERFACE); \ + \ + /* Get accessor to the message buffer. */ \ + svc::ipc::MessageBuffer message_buffer(svc::ipc::GetMessageBuffer()); \ + \ + /* Get decision variables. */ \ + const auto tag = svc::ipc::MessageBuffer::MessageHeader(message_buffer).GetTag(); \ + const auto fw_ver = hos::GetVersion(); \ + \ + /* Process against the command ids. */ \ + if (false) { } \ + CMD_MACRO(ImplType, AMS_TIPC_IMPL_PROCESS_METHOD_REQUEST) \ + else { \ + return this->ProcessDefaultMethod(impl, message_buffer); \ + } \ + } \ + }; \ + \ + } + + + #define AMS_TIPC_DEFINE_INTERFACE(NAMESPACE, INTERFACE, CMD_MACRO) \ + AMS_TIPC_DEFINE_INTERFACE_WITH_DEFAULT_BASE(NAMESPACE, INTERFACE, ::ams::tipc::ServiceObjectBase, CMD_MACRO) + + #define AMS_TIPC_METHOD_INFO_7(CLASSNAME, HANDLER, CMD_ID, RETURN, NAME, ARGS, ARGNAMES) \ + HANDLER(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, hos::Version_Min, hos::Version_Max) + + #define AMS_TIPC_METHOD_INFO_8(CLASSNAME, HANDLER, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN) \ + HANDLER(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, hos::Version_Max) + + #define AMS_TIPC_METHOD_INFO_9(CLASSNAME, HANDLER, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \ + HANDLER(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) + + #define AMS_TIPC_METHOD_INFO_X(_, _0, _1, _2, _3, _4, _5, _6, _7, _8, FUNC, ...) FUNC + + #define AMS_TIPC_METHOD_INFO(...) \ + AMS_TIPC_METHOD_INFO_X(, ## __VA_ARGS__, AMS_TIPC_METHOD_INFO_9(__VA_ARGS__), AMS_TIPC_METHOD_INFO_8(__VA_ARGS__), AMS_TIPC_METHOD_INFO_7(__VA_ARGS__)) + +} diff --git a/libraries/libstratosphere/include/stratosphere/tipc/impl/tipc_impl_command_serialization.hpp b/libraries/libstratosphere/include/stratosphere/tipc/impl/tipc_impl_command_serialization.hpp index 09625e7a5..d39356b53 100644 --- a/libraries/libstratosphere/include/stratosphere/tipc/impl/tipc_impl_command_serialization.hpp +++ b/libraries/libstratosphere/include/stratosphere/tipc/impl/tipc_impl_command_serialization.hpp @@ -237,8 +237,11 @@ namespace ams::tipc::impl { size_t out_copy_handle_index; }; + template + struct CommandMetaInfo; + template - struct CommandMetaInfo { + struct CommandMetaInfo<_CommandId, std::tuple> { public: static constexpr u16 CommandId = _CommandId; @@ -587,10 +590,22 @@ namespace ams::tipc::impl { } }; - template + struct FunctionTraits { + public: + template + static std::tuple GetArgumentsImpl(R(C::*)(A...)); + + template + static R GetReturnImpl(R(C::*)(A...)); + }; + + template constexpr ALWAYS_INLINE Result InvokeServiceCommandImpl(ClassType *object, const svc::ipc::MessageBuffer &message_buffer) { - using CommandMeta = CommandMetaInfo<_CommmandId, Arguments...>; - using Processor = CommandProcessor; + using Return = decltype(FunctionTraits::GetReturnImpl(ServiceCommandImpl)); + using TrueArgumentsTuple = decltype(FunctionTraits::GetArgumentsImpl(ServiceCommandImpl)); + + using CommandMeta = CommandMetaInfo<_CommmandId, TrueArgumentsTuple>; + using Processor = CommandProcessor; /* TODO: ValidateClassType is valid? */ constexpr bool ReturnsResult = std::is_same::value; @@ -606,8 +621,6 @@ namespace ams::tipc::impl { const Result command_result = [&](std::index_sequence) ALWAYS_INLINE_LAMBDA { auto args_tuple = Processor::DeserializeArguments(message_buffer, out_raw_holder, out_handles_holder); - using TrueArgumentsTuple = std::tuple; - if constexpr (ReturnsResult) { return (object->*ServiceCommandImpl)(std::forward::type>(std::get(args_tuple))...); } else { diff --git a/libraries/libstratosphere/include/stratosphere/tipc/tipc_service_object.hpp b/libraries/libstratosphere/include/stratosphere/tipc/tipc_service_object.hpp index 2549a1a83..fe6307269 100644 --- a/libraries/libstratosphere/include/stratosphere/tipc/tipc_service_object.hpp +++ b/libraries/libstratosphere/include/stratosphere/tipc/tipc_service_object.hpp @@ -24,7 +24,8 @@ namespace ams::tipc { template class EmplacedImplHolderBaseGetter { - using Type = Impl; + public: + using Type = Impl; }; template diff --git a/libraries/libstratosphere/source/_test/test_tipc_serializer.cpp b/libraries/libstratosphere/source/_test/test_tipc_serializer.cpp index 07ac29545..fa04b9013 100644 --- a/libraries/libstratosphere/source/_test/test_tipc_serializer.cpp +++ b/libraries/libstratosphere/source/_test/test_tipc_serializer.cpp @@ -14,6 +14,16 @@ * along with this program. If not, see . */ #include +#include + +#define AMS_TEST_I_USER_INTERFACE_INTERFACE_INFO(C, H) \ + AMS_TIPC_METHOD_INFO(C, H, 0, Result, RegisterClient, (const tipc::ClientProcessId &client_process_id), (client_process_id)) \ + AMS_TIPC_METHOD_INFO(C, H, 1, Result, GetServiceHandle, (tipc::OutMoveHandle out_h, sm::ServiceName service), (out_h, service)) \ + AMS_TIPC_METHOD_INFO(C, H, 2, Result, RegisterService, (tipc::OutMoveHandle out_h, sm::ServiceName service, u32 max_sessions, bool is_light), (out_h, service, max_sessions, is_light)) \ + AMS_TIPC_METHOD_INFO(C, H, 3, Result, UnregisterService, (sm::ServiceName service), (service)) + +AMS_TIPC_DEFINE_INTERFACE(ams::_test::impl, IUserInterface, AMS_TEST_I_USER_INTERFACE_INTERFACE_INFO) + namespace ams::_test { @@ -24,39 +34,31 @@ namespace ams::_test { Result RegisterService(tipc::OutMoveHandle out_h, sm::ServiceName service, u32 max_sessions, bool is_light); Result UnregisterService(sm::ServiceName service); }; + static_assert(impl::IsIUserInterface); - Result TestRegisterClient(UserInterfaceFacade *facade, const svc::ipc::MessageBuffer &message_buffer) { - return tipc::impl::InvokeServiceCommandImpl<&UserInterfaceFacade::RegisterClient, 16, Result, UserInterfaceFacade, const tipc::ClientProcessId &>(facade, message_buffer); - } - - Result TestGetServiceHandle(UserInterfaceFacade *facade, const svc::ipc::MessageBuffer &message_buffer) { - return tipc::impl::InvokeServiceCommandImpl<&UserInterfaceFacade::GetServiceHandle, 17, Result, UserInterfaceFacade, tipc::OutMoveHandle, sm::ServiceName>(facade, message_buffer); - } - - Result TestRegisterService(UserInterfaceFacade *facade, const svc::ipc::MessageBuffer &message_buffer) { - return tipc::impl::InvokeServiceCommandImpl<&UserInterfaceFacade::RegisterService, 18, Result, UserInterfaceFacade, tipc::OutMoveHandle, sm::ServiceName, u32, bool>(facade, message_buffer); - } - - Result TestUnregisterService(UserInterfaceFacade *facade, const svc::ipc::MessageBuffer &message_buffer) { - return tipc::impl::InvokeServiceCommandImpl<&UserInterfaceFacade::UnregisterService, 19, Result, UserInterfaceFacade, sm::ServiceName>(facade, message_buffer); - } Result TestManualDispatch(UserInterfaceFacade *facade) { svc::ipc::MessageBuffer message_buffer(svc::ipc::GetMessageBuffer()); switch (svc::ipc::MessageBuffer::MessageHeader(message_buffer).GetTag()) { case 16: - return tipc::impl::InvokeServiceCommandImpl<&UserInterfaceFacade::RegisterClient, 16, Result, UserInterfaceFacade, const tipc::ClientProcessId &>(facade, message_buffer); + return tipc::impl::InvokeServiceCommandImpl<16, &UserInterfaceFacade::RegisterClient, UserInterfaceFacade>(facade, message_buffer); case 17: - return tipc::impl::InvokeServiceCommandImpl<&UserInterfaceFacade::GetServiceHandle, 17, Result, UserInterfaceFacade, tipc::OutMoveHandle, sm::ServiceName>(facade, message_buffer); + return tipc::impl::InvokeServiceCommandImpl<17, &UserInterfaceFacade::GetServiceHandle, UserInterfaceFacade>(facade, message_buffer); case 18: - return tipc::impl::InvokeServiceCommandImpl<&UserInterfaceFacade::RegisterService, 18, Result, UserInterfaceFacade, tipc::OutMoveHandle, sm::ServiceName, u32, bool>(facade, message_buffer); + return tipc::impl::InvokeServiceCommandImpl<18, &UserInterfaceFacade::RegisterService, UserInterfaceFacade>(facade, message_buffer); case 19: - return tipc::impl::InvokeServiceCommandImpl<&UserInterfaceFacade::UnregisterService, 19, Result, UserInterfaceFacade, sm::ServiceName>(facade, message_buffer); + return tipc::impl::InvokeServiceCommandImpl<19, &UserInterfaceFacade::UnregisterService, UserInterfaceFacade>(facade, message_buffer); default: return tipc::ResultInvalidMethod(); } } + using UserInterfaceObject = ::ams::tipc::ServiceObject; + + Result TestAutomaticDispatch(UserInterfaceObject *object) { + return object->ProcessRequest(); + } + } \ No newline at end of file