From bb6d539c8650ef1d1248f59b7cb8bfdd42ea9f01 Mon Sep 17 00:00:00 2001 From: kodenamekrak <106346717+kodenamekrak@users.noreply.github.com> Date: Fri, 18 Oct 2024 14:05:20 +0100 Subject: [PATCH] Add template parameter to use invoker without boxing (#15) --- shared/delegate.hpp | 92 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 74 insertions(+), 18 deletions(-) diff --git a/shared/delegate.hpp b/shared/delegate.hpp index 71426e3..8d75d0e 100644 --- a/shared/delegate.hpp +++ b/shared/delegate.hpp @@ -113,6 +113,7 @@ struct DelegateWrapperStatic : Il2CppObject { // The invoke method that wraps the delegate call static RI Invoke(DelegateWrapperStatic* instance, make_boxed_t... args); + static RI InvokeUnboxed(DelegateWrapperStatic* instance, TArgsI... args); private: struct ___MethodRegistrator_Invoke : ::custom_types::MethodRegistrator { constexpr const char* name() const override { @@ -228,6 +229,27 @@ RI __attribute__((noinline)) DelegateWrapperStatic::Invoke(Delega } ) } + +/// @brief The unboxed invoker function for a delegate with a wrapped type. +/// @tparam R The return type of the function. +/// @tparam TArgs The argument types of the function. +/// @param instance The wrapped instance of this context function. +/// @param args The arguments to pass to this function. +/// @return The return from the wrapped function. +template +RI __attribute__((noinline)) DelegateWrapperStatic::InvokeUnboxed(DelegateWrapperStatic* instance, TArgsI... args) { + IL2CPP_CATCH_HANDLER( + if (!instance || !instance->wrappedFunc) { + custom_types::logger.critical("Attempting to invoke delegate on a null or destroyed instance: {}, class: {} ({})", fmt::ptr(instance), fmt::ptr(___TypeRegistration::klass_ptr), ___TypeRegistration::get()->name()); + } + if constexpr (std::is_same_v) { + instance->wrappedFunc(args...); + } else { + return instance->wrappedFunc(args...); + } + ) +} + template void DelegateWrapperStatic::dtor() { custom_types::logger.debug("Destroying delegate: {}, class: {} ({})", fmt::ptr(this), fmt::ptr(___TypeRegistration::klass_ptr), ___TypeRegistration::get()->name()); @@ -324,6 +346,7 @@ struct DelegateWrapperInstance : Il2CppObject { // The invoke method that wraps the delegate call static RI Invoke(DelegateWrapperInstance* instance, make_boxed_t... args); + static RI InvokeUnboxed(DelegateWrapperInstance* instance, TArgsI... args); private: struct ___MethodRegistrator_Invoke : ::custom_types::MethodRegistrator { constexpr const char* name() const override { @@ -465,6 +488,27 @@ RI __attribute__((noinline)) DelegateWrapperInstance::Invoke( } ) } + +/// @brief The unboxed invoker function for a delegate with a wrapped type. +/// @tparam R The return type of the function. +/// @tparam TArgs The argument types of the function. +/// @param instance The wrapped instance of this context function. +/// @param args The arguments to pass to this function. +/// @return The return from the wrapped function. +template +RI __attribute__((noinline)) DelegateWrapperInstance::InvokeUnboxed(DelegateWrapperInstance* instance, TArgsI... args) { + IL2CPP_CATCH_HANDLER( + if (!instance || !instance->wrappedFunc) { + custom_types::logger.critical("Attempting to invoke instance delegate that is null or has been destroyed! {}, class: {} ({})", fmt::ptr(instance), fmt::ptr(___TypeRegistration::klass_ptr), ___TypeRegistration::get()->name()); + } + if constexpr (std::is_same_v) { + instance->wrappedFunc(instance->obj, args...); + } else { + return instance->wrappedFunc(instance->obj, args...); + } + ) +} + template void DelegateWrapperInstance::dtor() { custom_types::logger.debug("Destroying delegate: {}, class: {} ({})", fmt::ptr(this), fmt::ptr(___TypeRegistration::klass_ptr), ___TypeRegistration::get()->name()); @@ -473,7 +517,7 @@ void DelegateWrapperInstance::dtor() { CUSTOM_TYPES_EXPORT void log_delegate(Il2CppDelegate* d); -template +template T MakeDelegate(const Il2CppClass* delegateClass, DelegateWrapperStatic* inst) { // TODO: We could still make this a native method info, but for now it's much much easier to just wrap it // This will make delegates a little bit slower than just a pure native call, since it'll have to box the args @@ -484,7 +528,7 @@ T MakeDelegate(const Il2CppClass* delegateClass, DelegateWrapperStatic::___dtor_MethodRegistrator.get())); - auto* method = DelegateWrapperStatic::___Invoke_MethodRegistrator.get(); + auto* method = il2cpp_utils::FindMethodUnsafe(delegateClass, "Invoke", -1); auto* delegate = reinterpret_cast(il2cpp_functions::object_new(delegateClass)); // find the ctor method that takes object, intptr @@ -499,10 +543,16 @@ T MakeDelegate(const Il2CppClass* delegateClass, DelegateWrapperStatic(delegate, ctor_minfo, inst, (void*)&method)); + CRASH_UNLESS(il2cpp_utils::RunMethodOpt(delegate, ctor_minfo, delegate, (void*)&method)); + + if constexpr (boxArgs) { + delegate->method_ptr = (void(*)())DelegateWrapperStatic::Invoke; + delegate->invoke_impl = (void(*)())DelegateWrapperStatic::Invoke; + } else { + delegate->method_ptr = (void(*)())DelegateWrapperStatic::InvokeUnboxed; + delegate->invoke_impl = (void(*)())DelegateWrapperStatic::InvokeUnboxed; + } - delegate->method_ptr = (void(*)())DelegateWrapperStatic::Invoke; - delegate->invoke_impl = (void(*)())DelegateWrapperStatic::Invoke; delegate->invoke_impl_this = (Il2CppObject*)inst; custom_types::logger.debug("Created delegate: {} ({}), for instance: {} with MethodInfo*: {}", fmt::ptr(delegate), fmt::ptr(delegateClass), fmt::ptr(inst), fmt::ptr(method)); @@ -510,10 +560,10 @@ T MakeDelegate(const Il2CppClass* delegateClass, DelegateWrapperStatic(delegate); } -template +template T MakeDelegate(const Il2CppClass* delegateClass, DelegateWrapperInstance* inst) { custom_types::logger.debug("Delegate instance dtor registrator: {}", fmt::ptr(DelegateWrapperInstance::___dtor_MethodRegistrator.get())); - auto* method = DelegateWrapperInstance::___Invoke_MethodRegistrator.get(); + auto* method = il2cpp_utils::FindMethodUnsafe(delegateClass, "Invoke", -1); auto* delegate = reinterpret_cast(il2cpp_functions::object_new(delegateClass)); // find the ctor method that takes object, intptr @@ -528,10 +578,16 @@ T MakeDelegate(const Il2CppClass* delegateClass, DelegateWrapperInstance(delegate, ctor_minfo, inst, (void*)&method)); + CRASH_UNLESS(il2cpp_utils::RunMethodOpt(delegate, ctor_minfo, delegate, (void*)&method)); + + if constexpr (boxArgs) { + delegate->method_ptr = (void(*)())DelegateWrapperInstance::Invoke; + delegate->invoke_impl = (void(*)())DelegateWrapperInstance::Invoke; + } else { + delegate->method_ptr = (void(*)())DelegateWrapperInstance::InvokeUnboxed; + delegate->invoke_impl = (void(*)())DelegateWrapperInstance::InvokeUnboxed; + } - delegate->method_ptr = (void(*)())DelegateWrapperInstance::Invoke; - delegate->invoke_impl = (void(*)())DelegateWrapperInstance::Invoke; delegate->invoke_impl_this = (Il2CppObject*)inst; custom_types::logger.debug("Created instance delegate: {} ({}), for instance: {} with MethodInfo*: {}", fmt::ptr(delegate), fmt::ptr(delegateClass), fmt::ptr(inst), fmt::ptr(method)); @@ -548,14 +604,14 @@ T MakeDelegate(const Il2CppClass* delegateClass, DelegateWrapperInstance +template T MakeDelegate(const Il2CppClass* delegateClass, std::function const& f) { il2cpp_functions::Init(); // NOTE: This static field MUST be used in order for it to be instantiated within the generic, thus, it is important to NOT remove this log custom_types::logger.debug("Type registration for delegate being created (forcibly instantiated): {}", DelegateWrapperStatic::__registration_instance_DelegateWrapperStatic.name()); auto* wrapperInstance = reinterpret_cast*>(il2cpp_functions::object_new(DelegateWrapperStatic::___TypeRegistration::klass_ptr)); wrapperInstance->wrappedFunc = f; - return custom_types::MakeDelegate(delegateClass, wrapperInstance); + return custom_types::MakeDelegate(delegateClass, wrapperInstance); } /// @brief Makes a delegate wrapping a context function (such as a context lambda). @@ -566,12 +622,12 @@ T MakeDelegate(const Il2CppClass* delegateClass, std::function cons /// @param instance The (move constructible) instance reference to provide to the delegate. This instance is moved and will no longer be valid. /// @param f The function to invoke with the delegate. /// @return The created delegate. -template +template T MakeDelegate(std::function const& f) { - return MakeDelegate(classof(T), f); + return MakeDelegate(classof(T), f); } -template +template // TODO: Requires that I has a classof T MakeDelegate(const Il2CppClass* delegateClass, I obj, std::function const& f) { il2cpp_functions::Init(); @@ -579,11 +635,11 @@ T MakeDelegate(const Il2CppClass* delegateClass, I obj, std::function*>(il2cpp_functions::object_new(DelegateWrapperInstance::___TypeRegistration::klass_ptr)); wrapperInstance->wrappedFunc = f; wrapperInstance->obj = obj; - return custom_types::MakeDelegate(delegateClass, wrapperInstance); + return custom_types::MakeDelegate(delegateClass, wrapperInstance); } -template +template T MakeDelegate(I obj, std::function const& f) { - return MakeDelegate(classof(T), obj, f); + return MakeDelegate(classof(T), obj, f); } }