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

SamboyCoding / Cpp2IL / 15052236685

15 May 2025 06:14PM UTC coverage: 34.038% (-0.4%) from 34.453%
15052236685

push

github

SamboyCoding
Implement requested change

1774 of 6622 branches covered (26.79%)

Branch coverage included in aggregate %.

1 of 2 new or added lines in 1 file covered. (50.0%)

320 existing lines in 15 files now uncovered.

4155 of 10797 relevant lines covered (38.48%)

188267.61 hits per line

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

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

16
namespace Cpp2IL.Core.Model.Contexts;
17

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

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

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

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

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

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

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

57
    public List<ParameterAnalysisContext> Parameters = [];
738,870✔
58

59
    /// <summary>
60
    /// Does this method return void?
61
    /// </summary>
62
    public virtual bool IsVoid => (Definition?.ReturnType?.ToString() ?? throw new("Subclasses of MethodAnalysisContext should override IsVoid")) == "System.Void";
×
63

64
    public virtual bool IsStatic => Definition?.IsStatic ?? throw new("Subclasses of MethodAnalysisContext should override IsStatic");
87,355!
65

66
    protected override int CustomAttributeIndex => Definition?.customAttributeIndex ?? throw new("Subclasses of MethodAnalysisContext should override CustomAttributeIndex if they have custom attributes");
365,526!
67

68
    public override AssemblyAnalysisContext CustomAttributeAssembly => DeclaringType?.DeclaringAssembly ?? throw new("Subclasses of MethodAnalysisContext should override CustomAttributeAssembly if they have custom attributes");
968,522!
69

70
    public override string DefaultName => Definition?.Name ?? throw new("Subclasses of MethodAnalysisContext should override DefaultName");
87,698!
71

72
    public string FullName => DeclaringType == null ? Name : $"{DeclaringType.FullName}::{Name}";
33!
73

74
    public string FullNameWithSignature => $"{ReturnTypeContext.FullName} {FullName}({string.Join(", ", Parameters.Select(p => p.HumanReadableSignature))})";
×
75

76
    public virtual MethodAttributes Attributes => Definition?.Attributes ?? throw new("Subclasses of MethodAnalysisContext should override Attributes");
174,060!
77

78
    public TypeAnalysisContext? InjectedReturnType { get; set; }
684,886✔
79

80
    public int ParameterCount => Parameters.Count;
10✔
81

82
    private List<GenericParameterTypeAnalysisContext>? _genericParameters;
83
    public override List<GenericParameterTypeAnalysisContext> GenericParameters
84
    {
85
        get
86
        {
87
            // Lazy load the generic parameters
88
            _genericParameters ??= Definition?.GenericContainer?.GenericParameters.Select(p => new GenericParameterTypeAnalysisContext(p, this)).ToList() ?? [];
524,714!
89
            return _genericParameters;
519,693✔
90
        }
91
    }
92

93
    public int GenericParameterCount => GenericParameters.Count;
338,335✔
94

95
    private ushort Slot => Definition?.slot ?? ushort.MaxValue;
75,705!
96

97
    //TODO Support custom attributes on return types (v31 feature)
98
    public TypeAnalysisContext ReturnTypeContext => InjectedReturnType ?? DeclaringType!.DeclaringAssembly.ResolveIl2CppType(Definition!.RawReturnType!);
385,996✔
99
    
100
    protected Memory<byte>? rawMethodBody;
101

102
    public MethodAnalysisContext? BaseMethod => Overrides.FirstOrDefault(m => m.DeclaringType?.IsInterface is false);
8!
103

104
    /// <summary>
105
    /// The set of methods which this method overrides.
106
    /// </summary>
107
    public virtual IEnumerable<MethodAnalysisContext> Overrides
108
    {
109
        get
110
        {
111
            if (Definition == null)
18,047!
UNCOV
112
                return [];
×
113

114
            var declaringTypeDefinition = DeclaringType?.Definition;
18,047!
115
            if (declaringTypeDefinition == null)
18,047!
UNCOV
116
                return [];
×
117

118
            var vtable = declaringTypeDefinition.VTable;
18,047✔
119
            if (vtable == null)
18,047!
UNCOV
120
                return [];
×
121

122
            return GetOverriddenMethods(declaringTypeDefinition, vtable);
18,047✔
123

124
            bool TryGetMethodForSlot(TypeAnalysisContext declaringType, int slot, [NotNullWhen(true)] out MethodAnalysisContext? method)
125
            {
126
                if (declaringType is GenericInstanceTypeAnalysisContext genericInstanceType)
13,520✔
127
                {
128
                    var genericMethod = genericInstanceType.GenericType.Methods.FirstOrDefault(m => m.Slot == slot);
7,102✔
129
                    if (genericMethod is not null)
1,802✔
130
                    {
131
                        method = new ConcreteGenericMethodAnalysisContext(genericMethod, genericInstanceType.GenericArguments.ToArray(), []);
330✔
132
                        return true;
330✔
133
                    }
134
                }
135
                else
136
                {
137
                    var baseMethod = declaringType.Methods.FirstOrDefault(m => m.Slot == slot);
82,123✔
138
                    if (baseMethod is not null)
11,718✔
139
                    {
140
                        method = baseMethod;
2,830✔
141
                        return true;
2,830✔
142
                    }
143
                }
144

145
                method = null;
10,360✔
146
                return false;
10,360✔
147
            }
148

149
            IEnumerable<MethodAnalysisContext> GetOverriddenMethods(Il2CppTypeDefinition declaringTypeDefinition, MetadataUsage?[] vtable)
150
            {
151
                for (var i = 0; i < vtable.Length; ++i)
683,696✔
152
                {
153
                    var vtableEntry = vtable[i];
323,803✔
154
                    if (vtableEntry is null or { Type: not MetadataUsageType.MethodDef })
323,803✔
155
                        continue;
156

157
                    if (vtableEntry.AsMethod() != Definition)
318,193✔
158
                        continue;
159

160
                    // Normal inheritance
161
                    var baseType = DeclaringType?.BaseType;
3,125!
162
                    while (baseType is not null)
7,853✔
163
                    {
164
                        if (TryGetMethodForSlot(baseType, i, out var method))
4,766✔
165
                        {
166
                            yield return method;
38✔
167
                            break; // We only want direct overrides, not the entire inheritance chain.
36✔
168
                        }
169
                        baseType = baseType.BaseType;
4,728✔
170
                    }
171

172
                    // Interface inheritance
173
                    foreach (var interfaceOffset in declaringTypeDefinition.InterfaceOffsets)
34,564✔
174
                    {
175
                        if (i >= interfaceOffset.offset)
14,159✔
176
                        {
177
                            var interfaceTypeContext = interfaceOffset.Type.ToContext(CustomAttributeAssembly);
8,754✔
178
                            if (interfaceTypeContext != null && TryGetMethodForSlot(interfaceTypeContext, i - interfaceOffset.offset, out var method))
8,754✔
179
                            {
180
                                yield return method;
3,122✔
181
                            }
182
                        }
183
                    }
184
                }
185
            }
18,045✔
186
        }
187
    }
188

UNCOV
189
    private static readonly List<IBlockProcessor> blockProcessors =
×
UNCOV
190
    [
×
UNCOV
191
        new MetadataProcessor(),
×
UNCOV
192
        new CallProcessor()
×
UNCOV
193
    ];
×
194

195
    public MethodAnalysisContext(Il2CppMethodDefinition? definition, TypeAnalysisContext parent) : base(definition?.token ?? 0, parent.AppContext)
738,870✔
196
    {
197
        DeclaringType = parent;
738,870✔
198
        Definition = definition;
738,870✔
199

200
        if (Definition != null)
738,870✔
201
        {
202
            InitCustomAttributeData();
439,980✔
203

204
            for (var i = 0; i < Definition.InternalParameterData!.Length; i++)
1,890,450✔
205
            {
206
                var parameterDefinition = Definition.InternalParameterData![i];
505,245✔
207
                Parameters.Add(new(parameterDefinition, i, this));
505,245✔
208
            }
209
        }
210
        else
211
            rawMethodBody = Array.Empty<byte>();
298,890✔
212
    }
298,890✔
213

214
    [MemberNotNull(nameof(rawMethodBody))]
215
    public void EnsureRawBytes()
216
    {
217
        rawMethodBody ??= InitRawBytes();
439,980✔
218
    }
439,980✔
219

220
    private Memory<byte> InitRawBytes()
221
    {
222
        //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.
223
        //E.g. UnityEngine.Purchasing.AppleCore.dll: UnityEngine.Purchasing.INativeAppleStore::SetUnityPurchasingCallback on among us (itch.io build)
224
        if (Definition != null && Definition.MethodPointer != 0 && !Definition.Attributes.HasFlag(MethodAttributes.Abstract))
439,980✔
225
        {
226
            var ret = AppContext.InstructionSet.GetRawBytesForMethod(this, false);
425,868✔
227

228
            if (ret.Length == 0)
425,868✔
229
            {
230
                Logger.VerboseNewline("\t\t\tUnexpectedly got 0-byte method body for " + this + $". Pointer was 0x{Definition.MethodPointer:X}", "MAC");
33!
231
            }
232

233
            return ret;
425,868✔
234
        }
235
        else
236
            return Array.Empty<byte>();
14,112✔
237
    }
238

239
    protected MethodAnalysisContext(ApplicationAnalysisContext context) : base(0, context)
×
240
    {
241
        rawMethodBody = Array.Empty<byte>();
×
242
    }
×
243

244
    [MemberNotNull(nameof(ConvertedIsil))]
245
    public void Analyze()
246
    {
247
        if (ConvertedIsil != null)
×
248
            return;
×
249

250
        if (UnderlyingPointer == 0)
×
251
        {
UNCOV
252
            ConvertedIsil = [];
×
UNCOV
253
            return;
×
254
        }
255

256
        ConvertedIsil = AppContext.InstructionSet.GetIsilFromMethod(this);
×
257

258
        if (ConvertedIsil.Count == 0)
×
UNCOV
259
            return; //Nothing to do, empty function
×
260

261
        ControlFlowGraph = new ISILControlFlowGraph();
×
UNCOV
262
        ControlFlowGraph.Build(ConvertedIsil);
×
263

264
        // Post step to convert metadata usage. Ldstr Opcodes etc.
265
        foreach (var block in ControlFlowGraph.Blocks)
×
266
        {
267
            foreach (var converter in blockProcessors)
×
268
            {
UNCOV
269
                converter.Process(this, block);
×
270
            }
271
        }
UNCOV
272
    }
×
273

274
    public void ReleaseAnalysisData()
275
    {
276
        ConvertedIsil = null;
×
UNCOV
277
        ControlFlowGraph = null;
×
278
    }
×
279

280
    public override string ToString() => $"Method: {FullName}";
33✔
281

282
    #region StableNameDot implementation
283

284
    ITypeInfoProvider IMethodInfoProvider.ReturnType =>
UNCOV
285
        Definition!.RawReturnType!.ThisOrElementIsGenericParam()
×
UNCOV
286
            ? new GenericParameterTypeInfoProviderWrapper(Definition.RawReturnType!.GetGenericParamName())
×
UNCOV
287
            : TypeAnalysisContext.GetSndnProviderForType(AppContext, Definition!.RawReturnType);
×
288

UNCOV
289
    IEnumerable<IParameterInfoProvider> IMethodInfoProvider.ParameterInfoProviders => Parameters;
×
290

291
    string IMethodInfoProvider.MethodName => Name;
×
292

293
    MethodAttributes IMethodInfoProvider.MethodAttributes => Attributes;
×
294

295
    MethodSemantics IMethodInfoProvider.MethodSemantics
296
    {
297
        get
298
        {
299
            if (DeclaringType != null)
×
300
            {
301
                //This one is a bit trickier, as il2cpp doesn't use semantics.
302
                foreach (var prop in DeclaringType.Properties)
×
303
                {
304
                    if (prop.Getter == this)
×
305
                        return MethodSemantics.Getter;
×
306
                    if (prop.Setter == this)
×
UNCOV
307
                        return MethodSemantics.Setter;
×
308
                }
309

310
                foreach (var evt in DeclaringType.Events)
×
311
                {
UNCOV
312
                    if (evt.Adder == this)
×
UNCOV
313
                        return MethodSemantics.AddOn;
×
UNCOV
314
                    if (evt.Remover == this)
×
UNCOV
315
                        return MethodSemantics.RemoveOn;
×
UNCOV
316
                    if (evt.Invoker == this)
×
UNCOV
317
                        return MethodSemantics.Fire;
×
318
                }
319
            }
320

UNCOV
321
            return 0;
×
UNCOV
322
        }
×
323
    }
324

325
    #endregion
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

© 2026 Coveralls, Inc