• 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

54.13
/Cpp2IL.Core/Utils/AsmResolver/AsmResolverUtils.cs
1
using System;
2
using System.Collections.Concurrent;
3
using System.Diagnostics;
4
using System.Globalization;
5
using System.Linq;
6
using AsmResolver.DotNet;
7
using AsmResolver.DotNet.Signatures;
8
using LibCpp2IL;
9
using LibCpp2IL.BinaryStructures;
10

11
namespace Cpp2IL.Core.Utils.AsmResolver;
12

13
public static class AsmResolverUtils
14
{
15
    private static readonly ConcurrentDictionary<string, TypeDefinition?> CachedTypeDefsByName = new();
1✔
16
    private static readonly ConcurrentDictionary<string, TypeSignature?> CachedTypeSignaturesByName = new();
1✔
17

18
    public static readonly ConcurrentDictionary<long, TypeDefinition> TypeDefsByIndex = new();
1✔
19
    public static readonly ConcurrentDictionary<long, GenericParameter> GenericParamsByIndexNew = new();
1✔
20

21
    internal static void Reset()
22
    {
23
        CachedTypeDefsByName.Clear();
35✔
24
        CachedTypeSignaturesByName.Clear();
35✔
25
        TypeDefsByIndex.Clear();
35✔
26
        GenericParamsByIndexNew.Clear();
35✔
27
    }
35✔
28

29
    public static TypeDefinition GetPrimitiveTypeDef(Il2CppTypeEnum type) =>
30
        type switch
5,035!
31
        {
5,035✔
32
            Il2CppTypeEnum.IL2CPP_TYPE_OBJECT => TypeDefinitionsAsmResolver.Object,
4,555✔
33
            Il2CppTypeEnum.IL2CPP_TYPE_VOID => TypeDefinitionsAsmResolver.Void,
×
34
            Il2CppTypeEnum.IL2CPP_TYPE_BOOLEAN => TypeDefinitionsAsmResolver.Boolean,
35✔
35
            Il2CppTypeEnum.IL2CPP_TYPE_CHAR => TypeDefinitionsAsmResolver.Char,
30✔
36
            Il2CppTypeEnum.IL2CPP_TYPE_I1 => TypeDefinitionsAsmResolver.SByte,
20✔
37
            Il2CppTypeEnum.IL2CPP_TYPE_U1 => TypeDefinitionsAsmResolver.Byte,
25✔
38
            Il2CppTypeEnum.IL2CPP_TYPE_I2 => TypeDefinitionsAsmResolver.Int16,
20✔
39
            Il2CppTypeEnum.IL2CPP_TYPE_U2 => TypeDefinitionsAsmResolver.UInt16,
20✔
40
            Il2CppTypeEnum.IL2CPP_TYPE_I4 => TypeDefinitionsAsmResolver.Int32,
80✔
41
            Il2CppTypeEnum.IL2CPP_TYPE_U4 => TypeDefinitionsAsmResolver.UInt32,
20✔
42
            Il2CppTypeEnum.IL2CPP_TYPE_I => TypeDefinitionsAsmResolver.IntPtr,
10✔
43
            Il2CppTypeEnum.IL2CPP_TYPE_U => TypeDefinitionsAsmResolver.UIntPtr,
10✔
44
            Il2CppTypeEnum.IL2CPP_TYPE_I8 => TypeDefinitionsAsmResolver.Int64,
20✔
45
            Il2CppTypeEnum.IL2CPP_TYPE_U8 => TypeDefinitionsAsmResolver.UInt64,
20✔
46
            Il2CppTypeEnum.IL2CPP_TYPE_R4 => TypeDefinitionsAsmResolver.Single,
45✔
47
            Il2CppTypeEnum.IL2CPP_TYPE_R8 => TypeDefinitionsAsmResolver.Double,
20✔
48
            Il2CppTypeEnum.IL2CPP_TYPE_STRING => TypeDefinitionsAsmResolver.String,
105✔
49
            Il2CppTypeEnum.IL2CPP_TYPE_TYPEDBYREF => TypeDefinitionsAsmResolver.TypedReference,
×
50
            Il2CppTypeEnum.IL2CPP_TYPE_IL2CPP_TYPE_INDEX => TypeDefinitionsAsmResolver.Type,
×
51
            _ => throw new ArgumentException($"Type is not a primitive - {type}", nameof(type))
×
52
        };
5,035✔
53

54
    public static TypeSignature GetTypeSignatureFromIl2CppType(ModuleDefinition module, Il2CppType il2CppType)
55
    {
56
        //Module is needed for generic params
57
        if (il2CppType == null)
4,230!
58
            throw new ArgumentNullException(nameof(il2CppType));
×
59

60
        TypeSignature ret;
61
        switch (il2CppType.Type)
4,230!
62
        {
63
            case Il2CppTypeEnum.IL2CPP_TYPE_OBJECT:
64
            case Il2CppTypeEnum.IL2CPP_TYPE_VOID:
65
            case Il2CppTypeEnum.IL2CPP_TYPE_BOOLEAN:
66
            case Il2CppTypeEnum.IL2CPP_TYPE_CHAR:
67
            case Il2CppTypeEnum.IL2CPP_TYPE_I1:
68
            case Il2CppTypeEnum.IL2CPP_TYPE_U1:
69
            case Il2CppTypeEnum.IL2CPP_TYPE_I2:
70
            case Il2CppTypeEnum.IL2CPP_TYPE_U2:
71
            case Il2CppTypeEnum.IL2CPP_TYPE_I4:
72
            case Il2CppTypeEnum.IL2CPP_TYPE_U4:
73
            case Il2CppTypeEnum.IL2CPP_TYPE_I:
74
            case Il2CppTypeEnum.IL2CPP_TYPE_U:
75
            case Il2CppTypeEnum.IL2CPP_TYPE_I8:
76
            case Il2CppTypeEnum.IL2CPP_TYPE_U8:
77
            case Il2CppTypeEnum.IL2CPP_TYPE_R4:
78
            case Il2CppTypeEnum.IL2CPP_TYPE_R8:
79
            case Il2CppTypeEnum.IL2CPP_TYPE_STRING:
80
            case Il2CppTypeEnum.IL2CPP_TYPE_TYPEDBYREF:
81
                ret = module.DefaultImporter.ImportType(GetPrimitiveTypeDef(il2CppType.Type))
570✔
82
                    .ToTypeSignature();
570✔
83
                break;
570✔
84
            case Il2CppTypeEnum.IL2CPP_TYPE_CLASS:
85
            case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE:
86
                ret = module.DefaultImporter.ImportType(TypeDefsByIndex[il2CppType.Data.ClassIndex])
740✔
87
                    .ToTypeSignature();
740✔
88
                break;
740✔
89
            case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY:
90
                ret = GetTypeSignatureFromIl2CppType(module, il2CppType.GetArrayElementType())
×
91
                    .MakeArrayTypeWithLowerBounds(il2CppType.GetArrayRank());
×
92
                break;
×
93
            case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY:
94
                ret = GetTypeSignatureFromIl2CppType(module, il2CppType.GetEncapsulatedType())
80✔
95
                    .MakeSzArrayType();
80✔
96
                break;
80✔
97
            case Il2CppTypeEnum.IL2CPP_TYPE_PTR:
UNCOV
98
                ret = GetTypeSignatureFromIl2CppType(module, il2CppType.GetEncapsulatedType())
×
UNCOV
99
                    .MakePointerType();
×
UNCOV
100
                break;
×
101
            case Il2CppTypeEnum.IL2CPP_TYPE_VAR: //Generic type parameter
102
            case Il2CppTypeEnum.IL2CPP_TYPE_MVAR: //Generic method parameter
103
                var method = il2CppType.Type == Il2CppTypeEnum.IL2CPP_TYPE_MVAR;
780✔
104
                ret = new GenericParameterSignature(module, method ? GenericParameterType.Method : GenericParameterType.Type, il2CppType.GetGenericParameterDef().genericParameterIndexInOwner);
780✔
105
                break;
780✔
106
            case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST:
107
            {
108
                var genericClass = il2CppType.GetGenericClass();
2,060✔
109

110
                //Get base type
111
                TypeDefsByIndex.TryGetValue(genericClass.TypeDefinitionIndex, out var typeDefinition);
2,060✔
112
                if (Cpp2IlApi.CurrentAppContext!.MetadataVersion >= 27f) //TODO: we should pass in the app context to this method
2,060!
113
                {
114
                    //V27 - type indexes are pointers now.
115
                    var type = LibCpp2IlMain.Binary!.ReadReadableAtVirtualAddress<Il2CppType>((ulong)genericClass.TypeDefinitionIndex);
×
116
                    typeDefinition = GetTypeSignatureFromIl2CppType(module, type).Resolve() ?? throw new Exception("Unable to resolve base type for generic inst");
×
117
                }
118

119
                var genericInstanceType = new GenericInstanceTypeSignature(module.DefaultImporter.ImportType(typeDefinition!), typeDefinition!.IsValueType);
2,060✔
120

121
                //Get generic arguments
122
                var genericArgumentTypes = genericClass.Context.ClassInst.Types;
2,060✔
123

124
                //Add arguments to generic instance
125
                foreach (var type in genericArgumentTypes)
8,540✔
126
                    genericInstanceType.TypeArguments.Add(GetTypeSignatureFromIl2CppType(module, type));
2,210✔
127

128
                ret = genericInstanceType;
2,060✔
129
                break;
2,060✔
130
            }
131
            default:
132
                throw new("Don't know how to make a type signature from " + il2CppType.Type);
×
133
        }
134

135
        if (il2CppType.Byref == 1)
4,230!
136
            ret = ret.MakeByReferenceType();
×
137

138
        return ret;
4,230✔
139
    }
140

141
    /// <summary>
142
    /// Imports the managed representation of the given il2cpp type using the given importer, and returns said type.
143
    /// <br/><br/>
144
    /// Prefer <see cref="GetTypeSignatureFromIl2CppType"/> where possible, only use this where an actual type reference is needed.
145
    /// Such cases would include generic parameter constraints, base types/interfaces, and event types.
146
    /// </summary>
147
    public static ITypeDefOrRef ImportReferenceFromIl2CppType(ModuleDefinition module, Il2CppType il2CppType)
148
    {
149
        if (il2CppType == null)
18,665!
150
            throw new ArgumentNullException(nameof(il2CppType));
×
151

152
        switch (il2CppType.Type)
18,665!
153
        {
154
            case Il2CppTypeEnum.IL2CPP_TYPE_OBJECT:
155
            case Il2CppTypeEnum.IL2CPP_TYPE_VOID:
156
            case Il2CppTypeEnum.IL2CPP_TYPE_BOOLEAN:
157
            case Il2CppTypeEnum.IL2CPP_TYPE_CHAR:
158
            case Il2CppTypeEnum.IL2CPP_TYPE_I1:
159
            case Il2CppTypeEnum.IL2CPP_TYPE_U1:
160
            case Il2CppTypeEnum.IL2CPP_TYPE_I2:
161
            case Il2CppTypeEnum.IL2CPP_TYPE_U2:
162
            case Il2CppTypeEnum.IL2CPP_TYPE_I4:
163
            case Il2CppTypeEnum.IL2CPP_TYPE_U4:
164
            case Il2CppTypeEnum.IL2CPP_TYPE_I:
165
            case Il2CppTypeEnum.IL2CPP_TYPE_U:
166
            case Il2CppTypeEnum.IL2CPP_TYPE_I8:
167
            case Il2CppTypeEnum.IL2CPP_TYPE_U8:
168
            case Il2CppTypeEnum.IL2CPP_TYPE_R4:
169
            case Il2CppTypeEnum.IL2CPP_TYPE_R8:
170
            case Il2CppTypeEnum.IL2CPP_TYPE_STRING:
171
            case Il2CppTypeEnum.IL2CPP_TYPE_TYPEDBYREF:
172
                //This case, and the one below, are faster to go this way rather than delegating to type signature creation, because we can go straight from def -> ref.
173
                return module.DefaultImporter.ImportType(GetPrimitiveTypeDef(il2CppType.Type));
4,465✔
174
            case Il2CppTypeEnum.IL2CPP_TYPE_CLASS:
175
            case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE:
176
                return module.DefaultImporter.ImportType(TypeDefsByIndex[il2CppType.Data.ClassIndex]);
12,260✔
177
            case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY:
178
            case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST:
179
            case Il2CppTypeEnum.IL2CPP_TYPE_PTR:
180
            case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY:
181
            case Il2CppTypeEnum.IL2CPP_TYPE_VAR:
182
            case Il2CppTypeEnum.IL2CPP_TYPE_MVAR:
183
                //For the rest of these, we have to make a type signature first anyway, so just delegate to signature getter
184
                return GetTypeSignatureFromIl2CppType(module, il2CppType).ToTypeDefOrRef();
1,940✔
185
            default:
186
                throw new("Don't know how to import a type reference from an il2cpp type of type " + il2CppType.Type);
×
187
        }
188
    }
189

190
    public static TypeDefinition? TryLookupTypeDefKnownNotGeneric(string? name)
191
    {
192
        if (name == null)
140!
193
            return null;
×
194

195
        if (TypeDefinitionsAsmResolver.GetPrimitive(name) is { } primitive)
140!
196
            return primitive;
×
197

198
        var key = name.ToLower(CultureInfo.InvariantCulture);
140✔
199

200
        if (CachedTypeDefsByName.TryGetValue(key, out var ret))
140✔
201
            return ret;
5✔
202

203
        var definedType = Cpp2IlApi.CurrentAppContext!.AllTypes.FirstOrDefault(t => t.Definition != null && string.Equals(t.Definition.FullName, name, StringComparison.OrdinalIgnoreCase));
36,055!
204

205
        //Try subclasses
206
        definedType ??= Cpp2IlApi.CurrentAppContext.AllTypes.FirstOrDefault(t =>
135!
207
        {
135✔
208
            return t.Definition?.FullName != null
×
209
                && t.Definition.FullName.Contains('/')
×
210
                && string.Equals(t.Definition.FullName.Replace('/', '.'), name, StringComparison.OrdinalIgnoreCase);
×
211
        });
135✔
212

213
        ret = definedType?.GetExtraData<TypeDefinition>("AsmResolverType");
135!
214
        
215
        if (ret == null)
135!
216
            return null;
×
217
        
218
        CachedTypeDefsByName.TryAdd(key, ret);
135✔
219
        return ret;
135✔
220
    }
221

222
    public static TypeSignature? TryLookupTypeSignatureByName(string? name, ReadOnlySpan<string> genericParameterNames = default)
223
    {
224
        if (name == null)
×
225
            return null;
×
226

227
        var key = name.ToLower(CultureInfo.InvariantCulture);
×
228

229
        if (genericParameterNames.Length == 0 && CachedTypeSignaturesByName.TryGetValue(key, out var ret))
×
230
            return ret;
×
231

232
        var result = InternalTryLookupTypeSignatureByName(name, genericParameterNames);
×
233

234
        if (genericParameterNames.Length == 0)
×
235
            CachedTypeSignaturesByName.TryAdd(key, result);
×
236

237
        return result;
×
238
    }
239

240
    private static TypeSignature? InternalTryLookupTypeSignatureByName(string name, ReadOnlySpan<string> genericParameterNames = default)
241
    {
242
        if (TypeDefinitionsAsmResolver.GetPrimitive(name) is { } primitive)
×
243
            return primitive.ToTypeSignature();
×
244

245
        //The only real cases we end up here are:
246
        //From explicit override resolving, because that has to be done by name
247
        //Sometimes in attribute restoration if we come across an object parameter, but this almost always has to be a system or cecil type, or an enum.
248
        //While originally initializing the TypeDefinitions class, which is always a system type
249
        //And during exception helper location, which is always a system type.
250
        //So really the only remapping we should have to handle is during explicit override restoration.
251

252
        if (name.EndsWith("[]", StringComparison.Ordinal))
×
253
        {
254
            var without = name[..^2];
×
255
            var result = InternalTryLookupTypeSignatureByName(without, genericParameterNames);
×
256
            return result?.MakeSzArrayType();
×
257
        }
258

259
        var parsedType = Parse(name);
×
260

261
        // Arrays should be handled above
262
        Debug.Assert(parsedType.Suffix is "");
263

264
        var genericParameterIndex = genericParameterNames.IndexOf(parsedType.BaseType);
×
265
        if (genericParameterIndex >= 0)
×
266
            return new GenericParameterSignature(GenericParameterType.Type, genericParameterIndex);
×
267

268
        var baseType = TryLookupTypeDefKnownNotGeneric(parsedType.BaseType);
×
269
        if (baseType == null)
×
270
            return null;
×
271

272
        if (parsedType.GenericArguments.Length == 0)
×
273
            return baseType.ToTypeSignature();
×
274

275
        var typeArguments = new TypeSignature[parsedType.GenericArguments.Length];
×
276
        for (var i = 0; i < parsedType.GenericArguments.Length; i++)
×
277
        {
278
            var typeArgument = InternalTryLookupTypeSignatureByName(parsedType.GenericArguments[i], genericParameterNames);
×
279
            if (typeArgument == null)
×
280
                return null;
×
281
            typeArguments[i] = typeArgument;
×
282
        }
283

284
        return baseType.MakeGenericInstanceType(typeArguments);
×
285
    }
286

287
    private readonly record struct ParsedTypeString(string BaseType, string Suffix, string[] GenericArguments);
×
288

289
    private static ParsedTypeString Parse(string name)
290
    {
291
        var firstAngleBracket = name.IndexOf('<');
×
292
        if (firstAngleBracket < 0)
×
293
        {
294
            var firstSquareBracket = name.IndexOf('[');
×
295
            if (firstSquareBracket < 0)
×
296
                return new ParsedTypeString(name, "", []);
×
297
            else
298
                return new ParsedTypeString(name[..firstSquareBracket], name[(firstSquareBracket + 1)..], []);
×
299
        }
300

301
        var lastAngleBracket = name.LastIndexOf('>');
×
302
        var genericParams = MiscUtils.GetGenericParams(name[(firstAngleBracket + 1)..(lastAngleBracket)]);
×
303

304
        var baseType = $"{name[..firstAngleBracket]}`{genericParams.Length}";
×
305
        var suffix = name[(lastAngleBracket + 1)..];
×
306

307
        return new ParsedTypeString(baseType, suffix, genericParams);
×
308
    }
309

310
    public static ITypeDefOrRef ImportTypeIfNeeded(this ReferenceImporter importer, ITypeDefOrRef type)
311
    {
312
        if (type is TypeSpecification spec)
375✔
313
            return new TypeSpecification(importer.ImportTypeSignature(spec.Signature!));
105✔
314

315
        return importer.ImportType(type);
270✔
316
    }
317

318
    internal static ArrayTypeSignature MakeArrayTypeWithLowerBounds(this TypeSignature elementType, int rank)
319
    {
320
        var result = new ArrayTypeSignature(elementType, rank);
5✔
321
        for (var i = 0; i < rank; i++)
30✔
322
            result.Dimensions[i] = new ArrayDimension(null, 0);
10✔
323

324
        return result;
5✔
325
    }
326
}
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