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

SamboyCoding / Cpp2IL / 12320749043

13 Dec 2024 06:07PM UTC coverage: 27.79% (-0.3%) from 28.128%
12320749043

Pull #393

github

web-flow
Merge b9caa35d0 into ac99859af
Pull Request #393: Add Type field to AttributeAttribute

1268 of 6370 branches covered (19.91%)

Branch coverage included in aggregate %.

3 of 93 new or added lines in 7 files covered. (3.23%)

45 existing lines in 4 files now uncovered.

3386 of 10377 relevant lines covered (32.63%)

123656.67 hits per line

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

0.63
/Cpp2IL.Core/ProcessingLayers/AttributeInjectorProcessingLayer.cs
1
using System;
2
using System.Diagnostics;
3
using System.Linq;
4
using System.Reflection;
5
using System.Threading.Tasks;
6
using Cpp2IL.Core.Api;
7
using Cpp2IL.Core.Model.Contexts;
8
using Cpp2IL.Core.Model.CustomAttributes;
9
using Cpp2IL.Core.Utils;
10
using LibCpp2IL;
11

12
namespace Cpp2IL.Core.ProcessingLayers;
13

14
public class AttributeInjectorProcessingLayer : Cpp2IlProcessingLayer
15
{
16
    public override string Name => "Attribute Injector";
×
17
    public override string Id => "attributeinjector";
1✔
18

19
    private static bool _useEzDiffMode;
20

21
    public override void Process(ApplicationAnalysisContext appContext, Action<int, int>? progressCallback = null)
22
    {
23
        //EZ diff mode removes token attributes and method address/rva fields to make diffing using e.g. git easier
24
        _useEzDiffMode = appContext.GetExtraData<string>("attr-injector-use-ez-diff") != null;
×
25

26
        InjectAttributeAttribute(appContext);
×
27

28
        if (!_useEzDiffMode)
×
29
            InjectTokenAttribute(appContext);
×
30

31
        InjectAddressAttribute(appContext);
×
32
        InjectFieldOffsetAttribute(appContext);
×
33
    }
×
34

35
    private static void InjectFieldOffsetAttribute(ApplicationAnalysisContext appContext)
36
    {
37
        var fieldOffsetAttributes = appContext.InjectTypeIntoAllAssemblies("Cpp2ILInjected", "FieldOffsetAttribute", appContext.SystemTypes.SystemAttributeType);
×
38

39
        var offsetFields = fieldOffsetAttributes.InjectFieldToAllAssemblies("Offset", appContext.SystemTypes.SystemStringType, FieldAttributes.Public);
×
40

41
        var fieldOffsetConstructors = fieldOffsetAttributes.InjectConstructor(false);
×
42

43
        foreach (var assemblyAnalysisContext in appContext.Assemblies)
×
44
        {
45
            var offsetField = offsetFields[assemblyAnalysisContext];
×
46

47
            var fieldOffsetConstructor = fieldOffsetConstructors[assemblyAnalysisContext];
×
48

49
            foreach (var f in assemblyAnalysisContext.Types.SelectMany(t => t.Fields))
×
50
            {
51
                if (f.CustomAttributes == null || f.BackingData == null || f.IsStatic)
×
52
                    continue;
53

54
                var newAttribute = new AnalyzedCustomAttribute(fieldOffsetConstructor);
×
55

56
                //This loop is not done parallel because f.Offset has heavy lock contention
57
                newAttribute.Fields.Add(new(offsetField, new CustomAttributePrimitiveParameter($"0x{f.Offset:X}", newAttribute, CustomAttributeParameterKind.Field, 0)));
×
58
                f.CustomAttributes.Add(newAttribute);
×
59
            }
60
        }
61
    }
×
62

63
    private static void InjectAddressAttribute(ApplicationAnalysisContext appContext)
64
    {
65
        var addressAttributes = appContext.InjectTypeIntoAllAssemblies("Cpp2ILInjected", "AddressAttribute", appContext.SystemTypes.SystemAttributeType);
×
66

67
        var rvaFields = addressAttributes.InjectFieldToAllAssemblies("RVA", appContext.SystemTypes.SystemStringType, FieldAttributes.Public);
×
68
        var offsetFields = addressAttributes.InjectFieldToAllAssemblies("Offset", appContext.SystemTypes.SystemStringType, FieldAttributes.Public);
×
69
        var lengthFields = addressAttributes.InjectFieldToAllAssemblies("Length", appContext.SystemTypes.SystemStringType, FieldAttributes.Public);
×
70

71
        var addressConstructors = addressAttributes.InjectConstructor(false);
×
72

73
        foreach (var assemblyAnalysisContext in appContext.Assemblies)
×
74
        {
75
            var rvaField = rvaFields[assemblyAnalysisContext];
×
76
            var offsetField = offsetFields[assemblyAnalysisContext];
×
77
            var lengthField = lengthFields[assemblyAnalysisContext];
×
78

79
            var addressConstructor = addressConstructors[assemblyAnalysisContext];
×
80

81
            foreach (var m in assemblyAnalysisContext.Types.SelectMany(t => t.Methods))
×
82
            {
83
                if (m.CustomAttributes == null || m.UnderlyingPointer == 0)
×
84
                    continue;
85

86
                var newAttribute = new AnalyzedCustomAttribute(addressConstructor);
×
87

88
                if (!_useEzDiffMode)
×
89
                {
90
                    newAttribute.Fields.Add(new(rvaField, new CustomAttributePrimitiveParameter($"0x{m.Rva:X}", newAttribute, CustomAttributeParameterKind.Field, 0)));
×
91
                    if (appContext.Binary.TryMapVirtualAddressToRaw(m.UnderlyingPointer, out var offset))
×
92
                        newAttribute.Fields.Add(new(offsetField, new CustomAttributePrimitiveParameter($"0x{offset:X}", newAttribute, CustomAttributeParameterKind.Field, 1)));
×
93
                }
94

95
                newAttribute.Fields.Add(new(lengthField, new CustomAttributePrimitiveParameter($"0x{m.RawBytes.Length:X}", newAttribute, CustomAttributeParameterKind.Field, newAttribute.Fields.Count)));
×
96
                m.CustomAttributes.Add(newAttribute);
×
97
            }
98
        }
99
    }
×
100

101
    private static void InjectTokenAttribute(ApplicationAnalysisContext appContext)
102
    {
103
        var tokenAttributes = appContext.InjectTypeIntoAllAssemblies("Cpp2ILInjected", "TokenAttribute", appContext.SystemTypes.SystemAttributeType);
×
104

105
        var tokenFields = tokenAttributes.InjectFieldToAllAssemblies("Token", appContext.SystemTypes.SystemStringType, FieldAttributes.Public);
×
106

107
        var tokenConstructors = tokenAttributes.InjectConstructor(false);
×
108

109
        foreach (var assemblyAnalysisContext in appContext.Assemblies)
×
110
        {
111
            var tokenField = tokenFields[assemblyAnalysisContext];
×
112

113
            var tokenConstructor = tokenConstructors[assemblyAnalysisContext];
×
114

115
            var toProcess = assemblyAnalysisContext.Types.SelectMany(ctx => ctx.Methods.Cast<HasCustomAttributes>()
×
116
                    .Concat(ctx.Fields)
×
117
                    .Concat(ctx.Events)
×
118
                    .Concat(ctx.Properties)
×
119
                    .Append(ctx))
×
120
                .Append(assemblyAnalysisContext);
×
121

122
            Parallel.ForEach(toProcess, context =>
×
123
            {
×
124
                if (context.CustomAttributes == null || context.Token == 0)
×
125
                    return;
×
126

×
127
                var newAttribute = new AnalyzedCustomAttribute(tokenConstructor);
×
128
                newAttribute.Fields.Add(new(tokenField, new CustomAttributePrimitiveParameter($"0x{context.Token:X}", newAttribute, CustomAttributeParameterKind.Field, 0)));
×
129
                context.CustomAttributes.Add(newAttribute);
×
130
            });
×
131
        }
132
    }
×
133

134
    private static void InjectAttributeAttribute(ApplicationAnalysisContext appContext)
135
    {
136
        if (LibCpp2IlMain.MetadataVersion >= 29f)
×
137
        {
138
            //All attributes should be fully serializable anyway, as they're stored in metadata
139
            //However, we still need to read them all
140
            var toAnalyze = appContext.Assemblies.SelectMany(ctx => ctx.Types)
×
141
                .SelectMany(ctx => ctx.Methods
×
142
                    .SelectMany(m => m.Parameters.Cast<HasCustomAttributes>().Append(m))
×
143
                    .Concat(ctx.Fields)
×
144
                    .Concat(ctx.Events)
×
145
                    .Concat(ctx.Properties)
×
146
                    .Append(ctx));
×
147

148
            foreach (var hasCustomAttributes in toAnalyze)
×
149
                hasCustomAttributes.AnalyzeCustomAttributeData();
×
150

151
            // MiscUtils.ExecuteParallel(toAnalyze, ctx => ctx.AnalyzeCustomAttributeData());
152
            return;
×
153
        }
154

155
        var attributeAttributes = appContext.InjectTypeIntoAllAssemblies("Cpp2ILInjected", "AttributeAttribute", appContext.SystemTypes.SystemAttributeType);
×
156

NEW
157
        var attributeTypeFields = attributeAttributes.InjectFieldToAllAssemblies("Type", appContext.SystemTypes.SystemTypeType, FieldAttributes.Public);
×
158
        var attributeNameFields = attributeAttributes.InjectFieldToAllAssemblies("Name", appContext.SystemTypes.SystemStringType, FieldAttributes.Public);
×
159
        var attributeRvaFields = attributeAttributes.InjectFieldToAllAssemblies("RVA", appContext.SystemTypes.SystemStringType, FieldAttributes.Public);
×
160
        var attributeOffsetFields = attributeAttributes.InjectFieldToAllAssemblies("Offset", appContext.SystemTypes.SystemStringType, FieldAttributes.Public);
×
161

162
        var attributeConstructors = attributeAttributes.InjectConstructor(false);
×
163

164
        foreach (var assemblyAnalysisContext in appContext.Assemblies)
×
165
        {
166
            // Todo: Remove nameField because typeField makes it redundant. It only remains for backwards compatibility with Il2CppInterop.
167
            // https://github.com/BepInEx/Il2CppInterop/blob/9d4599dc78d69ede49a2ee96a1ccf41eec02db5b/Il2CppInterop.Generator/Passes/Pass70GenerateProperties.cs#L46
NEW
168
            var typeField = attributeTypeFields[assemblyAnalysisContext];
×
169
            var nameField = attributeNameFields[assemblyAnalysisContext];
×
170
            var rvaField = attributeRvaFields[assemblyAnalysisContext];
×
171
            var offsetField = attributeOffsetFields[assemblyAnalysisContext];
×
172

173
            var attributeConstructor = attributeConstructors[assemblyAnalysisContext];
×
174

175
            var toProcess = assemblyAnalysisContext.Types
×
176
                .SelectMany(ctx => ctx.Methods.SelectMany(m => m.Parameters.Cast<HasCustomAttributes>().Append(m))
×
177
                    .Concat(ctx.Fields)
×
178
                    .Concat(ctx.Events)
×
179
                    .Concat(ctx.Properties)
×
180
                    .Append(ctx))
×
181
                .Append(assemblyAnalysisContext);
×
182

NEW
183
            MiscUtils.ExecuteParallel(toProcess, c => ProcessCustomAttributesForContext(c, typeField, nameField, rvaField, offsetField, attributeConstructor));
×
184
        }
185
    }
×
186

187
    private static void ProcessCustomAttributesForContext(HasCustomAttributes context, FieldAnalysisContext typeField, FieldAnalysisContext nameField, FieldAnalysisContext rvaField, FieldAnalysisContext offsetField, MethodAnalysisContext ctor)
188
    {
189
        if (_useEzDiffMode)
×
190
            context.CustomAttributes = [];
×
191
        else
192
        {
193
            context.AnalyzeCustomAttributeData(false);
×
194

195
            if (context.CustomAttributes == null)
×
196
                return;
×
197
        }
198

199
        for (var index = 0; index < context.CustomAttributes.Count; index++)
×
200
        {
201
            var attribute = context.CustomAttributes[index];
×
202

203
            if (attribute.IsSuitableForEmission)
×
204
                //Attribute has all required parameters, so we don't need an injected one
205
                continue;
206

207
            //Create replacement attribute
208
            var replacementAttribute = new AnalyzedCustomAttribute(ctor);
×
209

210
            //Get ptr, and from it, rva and offset
211
            var generatorPtr = context.CaCacheGeneratorAnalysis!.UnderlyingPointer;
×
212
            var generatorRva = context.AppContext.Binary.GetRva(generatorPtr);
×
213
            if (!context.AppContext.Binary.TryMapVirtualAddressToRaw(generatorPtr, out var offsetInBinary))
×
214
                offsetInBinary = 0;
×
215

216
            //Add the 3 fields to the replacement attribute
NEW
217
            replacementAttribute.Fields.Add(new(typeField, new CustomAttributeTypeParameter(attribute.Constructor.DeclaringType, replacementAttribute, CustomAttributeParameterKind.Field, 0)));
×
218
            replacementAttribute.Fields.Add(new(rvaField, new CustomAttributePrimitiveParameter($"0x{generatorRva:X}", replacementAttribute, CustomAttributeParameterKind.Field, 1)));
×
219
            replacementAttribute.Fields.Add(new(offsetField, new CustomAttributePrimitiveParameter($"0x{offsetInBinary:X}", replacementAttribute, CustomAttributeParameterKind.Field, 2)));
×
NEW
220
            replacementAttribute.Fields.Add(new(nameField, new CustomAttributePrimitiveParameter(attribute.Constructor.DeclaringType!.Name, replacementAttribute, CustomAttributeParameterKind.Field, 3)));
×
221

222
            //Replace the original attribute with the replacement attribute
223
            context.CustomAttributes[index] = replacementAttribute;
×
224
        }
225
    }
×
226
}
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