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

SamboyCoding / Cpp2IL / 11419919232

19 Oct 2024 06:43PM UTC coverage: 28.24% (-0.1%) from 28.388%
11419919232

push

github

web-flow
Support emitting metadata for explicit interface implementations (#346)

1239 of 6152 branches covered (20.14%)

Branch coverage included in aggregate %.

9 of 91 new or added lines in 4 files covered. (9.89%)

2 existing lines in 2 files now uncovered.

3350 of 10098 relevant lines covered (33.17%)

101421.74 hits per line

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

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

12
namespace Cpp2IL.Core.Utils.AsmResolver;
13

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

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

23
    internal static void Reset()
24
    {
25
        CachedTypeDefsByName.Clear();
22✔
26
        CachedTypeSignaturesByName.Clear();
22✔
27
        ImportersByAssembly.Clear();
22✔
28
        TypeDefsByIndex.Clear();
22✔
29
        GenericParamsByIndexNew.Clear();
22✔
30
    }
22✔
31

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

57
    public static TypeSignature GetTypeSignatureFromIl2CppType(ModuleDefinition module, Il2CppType il2CppType)
58
    {
59
        //Module is needed for generic params
60
        if (il2CppType == null)
×
61
            throw new ArgumentNullException(nameof(il2CppType));
×
62

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

113
                //Get base type
114
                TypeDefsByIndex.TryGetValue(genericClass.TypeDefinitionIndex, out var typeDefinition);
×
115
                if (LibCpp2IlMain.MetadataVersion >= 27f)
×
116
                {
117
                    //V27 - type indexes are pointers now.
118
                    var type = LibCpp2IlMain.Binary!.ReadReadableAtVirtualAddress<Il2CppType>((ulong)genericClass.TypeDefinitionIndex);
×
119
                    typeDefinition = GetTypeSignatureFromIl2CppType(module, type).Resolve() ?? throw new Exception("Unable to resolve base type for generic inst");
×
120
                }
121

122
                var genericInstanceType = new GenericInstanceTypeSignature(typeDefinition!, typeDefinition!.IsValueType);
×
123

124
                //If the generic type is declared in a type with generic params, we have to fill all its parent's params with dummy values.
125
                // if (typeDefinition.DeclaringType?.GenericParameters?.Count is {} declTypeGpCount)
126
                // {
127
                //     for (var i = 0; i < declTypeGpCount; i++)
128
                //     {
129
                //         genericInstanceType.TypeArguments.Add(TypeDefinitionsAsmResolver.Object.ToTypeSignature());
130
                //     }
131
                // }
132

133
                //Get generic arguments
134
                var genericArgumentTypes = genericClass.Context.ClassInst.Types;
×
135

136
                //Add arguments to generic instance
137
                foreach (var type in genericArgumentTypes)
×
138
                    genericInstanceType.TypeArguments.Add(GetTypeSignatureFromIl2CppType(module, type));
×
139

140
                ret = genericInstanceType;
×
141
                break;
×
142
            }
143
            default:
144
                throw new("Don't know how to make a type signature from " + il2CppType.Type);
×
145
        }
146

147
        if (il2CppType.Byref == 1)
×
148
            ret = ret.MakeByReferenceType();
×
149

150
        return ret;
×
151
    }
152

153
    /// <summary>
154
    /// Imports the managed representation of the given il2cpp type using the given importer, and returns said type.
155
    /// <br/><br/>
156
    /// Prefer <see cref="GetTypeSignatureFromIl2CppType"/> where possible, only use this where an actual type reference is needed.
157
    /// Such cases would include generic parameter constraints, base types/interfaces, and event types.
158
    /// </summary>
159
    public static ITypeDefOrRef ImportReferenceFromIl2CppType(ModuleDefinition module, Il2CppType il2CppType)
160
    {
161
        if (il2CppType == null)
×
162
            throw new ArgumentNullException(nameof(il2CppType));
×
163

164
        switch (il2CppType.Type)
×
165
        {
166
            case Il2CppTypeEnum.IL2CPP_TYPE_OBJECT:
167
            case Il2CppTypeEnum.IL2CPP_TYPE_VOID:
168
            case Il2CppTypeEnum.IL2CPP_TYPE_BOOLEAN:
169
            case Il2CppTypeEnum.IL2CPP_TYPE_CHAR:
170
            case Il2CppTypeEnum.IL2CPP_TYPE_I1:
171
            case Il2CppTypeEnum.IL2CPP_TYPE_U1:
172
            case Il2CppTypeEnum.IL2CPP_TYPE_I2:
173
            case Il2CppTypeEnum.IL2CPP_TYPE_U2:
174
            case Il2CppTypeEnum.IL2CPP_TYPE_I4:
175
            case Il2CppTypeEnum.IL2CPP_TYPE_U4:
176
            case Il2CppTypeEnum.IL2CPP_TYPE_I:
177
            case Il2CppTypeEnum.IL2CPP_TYPE_U:
178
            case Il2CppTypeEnum.IL2CPP_TYPE_I8:
179
            case Il2CppTypeEnum.IL2CPP_TYPE_U8:
180
            case Il2CppTypeEnum.IL2CPP_TYPE_R4:
181
            case Il2CppTypeEnum.IL2CPP_TYPE_R8:
182
            case Il2CppTypeEnum.IL2CPP_TYPE_STRING:
183
            case Il2CppTypeEnum.IL2CPP_TYPE_TYPEDBYREF:
184
                //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.
185
                return GetPrimitiveTypeDef(il2CppType.Type);
×
186
            case Il2CppTypeEnum.IL2CPP_TYPE_CLASS:
187
            case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE:
188
                return TypeDefsByIndex[il2CppType.Data.ClassIndex];
×
189
            case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY:
190
            case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST:
191
            case Il2CppTypeEnum.IL2CPP_TYPE_PTR:
192
            case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY:
193
            case Il2CppTypeEnum.IL2CPP_TYPE_VAR:
194
            case Il2CppTypeEnum.IL2CPP_TYPE_MVAR:
195
                //For the rest of these, we have to make a type signature first anyway, so just delegate to signature getter
196
                return GetTypeSignatureFromIl2CppType(module, il2CppType).ToTypeDefOrRef();
×
197
            default:
198
                throw new("Don't know how to import a type reference from an il2cpp type of type " + il2CppType.Type);
×
199
        }
200
    }
201

202
    public static TypeDefinition? TryLookupTypeDefKnownNotGeneric(string? name)
203
    {
204
        if (name == null)
×
205
            return null;
×
206

NEW
207
        if (TypeDefinitionsAsmResolver.GetPrimitive(name) is { } primitive)
×
NEW
208
            return primitive;
×
209

UNCOV
210
        var key = name.ToLower(CultureInfo.InvariantCulture);
×
211

212
        if (CachedTypeDefsByName.TryGetValue(key, out var ret))
×
213
            return ret;
×
214

NEW
215
        var definedType = Cpp2IlApi.CurrentAppContext!.AllTypes.FirstOrDefault(t => t.Definition != null && string.Equals(t.Definition.FullName, name, StringComparison.OrdinalIgnoreCase));
×
216

217
        //Try subclasses
NEW
218
        definedType ??= Cpp2IlApi.CurrentAppContext.AllTypes.FirstOrDefault(t =>
×
NEW
219
        {
×
NEW
220
            return t.Definition?.FullName != null
×
NEW
221
                && t.Definition.FullName.Contains('/')
×
NEW
222
                && string.Equals(t.Definition.FullName.Replace('/', '.'), name, StringComparison.OrdinalIgnoreCase);
×
NEW
223
        });
×
224

NEW
225
        return definedType?.GetExtraData<TypeDefinition>("AsmResolverType");
×
226
    }
227

228
    public static TypeSignature? TryLookupTypeSignatureByName(string? name, ReadOnlySpan<string> genericParameterNames = default)
229
    {
NEW
230
        if (name == null)
×
NEW
231
            return null;
×
232

NEW
233
        var key = name.ToLower(CultureInfo.InvariantCulture);
×
234

NEW
235
        if (genericParameterNames.Length == 0 && CachedTypeSignaturesByName.TryGetValue(key, out var ret))
×
NEW
236
            return ret;
×
237

NEW
238
        var result = InternalTryLookupTypeSignatureByName(name, genericParameterNames);
×
239

NEW
240
        if (genericParameterNames.Length == 0)
×
NEW
241
            CachedTypeSignaturesByName[key] = result;
×
242

243
        return result;
×
244
    }
245

246
    private static TypeSignature? InternalTryLookupTypeSignatureByName(string name, ReadOnlySpan<string> genericParameterNames = default)
247
    {
248
        if (TypeDefinitionsAsmResolver.GetPrimitive(name) is { } primitive)
×
NEW
249
            return primitive.ToTypeSignature();
×
250

251
        //The only real cases we end up here are:
252
        //From explicit override resolving, because that has to be done by name
253
        //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.
254
        //While originally initializing the TypeDefinitions class, which is always a system type
255
        //And during exception helper location, which is always a system type.
256
        //So really the only remapping we should have to handle is during explicit override restoration.
257

NEW
258
        if (name.EndsWith("[]", StringComparison.Ordinal))
×
259
        {
260
            var without = name[..^2];
×
NEW
261
            var result = InternalTryLookupTypeSignatureByName(without, genericParameterNames);
×
NEW
262
            return result?.MakeSzArrayType();
×
263
        }
264

NEW
265
        var parsedType = Parse(name);
×
266

267
        // Arrays should be handled above
268
        Debug.Assert(parsedType.Suffix is "");
269

NEW
270
        var genericParameterIndex = genericParameterNames.IndexOf(parsedType.BaseType);
×
NEW
271
        if (genericParameterIndex >= 0)
×
NEW
272
            return new GenericParameterSignature(GenericParameterType.Type, genericParameterIndex);
×
273

NEW
274
        var baseType = TryLookupTypeDefKnownNotGeneric(parsedType.BaseType);
×
NEW
275
        if (baseType == null)
×
NEW
276
            return null;
×
277

NEW
278
        if (parsedType.GenericArguments.Length == 0)
×
NEW
279
            return baseType.ToTypeSignature();
×
280

NEW
281
        var typeArguments = new TypeSignature[parsedType.GenericArguments.Length];
×
NEW
282
        for (var i = 0; i < parsedType.GenericArguments.Length; i++)
×
283
        {
NEW
284
            var typeArgument = InternalTryLookupTypeSignatureByName(parsedType.GenericArguments[i], genericParameterNames);
×
NEW
285
            if (typeArgument == null)
×
NEW
286
                return null;
×
NEW
287
            typeArguments[i] = typeArgument;
×
288
        }
289

NEW
290
        return baseType.MakeGenericInstanceType(typeArguments);
×
291
    }
292

NEW
293
    private readonly record struct ParsedTypeString(string BaseType, string Suffix, string[] GenericArguments);
×
294

295
    private static ParsedTypeString Parse(string name)
296
    {
NEW
297
        var firstAngleBracket = name.IndexOf('<');
×
NEW
298
        if (firstAngleBracket < 0)
×
299
        {
NEW
300
            var firstSquareBracket = name.IndexOf('[');
×
NEW
301
            if (firstSquareBracket < 0)
×
NEW
302
                return new ParsedTypeString(name, "", []);
×
303
            else
NEW
304
                return new ParsedTypeString(name[..firstSquareBracket], name[(firstSquareBracket + 1)..], []);
×
305
        }
306

NEW
307
        var lastAngleBracket = name.LastIndexOf('>');
×
NEW
308
        var genericParams = MiscUtils.GetGenericParams(name[(firstAngleBracket + 1)..(lastAngleBracket)]);
×
309

NEW
310
        var baseType = $"{name[..firstAngleBracket]}`{genericParams.Length}";
×
NEW
311
        var suffix = name[(lastAngleBracket + 1)..];
×
312

NEW
313
        return new ParsedTypeString(baseType, suffix, genericParams);
×
314
    }
315

316
    public static ReferenceImporter GetImporter(this AssemblyDefinition assemblyDefinition)
317
    {
318
        if (ImportersByAssembly.TryGetValue(assemblyDefinition, out var ret))
×
319
            return ret;
×
320

321
        ImportersByAssembly[assemblyDefinition] = ret = new(assemblyDefinition.Modules[0]);
×
322

323
        return ret;
×
324
    }
325

326
    public static ITypeDefOrRef ImportTypeIfNeeded(this ReferenceImporter importer, ITypeDefOrRef type)
327
    {
328
        if (type is TypeSpecification spec)
×
329
            return new TypeSpecification(importer.ImportTypeSignature(spec.Signature!));
×
330

331
        return importer.ImportType(type);
×
332
    }
333

334
    public static bool IsManagedMethodWithBody(this MethodDefinition managedMethod) =>
335
        managedMethod.Managed && !managedMethod.IsAbstract && !managedMethod.IsPInvokeImpl
×
336
        && !managedMethod.IsInternalCall && !managedMethod.IsNative && !managedMethod.IsRuntime;
×
337
}
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