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

SamboyCoding / Cpp2IL / 15172433337

21 May 2025 08:57PM UTC coverage: 34.294% (+0.2%) from 34.062%
15172433337

Pull #462

github

web-flow
Merge 32601968e into 5807d2b6c
Pull Request #462: Support overriding member types

1801 of 6644 branches covered (27.11%)

Branch coverage included in aggregate %.

128 of 232 new or added lines in 35 files covered. (55.17%)

5 existing lines in 5 files now uncovered.

4199 of 10852 relevant lines covered (38.69%)

186271.78 hits per line

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

79.23
/Cpp2IL.Core/OutputFormats/AsmResolverDummyDllOutputFormat.cs
1
// #define VERBOSE_LOGGING
2

3
using System;
4
using System.Collections.Generic;
5
using System.IO;
6
using System.Linq;
7
using System.Threading.Tasks;
8
using AsmResolver.DotNet;
9
using AsmResolver.DotNet.Builder;
10
using AsmResolver.PE.Builder;
11
using AsmResolver.PE.DotNet.Metadata.Tables;
12
using Cpp2IL.Core.Api;
13
using Cpp2IL.Core.Logging;
14
using Cpp2IL.Core.Model.Contexts;
15
using Cpp2IL.Core.Utils;
16
using Cpp2IL.Core.Utils.AsmResolver;
17
using LibCpp2IL.Metadata;
18

19
namespace Cpp2IL.Core.OutputFormats;
20

21
public abstract class AsmResolverDllOutputFormat : Cpp2IlOutputFormat
22
{
23
    private AssemblyDefinition? MostRecentCorLib { get; set; }
225✔
24

25
    public sealed override void DoOutput(ApplicationAnalysisContext context, string outputRoot)
26
    {
27
        var ret = BuildAssemblies(context);
×
28

29
        var start = DateTime.Now;
×
30
        Logger.Verbose("Generating PE images...", "DllOutput");
×
31

32
        if (!Directory.Exists(outputRoot))
×
33
            Directory.CreateDirectory(outputRoot);
×
34

35
        //Convert assembly definitions to PE files
36
        var peImagesToWrite = ret
×
37
            .AsParallel()
×
38
            .Select(a => (image: a.ManifestModule!.ToPEImage(new ManagedPEImageBuilder()), name: a.ManifestModule.Name!))
×
39
            .ToList();
×
40

41
        Logger.VerboseNewline($"{(DateTime.Now - start).TotalMilliseconds:F1}ms", "DllOutput");
×
42

43
        start = DateTime.Now;
×
44
        Logger.Verbose("Building and writing managed PE files to disk...", "DllOutput");
×
45

46
        //Save them
47
        var fileBuilder = new ManagedPEFileBuilder();
×
48
        foreach (var (image, name) in peImagesToWrite)
×
49
        {
50
            var dllPath = Path.Combine(outputRoot, name);
×
51
            fileBuilder.CreateFile(image).Write(dllPath);
×
52
        }
53

54
        Logger.VerboseNewline($"{(DateTime.Now - start).TotalMilliseconds:F1}ms", "DllOutput");
×
55
    }
×
56

57
    public List<AssemblyDefinition> BuildAssemblies(ApplicationAnalysisContext context)
58
    {
59
#if VERBOSE_LOGGING
60
        var asmCount = context.Assemblies.Count;
61
        var typeCount = context.AllTypes.Count();
62
        var methodCount = context.AllTypes.SelectMany(t => t.Methods).Count();
63
        var fieldCount = context.AllTypes.SelectMany(t => t.Fields).Count();
64
        var propertyCount = context.AllTypes.SelectMany(t => t.Properties).Count();
65
        var eventCount = context.AllTypes.SelectMany(t => t.Events).Count();
66
#endif
67

68
        //Build the stub assemblies
69
        var start = DateTime.Now;
5✔
70
#if VERBOSE_LOGGING
71
        Logger.Verbose($"Building stub assemblies ({asmCount} assemblies, {typeCount} types)...", "DllOutput");
72
#else
73
        Logger.Verbose($"Building stub assemblies...", "DllOutput");
5✔
74
#endif
75
        List<AssemblyDefinition> ret = BuildStubAssemblies(context);
5✔
76
        Logger.VerboseNewline($"{(DateTime.Now - start).TotalMilliseconds:F1}ms", "DllOutput");
5✔
77

78
        start = DateTime.Now;
5✔
79
        Logger.Verbose("Configuring inheritance and generics...", "DllOutput");
5✔
80

81
        Parallel.ForEach(context.Assemblies, AsmResolverAssemblyPopulator.ConfigureHierarchy);
5✔
82

83
        Logger.VerboseNewline($"{(DateTime.Now - start).TotalMilliseconds:F1}ms", "DllOutput");
5✔
84

85
        //Populate them
86
        start = DateTime.Now;
5✔
87

88
#if VERBOSE_LOGGING
89
        Logger.Verbose($"Adding {fieldCount} fields, {methodCount} methods, {propertyCount} properties, and {eventCount} events (in parallel)...", "DllOutput");
90
#else
91
        Logger.Verbose($"Adding fields, methods, properties, and events (in parallel)...", "DllOutput");
5✔
92
#endif
93

94
        MiscUtils.ExecuteParallel(context.Assemblies, AsmResolverAssemblyPopulator.CopyDataFromIl2CppToManaged);
5✔
95
        MiscUtils.ExecuteParallel(context.Assemblies, AsmResolverAssemblyPopulator.AddExplicitInterfaceImplementations);
5✔
96

97
        Logger.VerboseNewline($"{(DateTime.Now - start).TotalMilliseconds:F1}ms", "DllOutput");
5✔
98

99
        //Populate custom attributes
100
        start = DateTime.Now;
5✔
101
        Logger.Verbose("Adding custom attributes to all of the above...", "DllOutput");
5✔
102
        MiscUtils.ExecuteParallel(context.Assemblies, AsmResolverAssemblyPopulator.PopulateCustomAttributes);
5✔
103

104
        Logger.VerboseNewline($"{(DateTime.Now - start).TotalMilliseconds:F1}ms", "DllOutput");
5✔
105

106
        //Fill method bodies - this should always be done last
107
        start = DateTime.Now;
5✔
108
        Logger.Verbose($"Filling method bodies (in parallel)...", "DllOutput");
5✔
109
        MiscUtils.ExecuteParallel(context.Assemblies, FillMethodBodies);
5✔
110

111
        Logger.VerboseNewline($"{(DateTime.Now - start).TotalMilliseconds:F1}ms", "DllOutput");
5✔
112

113
        TypeDefinitionsAsmResolver.Reset();
5✔
114

115
        return ret;
5✔
116
    }
117

118
    protected abstract void FillMethodBody(MethodDefinition methodDefinition, MethodAnalysisContext methodContext);
119

120
    protected virtual void FillMethodBodies(AssemblyAnalysisContext context)
121
    {
122
        foreach (var typeContext in context.Types)
29,680✔
123
        {
124
            if (AsmResolverAssemblyPopulator.IsTypeContextModule(typeContext))
14,630✔
125
                continue;
126

127
#if !DEBUG
128
            try
129
#endif
130
            {
131
                foreach (var methodCtx in typeContext.Methods)
202,900✔
132
                {
133
                    var managedMethod = methodCtx.GetExtraData<MethodDefinition>("AsmResolverMethod") ?? throw new($"AsmResolver method not found in method analysis context for {typeContext.FullName}.{methodCtx.Name}");
87,030!
134

135
                    FillMethodBody(managedMethod, methodCtx);
87,030✔
136
                }
137
            }
14,420✔
138
#if !DEBUG
139
            catch (System.Exception e)
×
140
            {
NEW
141
                var managedType = typeContext.GetExtraData<TypeDefinition>("AsmResolverType") ?? throw new($"AsmResolver type not found in type analysis context for {typeContext.FullName}");
×
NEW
142
                throw new($"Failed to process type {managedType.FullName} (module {managedType.Module?.Name}, declaring type {managedType.DeclaringType?.FullName}) in {context.Name}", e);
×
143
            }
144
#endif
145
        }
146
    }
210✔
147

148
    private List<AssemblyDefinition> BuildStubAssemblies(ApplicationAnalysisContext context)
149
    {
150
        var assemblyResolver = new Il2CppAssemblyResolver();
5✔
151
        var metadataResolver = new DefaultMetadataResolver(assemblyResolver);
5✔
152

153
        var corlib = context.Assemblies.First(a => a.Name == "mscorlib");
10✔
154
        MostRecentCorLib = BuildStubAssembly(corlib, null, metadataResolver);
5✔
155
        assemblyResolver.DummyAssemblies.Add(MostRecentCorLib.Name!, MostRecentCorLib);
5✔
156

157
        var ret = context.Assemblies
5✔
158
            // .AsParallel()
5✔
159
            .Where(a => a.Name != "mscorlib")
210✔
160
            .Select(a => BuildStubAssembly(a, MostRecentCorLib, metadataResolver))
205✔
161
            .ToList();
5✔
162

163
        ret.ForEach(a => assemblyResolver.DummyAssemblies.Add(a.Name!, a));
210✔
164

165
        ret.Add(MostRecentCorLib);
5✔
166
        return ret;
5✔
167
    }
168

169
    private sealed class CorLibModuleDefinition : ModuleDefinition
170
    {
171
        public CorLibModuleDefinition(string? name, AssemblyDefinition assembly) : base(name, new(assembly))
5✔
172
        {
173
            // https://github.com/Washi1337/AsmResolver/issues/620
174
            CorLibTypeFactory = new(this);
5✔
175
            AssemblyReferences.Clear();
5✔
176
        }
5✔
177
    }
178

179
    private static AssemblyDefinition BuildStubAssembly(AssemblyAnalysisContext assemblyContext, AssemblyDefinition? corLib, IMetadataResolver metadataResolver)
180
    {
181
        //Get the name of the assembly (= the name of the DLL without the file extension)
182
        //Build an AsmResolver assembly from this definition
183
        var ourAssembly = new AssemblyDefinition(assemblyContext.Name, assemblyContext.Version)
210✔
184
        {
210✔
185
            HashAlgorithm = (AssemblyHashAlgorithm)assemblyContext.HashAlgorithm,
210✔
186
            Attributes = (AssemblyAttributes)assemblyContext.Flags,
210✔
187
            Culture = assemblyContext.Culture,
210✔
188
            PublicKey = assemblyContext.PublicKey,
210✔
189
        };
210✔
190

191
        //Setting the corlib module allows element types in references to that assembly to be set correctly without us having to manually set them.
192
        var moduleName = assemblyContext.ModuleName;
210✔
193

194
        var managedModule = corLib is not null //Use either ourself as corlib, if we are corlib, otherwise the provided one
210✔
195
            ? new ModuleDefinition(moduleName, new(corLib))
210✔
196
            {
210✔
197
                MetadataResolver = metadataResolver
210✔
198
            }
210✔
199
            : new CorLibModuleDefinition(moduleName, ourAssembly)
210✔
200
            {
210✔
201
                MetadataResolver = metadataResolver
210✔
202
            };
210✔
203
        ourAssembly.Modules.Add(managedModule);
210✔
204

205
        foreach (var il2CppTypeDefinition in assemblyContext.TopLevelTypes)
23,340✔
206
        {
207
            if (il2CppTypeDefinition.Name != "<Module>")
11,460✔
208
                //We skip module because I've never come across an il2cpp assembly with any top-level functions, and it's simpler to skip it as AsmResolver adds one by default.
209
                managedModule.TopLevelTypes.Add(BuildStubType(il2CppTypeDefinition));
11,250✔
210
        }
211

212
        if (corLib == null)
210✔
213
        {
214
            //We *are* the corlib, so cache defs now
215
            TypeDefinitionsAsmResolver.CacheNeededTypeDefinitions();
5✔
216
        }
217

218
        //We can get issues with consumers of the API if the base type is not set correctly for value types or enums, so we set it here (as early as possible) if we can
219
        foreach (var assemblyContextType in assemblyContext.Types)
29,680✔
220
        {
221
            if (assemblyContextType.Definition is not { } def || assemblyContextType.GetExtraData<TypeDefinition>("AsmResolverType") is not { } asmResolverType)
14,630✔
222
                continue;
223

224
            if (def.IsValueType)
14,420✔
225
                asmResolverType.BaseType = managedModule.DefaultImporter.ImportType(TypeDefinitionsAsmResolver.ValueType);
4,475✔
226
            else if (def.IsEnumType)
9,945!
227
                asmResolverType.BaseType = managedModule.DefaultImporter.ImportType(TypeDefinitionsAsmResolver.Enum);
×
228
        }
229

230
        //Store the managed assembly in the context so we can use it later.
231
        assemblyContext.PutExtraData("AsmResolverAssembly", ourAssembly);
210✔
232

233
        return ourAssembly;
210✔
234
    }
235

236
    private static TypeDefinition BuildStubType(TypeAnalysisContext typeContext)
237
    {
238
        var typeDef = typeContext.Definition;
14,420✔
239

240
        //Initialize an empty type definition
241
        var ret = new TypeDefinition(typeContext.Namespace, typeContext.Name, (TypeAttributes)typeContext.Attributes);
14,420✔
242

243
        //Set up its layout
244
        if (typeDef != null)
14,420✔
245
            ConfigureTypeSize(typeDef, ret);
14,420✔
246

247
        //Create nested types
248
        foreach (var cppNestedType in typeContext.NestedTypes)
35,180✔
249
            ret.NestedTypes.Add(BuildStubType(cppNestedType));
3,170✔
250

251
        //Associate this asm resolve td with the type context
252
        typeContext.PutExtraData("AsmResolverType", ret);
14,420✔
253

254
        //Add to the lookup-by-id table used by the resolver
255
        if (typeDef != null)
14,420✔
256
            AsmResolverUtils.TypeDefsByIndex[typeDef.TypeIndex] = ret;
14,420✔
257

258
        return ret;
14,420✔
259
    }
260

261
    private static void ConfigureTypeSize(Il2CppTypeDefinition il2CppDefinition, TypeDefinition asmResolverDefinition)
262
    {
263
        if (!il2CppDefinition.IsValueType || il2CppDefinition.IsEnumType)
14,420✔
264
            return; // Only structs can have their layout changed
11,865✔
265

266
        ushort packingSize = 0;
2,555✔
267
        var classSize = 0U;
2,555✔
268
        if (!il2CppDefinition.PackingSizeIsDefault)
2,555✔
269
            packingSize = (ushort)il2CppDefinition.PackingSize;
915✔
270

271
        if (!il2CppDefinition.ClassSizeIsDefault)
2,555✔
272
        {
273
            if (il2CppDefinition.Size > 1 << 30)
915!
274
                throw new Exception($"Got invalid size for type {il2CppDefinition}: {il2CppDefinition.RawSizes}");
×
275

276
            if (il2CppDefinition.Size != -1)
915!
277
                classSize = (uint)il2CppDefinition.Size;
915✔
278
            else
279
                classSize = 0; //Not sure what this value actually implies but it seems to work
×
280
        }
281

282
        if (packingSize != 0 || classSize != 0)
2,555✔
283
            asmResolverDefinition.ClassLayout = new(packingSize, classSize);
915✔
284
    }
2,555✔
285
}
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