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

SamboyCoding / Cpp2IL / 28725945259

05 Jul 2026 01:40AM UTC coverage: 36.238% (+0.1%) from 36.139%
28725945259

Pull #549

github

web-flow
Merge 5f32b622e into b1cb5091b
Pull Request #549: Ensure the base type of generic instance types is instantiated

2691 of 8492 branches covered (31.69%)

Branch coverage included in aggregate %.

92 of 118 new or added lines in 27 files covered. (77.97%)

2 existing lines in 2 files now uncovered.

5078 of 12947 relevant lines covered (39.22%)

171675.64 hits per line

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

68.18
/Cpp2IL.Core/Model/Contexts/GenericInstanceTypeAnalysisContext.cs
1
using System;
2
using System.Collections.Generic;
3
using System.Linq;
4
using System.Reflection;
5
using System.Text;
6
using Cpp2IL.Core.Utils;
7
using LibCpp2IL.BinaryStructures;
8

9
namespace Cpp2IL.Core.Model.Contexts;
10

11
public class GenericInstanceTypeAnalysisContext : ReferencedTypeAnalysisContext
12
{
13
    public TypeAnalysisContext GenericType { get; }
373,718✔
14

15
    public List<TypeAnalysisContext> GenericArguments { get; }
516,857✔
16

17
    public sealed override TypeAttributes DefaultAttributes => GenericType.DefaultAttributes;
×
18

19
    public sealed override TypeAttributes? OverrideAttributes { get => GenericType.OverrideAttributes; set => GenericType.OverrideAttributes = value; }
×
20

21
    public sealed override string DefaultName => $"{GenericType.DefaultName}<{string.Join(", ", GenericArguments.Select(a => a.DefaultFullName))}>";
×
22

23
    public sealed override string? OverrideName
24
    {
25
        get => $"{GenericType.Name}<{string.Join(", ", GenericArguments.Select(a => a.FullName))}>";
1,623✔
26
        set => throw new NotSupportedException();
×
27
    }
28

29
    public sealed override string DefaultNamespace => GenericType.DefaultNamespace;
612✔
30

31
    public sealed override string? OverrideNamespace
32
    {
33
        get => GenericType.OverrideNamespace;
612✔
34
        set => GenericType.OverrideNamespace = value;
×
35
    }
36

37
    public sealed override TypeAnalysisContext? DefaultBaseType
38
    {
39
        get
40
        {
41
            field ??= GenericType.DefaultBaseType is null ? null : GenericInstantiation.Instantiate(GenericType.DefaultBaseType, GenericArguments, []);
3!
42
            return field;
3✔
43
        }
44
    }
45

46
    public sealed override TypeAnalysisContext? OverrideBaseType
47
    {
48
        get
49
        {
50
            if (base.OverrideBaseType is null && GenericType.OverrideBaseType is not null)
3!
51
            {
NEW
52
                base.OverrideBaseType = GenericInstantiation.Instantiate(GenericType.OverrideBaseType, GenericArguments, []);
×
53
            }
54
            return base.OverrideBaseType;
3✔
55
        }
NEW
56
        set => throw new NotSupportedException();
×
57
    }
58

59
    public sealed override Il2CppTypeEnum Type => Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST;
×
60

61
    public sealed override bool IsGenericInstance => true;
×
62

63
    public sealed override bool IsValueType => GenericType.IsValueType; //We don't set a definition so the default implementation cannot determine if we're a value type or not. 
8,920✔
64

65
    // instances being constructed on the current thread, keyed by their cache identity (see GetOrCreate for why)
66
    [ThreadStatic] private static Dictionary<(ApplicationAnalysisContext, Il2CppType), GenericInstanceTypeAnalysisContext>? _underConstruction1;
67
    [ThreadStatic] private static Dictionary<(ApplicationAnalysisContext, TypeAnalysisContext, GenericArgumentList), GenericInstanceTypeAnalysisContext>? _underConstruction2;
68

69
    private GenericInstanceTypeAnalysisContext(Il2CppType rawType, ApplicationAnalysisContext context) : base(context.ResolveContextForAssembly(rawType.GetGenericClass().TypeDefinition.DeclaringAssembly!))
17,574✔
70
    {
71
        var underConstruction1 = _underConstruction1 ??= new();
17,574✔
72
        underConstruction1[(context, rawType)] = this;
17,574✔
73
        try
74
        {
75
            //Generic type has to be a type definition
76
            var gClass = rawType.GetGenericClass();
17,574✔
77
            GenericType = AppContext.ResolveContextForType(gClass.TypeDefinition) ?? throw new($"Could not resolve type {gClass.TypeDefinition.FullName} for generic instance base type");
17,574!
78

79
            GenericArguments = [.. gClass.Context.ClassInst!.Types.Select(t => context.ResolveIl2CppType(t))];
39,099✔
80

81
            var underConstruction2 = _underConstruction2 ??= new();
17,574✔
82
            underConstruction2.Add((context, GenericType, GenericArguments), this);
17,574✔
83
            try
84
            {
85
                SetDeclaringType();
17,574✔
86
            }
17,574✔
87
            finally
88
            {
89
                underConstruction2.Remove((context, GenericType, GenericArguments));
17,574✔
90
            }
17,574✔
91
        }
92
        finally
93
        {
94
            underConstruction1.Remove((context, rawType));
17,574✔
95
        }
17,574✔
96
    }
17,574✔
97

98
    private GenericInstanceTypeAnalysisContext(TypeAnalysisContext genericType, List<TypeAnalysisContext> genericArguments) : base(genericType.DeclaringAssembly)
85,379✔
99
    {
100
        GenericType = genericType;
85,379✔
101
        GenericArguments = genericArguments;
85,379✔
102

103
        var underConstruction = _underConstruction2 ??= new();
85,379!
104
        underConstruction.Add((genericType.AppContext, genericType, genericArguments), this);
85,379✔
105
        try
106
        {
107
            SetDeclaringType();
85,379✔
108
        }
85,379✔
109
        finally
110
        {
111
            underConstruction.Remove((genericType.AppContext, genericType, genericArguments));
85,379✔
112
        }
85,379✔
113
    }
85,379✔
114

115
    /// <summary>
116
    /// Get or create a <see cref="GenericInstanceTypeAnalysisContext"/> from an <see cref="Il2CppType"/>.
117
    /// </summary>
118
    /// <param name="rawType">The underlying <see cref="Il2CppType"/>.</param>
119
    /// <param name="referencedFrom">The assembly that is referencing this generic instance.</param>
120
    /// <returns>The context for the <paramref name="rawType"/>.</returns>
121
    public static GenericInstanceTypeAnalysisContext GetOrCreate(Il2CppType rawType, ApplicationAnalysisContext referencedFrom)
122
    {
123
        if (rawType.Type != Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST)
117,318!
124
            throw new ArgumentException($"Cannot create {nameof(GenericInstanceTypeAnalysisContext)} from type {rawType.Type}. Expected {Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST}.");
×
125

126
        // A self-referencing generic re-enters here while we're still building it (#469). In this case, the constructing thread gets 
127
        // its own in-progress instance from a thread-local map instead (otherwise it recurses into Lazy.Value, which throws).
128
        if (_underConstruction1 != null && _underConstruction1.TryGetValue((referencedFrom, rawType), out var partial))
117,318!
129
            return partial;
×
130

131
        return referencedFrom.GenericInstanceTypesByIl2CppType
117,318✔
132
            .GetOrAdd(rawType, key => new Lazy<GenericInstanceTypeAnalysisContext>(() => new GenericInstanceTypeAnalysisContext(key, referencedFrom)))
35,148✔
133
            .Value;
117,318✔
134
    }
135

136
    public static GenericInstanceTypeAnalysisContext GetOrCreate(TypeAnalysisContext genericType, IEnumerable<TypeAnalysisContext> genericArguments)
137
    {
138
        var genericArgumentsList = genericArguments.ToList();
685,620✔
139

140
        // A self-referencing generic re-enters here while we're still building it (#469). In this case, the constructing thread gets 
141
        // its own in-progress instance from a thread-local map instead (otherwise it recurses into Lazy.Value, which throws).
142
        if (_underConstruction2 != null && _underConstruction2.TryGetValue((genericType.AppContext, genericType, genericArgumentsList), out var partial))
685,620!
NEW
143
            return partial;
×
144

145
        return genericType.AppContext.GenericInstanceTypesByConstruction
685,620✔
146
            .GetOrAdd((genericType, genericArgumentsList), key => new Lazy<GenericInstanceTypeAnalysisContext>(() => new GenericInstanceTypeAnalysisContext(key.Item1, key.Item2)))
170,758✔
147
            .Value;
685,620✔
148
    }
149

150
    public override string GetCSharpSourceString()
151
    {
152
        var sb = new StringBuilder();
×
153

154
        sb.Append(GenericType.GetCSharpSourceString());
×
155
        sb.Append('<');
×
156
        var first = true;
×
157
        foreach (var genericArgument in GenericArguments)
×
158
        {
159
            if (!first)
×
160
                sb.Append(", ");
×
161
            else
162
                first = false;
×
163

164
            sb.Append(genericArgument.GetCSharpSourceString());
×
165
        }
166

167
        sb.Append('>');
×
168

169
        return sb.ToString();
×
170
    }
171

172
    protected override List<TypeAnalysisContext> GetInterfaceContexts()
173
    {
174
        return GenericType.InterfaceContexts.Select(i => GenericInstantiation.Instantiate(i, GenericArguments, [])).ToList();
5✔
175
    }
176

177
    private void SetDeclaringType()
178
    {
179
        var declaringType = GenericType.DeclaringType;
102,953✔
180
        if (declaringType is null)
102,953✔
181
            return;
69,277✔
182

183
        DeclaringType = declaringType.GenericParameters.Count == 0
33,676✔
184
            ? declaringType
33,676✔
185
            : declaringType.MakeGenericInstanceType(GenericArguments.Take(declaringType.GenericParameters.Count));
33,676✔
186
    }
33,676✔
187
}
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

© 2026 Coveralls, Inc