C#: Parallelize generation of type definitions

This commit is contained in:
Katy Coe
2020-09-14 12:30:10 +02:00
parent 2543e1ad77
commit 835636d99e

View File

@@ -2,6 +2,7 @@
// All rights reserved // All rights reserved
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -201,12 +202,15 @@ namespace Il2CppInspector.Outputs
if (outputAssemblyAttributes) if (outputAssemblyAttributes)
nsRefs.UnionWith(assemblies.SelectMany(a => a.CustomAttributes).Select(a => a.AttributeType.Namespace)); nsRefs.UnionWith(assemblies.SelectMany(a => a.CustomAttributes).Select(a => a.AttributeType.Namespace));
// Generate each type var results = new ConcurrentBag<Dictionary<TypeInfo, StringBuilder>>();
foreach (var type in types) {
// Generate each type
Parallel.ForEach(types, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount / 2 },
() => new Dictionary<TypeInfo, StringBuilder>(),
(type, _, dict) => {
// Skip namespace and any children if requested // Skip namespace and any children if requested
if (ExcludedNamespaces?.Any(x => x == type.Namespace || type.Namespace.StartsWith(x + ".")) ?? false) if (ExcludedNamespaces?.Any(x => x == type.Namespace || type.Namespace.StartsWith(x + ".")) ?? false)
continue; return dict;
// Don't output global::Locale if desired // Don't output global::Locale if desired
if (MustCompile if (MustCompile
@@ -215,15 +219,26 @@ namespace Il2CppInspector.Outputs
&& type.IsClass && type.IsSealed && type.IsNotPublic && !type.ContainsGenericParameters && type.IsClass && type.IsSealed && type.IsNotPublic && !type.ContainsGenericParameters
&& type.DeclaredMembers.Count == type.DeclaredMethods.Count && type.DeclaredMembers.Count == type.DeclaredMethods.Count
&& type.GetMethods("GetText").Length == type.DeclaredMethods.Count) && type.GetMethods("GetText").Length == type.DeclaredMethods.Count)
continue; return dict;
// Assembly.DefinedTypes returns nested types in the assembly by design - ignore them // Assembly.DefinedTypes returns nested types in the assembly by design - ignore them
if (type.IsNested) if (type.IsNested)
continue; return dict;
// Get code // Get code
var text = generateType(type, nsRefs); var code = generateType(type, nsRefs);
if (string.IsNullOrEmpty(text)) if (code.Length > 0)
dict.Add(type, code);
return dict;
},
dict => results.Add(dict));
// Flatten
var sortedResults = results.SelectMany(d => d).ToDictionary(i => i.Key, i => i.Value);
// Process in order according to original sorted type list
foreach (var type in types) {
if (!sortedResults.TryGetValue(type, out var text))
continue; continue;
// Determine if we need to change namespace (after we have established the code block is not empty) // Determine if we need to change namespace (after we have established the code block is not empty)
@@ -239,8 +254,9 @@ namespace Il2CppInspector.Outputs
} }
if (!string.IsNullOrEmpty(nsContext)) { if (!string.IsNullOrEmpty(nsContext)) {
text = "\t" + text.Replace("\n", "\n\t"); text.Insert(0, "\t");
text = text.Remove(text.Length - 1); text.Replace("\n", "\n\t");
text.Length--;
} }
} }
@@ -249,7 +265,8 @@ namespace Il2CppInspector.Outputs
code.Append($"// Namespace: {(!string.IsNullOrEmpty(type.Namespace) ? type.Namespace : "<global namespace>")}\n"); code.Append($"// Namespace: {(!string.IsNullOrEmpty(type.Namespace) ? type.Namespace : "<global namespace>")}\n");
// Append type definition // Append type definition
code.Append(text + "\n"); code.Append(text);
code.Append("\n");
// Add to list of used types // Add to list of used types
usedTypes.Add(type); usedTypes.Add(type);
@@ -351,14 +368,14 @@ namespace Il2CppInspector.Outputs
return text.ToString().TrimEnd(); return text.ToString().TrimEnd();
} }
private string generateType(TypeInfo type, IEnumerable<string> namespaces, string prefix = "") { private StringBuilder generateType(TypeInfo type, IEnumerable<string> namespaces, string prefix = "") {
// Don't output compiler-generated types if desired // Don't output compiler-generated types if desired
if (MustCompile && type.GetCustomAttributes(CGAttribute).Any()) if (MustCompile && type.GetCustomAttributes(CGAttribute).Any())
return string.Empty; return new StringBuilder();
var codeBlocks = new Dictionary<string, string>(); var codeBlocks = new Dictionary<string, StringBuilder>();
var usedMethods = new List<MethodInfo>(); var usedMethods = new List<MethodInfo>();
var sb = new StringBuilder(); StringBuilder sb;
var scope = new Scope { var scope = new Scope {
Current = type, Current = type,
@@ -366,7 +383,7 @@ namespace Il2CppInspector.Outputs
}; };
// Fields // Fields
sb.Clear(); sb = new StringBuilder();
if (!type.IsEnum) { if (!type.IsEnum) {
foreach (var field in type.DeclaredFields) { foreach (var field in type.DeclaredFields) {
if (MustCompile && field.GetCustomAttributes(CGAttribute).Any()) if (MustCompile && field.GetCustomAttributes(CGAttribute).Any())
@@ -409,11 +426,11 @@ namespace Il2CppInspector.Outputs
} }
sb.Append("\n"); sb.Append("\n");
} }
codeBlocks.Add("Fields", sb.ToString()); codeBlocks.Add("Fields", sb);
} }
// Properties // Properties
sb.Clear(); sb = new StringBuilder();
var hasIndexer = false; var hasIndexer = false;
foreach (var prop in type.DeclaredProperties) { foreach (var prop in type.DeclaredProperties) {
// Attributes // Attributes
@@ -467,10 +484,10 @@ namespace Il2CppInspector.Outputs
usedMethods.Add(prop.GetMethod); usedMethods.Add(prop.GetMethod);
usedMethods.Add(prop.SetMethod); usedMethods.Add(prop.SetMethod);
} }
codeBlocks.Add("Properties", sb.ToString()); codeBlocks.Add("Properties", sb);
// Events // Events
sb.Clear(); sb = new StringBuilder();
foreach (var evt in type.DeclaredEvents) { foreach (var evt in type.DeclaredEvents) {
// Attributes // Attributes
sb.Append(evt.CustomAttributes.OrderBy(a => a.AttributeType.Name) sb.Append(evt.CustomAttributes.OrderBy(a => a.AttributeType.Name)
@@ -493,17 +510,16 @@ namespace Il2CppInspector.Outputs
usedMethods.Add(evt.RemoveMethod); usedMethods.Add(evt.RemoveMethod);
usedMethods.Add(evt.RaiseMethod); usedMethods.Add(evt.RaiseMethod);
} }
codeBlocks.Add("Events", sb.ToString()); codeBlocks.Add("Events", sb);
// Nested types // Nested types
codeBlocks.Add("Nested types", string.Join("\n", type.DeclaredNestedTypes codeBlocks.Add("Nested types", new StringBuilder().AppendJoin("\n", type.DeclaredNestedTypes
.Select(n => generateType(n, namespaces, prefix + "\t")).Where(c => !string.IsNullOrEmpty(c)))); .Select(n => generateType(n, namespaces, prefix + "\t")).Where(c => c.Length > 0)));
// Constructors // Constructors
sb = new StringBuilder();
var fields = type.DeclaredFields.Where(f => !f.GetCustomAttributes(CGAttribute).Any()); var fields = type.DeclaredFields.Where(f => !f.GetCustomAttributes(CGAttribute).Any());
sb.Clear();
// Crete a parameterless constructor for every relevant type when making code that compiles to mitigate CS1729 and CS7036 // Crete a parameterless constructor for every relevant type when making code that compiles to mitigate CS1729 and CS7036
if (MustCompile && !type.IsInterface && !(type.IsAbstract && type.IsSealed) && !type.IsValueType if (MustCompile && !type.IsInterface && !(type.IsAbstract && type.IsSealed) && !type.IsValueType
&& type.DeclaredConstructors.All(c => c.IsStatic || c.DeclaredParameters.Any())) && type.DeclaredConstructors.All(c => c.IsStatic || c.DeclaredParameters.Any()))
@@ -546,19 +562,24 @@ namespace Il2CppInspector.Outputs
sb.Append((!SuppressMetadata && method.VirtualAddress != null ? $" // {method.VirtualAddress.ToAddressString()}" : "") + "\n"); sb.Append((!SuppressMetadata && method.VirtualAddress != null ? $" // {method.VirtualAddress.ToAddressString()}" : "") + "\n");
} }
codeBlocks.Add("Constructors", sb.ToString()); codeBlocks.Add("Constructors", sb);
// Methods // Methods
// Don't re-output methods for constructors, properties, events etc. // Don't re-output methods for constructors, properties, events etc.
var methods = type.DeclaredMethods.Except(usedMethods).Where(m => m.CustomAttributes.All(a => a.AttributeType.FullName != ExtAttribute)); var methods = type.DeclaredMethods.Except(usedMethods).Where(m => m.CustomAttributes.All(a => a.AttributeType.FullName != ExtAttribute));
codeBlocks.Add("Methods", string.Concat(methods.Select(m => generateMethod(m, scope, prefix)))); codeBlocks.Add("Methods", methods
.Select(m => generateMethod(m, scope, prefix))
.Aggregate(new StringBuilder(), (r, i) => r.Append(i)));
usedMethods.AddRange(methods); usedMethods.AddRange(methods);
// Extension methods // Extension methods
codeBlocks.Add("Extension methods", string.Concat(type.DeclaredMethods.Except(usedMethods).Select(m => generateMethod(m, scope, prefix)))); codeBlocks.Add("Extension methods", type.DeclaredMethods.Except(usedMethods)
.Select(m => generateMethod(m, scope, prefix))
.Aggregate(new StringBuilder(), (r, i) => r.Append(i)));
// Type declaration // Type declaration
sb.Clear(); sb = new StringBuilder();
if (type.IsImport) if (type.IsImport)
sb.Append(prefix + "[ComImport]\n"); sb.Append(prefix + "[ComImport]\n");
@@ -584,7 +605,7 @@ namespace Il2CppInspector.Outputs
if (!SuppressMetadata) if (!SuppressMetadata)
sb.Append($" // TypeDefIndex: {type.Index}; {del.VirtualAddress.ToAddressString()}"); sb.Append($" // TypeDefIndex: {type.Index}; {del.VirtualAddress.ToAddressString()}");
sb.Append("\n"); sb.Append("\n");
return sb.ToString(); return sb;
} }
sb.Append(prefix + type.GetModifierString()); sb.Append(prefix + type.GetModifierString());
@@ -611,24 +632,25 @@ namespace Il2CppInspector.Outputs
// Enumeration // Enumeration
if (type.IsEnum) { if (type.IsEnum) {
sb.Append(string.Join(",\n", type.GetEnumNames().Zip(type.GetEnumValues().OfType<object>(), sb.AppendJoin(",\n", type.GetEnumNames().Zip(type.GetEnumValues().OfType<object>(),
(k, v) => new { k, v }).OrderBy(x => x.v).Select(x => $"{prefix}\t{x.k} = {x.v}")) + "\n"); (k, v) => new { k, v }).OrderBy(x => x.v).Select(x => $"{prefix}\t{x.k} = {x.v}"));
sb.Append("\n");
} }
// Type definition // Type definition
else else
sb.Append(string.Join("\n", codeBlocks.Where(b => b.Value != string.Empty).Select(b => prefix + "\t// " + b.Key + "\n" + b.Value))); sb.AppendJoin("\n", codeBlocks.Where(b => b.Value.Length > 0).Select(b => prefix + "\t// " + b.Key + "\n" + b.Value));
sb.Append(prefix + "}\n"); sb.Append(prefix + "}\n");
return sb.ToString(); return sb;
} }
private string generateMethod(MethodInfo method, Scope scope, string prefix) { private StringBuilder generateMethod(MethodInfo method, Scope scope, string prefix) {
if (MustCompile && method.GetCustomAttributes(CGAttribute).Any())
return string.Empty;
var writer = new StringBuilder(); var writer = new StringBuilder();
if (MustCompile && method.GetCustomAttributes(CGAttribute).Any())
return writer;
// Attributes // Attributes
writer.Append(method.CustomAttributes.Where(a => a.AttributeType.FullName != ExtAttribute && a.AttributeType.FullName != AsyncAttribute) writer.Append(method.CustomAttributes.Where(a => a.AttributeType.FullName != ExtAttribute && a.AttributeType.FullName != AsyncAttribute)
.OrderBy(a => a.AttributeType.Name) .OrderBy(a => a.AttributeType.Name)
@@ -693,7 +715,7 @@ namespace Il2CppInspector.Outputs
if (MustCompile && method.ReturnType.IsByRef) if (MustCompile && method.ReturnType.IsByRef)
writer.Append($"{prefix}\tprivate {method.ReturnType.GetScopedCSharpName(scope)} _refReturnTypeFor{method.CSharpName}; // Dummy field\n"); writer.Append($"{prefix}\tprivate {method.ReturnType.GetScopedCSharpName(scope)} _refReturnTypeFor{method.CSharpName}; // Dummy field\n");
return writer.ToString(); return writer;
} }
} }
} }