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

SamboyCoding / Cpp2IL / 12324180199

13 Dec 2024 10:46PM UTC coverage: 27.497% (-0.3%) from 27.79%
12324180199

push

github

SamboyCoding
Core: Fix more test failures

1251 of 6374 branches covered (19.63%)

Branch coverage included in aggregate %.

18 of 21 new or added lines in 1 file covered. (85.71%)

429 existing lines in 16 files now uncovered.

3360 of 10395 relevant lines covered (32.32%)

124828.12 hits per line

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

45.28
/Cpp2IL.Core/Model/Contexts/HasCustomAttributes.cs
1
using System;
2
using System.Collections.Generic;
3
using System.Diagnostics;
4
using System.IO;
5
using System.Linq;
6
using Cpp2IL.Core.Extensions;
7
using Cpp2IL.Core.Logging;
8
using Cpp2IL.Core.Model.CustomAttributes;
9
using Cpp2IL.Core.Utils;
10
using LibCpp2IL;
11
using LibCpp2IL.BinaryStructures;
12
using LibCpp2IL.Metadata;
13

14
namespace Cpp2IL.Core.Model.Contexts;
15

16
/// <summary>
17
/// A base class to represent any type which has, or can have, custom attributes.
18
/// </summary>
19
public abstract class HasCustomAttributes(uint token, ApplicationAnalysisContext appContext)
20
    : HasToken(token, appContext)
1,488,740✔
21
{
22
    private bool _hasAnalyzedCustomAttributeData;
23

24
    /// <summary>
25
    /// On V29, stores the custom attribute blob. Pre-29, stores the bytes for the custom attribute generator function.
26
    /// </summary>
27
    public Memory<byte> RawIl2CppCustomAttributeData = Memory<byte>.Empty;
1,488,740✔
28

29
    /// <summary>
30
    /// Stores the analyzed custom attribute data once analysis has actually run.
31
    /// </summary>
32
    public List<AnalyzedCustomAttribute>? CustomAttributes;
33

34
    /// <summary>
35
    /// Stores the attribute type range for this member, which references which custom attributes are present.
36
    ///
37
    /// Null on v29+, nonnull prior to that
38
    /// </summary>
39
    public Il2CppCustomAttributeTypeRange? AttributeTypeRange;
40

41
    /// <summary>
42
    /// Stores the raw types of the custom attributes on this member.
43
    ///
44
    /// Null on v29+ (constructors are in the blob), nonnull prior to that
45
    /// </summary>
46
    public List<Il2CppType>? AttributeTypes;
47

48
    /// <summary>
49
    /// Prior to v29, stores the analysis context for custom attribute cache generator function.
50
    ///
51
    /// On v29, is null because there is no method, the attribute blob is stored instead, in the metadata file.
52
    /// </summary>
53
    public AttributeGeneratorMethodAnalysisContext? CaCacheGeneratorAnalysis;
54

55
    /// <summary>
56
    /// Returns this member's custom attribute index, or -1 if it has no custom attributes.
57
    /// </summary>
58
    protected abstract int CustomAttributeIndex { get; }
59

60
    /// <summary>
61
    /// Returns this member's assembly context for use in custom attribute reconstruction.
62
    /// </summary>
63
    public abstract AssemblyAnalysisContext CustomAttributeAssembly { get; }
64

65
    public abstract string CustomAttributeOwnerName { get; }
66

67
    /// <summary>
68
    /// Pre-v29, stores the index of the custom attribute range for this member. Post-v29, always 0.
69
    /// </summary>
70
    private int Pre29RangeIndex;
71

72
    public bool IsCompilerGeneratedBasedOnCustomAttributes =>
73
        CustomAttributes?.Any(a => a.Constructor.DeclaringType!.FullName.Contains("CompilerGeneratedAttribute"))
×
74
        ?? AttributeTypes?.Any(t => t.Type == Il2CppTypeEnum.IL2CPP_TYPE_CLASS && t.AsClass().FullName!.Contains("CompilerGeneratedAttribute"))
×
75
        ?? false;
×
76

77

78
#pragma warning disable CS8618 //Non-null member is not initialized.
79
#pragma warning restore CS8618
80

81
    protected void InitCustomAttributeData()
82
    {
83
        if (AppContext.MetadataVersion >= 29)
795,458✔
84
        {
85
            var offsets = GetV29BlobOffsets();
246,588✔
86

87
            if (!offsets.HasValue)
246,588✔
88
                return;
228,024✔
89

90
            var (blobStart, blobEnd) = offsets.Value;
18,564✔
91
            RawIl2CppCustomAttributeData = AppContext.Metadata.ReadByteArrayAtRawAddress(blobStart, (int)(blobEnd - blobStart));
18,564✔
92

93
            return;
18,564✔
94
        }
95

96
        AttributeTypeRange = AppContext.Metadata.GetCustomAttributeData(CustomAttributeAssembly.Definition.Image, CustomAttributeIndex, Token, out Pre29RangeIndex);
548,870✔
97

98
        if (AttributeTypeRange == null || AttributeTypeRange.count == 0)
548,870✔
99
        {
100
            RawIl2CppCustomAttributeData = Array.Empty<byte>();
506,270✔
101
            AttributeTypes = [];
506,270✔
102
            return; //No attributes
506,270✔
103
        }
104

105
        AttributeTypes = Enumerable.Range(AttributeTypeRange.start, AttributeTypeRange.count)
42,600✔
106
            .Select(attrIdx => AppContext.Metadata!.attributeTypes![attrIdx]) //Not null because we've checked we're not on v29
95,900✔
107
            .Select(typeIdx => AppContext.Binary!.GetType(typeIdx))
95,900✔
108
            .ToList();
42,600✔
109
    }
42,600✔
110

111
    private (long blobStart, long blobEnd)? GetV29BlobOffsets()
112
    {
113
        var target = new Il2CppCustomAttributeDataRange() { token = Token };
246,588✔
114
        var caIndex = AppContext.Metadata.AttributeDataRanges.BinarySearch
246,588✔
115
        (
246,588✔
116
            CustomAttributeAssembly.Definition.Image.customAttributeStart,
246,588✔
117
            (int)CustomAttributeAssembly.Definition.Image.customAttributeCount,
246,588✔
118
            target,
246,588✔
119
            new TokenComparer()
246,588✔
120
        );
246,588✔
121

122
        if (caIndex < 0)
246,588✔
123
        {
124
            RawIl2CppCustomAttributeData = Array.Empty<byte>();
228,024✔
125
            return null;
228,024✔
126
        }
127

128
        var attributeDataRange = AppContext.Metadata.AttributeDataRanges[caIndex];
18,564✔
129
        var next = AppContext.Metadata.AttributeDataRanges[caIndex + 1];
18,564✔
130

131
        var blobStart = AppContext.Metadata.metadataHeader.attributeDataOffset + attributeDataRange.startOffset;
18,564✔
132
        var blobEnd = AppContext.Metadata.metadataHeader.attributeDataOffset + next.startOffset;
18,564✔
133
        return (blobStart, blobEnd);
18,564✔
134
    }
135

136
    private void InitPre29AttributeGeneratorAnalysis(int rangeIndex)
137
    {
138
        ulong generatorPtr;
UNCOV
139
        if (AppContext.MetadataVersion < 27)
×
140
            try
141
            {
UNCOV
142
                generatorPtr = AppContext.Binary.GetCustomAttributeGenerator(rangeIndex);
×
UNCOV
143
            }
×
144
            catch (IndexOutOfRangeException)
×
145
            {
146
                Logger.WarnNewline("Custom attribute generator out of range for " + this, "CA Restore");
×
147
                RawIl2CppCustomAttributeData = Array.Empty<byte>();
×
NEW
148
                return;
×
149
            }
150
        else
151
        {
152
            var baseAddress = CustomAttributeAssembly.CodeGenModule!.customAttributeCacheGenerator;
×
153
            var relativeIndex = rangeIndex - CustomAttributeAssembly.Definition.Image.customAttributeStart;
×
154
            var ptrToAddress = baseAddress + (ulong)relativeIndex * AppContext.Binary.PointerSize;
×
155
            generatorPtr = AppContext.Binary.ReadPointerAtVirtualAddress(ptrToAddress);
×
156
        }
157

UNCOV
158
        if (generatorPtr == 0 || !AppContext.Binary.TryMapVirtualAddressToRaw(generatorPtr, out _))
×
159
        {
160
            Logger.WarnNewline($"Supposedly had custom attributes ({string.Join(", ", AttributeTypes)}), but generator was null for " + this, "CA Restore");
×
161
            RawIl2CppCustomAttributeData = Memory<byte>.Empty;
×
NEW
162
            return;
×
163
        }
164

UNCOV
165
        CaCacheGeneratorAnalysis = new(generatorPtr, AppContext, this);
×
UNCOV
166
        RawIl2CppCustomAttributeData = CaCacheGeneratorAnalysis.RawBytes;
×
UNCOV
167
    }
×
168

169
    /// <summary>
170
    /// Attempt to parse the Il2CppCustomAttributeData blob into custom attributes.
171
    /// </summary>
172
    public void AnalyzeCustomAttributeData(bool allowAnalysis = true)
173
    {
174
        if (_hasAnalyzedCustomAttributeData)
6!
175
            return;
×
176

177
        _hasAnalyzedCustomAttributeData = true;
6✔
178

179
        CustomAttributes = [];
6✔
180

181
        if (AppContext.MetadataVersion >= 29)
6!
182
        {
183
            AnalyzeCustomAttributeDataV29();
6✔
184
            return;
6✔
185
        }
186
        
NEW
187
        InitPre29AttributeGeneratorAnalysis(Pre29RangeIndex);
×
188

189
        if (RawIl2CppCustomAttributeData.Length == 0)
×
190
            return;
×
191

192
        if (allowAnalysis)
×
193
        {
194
            try
195
            {
196
                CaCacheGeneratorAnalysis!.Analyze();
×
197
            }
×
198
            catch (Exception e)
×
199
            {
200
                Logger.WarnNewline("Failed to analyze custom attribute cache generator for " + this + " because " + e.Message, "CA Restore");
×
201
                return;
×
202
            }
203
        }
204

205
        //Basically, extract actions from the analysis, and compare with the type list we have to resolve parameters and populate the CustomAttributes list.
206

207
        foreach (var il2CppType in AttributeTypes!) //Assert nonnull because we're pre-29 at this point
×
208
        {
209
            var typeDef = il2CppType.AsClass();
×
210
            var attributeTypeContext = AppContext.ResolveContextForType(typeDef) ?? throw new("Unable to find type " + typeDef.FullName);
×
211

212
            AnalyzedCustomAttribute attribute;
213
            if (attributeTypeContext.Methods.FirstOrDefault(c => c.Name == ".ctor" && c.Definition!.parameterCount == 0) is { } constructor)
×
214
            {
215
                attribute = new(constructor);
×
216
            }
217
            else if (attributeTypeContext.Methods.FirstOrDefault(c => c.Name == ".ctor") is { } anyConstructor)
×
218
            {
219
                //TODO change this to actual constructor w/ params once anaylsis is available
220
                attribute = new(anyConstructor);
×
221
            }
222
            else
223
                //No constructor - shouldn't happen?
224
                continue;
225

226
            //Add the attribute, even if we don't have constructor params, so it can be read regardless
227
            CustomAttributes.Add(attribute);
×
228
        }
229
    }
×
230

231
    /// <summary>
232
    /// Parses the Il2CppCustomAttributeData blob as a v29 metadata attribute blob into custom attributes.
233
    /// </summary>
234
    private void AnalyzeCustomAttributeDataV29()
235
    {
236
        if (RawIl2CppCustomAttributeData.Length == 0)
6!
237
            return;
×
238

239
        using var blobStream = new MemoryStream(RawIl2CppCustomAttributeData.ToArray());
6✔
240
        var attributeCount = blobStream.ReadUnityCompressedUint();
6✔
241
        var constructors = V29AttributeUtils.ReadConstructors(blobStream, attributeCount, AppContext);
6✔
242

243
        //Diagnostic data
244
        var startOfData = blobStream.Position;
6✔
245
        var perAttributeStartOffsets = new Dictionary<Il2CppMethodDefinition, long>();
6✔
246

247
        CustomAttributes = [];
6✔
248
        foreach (var constructor in constructors)
36✔
249
        {
250
            perAttributeStartOffsets[constructor] = blobStream.Position;
12✔
251

252
            var attributeTypeContext = AppContext.ResolveContextForType(constructor.DeclaringType!) ?? throw new($"Unable to find type {constructor.DeclaringType!.FullName}");
12!
253
            var attributeMethodContext = attributeTypeContext.GetMethod(constructor) ?? throw new($"Unable to find method {constructor.Name} in type {attributeTypeContext.Definition?.FullName}");
12!
254

255
            try
256
            {
257
                CustomAttributes.Add(V29AttributeUtils.ReadAttribute(blobStream, attributeMethodContext, AppContext));
12✔
258
            }
12✔
259
            catch (Exception e)
×
260
            {
261
                Logger.ErrorNewline($"Failed to read attribute data for {constructor}, which has parameters {string.Join(", ", constructor.Parameters!.Select(p => p.Type))}", "CA Restore");
×
262
                Logger.ErrorNewline($"This member ({ToString()}) has {RawIl2CppCustomAttributeData.Length} bytes of data starting at 0x{GetV29BlobOffsets()!.Value.blobStart:X}", "CA Restore");
×
263
                Logger.ErrorNewline($"The post-constructor data started at 0x{startOfData:X} bytes into our blob", "CA Restore");
×
264
                Logger.ErrorNewline($"Data for this constructor started at 0x{perAttributeStartOffsets[constructor]:X} bytes into our blob, we are now 0x{blobStream.Position:X} bytes into the blob", "CA Restore");
×
265
                Logger.ErrorNewline($"The exception message was {e.Message}", "CA Restore");
×
266

267
                throw;
×
268
            }
269
        }
270
    }
12✔
271
}
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