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

SamboyCoding / Cpp2IL / 14150416101

29 Mar 2025 11:29PM UTC coverage: 34.814% (+0.08%) from 34.736%
14150416101

Pull #436

github

web-flow
Merge 73b376abb into 0fc947d77
Pull Request #436: Improvements to method overrides

1802 of 6468 branches covered (27.86%)

Branch coverage included in aggregate %.

23 of 23 new or added lines in 1 file covered. (100.0%)

23 existing lines in 1 file now uncovered.

4153 of 10637 relevant lines covered (39.04%)

161326.51 hits per line

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

54.29
/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 : HasCustomAttributesAndName, 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");
694,548!
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 = [];
582,203✔
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");
278,496!
67

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

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

72
    public string FullName => DeclaringType == null ? Name : $"{DeclaringType.FullName}::{Name}";
28!
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; }
545,738✔
79

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

82
    public int GenericParameterCount => Definition?.GenericContainer?.genericParameterCount ?? 0;
279,909!
83

84
    private ushort Slot => Definition?.slot ?? ushort.MaxValue;
75,670!
85

86
    //TODO Support custom attributes on return types (v31 feature)
87
    public TypeAnalysisContext ReturnTypeContext => InjectedReturnType ?? DeclaringType!.DeclaringAssembly.ResolveIl2CppType(Definition!.RawReturnType!);
316,485✔
88
    
89
    protected Memory<byte>? rawMethodBody;
90

91
    public MethodAnalysisContext? BaseMethod => Overrides.FirstOrDefault(m => m.DeclaringType?.IsInterface is false);
6!
92

93
    /// <summary>
94
    /// The set of methods which this method overrides.
95
    /// </summary>
96
    public virtual IEnumerable<MethodAnalysisContext> Overrides
97
    {
98
        get
99
        {
100
            if (Definition == null)
18,034!
101
                return [];
×
102

103
            var declaringTypeDefinition = DeclaringType?.Definition;
18,034!
104
            if (declaringTypeDefinition == null)
18,034!
105
                return [];
×
106

107
            var vtable = declaringTypeDefinition.VTable;
18,034✔
108
            if (vtable == null)
18,034!
109
                return [];
×
110

111
            return GetOverriddenMethods(declaringTypeDefinition, vtable);
18,034✔
112

113
            bool TryGetMethodForSlot(TypeAnalysisContext declaringType, int slot, [NotNullWhen(true)] out MethodAnalysisContext? method)
114
            {
115
                if (declaringType is GenericInstanceTypeAnalysisContext genericInstanceType)
13,515✔
116
                {
117
                    var genericMethod = genericInstanceType.GenericType.Methods.FirstOrDefault(m => m.Slot == slot);
7,093✔
118
                    if (genericMethod is not null)
1,800✔
119
                    {
120
                        method = new ConcreteGenericMethodAnalysisContext(genericMethod, genericInstanceType.GenericArguments.ToArray(), []);
329✔
121
                        return true;
329✔
122
                    }
123
                }
124
                else
125
                {
126
                    var baseMethod = declaringType.Methods.FirstOrDefault(m => m.Slot == slot);
82,092✔
127
                    if (baseMethod is not null)
11,715✔
128
                    {
129
                        method = baseMethod;
2,829✔
130
                        return true;
2,829✔
131
                    }
132
                }
133

134
                method = null;
10,357✔
135
                return false;
10,357✔
136
            }
137

138
            IEnumerable<MethodAnalysisContext> GetOverriddenMethods(Il2CppTypeDefinition declaringTypeDefinition, MetadataUsage?[] vtable)
139
            {
140
                for (var i = 0; i < vtable.Length; ++i)
683,538✔
141
                {
142
                    var vtableEntry = vtable[i];
323,736✔
143
                    if (vtableEntry is null or { Type: not MetadataUsageType.MethodDef })
323,736✔
144
                        continue;
145

146
                    if (vtableEntry.AsMethod() != Definition)
318,126✔
147
                        continue;
148

149
                    // Normal inheritance
150
                    var baseType = DeclaringType?.BaseType;
3,123!
151
                    while (baseType is not null)
7,849✔
152
                    {
153
                        if (TryGetMethodForSlot(baseType, i, out var method))
4,763✔
154
                        {
155
                            yield return method;
37✔
156
                            break;
36✔
157
                        }
158
                        baseType = baseType.BaseType;
4,726✔
159
                    }
160

161
                    // Interface inheritance
162
                    foreach (var interfaceOffset in declaringTypeDefinition.InterfaceOffsets)
34,546✔
163
                    {
164
                        if (i >= interfaceOffset.offset)
14,151✔
165
                        {
166
                            var interfaceTypeContext = interfaceOffset.Type.ToContext(CustomAttributeAssembly);
8,752✔
167
                            if (interfaceTypeContext != null && TryGetMethodForSlot(interfaceTypeContext, i - interfaceOffset.offset, out var method))
8,752✔
168
                                yield return method;
3,121✔
169
                        }
170
                    }
171
                }
172
            }
18,033✔
173
        }
174
    }
175

UNCOV
176
    private static readonly List<IBlockProcessor> blockProcessors =
×
UNCOV
177
    [
×
178
        new MetadataProcessor(),
×
179
        new CallProcessor()
×
180
    ];
×
181

182
    public MethodAnalysisContext(Il2CppMethodDefinition? definition, TypeAnalysisContext parent) : base(definition?.token ?? 0, parent.AppContext)
582,203✔
183
    {
184
        DeclaringType = parent;
582,203✔
185
        Definition = definition;
582,203✔
186

187
        if (Definition != null)
582,203✔
188
        {
189
            InitCustomAttributeData();
352,950✔
190

191
            for (var i = 0; i < Definition.InternalParameterData!.Length; i++)
1,519,920✔
192
            {
193
                var parameterDefinition = Definition.InternalParameterData![i];
407,010✔
194
                Parameters.Add(new(parameterDefinition, i, this));
407,010✔
195
            }
196
        }
197
        else
198
            rawMethodBody = Array.Empty<byte>();
229,253✔
199
    }
229,253✔
200

201
    [MemberNotNull(nameof(rawMethodBody))]
202
    public void EnsureRawBytes()
203
    {
204
        rawMethodBody ??= InitRawBytes();
352,950✔
205
    }
352,950✔
206

207
    private Memory<byte> InitRawBytes()
208
    {
209
        //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.
210
        //E.g. UnityEngine.Purchasing.AppleCore.dll: UnityEngine.Purchasing.INativeAppleStore::SetUnityPurchasingCallback on among us (itch.io build)
211
        if (Definition != null && Definition.MethodPointer != 0 && !Definition.Attributes.HasFlag(MethodAttributes.Abstract))
352,950✔
212
        {
213
            var ret = AppContext.InstructionSet.GetRawBytesForMethod(this, false);
341,598✔
214

215
            if (ret.Length == 0)
341,598✔
216
            {
217
                Logger.VerboseNewline("\t\t\tUnexpectedly got 0-byte method body for " + this + $". Pointer was 0x{Definition.MethodPointer:X}", "MAC");
28!
218
            }
219

220
            return ret;
341,598✔
221
        }
222
        else
223
            return Array.Empty<byte>();
11,352✔
224
    }
225

UNCOV
226
    protected MethodAnalysisContext(ApplicationAnalysisContext context) : base(0, context)
×
227
    {
228
        rawMethodBody = Array.Empty<byte>();
×
UNCOV
229
    }
×
230

231
    [MemberNotNull(nameof(ConvertedIsil))]
232
    public void Analyze()
233
    {
UNCOV
234
        if (ConvertedIsil != null)
×
UNCOV
235
            return;
×
236

237
        if (UnderlyingPointer == 0)
×
238
        {
239
            ConvertedIsil = [];
×
UNCOV
240
            return;
×
241
        }
242

UNCOV
243
        ConvertedIsil = AppContext.InstructionSet.GetIsilFromMethod(this);
×
244

245
        if (ConvertedIsil.Count == 0)
×
UNCOV
246
            return; //Nothing to do, empty function
×
247

248
        ControlFlowGraph = new ISILControlFlowGraph();
×
UNCOV
249
        ControlFlowGraph.Build(ConvertedIsil);
×
250

251
        // Post step to convert metadata usage. Ldstr Opcodes etc.
UNCOV
252
        foreach (var block in ControlFlowGraph.Blocks)
×
253
        {
254
            foreach (var converter in blockProcessors)
×
255
            {
256
                converter.Process(this, block);
×
257
            }
258
        }
UNCOV
259
    }
×
260

261
    public void ReleaseAnalysisData()
262
    {
UNCOV
263
        ConvertedIsil = null;
×
UNCOV
264
        ControlFlowGraph = null;
×
265
    }
×
266

267
    public override string ToString() => $"Method: {FullName}";
28✔
268

269
    #region StableNameDot implementation
270

271
    ITypeInfoProvider IMethodInfoProvider.ReturnType =>
UNCOV
272
        Definition!.RawReturnType!.ThisOrElementIsGenericParam()
×
UNCOV
273
            ? new GenericParameterTypeInfoProviderWrapper(Definition.RawReturnType!.GetGenericParamName())
×
274
            : TypeAnalysisContext.GetSndnProviderForType(AppContext, Definition!.RawReturnType);
×
275

276
    IEnumerable<IParameterInfoProvider> IMethodInfoProvider.ParameterInfoProviders => Parameters;
×
277

278
    string IMethodInfoProvider.MethodName => Name;
×
279

280
    MethodAttributes IMethodInfoProvider.MethodAttributes => Attributes;
×
281

282
    MethodSemantics IMethodInfoProvider.MethodSemantics
283
    {
284
        get
285
        {
UNCOV
286
            if (DeclaringType != null)
×
287
            {
288
                //This one is a bit trickier, as il2cpp doesn't use semantics.
UNCOV
289
                foreach (var prop in DeclaringType.Properties)
×
290
                {
291
                    if (prop.Getter == this)
×
UNCOV
292
                        return MethodSemantics.Getter;
×
293
                    if (prop.Setter == this)
×
294
                        return MethodSemantics.Setter;
×
295
                }
296

UNCOV
297
                foreach (var evt in DeclaringType.Events)
×
298
                {
299
                    if (evt.Adder == this)
×
UNCOV
300
                        return MethodSemantics.AddOn;
×
301
                    if (evt.Remover == this)
×
302
                        return MethodSemantics.RemoveOn;
×
303
                    if (evt.Invoker == this)
×
304
                        return MethodSemantics.Fire;
×
305
                }
306
            }
307

UNCOV
308
            return 0;
×
UNCOV
309
        }
×
310
    }
311

312
    #endregion
313
}
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