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

SamboyCoding / Cpp2IL / 15144219583

20 May 2025 05:38PM UTC coverage: 34.28% (+0.2%) from 34.047%
15144219583

Pull #462

github

web-flow
Merge f80b990bc into 5807d2b6c
Pull Request #462: Support overriding member types

1799 of 6646 branches covered (27.07%)

Branch coverage included in aggregate %.

115 of 202 new or added lines in 33 files covered. (56.93%)

22 existing lines in 6 files now uncovered.

4197 of 10845 relevant lines covered (38.7%)

186399.11 hits per line

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

57.22
/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

12
namespace Cpp2IL.Core.Utils.AsmResolver;
13

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

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

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

30
            //Type generic params.
31
            PopulateGenericParamsForType(typeCtx, typeDefinition);
14,420✔
32

33
            //Set base type
34
            typeDefinition.BaseType = typeCtx.BaseType?.ToTypeSignature(typeDefinition.Module!).ToTypeDefOrRef();
14,420✔
35

36
            //Set interfaces
37
            foreach (var interfaceType in typeCtx.InterfaceContexts)
38,230✔
38
                typeDefinition.Interfaces.Add(new(interfaceType.ToTypeSignature(typeDefinition.Module!).ToTypeDefOrRef()));
4,695✔
39
        }
40
    }
210✔
41

42
    private static void PopulateGenericParamsForType(TypeAnalysisContext cppTypeDefinition, TypeDefinition ilTypeDefinition)
43
    {
44
        var importer = ilTypeDefinition.Module!.DefaultImporter;
14,420✔
45

46
        foreach (var param in cppTypeDefinition.GenericParameters)
31,410✔
47
        {
48
            var p = new GenericParameter(param.Name, (GenericParameterAttributes)param.Attributes);
1,285✔
49

50
            ilTypeDefinition.GenericParameters.Add(p);
1,285✔
51

52
            param.ConstraintTypes
1,285✔
53
                .Select(c => new GenericParameterConstraint(c.ToTypeSignature(ilTypeDefinition.Module).ToTypeDefOrRef()))
200✔
54
                .ToList()
1,285✔
55
                .ForEach(p.Constraints.Add);
1,285✔
56
        }
57
    }
14,420✔
58

59
    private static TypeSignature GetTypeSigFromAttributeArg(AssemblyDefinition parentAssembly, BaseCustomAttributeParameter parameter) =>
60
        parameter switch
×
61
        {
×
62
            CustomAttributePrimitiveParameter primitiveParameter => AsmResolverUtils.GetPrimitiveTypeDef(primitiveParameter.PrimitiveType).ToTypeSignature(),
×
63
            CustomAttributeEnumParameter enumParameter => AsmResolverUtils.GetTypeSignatureFromIl2CppType(parentAssembly.ManifestModule!, enumParameter.EnumType ?? throw new("Enum type not found for " + enumParameter)),
×
64
            BaseCustomAttributeTypeParameter => TypeDefinitionsAsmResolver.Type.ToTypeSignature(),
×
65
            CustomAttributeArrayParameter arrayParameter => AsmResolverUtils.GetPrimitiveTypeDef(arrayParameter.ArrType).ToTypeSignature().MakeSzArrayType(),
×
66
            _ => throw new ArgumentException("Unknown custom attribute parameter type: " + parameter.GetType().FullName)
×
67
        };
×
68

69
    private static CustomAttributeArgument BuildArrayArgument(AssemblyDefinition parentAssembly, CustomAttributeArrayParameter arrayParameter)
70
    {
71
#if !DEBUG
72
        try
73
#endif
74
        {
75
            if (arrayParameter.IsNullArray)
×
76
                return BuildEmptyArrayArgument(parentAssembly, arrayParameter);
×
77

78
            var typeSig = GetTypeSigFromAttributeArg(parentAssembly, arrayParameter);
×
79

80
            var isObjectArray = arrayParameter.ArrType == Il2CppTypeEnum.IL2CPP_TYPE_OBJECT;
×
81

82
            var arrayElements = arrayParameter.ArrayElements.Select(e =>
×
83
            {
×
84
                var rawValue = e switch
×
85
                {
×
86
                    CustomAttributePrimitiveParameter primitiveParameter => primitiveParameter.PrimitiveValue,
×
87
                    CustomAttributeEnumParameter enumParameter => enumParameter.UnderlyingPrimitiveParameter.PrimitiveValue,
×
88
                    BaseCustomAttributeTypeParameter type => (object?)type.TypeContext?.ToTypeSignature(parentAssembly.ManifestModule!),
×
89
                    CustomAttributeNullParameter => null,
×
90
                    _ => throw new("Not supported array element type: " + e.GetType().FullName)
×
91
                };
×
92

×
93
                if (isObjectArray)
×
94
                    //Object params have to be boxed
×
95
                    return new BoxedArgument(GetTypeSigFromAttributeArg(parentAssembly, e), rawValue);
×
96

×
97
                return rawValue;
×
98
            }).ToArray();
×
99

100
            return new(typeSig, arrayElements);
×
101
        }
102
#if !DEBUG
103
        catch (Exception e)
×
104
        {
105
            throw new("Failed to build array argument for " + arrayParameter, e);
×
106
        }
107
#endif
108
    }
×
109

110
    private static CustomAttributeArgument BuildEmptyArrayArgument(AssemblyDefinition parentAssembly, CustomAttributeArrayParameter arrayParameter)
111
    {
112
        //Need to resolve the type of the array because it's not in the blob and AsmResolver needs it.
113

114
        var typeSig = arrayParameter.Kind switch
×
115
        {
×
116
            CustomAttributeParameterKind.ConstructorParam => arrayParameter.Owner.Constructor.Parameters[arrayParameter.Index].ToTypeSignature(parentAssembly.ManifestModule!),
×
117
            CustomAttributeParameterKind.Property => arrayParameter.Owner.Properties[arrayParameter.Index].Property.ToTypeSignature(parentAssembly.ManifestModule!),
×
118
            CustomAttributeParameterKind.Field => arrayParameter.Owner.Fields[arrayParameter.Index].Field.ToTypeSignature(parentAssembly.ManifestModule!),
×
119
            CustomAttributeParameterKind.ArrayElement => throw new("Array element cannot be an array (or at least, not implemented!)"),
×
120
            _ => throw new("Unknown array parameter kind: " + arrayParameter.Kind)
×
121
        };
×
122

123
        return new(typeSig) { IsNullArray = true };
×
124
    }
125

126
    /// <summary>
127
    /// Converts the given parameter to a custom attribute argument, given the context of the parent assembly.
128
    /// </summary>
129
    /// <param name="parentAssembly">The assembly that the resulting attribute will be part of.</param>
130
    /// <param name="parameter">The parameter to convert</param>
131
    /// <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>
132
    /// <remarks>
133
    /// 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.
134
    /// </remarks>
135
    private static CustomAttributeArgument FromAnalyzedAttributeArgument(AssemblyDefinition parentAssembly, BaseCustomAttributeParameter parameter, bool boxIfNeeded)
136
    {
137
#if !DEBUG
138
        try
139
#endif
140
        {
141
            return parameter switch
×
142
            {
×
143
                CustomAttributePrimitiveParameter primitiveParameter => new(GetTypeSigFromAttributeArg(parentAssembly, primitiveParameter), primitiveParameter.PrimitiveValue),
×
144
                
×
145
                CustomAttributeEnumParameter enumParameter when boxIfNeeded => new(TypeDefinitionsAsmResolver.Object.ToTypeSignature(), new BoxedArgument(GetTypeSigFromAttributeArg(parentAssembly, enumParameter), enumParameter.UnderlyingPrimitiveParameter.PrimitiveValue)),
×
146
                CustomAttributeEnumParameter enumParameter => new(GetTypeSigFromAttributeArg(parentAssembly, enumParameter), enumParameter.UnderlyingPrimitiveParameter.PrimitiveValue),
×
147
                
×
148
                //BaseCustomAttributeTypeParameter typeParameter when boxIfNeeded => new(TypeDefinitionsAsmResolver.Object.ToTypeSignature(), new BoxedArgument(GetTypeSigFromAttributeArg(parentAssembly, typeParameter), typeParameter.TypeContext?.ToTypeSignature(parentAssembly.ManifestModule!))),
×
149
                BaseCustomAttributeTypeParameter typeParameter => new(TypeDefinitionsAsmResolver.Type.ToTypeSignature(), typeParameter.TypeContext?.ToTypeSignature(parentAssembly.ManifestModule!)),
×
150
                
×
151
                CustomAttributeArrayParameter arrayParameter => BuildArrayArgument(parentAssembly, arrayParameter),
×
152
                _ => throw new ArgumentException("Unknown custom attribute parameter type: " + parameter.GetType().FullName)
×
153
            };
×
154
        }
155
#if !DEBUG
156
        catch (Exception e)
×
157
        {
158
            throw new("Failed to build custom attribute argument for " + parameter, e);
×
159
        }
160
#endif
161
    }
×
162

163
    private static CustomAttributeNamedArgument FromAnalyzedAttributeField(AssemblyDefinition parentAssembly, CustomAttributeField field)
NEW
164
        => new(CustomAttributeArgumentMemberType.Field, field.Field.Name, GetTypeSigFromAttributeArg(parentAssembly, field.Value), FromAnalyzedAttributeArgument(parentAssembly, field.Value, field.Field.FieldType == field.Field.AppContext.SystemTypes.SystemObjectType));
×
165

166
    private static CustomAttributeNamedArgument FromAnalyzedAttributeProperty(AssemblyDefinition parentAssembly, CustomAttributeProperty property)
NEW
167
        => new(CustomAttributeArgumentMemberType.Property, property.Property.Name, GetTypeSigFromAttributeArg(parentAssembly, property.Value), FromAnalyzedAttributeArgument(parentAssembly, property.Value, property.Property.PropertyType == property.Property.AppContext.SystemTypes.SystemObjectType));
×
168

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

173
        CustomAttributeSignature signature;
174
        var numNamedArgs = analyzedCustomAttribute.Fields.Count + analyzedCustomAttribute.Properties.Count;
×
175

176
#if !DEBUG
177
        try
178
#endif
179
        {
180
            if (!analyzedCustomAttribute.HasAnyParameters && numNamedArgs == 0)
×
181
                signature = new();
×
182
            else if (analyzedCustomAttribute.IsSuitableForEmission)
×
183
            {
184
                if (numNamedArgs == 0)
×
185
                {
186
                    //Only fixed arguments.
NEW
187
                    signature = new(analyzedCustomAttribute.ConstructorParameters.Select(p => FromAnalyzedAttributeArgument(assemblyDefinition, p, analyzedCustomAttribute.Constructor.Parameters[p.Index].ParameterType == analyzedCustomAttribute.Constructor.AppContext.SystemTypes.SystemObjectType)));
×
188
                }
189
                else
190
                {
191
                    //Has named arguments.
192
                    signature = new(
×
NEW
193
                        analyzedCustomAttribute.ConstructorParameters.Select(p => FromAnalyzedAttributeArgument(assemblyDefinition, p, analyzedCustomAttribute.Constructor.Parameters[p.Index].ParameterType == analyzedCustomAttribute.Constructor.AppContext.SystemTypes.SystemObjectType)),
×
194
                        analyzedCustomAttribute.Fields
×
195
                            .Select(f => FromAnalyzedAttributeField(assemblyDefinition, f))
×
196
                            .Concat(analyzedCustomAttribute.Properties.Select(p => FromAnalyzedAttributeProperty(assemblyDefinition, p)))
×
197
                    );
×
198
                }
199
            }
200
            else
201
            {
202
                return null;
×
203
            }
204
        }
×
205
#if !DEBUG
206
        catch (Exception e)
×
207
        {
208
            throw new("Failed to build custom attribute signature for " + analyzedCustomAttribute, e);
×
209
        }
210
#endif
211

212
        var importedCtor = assemblyDefinition.ManifestModule!.DefaultImporter.ImportMethod(ctor);
×
213

214
        var newAttribute = new CustomAttribute((ICustomAttributeType)importedCtor, signature);
×
215
        return newAttribute;
×
216
    }
×
217

218
    private static void CopyCustomAttributes(HasCustomAttributes source, IList<CustomAttribute> destination)
219
    {
220
        if (source.CustomAttributes == null)
274,225!
221
            return;
274,225✔
222

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

225
#if !DEBUG
226
        try
227
#endif
228
        {
229
            foreach (var analyzedCustomAttribute in source.CustomAttributes)
×
230
            {
231
                var asmResolverCustomAttribute = ConvertCustomAttribute(analyzedCustomAttribute, assemblyDefinition);
×
232
                if (asmResolverCustomAttribute != null)
×
233
                    destination.Add(asmResolverCustomAttribute);
×
234
            }
235
        }
×
236
#if !DEBUG
237
        catch (Exception e)
×
238
        {
239
            throw new("Failed to copy custom attributes for " + source, e);
×
240
        }
241
#endif
242
    }
×
243

244
    public static void PopulateCustomAttributes(AssemblyAnalysisContext asmContext)
245
    {
246
#if !DEBUG
247
        try
248
#endif
249
        {
250
            CopyCustomAttributes(asmContext, asmContext.GetExtraData<AssemblyDefinition>("AsmResolverAssembly")!.CustomAttributes);
210✔
251

252
            foreach (var type in asmContext.Types)
29,680✔
253
            {
254
                if (IsTypeContextModule(type))
14,630✔
255
                    continue;
256

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

259
                foreach (var method in type.Methods)
202,900✔
260
                {
261
                    var methodDef = method.GetExtraData<MethodDefinition>("AsmResolverMethod")!;
87,030✔
262
                    CopyCustomAttributes(method, methodDef.CustomAttributes);
87,030✔
263

264
                    var parameterDefinitions = methodDef.ParameterDefinitions;
87,030✔
265
                    foreach (var parameterAnalysisContext in method.Parameters)
370,530✔
266
                    {
267
                        CopyCustomAttributes(parameterAnalysisContext, parameterDefinitions[parameterAnalysisContext.ParameterIndex].CustomAttributes);
98,235✔
268
                    }
269
                }
270

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

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

277
                foreach (var eventDefinition in type.Events)
28,990✔
278
                    CopyCustomAttributes(eventDefinition, eventDefinition.GetExtraData<EventDefinition>("AsmResolverEvent")!.CustomAttributes);
75✔
279
            }
280
        }
210✔
281
#if !DEBUG
282
        catch (Exception e)
×
283
        {
284
            throw new($"Failed to populate custom attributes in {asmContext}", e);
×
285
        }
286
#endif
287
    }
210✔
288

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

293
        foreach (var typeContext in asmContext.Types)
29,680✔
294
        {
295
            if (IsTypeContextModule(typeContext))
14,630✔
296
                continue;
297

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

301
#if !DEBUG
302
            try
303
#endif
304
            {
305
                CopyIl2CppDataToManagedType(typeContext, managedType);
14,420✔
306
            }
14,420✔
307
#if !DEBUG
308
            catch (Exception e)
×
309
            {
310
                throw new Exception($"Failed to process type {managedType.FullName} (module {managedType.Module?.Name}, declaring type {managedType.DeclaringType?.FullName}) in {asmContext.Definition.AssemblyName.Name}", e);
×
311
            }
312
#endif
313
        }
314
    }
210✔
315

316
    private static void CopyIl2CppDataToManagedType(TypeAnalysisContext typeContext, TypeDefinition ilTypeDefinition)
317
    {
318
        var importer = ilTypeDefinition.Module!.DefaultImporter;
14,420✔
319

320
        CopyFieldsInType(importer, typeContext, ilTypeDefinition);
14,420✔
321

322
        CopyMethodsInType(importer, typeContext, ilTypeDefinition);
14,420✔
323

324
        CopyPropertiesInType(importer, typeContext, ilTypeDefinition);
14,420✔
325

326
        CopyEventsInType(importer, typeContext, ilTypeDefinition);
14,420✔
327
    }
14,420✔
328

329
    private static void CopyFieldsInType(ReferenceImporter importer, TypeAnalysisContext typeContext, TypeDefinition ilTypeDefinition)
330
    {
331
        foreach (var fieldContext in typeContext.Fields)
146,630✔
332
        {
333
            var fieldInfo = fieldContext.BackingData;
58,895✔
334

335
            var fieldTypeSig = fieldContext.ToTypeSignature(importer.TargetModule);
58,895✔
336

337
            var managedField = new FieldDefinition(fieldContext.Name, (FieldAttributes)fieldContext.Attributes, fieldTypeSig);
58,895✔
338

339
            if (fieldInfo != null)
58,895✔
340
            {
341
                //Field default values
342
                if (managedField.HasDefault && fieldInfo.Field.DefaultValue?.Value is { } constVal)
58,895!
343
                    managedField.Constant = AsmResolverConstants.GetOrCreateConstant(constVal);
21,360✔
344

345
                //Field Initial Values (used for allocation of Array Literals)
346
                if (managedField.HasFieldRva)
58,895✔
347
                    managedField.FieldRva = new DataSegment(fieldInfo.Field.StaticArrayInitialValue);
540✔
348

349
                if (ilTypeDefinition.IsExplicitLayout && !fieldContext.IsStatic)
58,895✔
350
                    //Copy field offset
351
                    managedField.FieldOffset = fieldInfo.FieldOffset;
260✔
352
            }
353

354
            fieldContext.PutExtraData("AsmResolverField", managedField);
58,895✔
355

356
            ilTypeDefinition.Fields.Add(managedField);
58,895✔
357
        }
358
    }
14,420✔
359

360
    private static void CopyMethodsInType(ReferenceImporter importer, TypeAnalysisContext typeContext, TypeDefinition ilTypeDefinition)
361
    {
362
        foreach (var methodCtx in typeContext.Methods)
202,900✔
363
        {
364
            var returnType = methodCtx.ReturnType.ToTypeSignature(importer.TargetModule);
87,030✔
365

366
            var paramData = methodCtx.Parameters;
87,030✔
367
            var parameterTypes = new TypeSignature[paramData.Count];
87,030✔
368
            var parameterDefinitions = new ParameterDefinition[paramData.Count];
87,030✔
369
            foreach (var parameterAnalysisContext in methodCtx.Parameters)
370,530✔
370
            {
371
                var i = parameterAnalysisContext.ParameterIndex;
98,235✔
372
                parameterTypes[i] = parameterAnalysisContext.ParameterType.ToTypeSignature(importer.TargetModule);
98,235✔
373

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

377
                if (parameterAnalysisContext.DefaultValue is not { } defaultValueData)
98,235✔
378
                    continue;
379

380
                if (defaultValueData?.ContainedDefaultValue is { } constVal)
415!
381
                    parameterDefinitions[i].Constant = AsmResolverConstants.GetOrCreateConstant(constVal);
350✔
382
                else if (defaultValueData is { dataIndex: -1 })
65✔
383
                {
384
                    //Literal null
385
                    parameterDefinitions[i].Constant = AsmResolverConstants.Null;
65✔
386
                }
387
            }
388

389

390
            var signature = methodCtx.IsStatic
87,030✔
391
                ? MethodSignature.CreateStatic(returnType, methodCtx.GenericParameterCount, parameterTypes)
87,030✔
392
                : MethodSignature.CreateInstance(returnType, methodCtx.GenericParameterCount, parameterTypes);
87,030✔
393

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

396
            if (methodCtx.Definition != null)
87,030✔
397
            {
398
                managedMethod.ImplAttributes = (MethodImplAttributes)methodCtx.Definition.MethodImplAttributes;
87,030✔
399
                if (methodCtx.Definition.IsUnmanagedCallersOnly && typeContext.AppContext.SystemTypes.UnmanagedCallersOnlyAttributeType != null)
87,030!
400
                {
401
                    var unmanagedCallersOnlyType = typeContext.AppContext.SystemTypes.UnmanagedCallersOnlyAttributeType.GetExtraData<TypeDefinition>("AsmResolverType");
×
402
                    if(unmanagedCallersOnlyType != null)
×
403
                        managedMethod.CustomAttributes.Add(new CustomAttribute((ICustomAttributeType)importer.ImportMethod(unmanagedCallersOnlyType.GetConstructor()!), new()));
×
404
                }
405

406
            }
407

408
            //Add parameter definitions if we have them so we get names, defaults, out params, etc
409
            foreach (var parameterDefinition in parameterDefinitions)
370,530✔
410
            {
411
                managedMethod.ParameterDefinitions.Add(parameterDefinition);
98,235✔
412
            }
413

414
            //Handle generic parameters.
415
            methodCtx.GenericParameters
87,030✔
416
                .ForEach(p =>
87,030✔
417
                {
87,030✔
418
                    var gp = new GenericParameter(p.Name, (GenericParameterAttributes)p.Attributes);
975✔
419

87,030✔
420
                    if (!managedMethod.GenericParameters.Contains(gp))
975✔
421
                        managedMethod.GenericParameters.Add(gp);
975✔
422

87,030✔
423
                    p.ConstraintTypes
975✔
424
                        .Select(c => new GenericParameterConstraint(c.ToTypeSignature(ilTypeDefinition.Module!).ToTypeDefOrRef()))
175✔
425
                        .ToList()
975✔
426
                        .ForEach(gp.Constraints.Add);
975✔
427
                });
88,005✔
428

429

430
            methodCtx.PutExtraData("AsmResolverMethod", managedMethod);
87,030✔
431
            ilTypeDefinition.Methods.Add(managedMethod);
87,030✔
432
        }
433
    }
14,420✔
434

435
    private static void CopyPropertiesInType(ReferenceImporter importer, TypeAnalysisContext typeContext, TypeDefinition ilTypeDefinition)
436
    {
437
        foreach (var propertyCtx in typeContext.Properties)
59,560✔
438
        {
439
            var propertyTypeSig = propertyCtx.ToTypeSignature(importer.TargetModule);
15,360✔
440
            var propertySignature = propertyCtx.IsStatic
15,360✔
441
                ? PropertySignature.CreateStatic(propertyTypeSig)
15,360✔
442
                : PropertySignature.CreateInstance(propertyTypeSig);
15,360✔
443

444
            var managedProperty = new PropertyDefinition(propertyCtx.Name, (PropertyAttributes)propertyCtx.Attributes, propertySignature);
15,360✔
445

446
            var managedGetter = propertyCtx.Getter?.GetExtraData<MethodDefinition>("AsmResolverMethod");
15,360✔
447
            var managedSetter = propertyCtx.Setter?.GetExtraData<MethodDefinition>("AsmResolverMethod");
15,360✔
448

449
            managedProperty.SetSemanticMethods(managedGetter, managedSetter);
15,360✔
450

451
            //Indexer parameters
452
            if (managedGetter != null && managedGetter.Parameters.Count > 0)
15,360✔
453
            {
454
                foreach (var parameter in managedGetter.Parameters)
860✔
455
                {
456
                    propertySignature.ParameterTypes.Add(parameter.ParameterType);
215✔
457
                }
458
            }
459
            else if (managedSetter != null && managedSetter.Parameters.Count > 1)
15,145✔
460
            {
461
                //value parameter is always last
462
                for (var i = 0; i < managedSetter.Parameters.Count - 1; i++)
60✔
463
                {
464
                    var parameter = managedSetter.Parameters[i];
15✔
465
                    propertySignature.ParameterTypes.Add(parameter.ParameterType);
15✔
466
                }
467
            }
468

469
            propertyCtx.PutExtraData("AsmResolverProperty", managedProperty);
15,360✔
470

471
            ilTypeDefinition.Properties.Add(managedProperty);
15,360✔
472
        }
473
    }
14,420✔
474

475
    private static void CopyEventsInType(ReferenceImporter importer, TypeAnalysisContext cppTypeDefinition, TypeDefinition ilTypeDefinition)
476
    {
477
        foreach (var eventCtx in cppTypeDefinition.Events)
28,990✔
478
        {
479
            var eventType = eventCtx.ToTypeSignature(importer.TargetModule).ToTypeDefOrRef();
75✔
480

481
            var managedEvent = new EventDefinition(eventCtx.Name, (EventAttributes)eventCtx.Attributes, eventType);
75✔
482

483
            var managedAdder = eventCtx.Adder?.GetExtraData<MethodDefinition>("AsmResolverMethod");
75!
484
            var managedRemover = eventCtx.Remover?.GetExtraData<MethodDefinition>("AsmResolverMethod");
75!
485
            var managedInvoker = eventCtx.Invoker?.GetExtraData<MethodDefinition>("AsmResolverMethod");
75!
486

487
            managedEvent.SetSemanticMethods(managedAdder, managedRemover, managedInvoker);
75✔
488

489
            eventCtx.PutExtraData("AsmResolverEvent", managedEvent);
75✔
490

491
            ilTypeDefinition.Events.Add(managedEvent);
75✔
492
        }
493
    }
14,420✔
494

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

499
        var importer = managedAssembly.ManifestModule!.DefaultImporter;
210✔
500

501
        foreach (var typeContext in asmContext.Types)
29,680✔
502
        {
503
            if (IsTypeContextModule(typeContext))
14,630✔
504
                continue;
505

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

508
#if !DEBUG
509
            try
510
#endif
511
            {
512
                AddExplicitInterfaceImplementations(managedType, typeContext, importer);
14,420✔
513
            }
14,420✔
514
#if !DEBUG
515
            catch (Exception e)
×
516
            {
517
                throw new Exception($"Failed to process type {managedType.FullName} (module {managedType.Module?.Name}, declaring type {managedType.DeclaringType?.FullName}) in {asmContext.Definition.AssemblyName.Name}", e);
×
518
            }
519
#endif
520
        }
521
    }
210✔
522

523
    private static void AddExplicitInterfaceImplementations(TypeDefinition type, TypeAnalysisContext typeContext, ReferenceImporter importer)
524
    {
525
        List<(PropertyDefinition InterfaceProperty, TypeSignature InterfaceType, MethodDefinition Method)>? getMethodsToCreate = null;
14,420✔
526
        List<(PropertyDefinition InterfaceProperty, TypeSignature InterfaceType, MethodDefinition Method)>? setMethodsToCreate = null;
14,420✔
527

528
        foreach (var methodContext in typeContext.Methods)
202,900✔
529
        {
530
            if ((methodContext.Attributes & System.Reflection.MethodAttributes.MemberAccessMask) != System.Reflection.MethodAttributes.Private)
87,030✔
531
                continue;
532

533
            foreach (var overrideContext in methodContext.Overrides)
42,360✔
534
            {
535
                if (overrideContext.DeclaringType?.IsInterface ?? false)
3,150!
536
                {
537
                    var interfaceMethod = (IMethodDefOrRef)overrideContext.ToMethodDescriptor(importer.TargetModule);
3,115✔
538
                    var method = methodContext.GetExtraData<MethodDefinition>("AsmResolverMethod") ?? throw new($"AsmResolver method not found in method analysis context for {methodContext}");
3,115!
539
                    type.MethodImplementations.Add(new MethodImplementation(interfaceMethod, method));
3,115✔
540
                    var interfaceMethodResolved = interfaceMethod.Resolve();
3,115✔
541
                    if (interfaceMethodResolved != null)
3,115✔
542
                    {
543
                        if (interfaceMethodResolved.IsGetMethod && !method.IsGetMethod)
3,115✔
544
                        {
545
                            getMethodsToCreate ??= [];
40✔
546
                            var interfacePropertyResolved = interfaceMethodResolved.DeclaringType!.Properties.First(p => p.Semantics.Contains(interfaceMethodResolved.Semantics));
80✔
547
                            getMethodsToCreate.Add((interfacePropertyResolved, interfaceMethod.DeclaringType!.ToTypeSignature(), method));
40✔
548
                        }
549
                        else if (interfaceMethodResolved.IsSetMethod && !method.IsSetMethod)
3,075!
550
                        {
551
                            setMethodsToCreate ??= [];
×
552
                            var interfacePropertyResolved = interfaceMethodResolved.DeclaringType!.Properties.First(p => p.Semantics.Contains(interfaceMethodResolved.Semantics));
×
553
                            setMethodsToCreate.Add((interfacePropertyResolved, interfaceMethod.DeclaringType!.ToTypeSignature(), method));
×
554
                        }
555
                    }
556
                }
557
            }
558
        }
559

560
        // Il2Cpp doesn't include properties for explicit interface implementations, so we have to create them ourselves.
561
        if (getMethodsToCreate is not null)
14,420✔
562
        {
563
            foreach (var entry in getMethodsToCreate)
160✔
564
            {
565
                var (interfaceProperty, interfaceType, getMethod) = entry;
40✔
566
                var setMethod = setMethodsToCreate?
40!
567
                    .FirstOrDefault(e => e.InterfaceProperty == interfaceProperty && SignatureComparer.Default.Equals(e.InterfaceType, interfaceType))
×
568
                    .Method;
40✔
569

570
                var name = $"{interfaceType.FullName}.{interfaceProperty.Name}";
40✔
571
                var propertySignature = getMethod.IsStatic
40!
572
                    ? PropertySignature.CreateStatic(getMethod.Signature!.ReturnType, getMethod.Signature.ParameterTypes)
40✔
573
                    : PropertySignature.CreateInstance(getMethod.Signature!.ReturnType, getMethod.Signature.ParameterTypes);
40✔
574
                var property = new PropertyDefinition(name, interfaceProperty.Attributes, propertySignature);
40✔
575
                type.Properties.Add(property);
40✔
576
                property.SetSemanticMethods(getMethod, setMethod);
40✔
577
            }
578
        }
579
        if (setMethodsToCreate is not null)
14,420!
580
        {
581
            foreach (var entry in setMethodsToCreate)
×
582
            {
583
                var (interfaceProperty, interfaceType, setMethod) = entry;
×
584
                if (getMethodsToCreate?.Any(e => e.InterfaceProperty == interfaceProperty && SignatureComparer.Default.Equals(e.InterfaceType, interfaceType)) == true)
×
585
                    continue;
586
                var name = $"{interfaceType.FullName}.{interfaceProperty.Name}";
×
587
                var propertySignature = setMethod.IsStatic
×
588
                    ? PropertySignature.CreateStatic(setMethod.Signature!.ParameterTypes[^1], setMethod.Signature.ParameterTypes.Take(setMethod.Signature.ParameterTypes.Count - 1))
×
589
                    : PropertySignature.CreateInstance(setMethod.Signature!.ParameterTypes[^1], setMethod.Signature.ParameterTypes.Take(setMethod.Signature.ParameterTypes.Count - 1));
×
590
                var property = new PropertyDefinition(name, interfaceProperty.Attributes, propertySignature);
×
591
                type.Properties.Add(property);
×
592
                property.SetSemanticMethods(null, setMethod);
×
593
            }
594
        }
595
    }
14,420✔
596
}
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