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

SamboyCoding / Cpp2IL / 14010539955

22 Mar 2025 04:51PM UTC coverage: 27.127% (-0.005%) from 27.132%
14010539955

push

github

web-flow
Fix fixed buffer fields (#428)

1270 of 6490 branches covered (19.57%)

Branch coverage included in aggregate %.

0 of 4 new or added lines in 1 file covered. (0.0%)

2 existing lines in 1 file now uncovered.

3379 of 10648 relevant lines covered (31.73%)

122354.89 hits per line

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

0.0
/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; }
×
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;
×
70
#if VERBOSE_LOGGING
71
        Logger.Verbose($"Building stub assemblies ({asmCount} assemblies, {typeCount} types)...", "DllOutput");
72
#else
73
        Logger.Verbose($"Building stub assemblies...", "DllOutput");
×
74
#endif
75
        List<AssemblyDefinition> ret = BuildStubAssemblies(context);
×
76
        Logger.VerboseNewline($"{(DateTime.Now - start).TotalMilliseconds:F1}ms", "DllOutput");
×
77

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

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

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

85
        //Populate them
86
        start = DateTime.Now;
×
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");
×
92
#endif
93

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

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

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

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

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

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

UNCOV
113
        TypeDefinitionsAsmResolver.Reset();
×
114

115
        return ret;
×
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)
×
123
        {
124
            if (AsmResolverAssemblyPopulator.IsTypeContextModule(typeContext))
×
125
                continue;
126

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

135
                    FillMethodBody(managedMethod, methodCtx);
×
136
                }
137
            }
×
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.Definition?.FullName}");
×
142
                throw new($"Failed to process type {managedType.FullName} (module {managedType.Module?.Name}, declaring type {managedType.DeclaringType?.FullName}) in {context.Definition.AssemblyName.Name}", e);
×
143
            }
144
#endif
145
        }
146
    }
×
147

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

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

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

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

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

169
    private static AssemblyDefinition BuildStubAssembly(AssemblyAnalysisContext assemblyContext, AssemblyDefinition? corLib, IMetadataResolver metadataResolver)
170
    {
171
        var assemblyDefinition = assemblyContext.Definition;
×
172

173
        var imageDefinition = assemblyDefinition.Image;
×
174

175
        //Get the name of the assembly (= the name of the DLL without the file extension)
176
        var assemblyNameString = assemblyDefinition.AssemblyName.Name;
×
177

178
        //Build an AsmResolver assembly from this definition
179
        Version version;
180
        if (assemblyDefinition.AssemblyName.build >= 0)
×
181
            version = new(assemblyDefinition.AssemblyName.major, assemblyDefinition.AssemblyName.minor, assemblyDefinition.AssemblyName.build, assemblyDefinition.AssemblyName.revision);
×
182
        else
183
            //handle __Generated assembly on v29, which has a version of 0.0.-1.-1
184
            version = new(0, 0, 0, 0);
×
185

186
        var ourAssembly = new AssemblyDefinition(assemblyNameString, version)
×
187
        {
×
188
            HashAlgorithm = (AssemblyHashAlgorithm)assemblyDefinition.AssemblyName.hash_alg,
×
189
            Attributes = (AssemblyAttributes)assemblyDefinition.AssemblyName.flags,
×
190
            Culture = assemblyDefinition.AssemblyName.Culture,
×
191
            PublicKey = assemblyDefinition.AssemblyName.PublicKey,
×
192
        };
×
193

194
        //Setting the corlib module allows element types in references to that assembly to be set correctly without us having to manually set them.
195
        var moduleName = imageDefinition.Name;
×
196
        if (moduleName == "__Generated")
×
197
            moduleName += ".dll"; //__Generated doesn't have a .dll extension in the metadata but it is still of course a DLL
×
198

199
        var managedModule = new ModuleDefinition(moduleName, new(corLib ?? ourAssembly)) //Use either ourself as corlib, if we are corlib, otherwise the provided one
×
200
        {
×
201
            MetadataResolver = metadataResolver
×
202
        };
×
203
        ourAssembly.Modules.Add(managedModule);
×
204

205
        foreach (var il2CppTypeDefinition in assemblyContext.TopLevelTypes)
×
206
        {
207
            if (il2CppTypeDefinition.Name != "<Module>")
×
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));
×
210
        }
211

212
        if (corLib == null)
×
213
        {
214
            //We *are* the corlib, so cache defs now
215
            TypeDefinitionsAsmResolver.CacheNeededTypeDefinitions();
×
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)
×
220
        {
221
            if (assemblyContextType.Definition is not { } def || assemblyContextType.GetExtraData<TypeDefinition>("AsmResolverType") is not { } asmResolverType)
×
222
                continue;
223

224
            if (def.IsValueType)
×
225
                asmResolverType.BaseType = managedModule.DefaultImporter.ImportType(TypeDefinitionsAsmResolver.ValueType);
×
226
            else if (def.IsEnumType)
×
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);
×
232

233
        return ourAssembly;
×
234
    }
235

236
    private static TypeDefinition BuildStubType(TypeAnalysisContext typeContext)
237
    {
238
        var typeDef = typeContext.Definition;
×
239

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

243
        //Set up its layout
244
        if (typeDef != null && typeDef.BaseType?.ToString() != "System.Enum")
×
245
            ConfigureTypeSize(typeDef, ret);
×
246

247
        //Create nested types
248
        foreach (var cppNestedType in typeContext.NestedTypes)
×
249
            ret.NestedTypes.Add(BuildStubType(cppNestedType));
×
250

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

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

258
        return ret;
×
259
    }
260

261
    private static void ConfigureTypeSize(Il2CppTypeDefinition il2CppDefinition, TypeDefinition asmResolverDefinition)
262
    {
263
        ushort packingSize = 0;
×
264
        var classSize = 0U;
×
265
        if (!il2CppDefinition.PackingSizeIsDefault)
×
266
            packingSize = (ushort)il2CppDefinition.PackingSize;
×
267

268
        if (!il2CppDefinition.ClassSizeIsDefault && !il2CppDefinition.IsEnumType)
×
269
        {
270
            if (il2CppDefinition.Size > 1 << 30)
×
271
                throw new Exception($"Got invalid size for type {il2CppDefinition}: {il2CppDefinition.RawSizes}");
×
272

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

279
        if (packingSize != 0 || classSize != 0)
×
280
            asmResolverDefinition.ClassLayout = new(packingSize, classSize);
×
281
    }
×
282
}
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