|
| 1 | +using System; |
| 2 | +using System.Collections.Generic; |
| 3 | +using System.Runtime.CompilerServices; |
| 4 | +using System.Text; |
| 5 | +using Il2CppInterop.Generator.Contexts; |
| 6 | +using Il2CppInterop.Generator.Utils; |
| 7 | +using Mono.Cecil; |
| 8 | +using Mono.Cecil.Cil; |
| 9 | + |
| 10 | +namespace Il2CppInterop.Generator.Passes; |
| 11 | + |
| 12 | +internal class Pass61ImplementAwaiters |
| 13 | +{ |
| 14 | + public static void DoPass(RewriteGlobalContext context) |
| 15 | + { |
| 16 | + var corlib = context.GetAssemblyByName("mscorlib"); |
| 17 | + var actionUntyped = corlib.GetTypeByName("System.Action"); |
| 18 | + |
| 19 | + var actionConversionUntyped = actionUntyped.NewType.Methods.FirstOrDefault(m => m.Name == "op_Implicit") ?? throw new MissingMethodException("Untyped action conversion"); |
| 20 | + |
| 21 | + foreach (var assemblyContext in context.Assemblies) |
| 22 | + { |
| 23 | + // dont actually import the references until they're needed |
| 24 | + Lazy<TypeReference> actionUntypedRef = new(() => assemblyContext.NewAssembly.MainModule.ImportReference(actionUntyped.OriginalType)); |
| 25 | + Lazy<MethodReference> actionConversionUntypedRef = new(() => assemblyContext.NewAssembly.MainModule.ImportReference(actionConversionUntyped)); |
| 26 | + Lazy<TypeReference> notifyCompletionRef = new(() => assemblyContext.NewAssembly.MainModule.ImportReference(typeof(INotifyCompletion))); |
| 27 | + var voidRef = assemblyContext.Imports.Module.Void(); |
| 28 | + foreach (var typeContext in assemblyContext.Types) |
| 29 | + { |
| 30 | + var interfaceImplementation = typeContext.OriginalType.Interfaces.FirstOrDefault(InterfaceImplementation => InterfaceImplementation.InterfaceType.Name == nameof(INotifyCompletion)); |
| 31 | + if (interfaceImplementation is null) |
| 32 | + continue; |
| 33 | + |
| 34 | + var isGeneric = typeContext.OriginalType.ContainsGenericParameter; |
| 35 | + |
| 36 | + var awaiterType = typeContext.OriginalType; |
| 37 | + |
| 38 | + var originalOnComplete = typeContext.TryGetMethodByName(nameof(INotifyCompletion.OnCompleted)) ?? throw new MissingMethodException("Original OnComplete"); |
| 39 | + |
| 40 | + var onCompletedAttr = MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot; |
| 41 | + var onComplete = new MethodDefinition(nameof(INotifyCompletion.OnCompleted), onCompletedAttr, voidRef); |
| 42 | + typeContext.NewType.Interfaces.Add(new(notifyCompletionRef.Value)); |
| 43 | + typeContext.NewType.Methods.Add(onComplete); |
| 44 | + |
| 45 | + onComplete.Parameters.Add(new ParameterDefinition("continuation", ParameterAttributes.None, actionUntypedRef.Value)); |
| 46 | + |
| 47 | + var onCompleteIl = onComplete.Body.GetILProcessor(); |
| 48 | + |
| 49 | + onCompleteIl.Emit(OpCodes.Nop); |
| 50 | + onCompleteIl.Emit(OpCodes.Ldarg_0); |
| 51 | + onCompleteIl.Emit(OpCodes.Ldarg_1); // ldarg1 bc not static, so ldarg0 is this & ldarg1 is the parameter |
| 52 | + onCompleteIl.Emit(OpCodes.Call, actionConversionUntypedRef.Value); |
| 53 | + onCompleteIl.Emit(OpCodes.Call, originalOnComplete.NewMethod); |
| 54 | + onCompleteIl.Emit(OpCodes.Nop); |
| 55 | + onCompleteIl.Emit(OpCodes.Ret); |
| 56 | + } |
| 57 | + } |
| 58 | + } |
| 59 | +} |
0 commit comments