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

SamboyCoding / Cpp2IL / 11419919232

19 Oct 2024 06:43PM UTC coverage: 28.24% (-0.1%) from 28.388%
11419919232

push

github

web-flow
Support emitting metadata for explicit interface implementations (#346)

1239 of 6152 branches covered (20.14%)

Branch coverage included in aggregate %.

9 of 91 new or added lines in 4 files covered. (9.89%)

2 existing lines in 2 files now uncovered.

3350 of 10098 relevant lines covered (33.17%)

101421.74 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);
×
NEW
95
        MiscUtils.ExecuteParallel(context.Assemblies, AsmResolverAssemblyPopulator.InferExplicitInterfaceImplementations);
×
UNCOV
96
        MiscUtils.ExecuteParallel(context.Assemblies, FillMethodBodies);
×
97

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

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

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

107
        TypeDefinitionsAsmResolver.Reset();
×
108

109
        return ret;
×
110
    }
111

112
    protected abstract void FillMethodBody(MethodDefinition methodDefinition, MethodAnalysisContext methodContext);
113

114
    protected virtual void FillMethodBodies(AssemblyAnalysisContext context)
115
    {
116
        foreach (var typeContext in context.Types)
×
117
        {
118
            if (AsmResolverAssemblyPopulator.IsTypeContextModule(typeContext))
×
119
                continue;
120

121
#if !DEBUG
122
            try
123
#endif
124
            {
125
                foreach (var methodCtx in typeContext.Methods)
×
126
                {
127
                    var managedMethod = methodCtx.GetExtraData<MethodDefinition>("AsmResolverMethod") ?? throw new($"AsmResolver method not found in method analysis context for {typeContext.Definition?.FullName}.{methodCtx.Definition?.Name}");
×
128

129
                    FillMethodBody(managedMethod, methodCtx);
×
130
                }
131
            }
×
132
#if !DEBUG
133
            catch (System.Exception e)
×
134
            {
135
                var managedType = typeContext.GetExtraData<TypeDefinition>("AsmResolverType") ?? throw new($"AsmResolver type not found in type analysis context for {typeContext.Definition?.FullName}");
×
136
                throw new($"Failed to process type {managedType.FullName} (module {managedType.Module?.Name}, declaring type {managedType.DeclaringType?.FullName}) in {context.Definition.AssemblyName.Name}", e);
×
137
            }
138
#endif
139
        }
140
    }
×
141

142
    private List<AssemblyDefinition> BuildStubAssemblies(ApplicationAnalysisContext context)
143
    {
144
        var assemblyResolver = new Il2CppAssemblyResolver();
×
145
        var metadataResolver = new DefaultMetadataResolver(assemblyResolver);
×
146

147
        var corlib = context.Assemblies.First(a => a.Definition.AssemblyName.Name == "mscorlib");
×
148
        MostRecentCorLib = BuildStubAssembly(corlib, null, metadataResolver);
×
149
        assemblyResolver.DummyAssemblies.Add(MostRecentCorLib.Name!, MostRecentCorLib);
×
150

151
        var ret = context.Assemblies
×
152
            // .AsParallel()
×
153
            .Where(a => a.Definition.AssemblyName.Name != "mscorlib")
×
154
            .Select(a => BuildStubAssembly(a, MostRecentCorLib, metadataResolver))
×
155
            .ToList();
×
156

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

159
        ret.Add(MostRecentCorLib);
×
160
        return ret;
×
161
    }
162

163
    private static AssemblyDefinition BuildStubAssembly(AssemblyAnalysisContext assemblyContext, AssemblyDefinition? corLib, IMetadataResolver metadataResolver)
164
    {
165
        var assemblyDefinition = assemblyContext.Definition;
×
166

167
        var imageDefinition = assemblyDefinition.Image;
×
168

169
        //Get the name of the assembly (= the name of the DLL without the file extension)
170
        var assemblyNameString = assemblyDefinition.AssemblyName.Name;
×
171

172
        //Build an AsmResolver assembly from this definition
173
        Version version;
174
        if (assemblyDefinition.AssemblyName.build >= 0)
×
175
            version = new(assemblyDefinition.AssemblyName.major, assemblyDefinition.AssemblyName.minor, assemblyDefinition.AssemblyName.build, assemblyDefinition.AssemblyName.revision);
×
176
        else
177
            //handle __Generated assembly on v29, which has a version of 0.0.-1.-1
178
            version = new(0, 0, 0, 0);
×
179

180
        var ourAssembly = new AssemblyDefinition(assemblyNameString, version)
×
181
        {
×
182
            HashAlgorithm = (AssemblyHashAlgorithm)assemblyDefinition.AssemblyName.hash_alg, Attributes = (AssemblyAttributes)assemblyDefinition.AssemblyName.flags, Culture = assemblyDefinition.AssemblyName.Culture,
×
183
            //TODO find a way to set hash? or not needed
×
184
        };
×
185

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

191
        var managedModule = new ModuleDefinition(moduleName, new(corLib ?? ourAssembly)) //Use either ourself as corlib, if we are corlib, otherwise the provided one
×
192
        {
×
193
            MetadataResolver = metadataResolver
×
194
        };
×
195
        ourAssembly.Modules.Add(managedModule);
×
196

197
        foreach (var il2CppTypeDefinition in assemblyContext.TopLevelTypes)
×
198
        {
199
            if (il2CppTypeDefinition.Name != "<Module>")
×
200
                //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.
201
                managedModule.TopLevelTypes.Add(BuildStubType(il2CppTypeDefinition));
×
202
        }
203

204
        if (corLib == null)
×
205
        {
206
            //We *are* the corlib, so cache defs now
207
            TypeDefinitionsAsmResolver.CacheNeededTypeDefinitions();
×
208
        }
209

210
        //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
211
        foreach (var assemblyContextType in assemblyContext.Types)
×
212
        {
213
            if (assemblyContextType.Definition is not { } def || assemblyContextType.GetExtraData<TypeDefinition>("AsmResolverType") is not { } asmResolverType)
×
214
                continue;
215

216
            if (def.IsValueType)
×
217
                asmResolverType.BaseType = managedModule.DefaultImporter.ImportType(TypeDefinitionsAsmResolver.ValueType);
×
218
            else if (def.IsEnumType)
×
219
                asmResolverType.BaseType = managedModule.DefaultImporter.ImportType(TypeDefinitionsAsmResolver.Enum);
×
220
        }
221

222
        //Store the managed assembly in the context so we can use it later.
223
        assemblyContext.PutExtraData("AsmResolverAssembly", ourAssembly);
×
224

225
        return ourAssembly;
×
226
    }
227

228
    private static TypeDefinition BuildStubType(TypeAnalysisContext typeContext)
229
    {
230
        var typeDef = typeContext.Definition;
×
231

232
        //Initialize an empty type definition
233
        var ret = new TypeDefinition(typeContext.Namespace, typeContext.Name, (TypeAttributes)typeContext.TypeAttributes);
×
234

235
        //Set up its layout
236
        if (typeDef != null && typeDef.BaseType?.ToString() != "System.Enum")
×
237
            ConfigureTypeSize(typeDef, ret);
×
238

239
        //Create nested types
240
        foreach (var cppNestedType in typeContext.NestedTypes)
×
241
            ret.NestedTypes.Add(BuildStubType(cppNestedType));
×
242

243
        //Associate this asm resolve td with the type context
244
        typeContext.PutExtraData("AsmResolverType", ret);
×
245

246
        //Add to the lookup-by-id table used by the resolver
247
        if (typeDef != null)
×
248
            AsmResolverUtils.TypeDefsByIndex[typeDef.TypeIndex] = ret;
×
249

250
        return ret;
×
251
    }
252

253
    private static void ConfigureTypeSize(Il2CppTypeDefinition il2CppDefinition, TypeDefinition asmResolverDefinition)
254
    {
255
        ushort packingSize = 0;
×
256
        var classSize = 0U;
×
257
        if (!il2CppDefinition.PackingSizeIsDefault)
×
258
            packingSize = (ushort)il2CppDefinition.PackingSize;
×
259

260
        if (!il2CppDefinition.ClassSizeIsDefault && !il2CppDefinition.IsEnumType)
×
261
        {
262
            if (il2CppDefinition.Size > 1 << 30)
×
263
                throw new Exception($"Got invalid size for type {il2CppDefinition}: {il2CppDefinition.RawSizes}");
×
264

265
            if (il2CppDefinition.Size != -1)
×
266
                classSize = (uint)il2CppDefinition.Size;
×
267
            else
268
                classSize = 0; //Not sure what this value actually implies but it seems to work
×
269
        }
270

271
        if (packingSize != 0 || classSize != 0)
×
272
            asmResolverDefinition.ClassLayout = new(packingSize, classSize);
×
273
    }
×
274
}
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