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

SamboyCoding / Cpp2IL / 15032750078

14 May 2025 11:00PM UTC coverage: 34.393% (-0.4%) from 34.826%
15032750078

Pull #451

github

web-flow
Merge d7a22ee91 into e93c0fecc
Pull Request #451: Support injecting anything

1786 of 6584 branches covered (27.13%)

Branch coverage included in aggregate %.

140 of 187 new or added lines in 27 files covered. (74.87%)

53 existing lines in 2 files now uncovered.

4167 of 10725 relevant lines covered (38.85%)

189623.56 hits per line

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

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

20
    internal static void Reset()
21
    {
22
        CachedTypeDefsByName.Clear();
92✔
23
        CachedTypeSignaturesByName.Clear();
92✔
24
        TypeDefsByIndex.Clear();
92✔
25
    }
92✔
26

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

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

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

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

UNCOV
117
                var genericInstanceType = new GenericInstanceTypeSignature(module.DefaultImporter.ImportType(typeDefinition!), typeDefinition!.IsValueType);
×
118

119
                //Get generic arguments
UNCOV
120
                var genericArgumentTypes = genericClass.Context.ClassInst.Types;
×
121

122
                //Add arguments to generic instance
UNCOV
123
                foreach (var type in genericArgumentTypes)
×
UNCOV
124
                    genericInstanceType.TypeArguments.Add(GetTypeSignatureFromIl2CppType(module, type));
×
125

UNCOV
126
                ret = genericInstanceType;
×
UNCOV
127
                break;
×
128
            }
129
            default:
130
                throw new("Don't know how to make a type signature from " + il2CppType.Type);
×
131
        }
132

UNCOV
133
        if (il2CppType.Byref == 1)
×
134
            ret = ret.MakeByReferenceType();
×
135

UNCOV
136
        return ret;
×
137
    }
138

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

UNCOV
150
        switch (il2CppType.Type)
×
151
        {
152
            case Il2CppTypeEnum.IL2CPP_TYPE_OBJECT:
153
            case Il2CppTypeEnum.IL2CPP_TYPE_VOID:
154
            case Il2CppTypeEnum.IL2CPP_TYPE_BOOLEAN:
155
            case Il2CppTypeEnum.IL2CPP_TYPE_CHAR:
156
            case Il2CppTypeEnum.IL2CPP_TYPE_I1:
157
            case Il2CppTypeEnum.IL2CPP_TYPE_U1:
158
            case Il2CppTypeEnum.IL2CPP_TYPE_I2:
159
            case Il2CppTypeEnum.IL2CPP_TYPE_U2:
160
            case Il2CppTypeEnum.IL2CPP_TYPE_I4:
161
            case Il2CppTypeEnum.IL2CPP_TYPE_U4:
162
            case Il2CppTypeEnum.IL2CPP_TYPE_I:
163
            case Il2CppTypeEnum.IL2CPP_TYPE_U:
164
            case Il2CppTypeEnum.IL2CPP_TYPE_I8:
165
            case Il2CppTypeEnum.IL2CPP_TYPE_U8:
166
            case Il2CppTypeEnum.IL2CPP_TYPE_R4:
167
            case Il2CppTypeEnum.IL2CPP_TYPE_R8:
168
            case Il2CppTypeEnum.IL2CPP_TYPE_STRING:
169
            case Il2CppTypeEnum.IL2CPP_TYPE_TYPEDBYREF:
170
                //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.
UNCOV
171
                return module.DefaultImporter.ImportType(GetPrimitiveTypeDef(il2CppType.Type));
×
172
            case Il2CppTypeEnum.IL2CPP_TYPE_CLASS:
173
            case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE:
UNCOV
174
                return module.DefaultImporter.ImportType(TypeDefsByIndex[il2CppType.Data.ClassIndex]);
×
175
            case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY:
176
            case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST:
177
            case Il2CppTypeEnum.IL2CPP_TYPE_PTR:
178
            case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY:
179
            case Il2CppTypeEnum.IL2CPP_TYPE_VAR:
180
            case Il2CppTypeEnum.IL2CPP_TYPE_MVAR:
181
                //For the rest of these, we have to make a type signature first anyway, so just delegate to signature getter
UNCOV
182
                return GetTypeSignatureFromIl2CppType(module, il2CppType).ToTypeDefOrRef();
×
183
            default:
184
                throw new("Don't know how to import a type reference from an il2cpp type of type " + il2CppType.Type);
×
185
        }
186
    }
187

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

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

196
        var key = name.ToLower(CultureInfo.InvariantCulture);
140✔
197

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

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

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

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

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

225
        var key = name.ToLower(CultureInfo.InvariantCulture);
×
226

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

230
        var result = InternalTryLookupTypeSignatureByName(name, genericParameterNames);
×
231

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

235
        return result;
×
236
    }
237

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

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

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

257
        var parsedType = Parse(name);
×
258

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

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

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

270
        if (parsedType.GenericArguments.Length == 0)
×
271
            return baseType.ToTypeSignature();
×
272

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

282
        return baseType.MakeGenericInstanceType(typeArguments);
×
283
    }
284

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

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

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

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

305
        return new ParsedTypeString(baseType, suffix, genericParams);
×
306
    }
307

308
    public static ITypeDefOrRef ImportTypeIfNeeded(this ReferenceImporter importer, ITypeDefOrRef type)
309
    {
UNCOV
310
        if (type is TypeSpecification spec)
×
UNCOV
311
            return new TypeSpecification(importer.ImportTypeSignature(spec.Signature!));
×
312

UNCOV
313
        return importer.ImportType(type);
×
314
    }
315

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

322
        return result;
5✔
323
    }
324
}
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