Skip to content

Commit

Permalink
InteropGen Refactor (#64)
Browse files Browse the repository at this point in the history
  • Loading branch information
xezno authored Sep 13, 2024
2 parents 4ade5be + 4aa189c commit 09dbdb3
Show file tree
Hide file tree
Showing 20 changed files with 491 additions and 233 deletions.
62 changes: 62 additions & 0 deletions Source/Mocha.Common/Utils/TaskPool.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
namespace Mocha.Common;

public sealed class TaskPool<T>
{
public delegate Task TaskCallback( T[] taskQueue );
public delegate void Continuation();

private readonly Task[] _tasks;

private TaskPool( T[] queue, TaskCallback taskStart )
{
var maxTasks = (int)(Environment.ProcessorCount * 0.5) - 1;
var batchSize = queue.Length / maxTasks;

if ( batchSize == 0 )
batchSize = 1;

var batched = queue
.Select( ( value, index ) => new
{
Value = value,
Index = index
} )
.GroupBy( p => p.Index / batchSize )
.Select( g => g.Select( p => p.Value ).ToArray() )
.ToArray();

_tasks = new Task[batched.Length];
for ( int i = 0; i < batched.Length; i++ )
{
var taskQueue = batched[i];

_tasks[i] = Task.Run( () => taskStart( taskQueue ) );
}
}

public static TaskPool<T> Dispatch( T[] queue, TaskCallback taskStart )
{
return new TaskPool<T>( queue, taskStart );
}

public static TaskPool<T> Dispatch( IEnumerable<T> queue, TaskCallback taskStart )
{
return new TaskPool<T>( queue.ToArray(), taskStart );
}

public TaskPool<T> Then( Continuation continuation )
{
Task.WhenAll( _tasks ).ContinueWith( t => continuation() ).Wait();
return this;
}

public async Task WaitForCompleteAsync()
{
await Task.WhenAll( _tasks );
}

public void WaitForComplete()
{
WaitForCompleteAsync().Wait();
}
}
20 changes: 11 additions & 9 deletions Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ internal static string GenerateCode( IEnumerable<IUnit> units )
{
switch ( unit )
{
case Class c when c.IsNamespace:
GenerateNamespaceCode( writer, c );
case Namespace n:
GenerateNamespaceCode( writer, n );
break;
case Class c:
GenerateClassCode( writer, c );
Expand Down Expand Up @@ -95,10 +95,11 @@ private static void GenerateClassCode( IndentedTextWriter writer, Class c )
//
// Gather everything we need into nice lists
//
List<string> decls = new();
var decls = new string[c.Methods.Length];

foreach ( var method in c.Methods )
for ( var i = 0; i < decls.Length; i++ )
{
var method = c.Methods[i];
var returnType = Utils.GetManagedType( method.ReturnType );
var name = method.Name;

Expand Down Expand Up @@ -128,7 +129,7 @@ private static void GenerateClassCode( IndentedTextWriter writer, Class c )
// any parameters. The return type is the last type argument passed to
// the delegate.
//
decls.Add( $"private static {delegateSignature} _{name} = ({delegateSignature})Mocha.Common.Global.UnmanagedArgs.__{c.Name}_{name}MethodPtr;" );
decls[i] = $"private static {delegateSignature} _{name} = ({delegateSignature})Mocha.Common.Global.UnmanagedArgs.__{c.Name}_{name}MethodPtr;";
}

//
Expand Down Expand Up @@ -269,15 +270,16 @@ private static void GenerateStructCode( IndentedTextWriter writer, Struct s )
/// </summary>
/// <param name="writer">The writer to append the code to.</param>
/// <param name="ns">The namespace to write code for.</param>
private static void GenerateNamespaceCode( IndentedTextWriter writer, Class ns )
private static void GenerateNamespaceCode( IndentedTextWriter writer, Namespace ns )
{
//
// Gather everything we need into nice lists
//
List<string> decls = new();
var decls = new string[ns.Methods.Length];

foreach ( var method in ns.Methods )
for ( var i = 0; i < decls.Length; i++ )
{
var method = ns.Methods[i];
var returnType = Utils.GetManagedType( method.ReturnType );
var name = method.Name;

Expand All @@ -298,7 +300,7 @@ private static void GenerateNamespaceCode( IndentedTextWriter writer, Class ns )
// any parameters. The return type is the last type argument passed to
// the delegate.
//
decls.Add( $"private static {delegateSignature} _{name} = ({delegateSignature})Mocha.Common.Global.UnmanagedArgs.__{ns.Name}_{name}MethodPtr;" );
decls[i] = $"private static {delegateSignature} _{name} = ({delegateSignature})Mocha.Common.Global.UnmanagedArgs.__{ns.Name}_{name}MethodPtr;";
}

//
Expand Down
15 changes: 7 additions & 8 deletions Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,18 @@ internal static string GenerateCode( string headerPath, IEnumerable<IUnit> units
writer.WriteLine();

writer.WriteLine( "#pragma once" );
writer.WriteLine( $"#include \"..\\{headerPath}\"" );
writer.WriteLine( $"#include \"{Path.Combine( "..", headerPath )}\"" );

writer.WriteLine();

foreach ( var unit in units )
{
if ( unit is not Class c )
continue;

if ( c.IsNamespace )
GenerateNamespaceCode( writer, c );
else
if ( unit is Namespace n )
GenerateNamespaceCode( writer, n );
else if ( unit is Class c )
GenerateClassCode( writer, c );
else
continue;

writer.WriteLine();
}
Expand Down Expand Up @@ -116,7 +115,7 @@ private static void GenerateClassCode( IndentedTextWriter writer, Class c )
/// </summary>
/// <param name="writer">The writer to append the code to.</param>
/// <param name="ns">The namespace to write code for.</param>
private static void GenerateNamespaceCode( IndentedTextWriter writer, Class ns )
private static void GenerateNamespaceCode( IndentedTextWriter writer, Namespace ns )
{
foreach ( var method in ns.Methods )
{
Expand Down
9 changes: 9 additions & 0 deletions Source/MochaTool.InteropGen/Extensions/ILoggerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ internal static partial class ILoggerExtensions
Message = "Generating C# <--> C++ interop code..." )]
internal static partial void LogIntro( this ILogger logger );

/// <summary>
/// Logs an error about missing required args.
/// </summary>
/// <param name="logger">The <see cref="ILogger"/> instance to log to.</param>
[LoggerMessage( EventId = -1,
Level = LogLevel.Error,
Message = "The base directory to generate code from is required to run this tool" )]
internal static partial void LogIntroError( this ILogger logger );

/// <summary>
/// Logs a timed operation to the user.
/// </summary>
Expand Down
6 changes: 3 additions & 3 deletions Source/MochaTool.InteropGen/MochaTool.InteropGen.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
<ItemGroup>
<PackageReference Include="ClangSharp" Version="17.0.1" />
<PackageReference Include="libClangSharp.runtime.win-x64" Version="17.0.4" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
</ItemGroup>

<ItemGroup>
Expand Down
36 changes: 11 additions & 25 deletions Source/MochaTool.InteropGen/Parsing/Class.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@
namespace MochaTool.InteropGen.Parsing;

/// <summary>
/// Represents a class or namespace in C++.
/// Represents a class in C++.
/// </summary>
internal sealed class Class : IUnit
internal sealed class Class : IContainerUnit
{
/// <inheritdoc/>
public string Name { get; }
/// <inheritdoc/>
public bool IsNamespace { get; }

/// <inheritdoc/>
public ImmutableArray<Variable> Fields { get; }
Expand All @@ -20,14 +18,12 @@ internal sealed class Class : IUnit
/// <summary>
/// Initializes a new instance of <see cref="Class"/>.
/// </summary>
/// <param name="name">The name of the class or namespace.</param>
/// <param name="isNamespace">Whether or not it is a class or namespace.</param>
/// <param name="name">The name of the class.</param>
/// <param name="fields">All of the fields that are contained.</param>
/// <param name="methods">All of the methods that are contained.</param>
private Class( string name, bool isNamespace, in ImmutableArray<Variable> fields, in ImmutableArray<Method> methods )
private Class( string name, in ImmutableArray<Variable> fields, in ImmutableArray<Method> methods )
{
Name = name;
IsNamespace = isNamespace;

Fields = fields;
Methods = methods;
Expand All @@ -40,7 +36,7 @@ private Class( string name, bool isNamespace, in ImmutableArray<Variable> fields
/// <returns>A new instance of the <see cref="Class"/> with the fields given.</returns>
internal Class WithFields( in ImmutableArray<Variable> fields )
{
return new Class( Name, IsNamespace, fields, Methods );
return new Class( Name, fields, Methods );
}

/// <summary>
Expand All @@ -50,7 +46,7 @@ internal Class WithFields( in ImmutableArray<Variable> fields )
/// <returns>A new instance of the <see cref="Class"/> with the methods given.</returns>
internal Class WithMethods( in ImmutableArray<Method> methods )
{
return new Class( Name, IsNamespace, Fields, methods );
return new Class( Name, Fields, methods );
}

/// <inheritdoc/>
Expand All @@ -60,9 +56,9 @@ public override string ToString()
}

/// <inheritdoc/>
IUnit IUnit.WithFields( in ImmutableArray<Variable> fields ) => WithFields( fields );
IContainerUnit IContainerUnit.WithFields( in ImmutableArray<Variable> fields ) => WithFields( fields );
/// <inheritdoc/>
IUnit IUnit.WithMethods( in ImmutableArray<Method> methods ) => WithMethods( methods );
IContainerUnit IContainerUnit.WithMethods( in ImmutableArray<Method> methods ) => WithMethods( methods );

/// <summary>
/// Returns a new instance of <see cref="Class"/>.
Expand All @@ -71,20 +67,10 @@ public override string ToString()
/// <param name="fields">The fields contained in the class.</param>
/// <param name="methods">The methods contained in the class.</param>
/// <returns>A new instance of <see cref="Class"/>.</returns>
internal static Class NewClass( string name, in ImmutableArray<Variable> fields, in ImmutableArray<Method> methods )
internal static Class Create( string name, in ImmutableArray<Variable> fields, in ImmutableArray<Method> methods )
{
return new Class( name, false, fields, methods );
return new Class( name, fields, methods );
}

/// <summary>
/// Returns a new instance of <see cref="Class"/> as a namespace.
/// </summary>
/// <param name="name">The name of the namespace.</param>
/// <param name="fields">The fields contained in the namespace.</param>
/// <param name="methods">The methods contained in the namespace.</param>
/// <returns>A new instance of <see cref="Class"/> as a namespace.</returns>
internal static Class NewNamespace( string name, in ImmutableArray<Variable> fields, in ImmutableArray<Method> methods )
{
return new Class( name, true, fields, methods );
}

}
81 changes: 81 additions & 0 deletions Source/MochaTool.InteropGen/Parsing/ContainerBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System.Collections.Immutable;

namespace MochaTool.InteropGen.Parsing;

/// <summary>
/// Builds a representation of a C++ container.
/// </summary>
internal sealed class ContainerBuilder
{
/// <summary>
/// The type of container that is being built.
/// </summary>
internal ContainerType Type { get; }
/// <summary>
/// The name of the container.
/// </summary>
internal string Name { get; }

/// <summary>
/// Whether or not the container has any items within it.
/// </summary>
internal bool IsEmpty => fields.Count == 0 && methods.Count == 0;

private readonly ImmutableArray<Variable>.Builder fields;
private readonly ImmutableArray<Method>.Builder methods;

/// <summary>
/// Initializes a new instance of <see cref="ContainerBuilder"/>.
/// </summary>
/// <param name="type">The type of the container.</param>
/// <param name="name">The name of the container.</param>
internal ContainerBuilder( ContainerType type, string name )
{
Type = type;
Name = name;

fields = ImmutableArray.CreateBuilder<Variable>();
methods = ImmutableArray.CreateBuilder<Method>();
}

/// <summary>
/// Adds a new field to the container.
/// </summary>
/// <param name="field">The field to add.</param>
/// <returns>The same instance of <see cref="ContainerBuilder"/>.</returns>
internal ContainerBuilder AddField( Variable field )
{
fields.Add( field );
return this;
}

/// <summary>
/// Adds a new method to the container.
/// </summary>
/// <param name="method">The method to add.</param>
/// <returns>The same instance of <see cref="ContainerBuilder"/>.</returns>
internal ContainerBuilder AddMethod( Method method )
{
methods.Add( method );
return this;
}

/// <summary>
/// Constructs a new instance of the container.
/// </summary>
/// <returns>A new container instance that implements the <see cref="IContainerUnit"/> interface.</returns>
/// <exception cref="ArgumentOutOfRangeException">Thrown when trying to build a container with an invalid type.</exception>
internal IContainerUnit Build()
{
fields.Capacity = fields.Count;
methods.Capacity = methods.Count;

return Type switch
{
ContainerType.Class => Class.Create( Name, fields.MoveToImmutable(), methods.MoveToImmutable() ),
ContainerType.Namespace => Namespace.Create( Name, fields.MoveToImmutable(), methods.MoveToImmutable() ),
ContainerType.Struct => Struct.Create( Name, fields.MoveToImmutable(), methods.MoveToImmutable() ),
_ => throw new ArgumentOutOfRangeException()
};
}
}
12 changes: 12 additions & 0 deletions Source/MochaTool.InteropGen/Parsing/ContainerType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace MochaTool.InteropGen.Parsing;

/// <summary>
/// Defines a type of container defined in C++.
/// </summary>
internal enum ContainerType : byte
{
Class,
Namespace,
Struct,
Invalid = 255
}
Loading

0 comments on commit 09dbdb3

Please sign in to comment.