|
| 1 | +using System; |
| 2 | +using System.Linq; |
| 3 | +using AsmResolver.DotNet; |
| 4 | +using AsmResolver.DotNet.Code.Cil; |
| 5 | +using AsmResolver.DotNet.Signatures; |
| 6 | +using AsmResolver.DotNet.Signatures.Types; |
| 7 | +using AsmResolver.PE.DotNet.Cil; |
| 8 | +using AsmResolver.PE.DotNet.Metadata.Tables.Rows; |
| 9 | +using Cpp2IL.Core.Model.Contexts; |
| 10 | + |
| 11 | +namespace Cpp2IL.Core.Utils.AsmResolver; |
| 12 | + |
| 13 | +internal static class AsmResolverMethodFiller |
| 14 | +{ |
| 15 | + public static void FillManagedMethodBodies(AssemblyAnalysisContext asmContext) |
| 16 | + { |
| 17 | + var managedAssembly = asmContext.GetExtraData<AssemblyDefinition>("AsmResolverAssembly") ?? throw new("AsmResolver assembly not found in assembly analysis context for " + asmContext); |
| 18 | + |
| 19 | + var injectedRefHelperMethod = MakeRefHelper(managedAssembly.ManifestModule!); |
| 20 | + |
| 21 | + foreach (var typeContext in asmContext.Types) |
| 22 | + { |
| 23 | + if (typeContext.Name == "<Module>") |
| 24 | + continue; |
| 25 | + |
| 26 | + var managedType = typeContext.GetExtraData<TypeDefinition>("AsmResolverType") ?? throw new($"AsmResolver type not found in type analysis context for {typeContext.Definition?.FullName}"); |
| 27 | + |
| 28 | +#if !DEBUG |
| 29 | + try |
| 30 | +#endif |
| 31 | + { |
| 32 | + foreach (var methodCtx in typeContext.Methods) |
| 33 | + { |
| 34 | + var managedMethod = methodCtx.GetExtraData<MethodDefinition>("AsmResolverMethod") ?? throw new($"AsmResolver method not found in method analysis context for {typeContext.Definition?.FullName}.{methodCtx.Definition?.Name}"); |
| 35 | + |
| 36 | + if (managedMethod.IsManagedMethodWithBody()) |
| 37 | + FillMethodBodyWithStub(managedMethod, injectedRefHelperMethod); |
| 38 | + } |
| 39 | + } |
| 40 | +#if !DEBUG |
| 41 | + catch (Exception e) |
| 42 | + { |
| 43 | + throw new Exception($"Failed to process type {managedType.FullName} (module {managedType.Module?.Name}, declaring type {managedType.DeclaringType?.FullName}) in {asmContext.Definition.AssemblyName.Name}", e); |
| 44 | + } |
| 45 | +#endif |
| 46 | + } |
| 47 | + } |
| 48 | + |
| 49 | + private static void AddDefaultValueForType(CilInstructionCollection instructions, TypeSignature type) |
| 50 | + { |
| 51 | + if (type is CorLibTypeSignature { ElementType: ElementType.Void }) |
| 52 | + { |
| 53 | + } |
| 54 | + else if (type.IsValueType) |
| 55 | + { |
| 56 | + var variable = instructions.AddLocalVariable(type); |
| 57 | + instructions.Add(CilOpCodes.Ldloca, variable); |
| 58 | + instructions.Add(CilOpCodes.Initobj, type.ToTypeDefOrRef()); |
| 59 | + instructions.Add(CilOpCodes.Ldloc, variable); |
| 60 | + } |
| 61 | + else |
| 62 | + { |
| 63 | + instructions.Add(CilOpCodes.Ldnull); |
| 64 | + } |
| 65 | + } |
| 66 | + |
| 67 | + private static void FillMethodBodyWithStub(MethodDefinition methodDefinition, MethodDefinition injectedRefHelperMethod) |
| 68 | + { |
| 69 | + methodDefinition.CilMethodBody = new(methodDefinition); |
| 70 | + var methodInstructions = methodDefinition.CilMethodBody.Instructions; |
| 71 | + |
| 72 | + if (methodDefinition.IsConstructor && !methodDefinition.IsStatic && !methodDefinition.DeclaringType!.IsValueType) |
| 73 | + { |
| 74 | + var baseConstructor = TryGetBaseConstructor(methodDefinition); |
| 75 | + if (baseConstructor is not null) |
| 76 | + { |
| 77 | + methodInstructions.Add(CilOpCodes.Ldarg_0); |
| 78 | + foreach (var baseParameter in baseConstructor.Parameters) |
| 79 | + { |
| 80 | + var importedBaseParameterType = methodDefinition.DeclaringType.Module!.DefaultImporter.ImportTypeSignatureIfNeeded(baseParameter.ParameterType); |
| 81 | + if (baseParameter.Definition is { IsOut: true }) |
| 82 | + { |
| 83 | + var variable = methodInstructions.AddLocalVariable(importedBaseParameterType); |
| 84 | + methodInstructions.Add(CilOpCodes.Ldloca, variable); |
| 85 | + } |
| 86 | + else if (baseParameter.Definition is { IsIn: true }) |
| 87 | + { |
| 88 | + var variable = methodInstructions.AddLocalVariable(importedBaseParameterType); |
| 89 | + if (importedBaseParameterType.IsValueType) |
| 90 | + { |
| 91 | + methodInstructions.Add(CilOpCodes.Ldloca, variable); |
| 92 | + methodInstructions.Add(CilOpCodes.Initobj, importedBaseParameterType.ToTypeDefOrRef()); |
| 93 | + } |
| 94 | + else |
| 95 | + { |
| 96 | + methodInstructions.Add(CilOpCodes.Ldnull); |
| 97 | + methodInstructions.Add(CilOpCodes.Stloc, variable); |
| 98 | + } |
| 99 | + methodInstructions.Add(CilOpCodes.Ldloca, variable); |
| 100 | + } |
| 101 | + else if (importedBaseParameterType is ByReferenceTypeSignature byReferenceTypeSignature) |
| 102 | + { |
| 103 | + var referencedType = byReferenceTypeSignature.BaseType; |
| 104 | + var genericRefHelperInstance = injectedRefHelperMethod.DeclaringType!.MakeGenericInstanceType(referencedType); |
| 105 | + var memberReference = new MemberReference(genericRefHelperInstance.ToTypeDefOrRef(), injectedRefHelperMethod.Name, injectedRefHelperMethod.Signature); |
| 106 | + methodInstructions.Add(CilOpCodes.Call, memberReference); |
| 107 | + } |
| 108 | + else |
| 109 | + { |
| 110 | + AddDefaultValueForType(methodInstructions, importedBaseParameterType); |
| 111 | + } |
| 112 | + } |
| 113 | + methodInstructions.Add(CilOpCodes.Call, methodDefinition.DeclaringType.Module!.DefaultImporter.ImportMethod(baseConstructor)); |
| 114 | + } |
| 115 | + } |
| 116 | + |
| 117 | + foreach (var parameter in methodDefinition.Parameters) |
| 118 | + { |
| 119 | + if (parameter.Definition?.IsOut ?? false) |
| 120 | + { |
| 121 | + if (parameter.ParameterType.IsValueType) |
| 122 | + { |
| 123 | + methodInstructions.Add(CilOpCodes.Ldarg, parameter); |
| 124 | + methodInstructions.Add(CilOpCodes.Initobj, parameter.ParameterType.ToTypeDefOrRef()); |
| 125 | + } |
| 126 | + else |
| 127 | + { |
| 128 | + methodInstructions.Add(CilOpCodes.Ldarg, parameter); |
| 129 | + methodInstructions.Add(CilOpCodes.Ldnull); |
| 130 | + methodInstructions.Add(CilOpCodes.Stind_Ref); |
| 131 | + } |
| 132 | + } |
| 133 | + } |
| 134 | + AddDefaultValueForType(methodInstructions, methodDefinition.Signature!.ReturnType); |
| 135 | + methodInstructions.Add(CilOpCodes.Ret); |
| 136 | + methodInstructions.OptimizeMacros(); |
| 137 | + } |
| 138 | + |
| 139 | + private static MethodDefinition? TryGetBaseConstructor(MethodDefinition methodDefinition) |
| 140 | + { |
| 141 | + var declaringType = methodDefinition.DeclaringType!; |
| 142 | + var baseType = declaringType.BaseType?.Resolve(); |
| 143 | + if (baseType is null) |
| 144 | + { |
| 145 | + return null; |
| 146 | + } |
| 147 | + else if (declaringType.Module == baseType.Module) |
| 148 | + { |
| 149 | + return baseType.Methods.FirstOrDefault(m => m.IsConstructor && !m.IsStatic && !m.IsPrivate); |
| 150 | + } |
| 151 | + else |
| 152 | + { |
| 153 | + return baseType.Methods.FirstOrDefault(m => m.IsConstructor && !m.IsStatic && (m.IsFamily || m.IsPublic)); |
| 154 | + } |
| 155 | + } |
| 156 | + |
| 157 | + private static MethodDefinition MakeRefHelper(ModuleDefinition module) |
| 158 | + { |
| 159 | + var staticClass = new TypeDefinition("Cpp2ILInjected", "RefHelper", TypeAttributes.NotPublic | TypeAttributes.Abstract | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit); |
| 160 | + module.TopLevelTypes.Add(staticClass); |
| 161 | + staticClass.GenericParameters.Add(new GenericParameter("T")); |
| 162 | + |
| 163 | + var fieldType = new GenericParameterSignature(GenericParameterType.Type, 0); |
| 164 | + var fieldSignature = new FieldSignature(fieldType); |
| 165 | + var field = new FieldDefinition("backingField", FieldAttributes.Private | FieldAttributes.Static, fieldSignature); |
| 166 | + staticClass.Fields.Add(field); |
| 167 | + |
| 168 | + var staticConstructorSignature = MethodSignature.CreateStatic(module.CorLibTypeFactory.Void); |
| 169 | + var staticConstructor = new MethodDefinition("..ctor", MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RuntimeSpecialName, staticConstructorSignature); |
| 170 | + staticConstructor.CilMethodBody = new CilMethodBody(staticConstructor); |
| 171 | + var staticConstructorInstructions = staticConstructor.CilMethodBody.Instructions; |
| 172 | + staticConstructorInstructions.Add(CilOpCodes.Ldsflda, field); |
| 173 | + staticConstructorInstructions.Add(CilOpCodes.Initobj, fieldType.ToTypeDefOrRef()); |
| 174 | + staticConstructorInstructions.Add(CilOpCodes.Ret); |
| 175 | + |
| 176 | + var genericInstance = staticClass.MakeGenericInstanceType(fieldType); |
| 177 | + var memberReference = new MemberReference(genericInstance.ToTypeDefOrRef(), field.Name, field.Signature); |
| 178 | + |
| 179 | + var methodSignature = MethodSignature.CreateStatic(new ByReferenceTypeSignature(fieldType)); |
| 180 | + var method = new MethodDefinition("GetSharedReference", MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, methodSignature); |
| 181 | + staticClass.Methods.Add(method); |
| 182 | + method.CilMethodBody = new CilMethodBody(method); |
| 183 | + var methodInstructions = method.CilMethodBody.Instructions; |
| 184 | + methodInstructions.Add(CilOpCodes.Ldsflda, memberReference); |
| 185 | + methodInstructions.Add(CilOpCodes.Ret); |
| 186 | + |
| 187 | + return method; |
| 188 | + } |
| 189 | +} |
0 commit comments