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

SamboyCoding / Cpp2IL / 20488056168

24 Dec 2025 02:19PM UTC coverage: 34.361% (+0.05%) from 34.31%
20488056168

Pull #499

github

web-flow
Merge 482cdd13f into 3a72c253a
Pull Request #499: Add ResolveContextForMethod overload taking Cpp2IlMethodRef as a parameter

1811 of 6624 branches covered (27.34%)

Branch coverage included in aggregate %.

0 of 3 new or added lines in 1 file covered. (0.0%)

187 existing lines in 10 files now uncovered.

4208 of 10893 relevant lines covered (38.63%)

201355.63 hits per line

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

54.45
/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs
1
using System;
2
using System.Collections.Generic;
3
using System.Diagnostics.CodeAnalysis;
4
using System.Linq;
5
using System.Reflection;
6
using Cpp2IL.Core.Graphs;
7
using Cpp2IL.Core.Graphs.Processors;
8
using Cpp2IL.Core.ISIL;
9
using Cpp2IL.Core.Logging;
10
using Cpp2IL.Core.Utils;
11
using LibCpp2IL;
12
using LibCpp2IL.Metadata;
13
using StableNameDotNet.Providers;
14

15
namespace Cpp2IL.Core.Model.Contexts;
16

17
/// <summary>
18
/// Represents one method within the application. Can be analyzed to attempt to reconstruct the function body.
19
/// </summary>
20
public class MethodAnalysisContext : HasGenericParameters, IMethodInfoProvider
21
{
22
    /// <summary>
23
    /// The underlying metadata for the method.
24
    ///
25
    /// Nullable iff this is a subclass.
26
    /// </summary>
27
    public readonly Il2CppMethodDefinition? Definition;
28

29
    /// <summary>
30
    /// The analysis context for the declaring type of this method.
31
    /// </summary>
32
    public readonly TypeAnalysisContext? DeclaringType;
33

34
    /// <summary>
35
    /// The address of this method as defined in the underlying metadata.
36
    /// </summary>
37
    public virtual ulong UnderlyingPointer => Definition?.MethodPointer ?? throw new("Subclasses of MethodAnalysisContext should override UnderlyingPointer");
1,002,888!
38

39
    public ulong Rva => UnderlyingPointer == 0 || LibCpp2IlMain.Binary == null ? 0 : LibCpp2IlMain.Binary.GetRva(UnderlyingPointer);
×
40

41
    /// <summary>
42
    /// The raw method body as machine code in the active instruction set.
43
    /// </summary>
44
    public Memory<byte> RawBytes => rawMethodBody ??= InitRawBytes();
×
45

46
    /// <summary>
47
    /// The first-stage-analyzed Instruction-Set-Independent Language Instructions.
48
    /// </summary>
49
    public List<InstructionSetIndependentInstruction>? ConvertedIsil;
50

51
    /// <summary>
52
    /// The control flow graph for this method, if one is built.
53
    /// </summary>
54
    public ISILControlFlowGraph? ControlFlowGraph;
55

56
    public List<ParameterAnalysisContext> Parameters = [];
864,100✔
57

58
    /// <summary>
59
    /// Does this method return void?
60
    /// </summary>
61
    public bool IsVoid => ReturnType == AppContext.SystemTypes.SystemVoidType;
×
62

63
    public bool IsStatic => (Attributes & MethodAttributes.Static) != 0;
87,687✔
64

65
    public bool IsVirtual => (Attributes & MethodAttributes.Virtual) != 0;
332✔
66

UNCOV
67
    public bool IsAbstract => (Attributes & MethodAttributes.Abstract) != 0;
×
68

69
    public bool IsNewSlot => (Attributes & MethodAttributes.NewSlot) != 0;
332✔
70

71
    protected override int CustomAttributeIndex => Definition?.customAttributeIndex ?? throw new("Subclasses of MethodAnalysisContext should override CustomAttributeIndex if they have custom attributes");
435,150!
72

73
    public override AssemblyAnalysisContext CustomAttributeAssembly => DeclaringType?.DeclaringAssembly ?? throw new("Subclasses of MethodAnalysisContext should override CustomAttributeAssembly if they have custom attributes");
1,108,468!
74

75
    public override string DefaultName => Definition?.Name ?? throw new("Subclasses of MethodAnalysisContext should override DefaultName");
87,702!
76

77
    public string FullName => DeclaringType == null ? Name : $"{DeclaringType.FullName}::{Name}";
37!
78

UNCOV
79
    public string FullNameWithSignature => $"{ReturnType.FullName} {FullName}({string.Join(", ", Parameters.Select(p => p.HumanReadableSignature))})";
×
80

81
    public virtual MethodAttributes DefaultAttributes => Definition?.Attributes ?? throw new($"Subclasses of MethodAnalysisContext should override {nameof(DefaultAttributes)}");
262,411!
82

83
    public virtual MethodAttributes? OverrideAttributes { get; set; }
262,411✔
84

85
    public MethodAttributes Attributes
86
    {
87
        get => OverrideAttributes ?? DefaultAttributes;
262,411!
UNCOV
88
        set => OverrideAttributes = value;
×
89
    }
90

91
    public virtual MethodImplAttributes DefaultImplAttributes => Definition?.MethodImplAttributes ?? throw new($"Subclasses of MethodAnalysisContext should override {nameof(DefaultImplAttributes)}");
87,030!
92

93
    public virtual MethodImplAttributes? OverrideImplAttributes { get; set; }
87,030✔
94

95
    public MethodImplAttributes ImplAttributes
96
    {
97
        get => OverrideImplAttributes ?? DefaultImplAttributes;
87,030!
98
        set => OverrideImplAttributes = value;
×
99
    }
100

101
    public MethodAttributes Visibility
102
    {
103
        get
104
        {
UNCOV
105
            return Attributes & MethodAttributes.MemberAccessMask;
×
106
        }
107
        set
108
        {
UNCOV
109
            Attributes = (Attributes & ~MethodAttributes.MemberAccessMask) | (value & MethodAttributes.MemberAccessMask);
×
UNCOV
110
        }
×
111
    }
112

113
    private List<GenericParameterTypeAnalysisContext>? _genericParameters;
114
    public override List<GenericParameterTypeAnalysisContext> GenericParameters
115
    {
116
        get
117
        {
118
            // Lazy load the generic parameters
119
            _genericParameters ??= Definition?.GenericContainer?.GenericParameters.Select(p => new GenericParameterTypeAnalysisContext(p, this)).ToList() ?? [];
534,527!
120
            return _genericParameters;
528,790✔
121
        }
122
    }
123

124
    private ushort Slot => Definition?.slot ?? ushort.MaxValue;
75,605!
125

126
    public virtual TypeAnalysisContext DefaultReturnType => DeclaringType?.DeclaringAssembly.ResolveIl2CppType(Definition?.RawReturnType) ?? throw new($"Subclasses of MethodAnalysisContext should override {nameof(DefaultReturnType)}");
441,599!
127

128
    public TypeAnalysisContext? OverrideReturnType { get; set; }
441,602✔
129

130
    //TODO Support custom attributes on return types (v31 feature)
131
    public TypeAnalysisContext ReturnType
132
    {
133
        get => OverrideReturnType ?? DefaultReturnType;
441,602✔
UNCOV
134
        set => OverrideReturnType = value;
×
135
    }
136
    
137
    protected Memory<byte>? rawMethodBody;
138

139
    public MethodAnalysisContext? BaseMethod => Overrides.FirstOrDefault(m => m.DeclaringType?.IsInterface is false);
340!
140

141
    private List<MethodAnalysisContext>? _overrides;
142

143
    /// <summary>
144
    /// The set of methods which this method overrides.
145
    /// </summary>
146
    public List<MethodAnalysisContext> Overrides
147
    {
148
        get
149
        {
150
            // Lazy load the overrides
151
            return _overrides ??= GetOverrides().ToList();
18,711✔
152
        }
153
    }
154

155
    private IEnumerable<MethodAnalysisContext> GetOverrides()
156
    {
157
        if (Definition == null)
18,709!
UNCOV
158
            return [];
×
159

160
        var declaringTypeDefinition = DeclaringType?.Definition;
18,709!
161
        if (declaringTypeDefinition == null)
18,709!
UNCOV
162
            return [];
×
163

164
        var vtable = declaringTypeDefinition.VTable;
18,709✔
165
        if (vtable == null)
18,709!
UNCOV
166
            return [];
×
167

168
        return GetOverriddenMethods(declaringTypeDefinition, vtable);
18,709✔
169

170
        bool TryGetMethodForSlot(TypeAnalysisContext declaringType, int slot, [NotNullWhen(true)] out MethodAnalysisContext? method)
171
        {
172
            if (declaringType is GenericInstanceTypeAnalysisContext genericInstanceType)
13,500✔
173
            {
174
                var genericMethod = genericInstanceType.GenericType.Methods.FirstOrDefault(m => m.Slot == slot);
7,058✔
175
                if (genericMethod is not null)
1,792✔
176
                {
177
                    method = new ConcreteGenericMethodAnalysisContext(genericMethod, genericInstanceType.GenericArguments, []);
328✔
178
                    return true;
328✔
179
                }
180
            }
181
            else
182
            {
183
                var baseMethod = declaringType.Methods.FirstOrDefault(m => m.Slot == slot);
82,047✔
184
                if (baseMethod is not null)
11,708✔
185
                {
186
                    method = baseMethod;
2,828✔
187
                    return true;
2,828✔
188
                }
189
            }
190

191
            method = null;
10,344✔
192
            return false;
10,344✔
193
        }
194

195
        IEnumerable<MethodAnalysisContext> GetOverriddenMethods(Il2CppTypeDefinition declaringTypeDefinition, MetadataUsage?[] vtable)
196
        {
197
            for (var i = 0; i < vtable.Length; ++i)
684,972✔
198
            {
199
                var vtableEntry = vtable[i];
323,777✔
200
                if (vtableEntry is null or { Type: not MetadataUsageType.MethodDef })
323,777✔
201
                    continue;
202

203
                if (vtableEntry.AsMethod() != Definition)
318,167✔
204
                    continue;
205

206
                // Normal inheritance
207
                var baseType = DeclaringType?.DefaultBaseType;
3,121!
208
                while (baseType is not null)
7,846✔
209
                {
210
                    if (TryGetMethodForSlot(baseType, i, out var method))
4,762✔
211
                    {
212
                        yield return method;
37✔
213
                        break; // We only want direct overrides, not the entire inheritance chain.
37✔
214
                    }
215
                    baseType = baseType.DefaultBaseType;
4,725✔
216
                }
217

218
                // Interface inheritance
219
                foreach (var interfaceOffset in declaringTypeDefinition.InterfaceOffsets)
34,520✔
220
                {
221
                    if (i >= interfaceOffset.offset)
14,139✔
222
                    {
223
                        var interfaceTypeContext = interfaceOffset.Type.ToContext(CustomAttributeAssembly);
8,738✔
224
                        if (interfaceTypeContext != null && TryGetMethodForSlot(interfaceTypeContext, i - interfaceOffset.offset, out var method))
8,738✔
225
                        {
226
                            yield return method;
3,119✔
227
                        }
228
                    }
229
                }
230
            }
231
        }
18,709✔
232
    }
233

UNCOV
234
    private static readonly List<IBlockProcessor> blockProcessors =
×
UNCOV
235
    [
×
UNCOV
236
        new MetadataProcessor(),
×
UNCOV
237
        new CallProcessor()
×
UNCOV
238
    ];
×
239

240
    public MethodAnalysisContext(Il2CppMethodDefinition? definition, TypeAnalysisContext parent) : base(definition?.token ?? 0, parent.AppContext)
864,100✔
241
    {
242
        DeclaringType = parent;
864,100✔
243
        Definition = definition;
864,100✔
244

245
        if (Definition != null)
864,100✔
246
        {
247
            InitCustomAttributeData();
509,604✔
248

249
            for (var i = 0; i < Definition.InternalParameterData!.Length; i++)
2,186,874✔
250
            {
251
                var parameterDefinition = Definition.InternalParameterData![i];
583,833✔
252
                Parameters.Add(new(parameterDefinition, i, this));
583,833✔
253
            }
254
        }
255
        else
256
            rawMethodBody = Array.Empty<byte>();
354,496✔
257
    }
354,496✔
258

259
    [MemberNotNull(nameof(rawMethodBody))]
260
    public void EnsureRawBytes()
261
    {
262
        rawMethodBody ??= InitRawBytes();
509,604✔
263
    }
509,604✔
264

265
    private Memory<byte> InitRawBytes()
266
    {
267
        //Some abstract methods (on interfaces, no less) apparently have a body? Unity doesn't support default interface methods so idk what's going on here.
268
        //E.g. UnityEngine.Purchasing.AppleCore.dll: UnityEngine.Purchasing.INativeAppleStore::SetUnityPurchasingCallback on among us (itch.io build)
269
        if (Definition != null && Definition.MethodPointer != 0 && !Definition.Attributes.HasFlag(MethodAttributes.Abstract))
509,604✔
270
        {
271
            var ret = AppContext.InstructionSet.GetRawBytesForMethod(this, false);
493,284✔
272

273
            if (ret.Length == 0)
493,284✔
274
            {
275
                Logger.VerboseNewline("\t\t\tUnexpectedly got 0-byte method body for " + this + $". Pointer was 0x{Definition.MethodPointer:X}", "MAC");
37!
276
            }
277

278
            return ret;
493,284✔
279
        }
280
        else
281
            return Array.Empty<byte>();
16,320✔
282
    }
283

UNCOV
284
    protected MethodAnalysisContext(ApplicationAnalysisContext context) : base(0, context)
×
285
    {
286
        rawMethodBody = Array.Empty<byte>();
×
UNCOV
287
    }
×
288

289
    [MemberNotNull(nameof(ConvertedIsil))]
290
    public void Analyze()
291
    {
UNCOV
292
        if (ConvertedIsil != null)
×
293
            return;
×
294

UNCOV
295
        if (UnderlyingPointer == 0)
×
296
        {
297
            ConvertedIsil = [];
×
298
            return;
×
299
        }
300

UNCOV
301
        ConvertedIsil = AppContext.InstructionSet.GetIsilFromMethod(this);
×
302

303
        if (ConvertedIsil.Count == 0)
×
UNCOV
304
            return; //Nothing to do, empty function
×
305

UNCOV
306
        ControlFlowGraph = new ISILControlFlowGraph();
×
UNCOV
307
        ControlFlowGraph.Build(ConvertedIsil);
×
308

309
        // Post step to convert metadata usage. Ldstr Opcodes etc.
UNCOV
310
        foreach (var block in ControlFlowGraph.Blocks)
×
311
        {
UNCOV
312
            foreach (var converter in blockProcessors)
×
313
            {
UNCOV
314
                converter.Process(this, block);
×
315
            }
316
        }
317
    }
×
318

319
    public void ReleaseAnalysisData()
320
    {
321
        ConvertedIsil = null;
×
UNCOV
322
        ControlFlowGraph = null;
×
UNCOV
323
    }
×
324

325
    public ConcreteGenericMethodAnalysisContext MakeGenericInstanceMethod(params IEnumerable<TypeAnalysisContext> methodGenericParameters)
326
    {
UNCOV
327
        if (this is ConcreteGenericMethodAnalysisContext methodOnGenericInstanceType)
×
328
        {
UNCOV
329
            return new ConcreteGenericMethodAnalysisContext(methodOnGenericInstanceType.BaseMethodContext, methodOnGenericInstanceType.TypeGenericParameters, methodGenericParameters);
×
330
        }
331
        else
332
        {
UNCOV
333
            return new ConcreteGenericMethodAnalysisContext(this, [], methodGenericParameters);
×
334
        }
335
    }
336

337
    public ConcreteGenericMethodAnalysisContext MakeConcreteGenericMethod(IEnumerable<TypeAnalysisContext> typeGenericParameters, IEnumerable<TypeAnalysisContext> methodGenericParameters)
338
    {
UNCOV
339
        if (this is ConcreteGenericMethodAnalysisContext)
×
340
        {
UNCOV
341
            throw new InvalidOperationException($"Attempted to make a {nameof(ConcreteGenericMethodAnalysisContext)} concrete: {this}");
×
342
        }
343
        else
344
        {
UNCOV
345
            return new ConcreteGenericMethodAnalysisContext(this, typeGenericParameters, methodGenericParameters);
×
346
        }
347
    }
348

349
    public override string ToString() => $"Method: {FullName}";
37✔
350

351
    #region StableNameDot implementation
352

353
    ITypeInfoProvider IMethodInfoProvider.ReturnType =>
UNCOV
354
        Definition!.RawReturnType!.ThisOrElementIsGenericParam()
×
355
            ? new GenericParameterTypeInfoProviderWrapper(Definition.RawReturnType!.GetGenericParamName())
×
UNCOV
356
            : TypeAnalysisContext.GetSndnProviderForType(AppContext, Definition!.RawReturnType);
×
357

358
    IEnumerable<IParameterInfoProvider> IMethodInfoProvider.ParameterInfoProviders => Parameters;
×
359

360
    string IMethodInfoProvider.MethodName => Name;
×
361

362
    MethodAttributes IMethodInfoProvider.MethodAttributes => Attributes;
×
363

364
    MethodSemantics IMethodInfoProvider.MethodSemantics
365
    {
366
        get
367
        {
UNCOV
368
            if (DeclaringType != null)
×
369
            {
370
                //This one is a bit trickier, as il2cpp doesn't use semantics.
UNCOV
371
                foreach (var prop in DeclaringType.Properties)
×
372
                {
UNCOV
373
                    if (prop.Getter == this)
×
UNCOV
374
                        return MethodSemantics.Getter;
×
UNCOV
375
                    if (prop.Setter == this)
×
UNCOV
376
                        return MethodSemantics.Setter;
×
377
                }
378

UNCOV
379
                foreach (var evt in DeclaringType.Events)
×
380
                {
UNCOV
381
                    if (evt.Adder == this)
×
UNCOV
382
                        return MethodSemantics.AddOn;
×
UNCOV
383
                    if (evt.Remover == this)
×
UNCOV
384
                        return MethodSemantics.RemoveOn;
×
UNCOV
385
                    if (evt.Invoker == this)
×
UNCOV
386
                        return MethodSemantics.Fire;
×
387
                }
388
            }
389

UNCOV
390
            return 0;
×
UNCOV
391
        }
×
392
    }
393

394
    #endregion
395
}
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