• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SamboyCoding / Cpp2IL / 14021498950

23 Mar 2025 05:50PM UTC coverage: 34.597% (-0.09%) from 34.682%
14021498950

Pull #432

github

web-flow
Merge acd436c24 into bb74a97d9
Pull Request #432: Use newer AsmResolver APIs and update the places we import references

1784 of 6478 branches covered (27.54%)

Branch coverage included in aggregate %.

22 of 24 new or added lines in 4 files covered. (91.67%)

3 existing lines in 1 file now uncovered.

4140 of 10645 relevant lines covered (38.89%)

155279.22 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

57.78
/Cpp2IL.Core/Utils/AsmResolver/AsmResolverAssemblyPopulator.cs
1
using System;
2
using System.Collections.Generic;
3
using System.Linq;
4
using AsmResolver;
5
using AsmResolver.DotNet;
6
using AsmResolver.DotNet.Signatures;
7
using AsmResolver.PE.DotNet.Metadata.Tables;
8
using Cpp2IL.Core.Model.Contexts;
9
using Cpp2IL.Core.Model.CustomAttributes;
10
using LibCpp2IL.BinaryStructures;
11
using LibCpp2IL.Metadata;
12
using LibCpp2IL.Reflection;
13

14
namespace Cpp2IL.Core.Utils.AsmResolver;
15

16
public static class AsmResolverAssemblyPopulator
17
{
18
    public static bool IsTypeContextModule(TypeAnalysisContext typeCtx)
19
    {
20
        return typeCtx.Name.StartsWith("<Module>") || typeCtx.FullName.StartsWith("<Module>");
73,150✔
21
    }
22

23
    public static void ConfigureHierarchy(AssemblyAnalysisContext asmCtx)
24
    {
25
        foreach (var typeCtx in asmCtx.Types)
29,680✔
26
        {
27
            if (IsTypeContextModule(typeCtx))
14,630✔
28
                continue;
29

30
            var il2CppTypeDef = typeCtx.Definition;
14,420✔
31
            var typeDefinition = typeCtx.GetExtraData<TypeDefinition>("AsmResolverType") ?? throw new($"AsmResolver type not found in type analysis context for {typeCtx.FullName}");
14,420!
32

33
            var importer = typeDefinition.Module!.DefaultImporter;
14,420✔
34

35
            //Type generic params.
36
            if (il2CppTypeDef != null)
14,420✔
37
                PopulateGenericParamsForType(il2CppTypeDef, typeDefinition);
14,420✔
38

39
            //Set base type
40
            if (typeCtx.OverrideBaseType is { } overrideBaseType)
14,420!
41
            {
42
                var baseTypeDef = overrideBaseType.GetExtraData<TypeDefinition>("AsmResolverType") ?? throw new($"{typeCtx} declares override base type {overrideBaseType} which has not had an AsmResolver type generated for it.");
×
43
                typeDefinition.BaseType = importer.ImportType(baseTypeDef);
×
44
            }
45
            else if (il2CppTypeDef?.RawBaseType is { } parent)
14,420!
46
                typeDefinition.BaseType = importer.ImportType(AsmResolverUtils.ImportReferenceFromIl2CppType(typeDefinition.Module, parent));
13,595✔
47

48
            //Set interfaces
49
            if (il2CppTypeDef != null)
14,420✔
50
                foreach (var interfaceType in il2CppTypeDef.RawInterfaces)
38,230✔
51
                    typeDefinition.Interfaces.Add(new(importer.ImportType(AsmResolverUtils.ImportReferenceFromIl2CppType(typeDefinition.Module, interfaceType))));
4,695✔
52
        }
53
    }
210✔
54

55
    private static void PopulateGenericParamsForType(Il2CppTypeDefinition cppTypeDefinition, TypeDefinition ilTypeDefinition)
56
    {
57
        if (cppTypeDefinition.GenericContainer == null)
14,420✔
58
            return;
13,485✔
59

60
        var importer = ilTypeDefinition.Module!.DefaultImporter;
935✔
61

62
        foreach (var param in cppTypeDefinition.GenericContainer.GenericParameters)
4,440✔
63
        {
64
            // if(parentParams.Any(p => p.Name == param.Name))
65
            //     continue;
66

67
            if (!AsmResolverUtils.GenericParamsByIndexNew.TryGetValue(param.Index, out var p))
1,285!
68
            {
69
                p = new GenericParameter(param.Name, (GenericParameterAttributes)param.flags);
1,285✔
70
                AsmResolverUtils.GenericParamsByIndexNew[param.Index] = p;
1,285✔
71

72
                ilTypeDefinition.GenericParameters.Add(p);
1,285✔
73

74
                param.ConstraintTypes!
1,285✔
75
                    .Select(c => new GenericParameterConstraint(importer.ImportTypeIfNeeded(AsmResolverUtils.ImportReferenceFromIl2CppType(ilTypeDefinition.Module, c))))
200✔
76
                    .ToList()
1,285✔
77
                    .ForEach(p.Constraints.Add);
1,285✔
78
            }
79
            else if (!ilTypeDefinition.GenericParameters.Contains(p))
×
80
                ilTypeDefinition.GenericParameters.Add(p);
×
81
        }
82
    }
935✔
83

84
    private static TypeSignature GetTypeSigFromAttributeArg(AssemblyDefinition parentAssembly, BaseCustomAttributeParameter parameter) =>
85
        parameter switch
×
86
        {
×
87
            CustomAttributePrimitiveParameter primitiveParameter => AsmResolverUtils.GetPrimitiveTypeDef(primitiveParameter.PrimitiveType).ToTypeSignature(),
×
88
            CustomAttributeEnumParameter enumParameter => AsmResolverUtils.GetTypeSignatureFromIl2CppType(parentAssembly.ManifestModule!, enumParameter.EnumType ?? throw new("Enum type not found for " + enumParameter)),
×
89
            BaseCustomAttributeTypeParameter => TypeDefinitionsAsmResolver.Type.ToTypeSignature(),
×
90
            CustomAttributeArrayParameter arrayParameter => AsmResolverUtils.GetPrimitiveTypeDef(arrayParameter.ArrType).ToTypeSignature().MakeSzArrayType(),
×
91
            _ => throw new ArgumentException("Unknown custom attribute parameter type: " + parameter.GetType().FullName)
×
92
        };
×
93

94
    private static CustomAttributeArgument BuildArrayArgument(AssemblyDefinition parentAssembly, CustomAttributeArrayParameter arrayParameter)
95
    {
96
#if !DEBUG
97
        try
98
#endif
99
        {
100
            if (arrayParameter.IsNullArray)
×
101
                return BuildEmptyArrayArgument(parentAssembly, arrayParameter);
×
102

103
            var typeSig = GetTypeSigFromAttributeArg(parentAssembly, arrayParameter);
×
104

105
            var isObjectArray = arrayParameter.ArrType == Il2CppTypeEnum.IL2CPP_TYPE_OBJECT;
×
106

107
            var arrayElements = arrayParameter.ArrayElements.Select(e =>
×
108
            {
×
109
                var rawValue = e switch
×
110
                {
×
111
                    CustomAttributePrimitiveParameter primitiveParameter => primitiveParameter.PrimitiveValue,
×
112
                    CustomAttributeEnumParameter enumParameter => enumParameter.UnderlyingPrimitiveParameter.PrimitiveValue,
×
113
                    BaseCustomAttributeTypeParameter type => (object?)type.TypeContext?.ToTypeSignature(parentAssembly.ManifestModule!),
×
114
                    CustomAttributeNullParameter => null,
×
115
                    _ => throw new("Not supported array element type: " + e.GetType().FullName)
×
116
                };
×
117

×
118
                if (isObjectArray)
×
119
                    //Object params have to be boxed
×
120
                    return new BoxedArgument(GetTypeSigFromAttributeArg(parentAssembly, e), rawValue);
×
121

×
122
                return rawValue;
×
123
            }).ToArray();
×
124

125
            return new(typeSig, arrayElements);
×
126
        }
127
#if !DEBUG
128
        catch (Exception e)
×
129
        {
130
            throw new("Failed to build array argument for " + arrayParameter, e);
×
131
        }
132
#endif
133
    }
×
134

135
    private static CustomAttributeArgument BuildEmptyArrayArgument(AssemblyDefinition parentAssembly, CustomAttributeArrayParameter arrayParameter)
136
    {
137
        //Need to resolve the type of the array because it's not in the blob and AsmResolver needs it.
138

139
        var typeSig = arrayParameter.Kind switch
×
140
        {
×
141
            CustomAttributeParameterKind.ConstructorParam => arrayParameter.Owner.Constructor.Parameters[arrayParameter.Index].ToTypeSignature(parentAssembly.ManifestModule!),
×
142
            CustomAttributeParameterKind.Property => arrayParameter.Owner.Properties[arrayParameter.Index].Property.ToTypeSignature(parentAssembly.ManifestModule!),
×
143
            CustomAttributeParameterKind.Field => arrayParameter.Owner.Fields[arrayParameter.Index].Field.ToTypeSignature(parentAssembly.ManifestModule!),
×
144
            CustomAttributeParameterKind.ArrayElement => throw new("Array element cannot be an array (or at least, not implemented!)"),
×
145
            _ => throw new("Unknown array parameter kind: " + arrayParameter.Kind)
×
146
        };
×
147

148
        return new(typeSig) { IsNullArray = true };
×
149
    }
150

151
    /// <summary>
152
    /// Converts the given parameter to a custom attribute argument, given the context of the parent assembly.
153
    /// </summary>
154
    /// <param name="parentAssembly">The assembly that the resulting attribute will be part of.</param>
155
    /// <param name="parameter">The parameter to convert</param>
156
    /// <param name="boxIfNeeded">Whether the returned attribute will be used in context of a member that is typed as object. If true, the resulting attribute will be an object-typed one wrapping a BoxedArgument containing the real value. If false, the real value will be returned directly.</param>
157
    /// <remarks>
158
    /// BoxIfNeeded will cause the resulting attribute to be boxed if the parameter is an enum or a type parameter. This is required if, for example, the enum or type is being passed as the argument in a constructor for which the parameter is typed as object.
159
    /// </remarks>
160
    private static CustomAttributeArgument FromAnalyzedAttributeArgument(AssemblyDefinition parentAssembly, BaseCustomAttributeParameter parameter, bool boxIfNeeded)
161
    {
162
#if !DEBUG
163
        try
164
#endif
165
        {
166
            return parameter switch
×
167
            {
×
168
                CustomAttributePrimitiveParameter primitiveParameter => new(GetTypeSigFromAttributeArg(parentAssembly, primitiveParameter), primitiveParameter.PrimitiveValue),
×
169
                
×
170
                CustomAttributeEnumParameter enumParameter when boxIfNeeded => new(TypeDefinitionsAsmResolver.Object.ToTypeSignature(), new BoxedArgument(GetTypeSigFromAttributeArg(parentAssembly, enumParameter), enumParameter.UnderlyingPrimitiveParameter.PrimitiveValue)),
×
171
                CustomAttributeEnumParameter enumParameter => new(GetTypeSigFromAttributeArg(parentAssembly, enumParameter), enumParameter.UnderlyingPrimitiveParameter.PrimitiveValue),
×
172
                
×
173
                //BaseCustomAttributeTypeParameter typeParameter when boxIfNeeded => new(TypeDefinitionsAsmResolver.Object.ToTypeSignature(), new BoxedArgument(GetTypeSigFromAttributeArg(parentAssembly, typeParameter), typeParameter.TypeContext?.ToTypeSignature(parentAssembly.ManifestModule!))),
×
174
                BaseCustomAttributeTypeParameter typeParameter => new(TypeDefinitionsAsmResolver.Type.ToTypeSignature(), typeParameter.TypeContext?.ToTypeSignature(parentAssembly.ManifestModule!)),
×
175
                
×
176
                CustomAttributeArrayParameter arrayParameter => BuildArrayArgument(parentAssembly, arrayParameter),
×
177
                _ => throw new ArgumentException("Unknown custom attribute parameter type: " + parameter.GetType().FullName)
×
178
            };
×
179
        }
180
#if !DEBUG
181
        catch (Exception e)
×
182
        {
183
            throw new("Failed to build custom attribute argument for " + parameter, e);
×
184
        }
185
#endif
186
    }
×
187

188
    private static CustomAttributeNamedArgument FromAnalyzedAttributeField(AssemblyDefinition parentAssembly, CustomAttributeField field)
189
        => new(CustomAttributeArgumentMemberType.Field, field.Field.FieldName, GetTypeSigFromAttributeArg(parentAssembly, field.Value), FromAnalyzedAttributeArgument(parentAssembly, field.Value, field.Field.FieldTypeContext == field.Field.AppContext.SystemTypes.SystemObjectType));
×
190

191
    private static CustomAttributeNamedArgument FromAnalyzedAttributeProperty(AssemblyDefinition parentAssembly, CustomAttributeProperty property)
192
        => new(CustomAttributeArgumentMemberType.Property, property.Property.Name, GetTypeSigFromAttributeArg(parentAssembly, property.Value), FromAnalyzedAttributeArgument(parentAssembly, property.Value, property.Property.PropertyTypeContext == property.Property.AppContext.SystemTypes.SystemObjectType));
×
193

194
    private static CustomAttribute? ConvertCustomAttribute(AnalyzedCustomAttribute analyzedCustomAttribute, AssemblyDefinition assemblyDefinition)
195
    {
196
        var ctor = analyzedCustomAttribute.Constructor.GetExtraData<MethodDefinition>("AsmResolverMethod") ?? throw new($"Found a custom attribute with no AsmResolver constructor: {analyzedCustomAttribute}");
×
197

198
        CustomAttributeSignature signature;
199
        var numNamedArgs = analyzedCustomAttribute.Fields.Count + analyzedCustomAttribute.Properties.Count;
×
200

201
#if !DEBUG
202
        try
203
#endif
204
        {
205
            if (!analyzedCustomAttribute.HasAnyParameters && numNamedArgs == 0)
×
206
                signature = new();
×
207
            else if (analyzedCustomAttribute.IsSuitableForEmission)
×
208
            {
209
                if (numNamedArgs == 0)
×
210
                {
211
                    //Only fixed arguments.
212
                    signature = new(analyzedCustomAttribute.ConstructorParameters.Select(p => FromAnalyzedAttributeArgument(assemblyDefinition, p, analyzedCustomAttribute.Constructor.Parameters[p.Index].ParameterTypeContext == analyzedCustomAttribute.Constructor.AppContext.SystemTypes.SystemObjectType)));
×
213
                }
214
                else
215
                {
216
                    //Has named arguments.
217
                    signature = new(
×
218
                        analyzedCustomAttribute.ConstructorParameters.Select(p => FromAnalyzedAttributeArgument(assemblyDefinition, p, analyzedCustomAttribute.Constructor.Parameters[p.Index].ParameterTypeContext == analyzedCustomAttribute.Constructor.AppContext.SystemTypes.SystemObjectType)),
×
219
                        analyzedCustomAttribute.Fields
×
220
                            .Select(f => FromAnalyzedAttributeField(assemblyDefinition, f))
×
221
                            .Concat(analyzedCustomAttribute.Properties.Select(p => FromAnalyzedAttributeProperty(assemblyDefinition, p)))
×
222
                    );
×
223
                }
224
            }
225
            else
226
            {
227
                return null;
×
228
            }
229
        }
×
230
#if !DEBUG
231
        catch (Exception e)
×
232
        {
233
            throw new("Failed to build custom attribute signature for " + analyzedCustomAttribute, e);
×
234
        }
235
#endif
236

NEW
237
        var importedCtor = assemblyDefinition.ManifestModule!.DefaultImporter.ImportMethod(ctor);
×
238

239
        var newAttribute = new CustomAttribute((ICustomAttributeType)importedCtor, signature);
×
240
        return newAttribute;
×
241
    }
×
242

243
    private static void CopyCustomAttributes(HasCustomAttributes source, IList<CustomAttribute> destination)
244
    {
245
        if (source.CustomAttributes == null)
274,225!
246
            return;
274,225✔
247

248
        var assemblyDefinition = source.CustomAttributeAssembly.GetExtraData<AssemblyDefinition>("AsmResolverAssembly") ?? throw new("AsmResolver assembly not found in assembly analysis context for " + source.CustomAttributeAssembly);
×
249

250
#if !DEBUG
251
        try
252
#endif
253
        {
254
            foreach (var analyzedCustomAttribute in source.CustomAttributes)
×
255
            {
256
                var asmResolverCustomAttribute = ConvertCustomAttribute(analyzedCustomAttribute, assemblyDefinition);
×
257
                if (asmResolverCustomAttribute != null)
×
258
                    destination.Add(asmResolverCustomAttribute);
×
259
            }
260
        }
×
261
#if !DEBUG
262
        catch (Exception e)
×
263
        {
264
            throw new("Failed to copy custom attributes for " + source, e);
×
265
        }
266
#endif
267
    }
×
268

269
    public static void PopulateCustomAttributes(AssemblyAnalysisContext asmContext)
270
    {
271
#if !DEBUG
272
        try
273
#endif
274
        {
275
            CopyCustomAttributes(asmContext, asmContext.GetExtraData<AssemblyDefinition>("AsmResolverAssembly")!.CustomAttributes);
210✔
276

277
            foreach (var type in asmContext.Types)
29,680✔
278
            {
279
                if (IsTypeContextModule(type))
14,630✔
280
                    continue;
281

282
                CopyCustomAttributes(type, type.GetExtraData<TypeDefinition>("AsmResolverType")!.CustomAttributes);
14,420✔
283

284
                foreach (var method in type.Methods)
202,900✔
285
                {
286
                    var methodDef = method.GetExtraData<MethodDefinition>("AsmResolverMethod")!;
87,030✔
287
                    CopyCustomAttributes(method, methodDef.CustomAttributes);
87,030✔
288

289
                    var parameterDefinitions = methodDef.ParameterDefinitions;
87,030✔
290
                    foreach (var parameterAnalysisContext in method.Parameters)
370,530✔
291
                    {
292
                        CopyCustomAttributes(parameterAnalysisContext, parameterDefinitions[parameterAnalysisContext.ParamIndex].CustomAttributes);
98,235✔
293
                    }
294
                }
295

296
                foreach (var field in type.Fields)
146,630✔
297
                    CopyCustomAttributes(field, field.GetExtraData<FieldDefinition>("AsmResolverField")!.CustomAttributes);
58,895✔
298

299
                foreach (var property in type.Properties)
59,560✔
300
                    CopyCustomAttributes(property, property.GetExtraData<PropertyDefinition>("AsmResolverProperty")!.CustomAttributes);
15,360✔
301

302
                foreach (var eventDefinition in type.Events)
28,990✔
303
                    CopyCustomAttributes(eventDefinition, eventDefinition.GetExtraData<EventDefinition>("AsmResolverEvent")!.CustomAttributes);
75✔
304
            }
305
        }
210✔
306
#if !DEBUG
307
        catch (Exception e)
×
308
        {
309
            throw new($"Failed to populate custom attributes in {asmContext}", e);
×
310
        }
311
#endif
312
    }
210✔
313

314
    public static void CopyDataFromIl2CppToManaged(AssemblyAnalysisContext asmContext)
315
    {
316
        var managedAssembly = asmContext.GetExtraData<AssemblyDefinition>("AsmResolverAssembly") ?? throw new("AsmResolver assembly not found in assembly analysis context for " + asmContext);
210!
317

318
        foreach (var typeContext in asmContext.Types)
29,680✔
319
        {
320
            if (IsTypeContextModule(typeContext))
14,630✔
321
                continue;
322

323
            var managedType = typeContext.GetExtraData<TypeDefinition>("AsmResolverType") ?? throw new($"AsmResolver type not found in type analysis context for {typeContext.Definition?.FullName}");
14,420!
324
            // CopyCustomAttributes(typeContext, managedType.CustomAttributes);
325

326
#if !DEBUG
327
            try
328
#endif
329
            {
330
                CopyIl2CppDataToManagedType(typeContext, managedType);
14,420✔
331
            }
14,420✔
332
#if !DEBUG
333
            catch (Exception e)
×
334
            {
335
                throw new Exception($"Failed to process type {managedType.FullName} (module {managedType.Module?.Name}, declaring type {managedType.DeclaringType?.FullName}) in {asmContext.Definition.AssemblyName.Name}", e);
×
336
            }
337
#endif
338
        }
339
    }
210✔
340

341
    private static void CopyIl2CppDataToManagedType(TypeAnalysisContext typeContext, TypeDefinition ilTypeDefinition)
342
    {
343
        var importer = ilTypeDefinition.Module!.DefaultImporter;
14,420✔
344

345
        CopyFieldsInType(importer, typeContext, ilTypeDefinition);
14,420✔
346

347
        CopyMethodsInType(importer, typeContext, ilTypeDefinition);
14,420✔
348

349
        CopyPropertiesInType(importer, typeContext, ilTypeDefinition);
14,420✔
350

351
        CopyEventsInType(importer, typeContext, ilTypeDefinition);
14,420✔
352
    }
14,420✔
353

354
    private static void CopyFieldsInType(ReferenceImporter importer, TypeAnalysisContext typeContext, TypeDefinition ilTypeDefinition)
355
    {
356
        foreach (var fieldContext in typeContext.Fields)
146,630✔
357
        {
358
            var fieldInfo = fieldContext.BackingData;
58,895✔
359

360
            var fieldTypeSig = fieldContext.ToTypeSignature(importer.TargetModule);
58,895✔
361

362
            var managedField = new FieldDefinition(fieldContext.FieldName, (FieldAttributes)fieldContext.Attributes, fieldTypeSig);
58,895✔
363

364
            if (fieldInfo != null)
58,895✔
365
            {
366
                //Field default values
367
                if (managedField.HasDefault && fieldInfo.Field.DefaultValue?.Value is { } constVal)
58,895!
368
                    managedField.Constant = AsmResolverConstants.GetOrCreateConstant(constVal);
21,360✔
369

370
                //Field Initial Values (used for allocation of Array Literals)
371
                if (managedField.HasFieldRva)
58,895✔
372
                    managedField.FieldRva = new DataSegment(fieldInfo.Field.StaticArrayInitialValue);
540✔
373

374
                if (ilTypeDefinition.IsExplicitLayout)
58,895✔
375
                    //Copy field offset
376
                    managedField.FieldOffset = fieldInfo.FieldOffset;
260✔
377
            }
378

379
            fieldContext.PutExtraData("AsmResolverField", managedField);
58,895✔
380

381
            ilTypeDefinition.Fields.Add(managedField);
58,895✔
382
        }
383
    }
14,420✔
384

385
    private static void CopyMethodsInType(ReferenceImporter importer, TypeAnalysisContext typeContext, TypeDefinition ilTypeDefinition)
386
    {
387
        foreach (var methodCtx in typeContext.Methods)
202,900✔
388
        {
389
            var methodDef = methodCtx.Definition;
87,030✔
390

391
            var returnType = methodCtx.ReturnTypeContext.ToTypeSignature(importer.TargetModule);
87,030✔
392

393
            var paramData = methodCtx.Parameters;
87,030✔
394
            var parameterTypes = new TypeSignature[paramData.Count];
87,030✔
395
            var parameterDefinitions = new ParameterDefinition[paramData.Count];
87,030✔
396
            foreach (var parameterAnalysisContext in methodCtx.Parameters)
370,530✔
397
            {
398
                var i = parameterAnalysisContext.ParamIndex;
98,235✔
399
                parameterTypes[i] = parameterAnalysisContext.ParameterTypeContext.ToTypeSignature(importer.TargetModule);
98,235✔
400

401
                var sequence = (ushort)(i + 1); //Add one because sequence 0 is the return type
98,235✔
402
                parameterDefinitions[i] = new(sequence, parameterAnalysisContext.Name, (ParameterAttributes)parameterAnalysisContext.ParameterAttributes);
98,235✔
403

404
                if (parameterAnalysisContext.DefaultValue is not { } defaultValueData)
98,235✔
405
                    continue;
406

407
                if (defaultValueData?.ContainedDefaultValue is { } constVal)
415!
408
                    parameterDefinitions[i].Constant = AsmResolverConstants.GetOrCreateConstant(constVal);
350✔
409
                else if (defaultValueData is { dataIndex: -1 })
65✔
410
                {
411
                    //Literal null
412
                    parameterDefinitions[i].Constant = AsmResolverConstants.Null;
65✔
413
                }
414
            }
415

416

417
            var signature = methodCtx.IsStatic
87,030✔
418
                ? MethodSignature.CreateStatic(returnType, methodCtx.GenericParameterCount, parameterTypes)
87,030✔
419
                : MethodSignature.CreateInstance(returnType, methodCtx.GenericParameterCount, parameterTypes);
87,030✔
420

421
            var managedMethod = new MethodDefinition(methodCtx.Name, (MethodAttributes)methodCtx.Attributes, signature);
87,030✔
422

423
            if (methodCtx.Definition != null)
87,030✔
424
            {
425
                managedMethod.ImplAttributes = (MethodImplAttributes)methodCtx.Definition.MethodImplAttributes;
87,030✔
426
                if (methodCtx.Definition.IsUnmanagedCallersOnly && typeContext.AppContext.SystemTypes.UnmanagedCallersOnlyAttributeType != null)
87,030!
427
                {
428
                    var unmanagedCallersOnlyType = typeContext.AppContext.SystemTypes.UnmanagedCallersOnlyAttributeType.GetExtraData<TypeDefinition>("AsmResolverType");
×
429
                    if(unmanagedCallersOnlyType != null)
×
430
                        managedMethod.CustomAttributes.Add(new CustomAttribute((ICustomAttributeType)importer.ImportMethod(unmanagedCallersOnlyType.GetConstructor()!), new()));
×
431
                }
432

433
            }
434

435
            //Add parameter definitions if we have them so we get names, defaults, out params, etc
436
            foreach (var parameterDefinition in parameterDefinitions)
370,530✔
437
            {
438
                managedMethod.ParameterDefinitions.Add(parameterDefinition);
98,235✔
439
            }
440

441
            //Handle generic parameters.
442
            methodDef?.GenericContainer?.GenericParameters.ToList()
87,030✔
443
                .ForEach(p =>
87,030✔
444
                {
87,030✔
445
                    if (AsmResolverUtils.GenericParamsByIndexNew.TryGetValue(p.Index, out var gp))
975!
446
                    {
87,030✔
447
                        if (!managedMethod.GenericParameters.Contains(gp))
×
448
                            managedMethod.GenericParameters.Add(gp);
×
449

87,030✔
450
                        return;
×
451
                    }
87,030✔
452

87,030✔
453
                    gp = new(p.Name, (GenericParameterAttributes)p.flags);
975✔
454

87,030✔
455
                    if (!managedMethod.GenericParameters.Contains(gp))
975✔
456
                        managedMethod.GenericParameters.Add(gp);
975✔
457

87,030✔
458
                    p.ConstraintTypes!
975✔
459
                        .Select(c => new GenericParameterConstraint(importer.ImportTypeIfNeeded(AsmResolverUtils.ImportReferenceFromIl2CppType(ilTypeDefinition.Module!, c))))
175✔
460
                        .ToList()
975✔
461
                        .ForEach(gp.Constraints.Add);
975✔
462
                });
88,005✔
463

464

465
            methodCtx.PutExtraData("AsmResolverMethod", managedMethod);
87,030✔
466
            ilTypeDefinition.Methods.Add(managedMethod);
87,030✔
467
        }
468
    }
14,420✔
469

470
    private static void CopyPropertiesInType(ReferenceImporter importer, TypeAnalysisContext typeContext, TypeDefinition ilTypeDefinition)
471
    {
472
        foreach (var propertyCtx in typeContext.Properties)
59,560✔
473
        {
474
            var propertyTypeSig = propertyCtx.ToTypeSignature(importer.TargetModule);
15,360✔
475
            var propertySignature = propertyCtx.IsStatic
15,360✔
476
                ? PropertySignature.CreateStatic(propertyTypeSig)
15,360✔
477
                : PropertySignature.CreateInstance(propertyTypeSig);
15,360✔
478

479
            var managedProperty = new PropertyDefinition(propertyCtx.Name, (PropertyAttributes)propertyCtx.PropertyAttributes, propertySignature);
15,360✔
480

481
            var managedGetter = propertyCtx.Getter?.GetExtraData<MethodDefinition>("AsmResolverMethod");
15,360✔
482
            var managedSetter = propertyCtx.Setter?.GetExtraData<MethodDefinition>("AsmResolverMethod");
15,360✔
483

484
            managedProperty.SetSemanticMethods(managedGetter, managedSetter);
15,360✔
485

486
            //Indexer parameters
487
            if (managedGetter != null && managedGetter.Parameters.Count > 0)
15,360✔
488
            {
489
                foreach (var parameter in managedGetter.Parameters)
860✔
490
                {
491
                    propertySignature.ParameterTypes.Add(parameter.ParameterType);
215✔
492
                }
493
            }
494
            else if (managedSetter != null && managedSetter.Parameters.Count > 1)
15,145✔
495
            {
496
                //value parameter is always last
497
                for (var i = 0; i < managedSetter.Parameters.Count - 1; i++)
60✔
498
                {
499
                    var parameter = managedSetter.Parameters[i];
15✔
500
                    propertySignature.ParameterTypes.Add(parameter.ParameterType);
15✔
501
                }
502
            }
503

504
            propertyCtx.PutExtraData("AsmResolverProperty", managedProperty);
15,360✔
505

506
            ilTypeDefinition.Properties.Add(managedProperty);
15,360✔
507
        }
508
    }
14,420✔
509

510
    private static void CopyEventsInType(ReferenceImporter importer, TypeAnalysisContext cppTypeDefinition, TypeDefinition ilTypeDefinition)
511
    {
512
        foreach (var eventCtx in cppTypeDefinition.Events)
28,990✔
513
        {
514
            var eventType = eventCtx.ToTypeSignature(importer.TargetModule).ToTypeDefOrRef();
75✔
515

516
            var managedEvent = new EventDefinition(eventCtx.Name, (EventAttributes)eventCtx.EventAttributes, eventType);
75✔
517

518
            var managedAdder = eventCtx.Adder?.GetExtraData<MethodDefinition>("AsmResolverMethod");
75!
519
            var managedRemover = eventCtx.Remover?.GetExtraData<MethodDefinition>("AsmResolverMethod");
75!
520
            var managedInvoker = eventCtx.Invoker?.GetExtraData<MethodDefinition>("AsmResolverMethod");
75!
521

522
            managedEvent.SetSemanticMethods(managedAdder, managedRemover, managedInvoker);
75✔
523

524
            eventCtx.PutExtraData("AsmResolverEvent", managedEvent);
75✔
525

526
            ilTypeDefinition.Events.Add(managedEvent);
75✔
527
        }
528
    }
14,420✔
529

530
    public static void AddExplicitInterfaceImplementations(AssemblyAnalysisContext asmContext)
531
    {
532
        var managedAssembly = asmContext.GetExtraData<AssemblyDefinition>("AsmResolverAssembly") ?? throw new("AsmResolver assembly not found in assembly analysis context for " + asmContext);
210!
533

534
        var importer = managedAssembly.ManifestModule!.DefaultImporter;
210✔
535

536
        foreach (var typeContext in asmContext.Types)
29,680✔
537
        {
538
            if (IsTypeContextModule(typeContext))
14,630✔
539
                continue;
540

541
            var managedType = typeContext.GetExtraData<TypeDefinition>("AsmResolverType") ?? throw new($"AsmResolver type not found in type analysis context for {typeContext.Definition?.FullName}");
14,420!
542

543
#if !DEBUG
544
            try
545
#endif
546
            {
547
                AddExplicitInterfaceImplementations(managedType, typeContext, importer);
14,420✔
548
            }
14,420✔
549
#if !DEBUG
550
            catch (Exception e)
×
551
            {
552
                throw new Exception($"Failed to process type {managedType.FullName} (module {managedType.Module?.Name}, declaring type {managedType.DeclaringType?.FullName}) in {asmContext.Definition.AssemblyName.Name}", e);
×
553
            }
554
#endif
555
        }
556
    }
210✔
557

558
    private static void AddExplicitInterfaceImplementations(TypeDefinition type, TypeAnalysisContext typeContext, ReferenceImporter importer)
559
    {
560
        List<(PropertyDefinition InterfaceProperty, TypeSignature InterfaceType, MethodDefinition Method)>? getMethodsToCreate = null;
14,420✔
561
        List<(PropertyDefinition InterfaceProperty, TypeSignature InterfaceType, MethodDefinition Method)>? setMethodsToCreate = null;
14,420✔
562

563
        foreach (var methodContext in typeContext.Methods)
202,900✔
564
        {
565
            if ((methodContext.Attributes & System.Reflection.MethodAttributes.MemberAccessMask) != System.Reflection.MethodAttributes.Private)
87,030✔
566
                continue;
567

568
            foreach (var overrideContext in methodContext.Overrides)
42,290✔
569
            {
570
                if (overrideContext.DeclaringType?.IsInterface ?? false)
3,115!
571
                {
572
                    var interfaceMethod = (IMethodDefOrRef)overrideContext.ToMethodDescriptor(importer.TargetModule);
3,115✔
573
                    var method = methodContext.GetExtraData<MethodDefinition>("AsmResolverMethod") ?? throw new($"AsmResolver method not found in method analysis context for {methodContext}");
3,115!
574
                    type.MethodImplementations.Add(new MethodImplementation(interfaceMethod, method));
3,115✔
575
                    var interfaceMethodResolved = interfaceMethod.Resolve();
3,115✔
576
                    if (interfaceMethodResolved != null)
3,115✔
577
                    {
578
                        if (interfaceMethodResolved.IsGetMethod && !method.IsGetMethod)
3,115✔
579
                        {
580
                            getMethodsToCreate ??= [];
40✔
581
                            var interfacePropertyResolved = interfaceMethodResolved.DeclaringType!.Properties.First(p => p.Semantics.Contains(interfaceMethodResolved.Semantics));
80✔
582
                            getMethodsToCreate.Add((interfacePropertyResolved, interfaceMethod.DeclaringType!.ToTypeSignature(), method));
40✔
583
                        }
584
                        else if (interfaceMethodResolved.IsSetMethod && !method.IsSetMethod)
3,075!
585
                        {
586
                            setMethodsToCreate ??= [];
×
587
                            var interfacePropertyResolved = interfaceMethodResolved.DeclaringType!.Properties.First(p => p.Semantics.Contains(interfaceMethodResolved.Semantics));
×
588
                            setMethodsToCreate.Add((interfacePropertyResolved, interfaceMethod.DeclaringType!.ToTypeSignature(), method));
×
589
                        }
590
                    }
591
                }
592
            }
593
        }
594

595
        // Il2Cpp doesn't include properties for explicit interface implementations, so we have to create them ourselves.
596
        if (getMethodsToCreate is not null)
14,420✔
597
        {
598
            foreach (var entry in getMethodsToCreate)
160✔
599
            {
600
                var (interfaceProperty, interfaceType, getMethod) = entry;
40✔
601
                var setMethod = setMethodsToCreate?
40!
602
                    .FirstOrDefault(e => e.InterfaceProperty == interfaceProperty && SignatureComparer.Default.Equals(e.InterfaceType, interfaceType))
×
603
                    .Method;
40✔
604

605
                var name = $"{interfaceType.FullName}.{interfaceProperty.Name}";
40✔
606
                var propertySignature = getMethod.IsStatic
40!
607
                    ? PropertySignature.CreateStatic(getMethod.Signature!.ReturnType, getMethod.Signature.ParameterTypes)
40✔
608
                    : PropertySignature.CreateInstance(getMethod.Signature!.ReturnType, getMethod.Signature.ParameterTypes);
40✔
609
                var property = new PropertyDefinition(name, interfaceProperty.Attributes, propertySignature);
40✔
610
                type.Properties.Add(property);
40✔
611
                property.SetSemanticMethods(getMethod, setMethod);
40✔
612
            }
613
        }
614
        if (setMethodsToCreate is not null)
14,420!
615
        {
616
            foreach (var entry in setMethodsToCreate)
×
617
            {
618
                var (interfaceProperty, interfaceType, setMethod) = entry;
×
619
                if (getMethodsToCreate?.Any(e => e.InterfaceProperty == interfaceProperty && SignatureComparer.Default.Equals(e.InterfaceType, interfaceType)) == true)
×
620
                    continue;
621
                var name = $"{interfaceType.FullName}.{interfaceProperty.Name}";
×
622
                var propertySignature = setMethod.IsStatic
×
623
                    ? PropertySignature.CreateStatic(setMethod.Signature!.ParameterTypes[^1], setMethod.Signature.ParameterTypes.Take(setMethod.Signature.ParameterTypes.Count - 1))
×
624
                    : PropertySignature.CreateInstance(setMethod.Signature!.ParameterTypes[^1], setMethod.Signature.ParameterTypes.Take(setMethod.Signature.ParameterTypes.Count - 1));
×
625
                var property = new PropertyDefinition(name, interfaceProperty.Attributes, propertySignature);
×
626
                type.Properties.Add(property);
×
627
                property.SetSemanticMethods(null, setMethod);
×
628
            }
629
        }
630
    }
14,420✔
631
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc