Skip to content

Commit

Permalink
.NET: Significant speed improvement to ref assembly generation
Browse files Browse the repository at this point in the history
  • Loading branch information
praydog committed Mar 24, 2024
1 parent 8474052 commit c44c3a0
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 81 deletions.
146 changes: 73 additions & 73 deletions csharp-api/AssemblyGenerator/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,19 @@
using System.Text.Json.Serialization;
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Collections.Concurrent;

public class Il2CppDump {
class Field {

};

public class Method {
private REFrameworkNET.Method impl;
public REFrameworkNET.Method Impl;

public Method(REFrameworkNET.Method impl) {
this.impl = impl;
this.Impl = impl;
}

public bool? Override { get; set;} // Not from JSON
Expand Down Expand Up @@ -66,7 +68,7 @@ public Type(REFrameworkNET.TypeDefinition impl) {


// Custom stuff below
public HashSet<REFrameworkNET.TypeDefinition>? NestedTypes { get; set;}
public HashSet<REFrameworkNET.TypeDefinition> NestedTypes = [];
};

static private Dictionary<REFrameworkNET.TypeDefinition, Type> typeExtensions = [];
Expand All @@ -79,6 +81,17 @@ public Type(REFrameworkNET.TypeDefinition impl) {
return null;
}

static public Type GetOrAddTypeExtension(REFrameworkNET.TypeDefinition type) {
if (typeExtensions.TryGetValue(type, out Type? value)) {
return value;
}

value = new Type(type);
typeExtensions[type] = value;

return value;
}

static public Method? GetMethodExtension(REFrameworkNET.Method method) {
if (methodExtensions.TryGetValue(method, out Method? value)) {
return value;
Expand All @@ -92,43 +105,52 @@ public static void FillTypeExtensions(REFrameworkNET.TDB context) {
return;
}

// Look for types that have a declaring type and add them to the declaring type's nested types
context.GetType(0).GetFullName(); // initialize the types

//Parallel.For(0, context.GetNumTypes(), i =>
foreach (REFrameworkNET.TypeDefinition t in context.Types) {
if (t.DeclaringType != null) {
if (!typeExtensions.TryGetValue(t.DeclaringType, out Type? value)) {
value = new Type(t.DeclaringType);
typeExtensions[t.DeclaringType] = value;
}
//var t = context.GetType((uint)i);
if (t == null) {
//Console.WriteLine("Failed to get type " + i);
continue;
}

//value.NestedTypes ??= [];
if (value.NestedTypes == null) {
value.NestedTypes = new HashSet<REFrameworkNET.TypeDefinition>();
}
value.NestedTypes.Add(t);
var tDeclaringType = t.DeclaringType;
if (tDeclaringType != null) {
var ext = GetOrAddTypeExtension(tDeclaringType);
ext.NestedTypes.Add(t);
}

//System.Console.WriteLine("Adding nested type " + t.GetFullName() + " to " + t.DeclaringType.GetFullName());
if (t.GetNumMethods() == 0 || t.ParentType == null) {
continue;
}

// Look for methods with the same name and mark them as overrides
for (var parent = t.ParentType; parent != null; parent = parent.ParentType) {
if (parent.Methods.Count == 0 || t.Methods.Count == 0) {
// We dont go through all parents, because GetMethod does that for us
// Going through all parents would exponentially increase the number of checks and they would be redundant
var parent = t.ParentType;
var tMethods = t.GetMethods();

//foreach (var method in t.Methods) {
//Parallel.ForEach(tMethods, method => {
foreach (var method in tMethods) { // parallel isn't necessary here because there arent many methods
if (method == null) {
continue;
}

foreach (var method in t.Methods) {
var parentMethod = parent.GetMethod(method.Name);
if (GetMethodExtension(method) != null) {
continue;
}

if (parentMethod != null) {
if (!methodExtensions.TryGetValue(method, out Method? value)) {
value = new Method(method);
methodExtensions[method] = value;
}
var parentMethod = parent.GetMethod(method.Name);

value.Override = true;
}
if (parentMethod != null) {
methodExtensions.Add(method, new Method(method) {
Override = true
});
}
}
}
}
}
}

Expand All @@ -139,50 +161,21 @@ public class AssemblyGenerator {
// Start with an empty CompilationUnitSyntax (represents an empty file)
static CompilationUnitSyntax compilationUnit = SyntaxFactory.CompilationUnit();

static public NamespaceDeclarationSyntax? ExtractNamespaceFromTypeName(REFrameworkNET.TDB context, string typeName) {
if (context == null || context.Types == null) {
return null;
}
static public NamespaceDeclarationSyntax? ExtractNamespaceFromType(REFrameworkNET.TypeDefinition t) {
var ns = t.GetNamespace();

var parts = typeName.Split('.');
var currentTypeName = "";

NamespaceDeclarationSyntax? currentNamespaceDecl = null;
string currentNamespaceName = "";

for (var i = 0; i < parts.Length; i++) {
var part = parts[i];
currentTypeName += part;

if (context.GetType(currentTypeName) != null) {
// Return a blank namespace
if (currentNamespaceDecl == null) {
System.Console.WriteLine("Creating blank namespace for " + currentTypeName);
currentNamespaceDecl = SyntaxTreeBuilder.CreateNamespace("");
}

return currentNamespaceDecl;
}

currentNamespaceName += part;

// Create via namespace in list of namespaces if not exist
if (!namespaces.TryGetValue(currentTypeName, out NamespaceDeclarationSyntax? value)) {
// Clean up the namespace name, remove any non-compliant characters other than "." and alphanumerics
currentNamespaceName = Regex.Replace(currentTypeName, @"[^a-zA-Z0-9.]", "_");

Console.WriteLine("Creating namespace " + currentNamespaceName);
value = SyntaxTreeBuilder.CreateNamespace(currentNamespaceName);
namespaces[currentNamespaceName] = value;
currentNamespaceDecl = value;
} else {
currentNamespaceDecl = value;
if (ns != null) {
if (!namespaces.TryGetValue(ns, out NamespaceDeclarationSyntax? value)) {
//ns = Regex.Replace(ns, @"[^a-zA-Z0-9.]", "_");
Console.WriteLine("Creating namespace " + ns);
value = SyntaxTreeBuilder.CreateNamespace(ns);
namespaces[ns] = value;
}

currentTypeName += ".";
return value;
}

return currentNamespaceDecl;
return null;
}

public static SortedSet<string> validTypes = [];
Expand All @@ -193,28 +186,35 @@ static void FillValidEntries(REFrameworkNET.TDB context) {
return;
}

// TDB only has GetType(index) and GetNumTypes()
foreach (REFrameworkNET.TypeDefinition t in context.Types) {
ConcurrentBag<string> threadSafeValidTypes = [];

Parallel.For(0, context.GetNumTypes(), i => {
var t = context.GetType((uint)i);
var typeName = t.GetFullName();

if (typeName.Length == 0) {
Console.WriteLine("Bad type name");
continue;
return;
}

if (typeName.Contains("WrappedArrayContainer")) {
continue;
return;
}

if (typeName.Contains("[") || typeName.Contains("]") || typeName.Contains('<')) {
continue;
return;
}

// Skip system types
// TODO: Fix this
if (typeName.StartsWith("System.")) {
continue;
return;
}

threadSafeValidTypes.Add(typeName);
});

foreach (var typeName in threadSafeValidTypes) {
validTypes.Add(typeName);
}
}
Expand Down Expand Up @@ -279,7 +279,7 @@ [.. methods]
return compilationUnit;
}

var generatedNamespace = ExtractNamespaceFromTypeName(context, typeName);
var generatedNamespace = ExtractNamespaceFromType(t);

if (generatedNamespace != null) {
// Split the using types by their namespace
Expand Down
14 changes: 8 additions & 6 deletions csharp-api/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ include(CSharpUtilities)

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
set(CMAKE_CSharp_FLAGS "${CMAKE_CSHARP_FLAGS} /langversion:latest /platform:x64")

# Disable exceptions
# string(REGEX REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
Expand Down Expand Up @@ -94,8 +95,6 @@ set_target_properties(REFCSharpCompiler PROPERTIES
net8.0-windows
VS_CONFIGURATION_TYPE
ClassLibrary
CMAKE_CSharp_FLAGS
"/langversion:latest /platform:x64"
)

set(CMKR_TARGET REFCSharpCompiler)
Expand Down Expand Up @@ -186,6 +185,13 @@ add_custom_command(
set(REFRAMEWORK_DOT_NET_ASSEMBLY_DIR "${CMAKE_BINARY_DIR}/bin")
set(REFRAMEWORK_DOT_NET_ASSEMBLY_PATH "${CMAKE_BINARY_DIR}/bin/REFramework.NET.dll")

set_target_properties(csharp-api PROPERTIES
VS_DOTNET_REFERENCE_REFCSharpCompiler
"${REFRAMEWORK_DOT_NET_ASSEMBLY_DIR}/REFCSharpCompiler.dll"
)

set_target_properties(csharp-api PROPERTIES VS_PACKAGE_REFERENCES "REFCSharpCompiler")

# Target: AssemblyGenerator
set(AssemblyGenerator_SOURCES
"AssemblyGenerator/ClassGenerator.cs"
Expand Down Expand Up @@ -217,8 +223,6 @@ set_target_properties(AssemblyGenerator PROPERTIES
net8.0-windows
VS_CONFIGURATION_TYPE
ClassLibrary
CMAKE_CSharp_FLAGS
"/langversion:latest /platform:x64"
)

set(CMKR_TARGET AssemblyGenerator)
Expand Down Expand Up @@ -255,8 +259,6 @@ set_target_properties(CSharpAPITest PROPERTIES
net8.0-windows
VS_CONFIGURATION_TYPE
ClassLibrary
CMAKE_CSharp_FLAGS
"/langversion:latest /platform:x64"
)

set(CMKR_TARGET CSharpAPITest)
Expand Down
9 changes: 8 additions & 1 deletion csharp-api/REFrameworkNET/TypeDefinition.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ public enum VMObjType {
ValType = 5,
};

public ref class TypeDefinition : public System::Dynamic::DynamicObject, public System::IEquatable<TypeDefinition^>
public
ref class TypeDefinition : public System::Dynamic::DynamicObject,
public System::IEquatable<TypeDefinition ^>
{
public:
TypeDefinition(reframework::API::TypeDefinition* td) : m_type(td) {}
Expand All @@ -37,6 +39,11 @@ public ref class TypeDefinition : public System::Dynamic::DynamicObject, public
NativeObject^ get();
}

TypeDefinition^ Clone() {
return gcnew TypeDefinition(m_type);
}


uint32_t GetIndex()
{
return m_type->get_index();
Expand Down
9 changes: 8 additions & 1 deletion csharp-api/cmake.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ languages = ["CXX", "C", "CSharp"]
cmake-after = """
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
set(CMAKE_CSharp_FLAGS "${CMAKE_CSHARP_FLAGS} /langversion:latest /platform:x64")
# Disable exceptions
# string(REGEX REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
Expand Down Expand Up @@ -54,7 +55,6 @@ LIBRARY_OUTPUT_DIRECTORY_RELEASE = "${CMAKE_BINARY_DIR}/lib"
DOTNET_SDK = "Microsoft.NET.Sdk"
DOTNET_TARGET_FRAMEWORK = "net8.0-windows"
VS_CONFIGURATION_TYPE = "ClassLibrary"
CMAKE_CSharp_FLAGS = "/langversion:latest /platform:x64"

[target.REFCSharpCompiler]
type = "CSharpSharedTarget"
Expand Down Expand Up @@ -89,6 +89,13 @@ add_custom_command(
)
set(REFRAMEWORK_DOT_NET_ASSEMBLY_DIR "${CMAKE_BINARY_DIR}/bin")
set(REFRAMEWORK_DOT_NET_ASSEMBLY_PATH "${CMAKE_BINARY_DIR}/bin/REFramework.NET.dll")
set_target_properties(csharp-api PROPERTIES
VS_DOTNET_REFERENCE_REFCSharpCompiler
"${REFRAMEWORK_DOT_NET_ASSEMBLY_DIR}/REFCSharpCompiler.dll"
)
set_target_properties(csharp-api PROPERTIES VS_PACKAGE_REFERENCES "REFCSharpCompiler")
"""

[target.csharp-api.properties]
Expand Down

0 comments on commit c44c3a0

Please sign in to comment.