public static TIl2Cpp ConvertDelegate <TIl2Cpp>(Delegate @delegate) where TIl2Cpp : Il2CppObjectBase
        {
            if (@delegate == null)
            {
                return(null);
            }

            if (!typeof(Il2CppSystem.Delegate).IsAssignableFrom(typeof(TIl2Cpp)))
            {
                throw new ArgumentException($"{typeof(TIl2Cpp)} is not a delegate");
            }

            var managedInvokeMethod = @delegate.GetType().GetMethod("Invoke") !;
            var parameterInfos      = managedInvokeMethod.GetParameters();

            foreach (var parameterInfo in parameterInfos)
            {
                var parameterType = parameterInfo.ParameterType;
                if (parameterType.IsGenericParameter)
                {
                    throw new ArgumentException($"Delegate has unsubstituted generic parameter ({parameterType}) which is not supported");
                }

                if (parameterType.BaseType == typeof(ValueType))
                {
                    throw new ArgumentException($"Delegate has parameter of type {parameterType} (non-blittable struct) which is not supported");
                }
            }

            var classTypePtr = Il2CppClassPointerStore <TIl2Cpp> .NativeClassPtr;

            if (classTypePtr == IntPtr.Zero)
            {
                throw new ArgumentException($"Type {typeof(TIl2Cpp)} has uninitialized class pointer");
            }

            if (Il2CppClassPointerStore <Il2CppToMonoDelegateReference> .NativeClassPtr == IntPtr.Zero)
            {
                ClassInjector.RegisterTypeInIl2Cpp <Il2CppToMonoDelegateReference>();
            }

            var il2CppDelegateType         = Il2CppSystem.Type.internal_from_handle(IL2CPP.il2cpp_class_get_type(classTypePtr));
            var nativeDelegateInvokeMethod = il2CppDelegateType.GetMethod("Invoke");

            var nativeParameters = nativeDelegateInvokeMethod.GetParameters();

            if (nativeParameters.Count != parameterInfos.Length)
            {
                throw new ArgumentException($"Managed delegate has {parameterInfos.Length} parameters, native has {nativeParameters.Count}, these should match");
            }

            for (var i = 0; i < nativeParameters.Count; i++)
            {
                var nativeType  = nativeParameters[i].ParameterType;
                var managedType = parameterInfos[i].ParameterType;

                if (nativeType.IsPrimitive || managedType.IsPrimitive)
                {
                    if (nativeType.FullName != managedType.FullName)
                    {
                        throw new ArgumentException($"Parameter type mismatch at parameter {i}: {nativeType.FullName} != {managedType.FullName}");
                    }

                    continue;
                }

                var classPointerFromManagedType = (IntPtr)typeof(Il2CppClassPointerStore <>).MakeGenericType(managedType)
                                                  .GetField(nameof(Il2CppClassPointerStore <int> .NativeClassPtr)).GetValue(null);

                var classPointerFromNativeType = IL2CPP.il2cpp_class_from_type(nativeType._impl.value);

                if (classPointerFromManagedType != classPointerFromNativeType)
                {
                    throw new ArgumentException($"Parameter type at {i} has mismatched native type pointers; types: {nativeType.FullName} != {managedType.FullName}");
                }

                if (nativeType.IsByRef || managedType.IsByRef)
                {
                    throw new ArgumentException($"Parameter at {i} is passed by reference, this is not supported");
                }
            }

            var signature         = new MethodSignature(nativeDelegateInvokeMethod, true);
            var managedTrampoline =
                GetOrCreateNativeToManagedTrampoline(signature, nativeDelegateInvokeMethod, managedInvokeMethod);

            var nativeDelegatePtr = IL2CPP.il2cpp_object_new(classTypePtr);
            var converted         = new Il2CppSystem.Delegate(nativeDelegatePtr);

            converted.method_ptr  = Marshal.GetFunctionPointerForDelegate(managedTrampoline);
            converted.method_info = nativeDelegateInvokeMethod; // todo: is this truly a good hack?
            var methodInfoSize    = Marshal.SizeOf <Il2CppMethodInfo>();
            var methodInfoPointer = Marshal.AllocHGlobal(methodInfoSize);
            var methodInfo        = (Il2CppMethodInfo *)methodInfoPointer;

            *methodInfo = default; // zero out everything
            converted.method = methodInfoPointer;

            methodInfo->methodPointer    = converted.method_ptr;
            methodInfo->invoker_method   = IntPtr.Zero;
            methodInfo->parameters_count = (byte)parameterInfos.Length;
            methodInfo->slot             = ushort.MaxValue;
            methodInfo->extra_flags      = MethodInfoExtraFlags.is_marshalled_from_native;

            converted.m_target = new Il2CppToMonoDelegateReference(@delegate, methodInfoPointer);

            return(converted.Cast <TIl2Cpp>());
        }
예제 #2
0
        public static TIl2Cpp ConvertDelegate <TIl2Cpp>(Delegate @delegate) where TIl2Cpp : Il2CppObjectBase
        {
            if (@delegate == null)
            {
                return(null);
            }

            if (!typeof(Il2CppSystem.Delegate).IsAssignableFrom(typeof(TIl2Cpp)))
            {
                throw new ArgumentException($"{typeof(TIl2Cpp)} is not a delegate");
            }

            var managedInvokeMethod = @delegate.GetType().GetMethod("Invoke") !;
            var parameterInfos      = managedInvokeMethod.GetParameters();

            foreach (var parameterInfo in parameterInfos)
            {
                var parameterType = parameterInfo.ParameterType;
                if (parameterType.IsGenericParameter)
                {
                    throw new ArgumentException($"Delegate has unsubstituted generic parameter ({parameterType}) which is not supported");
                }

                if (parameterType.BaseType == typeof(ValueType))
                {
                    throw new ArgumentException($"Delegate has parameter of type {parameterType} (non-blittable struct) which is not supported");
                }
            }

            var classTypePtr = Il2CppClassPointerStore <TIl2Cpp> .NativeClassPtr;

            if (classTypePtr == IntPtr.Zero)
            {
                throw new ArgumentException($"Type {typeof(TIl2Cpp)} has uninitialized class pointer");
            }

            if (Il2CppClassPointerStore <Il2CppToMonoDelegateReference> .NativeClassPtr == IntPtr.Zero)
            {
                ClassInjector.RegisterTypeInIl2Cpp <Il2CppToMonoDelegateReference>();
            }

            var il2CppDelegateType         = Il2CppSystem.Type.internal_from_handle(IL2CPP.il2cpp_class_get_type(classTypePtr));
            var nativeDelegateInvokeMethod = il2CppDelegateType.GetMethod("Invoke");

            var nativeParameters = nativeDelegateInvokeMethod.GetParameters();

            if (nativeParameters.Count != parameterInfos.Length)
            {
                throw new ArgumentException($"Managed delegate has {parameterInfos.Length} parameters, native has {nativeParameters.Count}, these should match");
            }

            for (var i = 0; i < nativeParameters.Count; i++)
            {
                var nativeType  = nativeParameters[i].ParameterType;
                var managedType = parameterInfos[i].ParameterType;

                if (nativeType.IsPrimitive || managedType.IsPrimitive)
                {
                    if (nativeType.FullName != managedType.FullName)
                    {
                        throw new ArgumentException($"Parameter type mismatch at parameter {i}: {nativeType.FullName} != {managedType.FullName}");
                    }

                    continue;
                }

                var classPointerFromManagedType = (IntPtr)typeof(Il2CppClassPointerStore <>).MakeGenericType(managedType)
                                                  .GetField(nameof(Il2CppClassPointerStore <int> .NativeClassPtr)).GetValue(null);

                var classPointerFromNativeType = IL2CPP.il2cpp_class_from_type(nativeType._impl.value);

                if (classPointerFromManagedType != classPointerFromNativeType)
                {
                    throw new ArgumentException($"Parameter type at {i} has mismatched native type pointers; types: {nativeType.FullName} != {managedType.FullName}");
                }

                if (nativeType.IsByRef || managedType.IsByRef)
                {
                    throw new ArgumentException($"Parameter at {i} is passed by reference, this is not supported");
                }
            }

            var signature         = new MethodSignature(nativeDelegateInvokeMethod, true);
            var managedTrampoline =
                GetOrCreateNativeToManagedTrampoline(signature, nativeDelegateInvokeMethod, managedInvokeMethod);

            var methodInfo = UnityVersionHandler.NewMethod();

            methodInfo.MethodPointer          = Marshal.GetFunctionPointerForDelegate(managedTrampoline);
            methodInfo.ParametersCount        = (byte)parameterInfos.Length;
            methodInfo.Slot                   = ushort.MaxValue;
            methodInfo.IsMarshalledFromNative = true;

            var delegateReference = new Il2CppToMonoDelegateReference(@delegate, methodInfo.Pointer);

            Il2CppSystem.Delegate converted;
            if (UnityVersionHandler.MustUseDelegateConstructor)
            {
                converted = ((TIl2Cpp)Activator.CreateInstance(typeof(TIl2Cpp), delegateReference.Cast <Object>(), methodInfo.Pointer)).Cast <Il2CppSystem.Delegate>();
            }
            else
            {
                var nativeDelegatePtr = IL2CPP.il2cpp_object_new(classTypePtr);
                converted = new Il2CppSystem.Delegate(nativeDelegatePtr);
            }

            converted.method_ptr  = methodInfo.MethodPointer;
            converted.method_info = nativeDelegateInvokeMethod; // todo: is this truly a good hack?
            converted.method      = methodInfo.Pointer;
            converted.m_target    = delegateReference;

            if (UnityVersionHandler.MustUseDelegateConstructor)
            { // U2021.2.0+ hack in case the constructor did the wrong thing anyway
                converted.invoke_impl = converted.method_ptr;
                converted.method_code = converted.m_target.Pointer;
            }

            return(converted.Cast <TIl2Cpp>());
        }