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

SamboyCoding / Cpp2IL / 22528343799

28 Feb 2026 08:24PM UTC coverage: 35.193%. Remained the same
22528343799

push

github

SamboyCoding
Address feedback

1907 of 6731 branches covered (28.33%)

Branch coverage included in aggregate %.

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

20 existing lines in 3 files now uncovered.

4343 of 11028 relevant lines covered (39.38%)

253861.48 hits per line

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

78.03
/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 virtual 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
            {
141
                var managedType = typeContext.GetExtraData<TypeDefinition>("AsmResolverType") ?? throw new($"AsmResolver type not found in type analysis context for {typeContext.FullName}");
×
142
                throw new($"Failed to process type {managedType.FullName} (module {managedType.DeclaringModule?.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 corlib = context.Assemblies.First(a => a.Name == "mscorlib");
10✔
151
        MostRecentCorLib = BuildStubAssembly(corlib, null, null);
5✔
152

153
        // The runtime info is irrelevant because we're creating our own corlib, but AsmResolver still requires that we specify one.
154
        var runtimeContext = new RuntimeContext(DotNetRuntimeInfo.NetCoreApp(9, 0), (bool?)null, MostRecentCorLib);
5✔
155
        runtimeContext.AddAssembly(MostRecentCorLib);
5✔
156

157
        context.PutExtraData("AsmResolverRuntimeContext", runtimeContext);
5✔
158

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

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

169
    private static AssemblyDefinition BuildStubAssembly(AssemblyAnalysisContext assemblyContext, AssemblyDefinition? corLib, RuntimeContext? runtimeContext)
170
    {
171
        //Get the name of the assembly (= the name of the DLL without the file extension)
172
        //Build an AsmResolver assembly from this definition
173
        var ourAssembly = new AssemblyDefinition(assemblyContext.Name, assemblyContext.Version)
210✔
174
        {
210✔
175
            HashAlgorithm = (AssemblyHashAlgorithm)assemblyContext.HashAlgorithm,
210✔
176
            Attributes = (AssemblyAttributes)assemblyContext.Flags,
210✔
177
            Culture = assemblyContext.Culture,
210✔
178
            PublicKey = assemblyContext.PublicKey,
210✔
179
        };
210✔
180

181
        //Setting the corlib module allows element types in references to that assembly to be set correctly without us having to manually set them.
182
        var moduleName = assemblyContext.CleanAssemblyName + ".dll";
210✔
183

184
        //Use either ourself as corlib, if we are corlib, otherwise the provided one
185
        var managedModule = new ModuleDefinition(moduleName, corLib is not null ? new(corLib) : null);
210✔
186
        ourAssembly.Modules.Add(managedModule);
210✔
187

188
        runtimeContext?.AddAssembly(ourAssembly);
210✔
189

190
        foreach (var il2CppTypeDefinition in assemblyContext.TopLevelTypes)
23,340✔
191
        {
192
            if (il2CppTypeDefinition.Name != "<Module>")
11,460✔
193
                //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.
194
                managedModule.TopLevelTypes.Add(BuildStubType(il2CppTypeDefinition));
11,250✔
195
        }
196

197
        if (corLib == null)
210✔
198
        {
199
            //We *are* the corlib, so cache defs now
200
            TypeDefinitionsAsmResolver.CacheNeededTypeDefinitions();
5✔
201
        }
202

203
        //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
204
        foreach (var assemblyContextType in assemblyContext.Types)
29,680✔
205
        {
206
            if (assemblyContextType.Definition is not { } def || assemblyContextType.GetExtraData<TypeDefinition>("AsmResolverType") is not { } asmResolverType)
14,630✔
207
                continue;
208

209
            if (def.IsValueType)
14,420✔
210
                asmResolverType.BaseType = managedModule.DefaultImporter.ImportType(TypeDefinitionsAsmResolver.ValueType);
4,475✔
211
            else if (def.IsEnumType)
9,945!
UNCOV
212
                asmResolverType.BaseType = managedModule.DefaultImporter.ImportType(TypeDefinitionsAsmResolver.Enum);
×
213
        }
214

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

218
        return ourAssembly;
210✔
219
    }
220

221
    private static TypeDefinition BuildStubType(TypeAnalysisContext typeContext)
222
    {
223
        var typeDef = typeContext.Definition;
14,420✔
224

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

228
        //Set up its layout
229
        if (typeDef != null)
14,420✔
230
            ConfigureTypeSize(typeDef, ret);
14,420✔
231

232
        //Create nested types
233
        foreach (var cppNestedType in typeContext.NestedTypes)
35,180✔
234
            ret.NestedTypes.Add(BuildStubType(cppNestedType));
3,170✔
235

236
        //Associate this asm resolve td with the type context
237
        typeContext.PutExtraData("AsmResolverType", ret);
14,420✔
238

239
        //Add to the lookup-by-id table used by the resolver
240
        if (typeDef != null)
14,420✔
241
            AsmResolverUtils.TypeDefsByIndex[typeDef.TypeIndex] = ret;
14,420✔
242

243
        return ret;
14,420✔
244
    }
245

246
    private static void ConfigureTypeSize(Il2CppTypeDefinition il2CppDefinition, TypeDefinition asmResolverDefinition)
247
    {
248
        if (!il2CppDefinition.IsValueType || il2CppDefinition.IsEnumType)
14,420✔
249
            return; // Only structs can have their layout changed
11,865✔
250

251
        ushort packingSize = 0;
2,555✔
252
        var classSize = 0U;
2,555✔
253
        if (!il2CppDefinition.PackingSizeIsDefault)
2,555✔
254
            packingSize = (ushort)il2CppDefinition.PackingSize;
915✔
255

256
        if (!il2CppDefinition.ClassSizeIsDefault)
2,555✔
257
        {
258
            if (il2CppDefinition.Size > 1 << 30)
915!
UNCOV
259
                throw new Exception($"Got invalid size for type {il2CppDefinition}: {il2CppDefinition.RawSizes}");
×
260

261
            if (il2CppDefinition.Size != -1)
915!
262
                classSize = (uint)il2CppDefinition.Size;
915✔
263
            else
UNCOV
264
                classSize = 0; //Not sure what this value actually implies but it seems to work
×
265
        }
266

267
        if (packingSize != 0 || classSize != 0)
2,555✔
268
            asmResolverDefinition.ClassLayout = new(packingSize, classSize);
915✔
269
    }
2,555✔
270
}
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