C#: Parallelize generation of type definitions
This commit is contained in:
@@ -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,29 +202,43 @@ 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));
|
||||||
|
|
||||||
|
var results = new ConcurrentBag<Dictionary<TypeInfo, StringBuilder>>();
|
||||||
|
|
||||||
// Generate each type
|
// 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
|
||||||
|
if (ExcludedNamespaces?.Any(x => x == type.Namespace || type.Namespace.StartsWith(x + ".")) ?? false)
|
||||||
|
return dict;
|
||||||
|
|
||||||
|
// Don't output global::Locale if desired
|
||||||
|
if (MustCompile
|
||||||
|
&& type.Name == "Locale" && type.Namespace == string.Empty
|
||||||
|
&& type.BaseType.FullName == "System.Object"
|
||||||
|
&& type.IsClass && type.IsSealed && type.IsNotPublic && !type.ContainsGenericParameters
|
||||||
|
&& type.DeclaredMembers.Count == type.DeclaredMethods.Count
|
||||||
|
&& type.GetMethods("GetText").Length == type.DeclaredMethods.Count)
|
||||||
|
return dict;
|
||||||
|
|
||||||
|
// Assembly.DefinedTypes returns nested types in the assembly by design - ignore them
|
||||||
|
if (type.IsNested)
|
||||||
|
return dict;
|
||||||
|
|
||||||
|
// Get code
|
||||||
|
var code = generateType(type, nsRefs);
|
||||||
|
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) {
|
foreach (var type in types) {
|
||||||
|
if (!sortedResults.TryGetValue(type, out var text))
|
||||||
// Skip namespace and any children if requested
|
|
||||||
if (ExcludedNamespaces?.Any(x => x == type.Namespace || type.Namespace.StartsWith(x + ".")) ?? false)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Don't output global::Locale if desired
|
|
||||||
if (MustCompile
|
|
||||||
&& type.Name == "Locale" && type.Namespace == string.Empty
|
|
||||||
&& type.BaseType.FullName == "System.Object"
|
|
||||||
&& type.IsClass && type.IsSealed && type.IsNotPublic && !type.ContainsGenericParameters
|
|
||||||
&& type.DeclaredMembers.Count == type.DeclaredMethods.Count
|
|
||||||
&& type.GetMethods("GetText").Length == type.DeclaredMethods.Count)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Assembly.DefinedTypes returns nested types in the assembly by design - ignore them
|
|
||||||
if (type.IsNested)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Get code
|
|
||||||
var text = generateType(type, nsRefs);
|
|
||||||
if (string.IsNullOrEmpty(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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user