Skip to content

Commit

Permalink
Merge pull request #57 from peter-r-g/interop-gen-cleanup
Browse files Browse the repository at this point in the history
InteropGen cleanup
  • Loading branch information
xezno authored Apr 5, 2023
2 parents 207942b + 3e0fdce commit 2837fe0
Show file tree
Hide file tree
Showing 25 changed files with 1,189 additions and 615 deletions.
26 changes: 0 additions & 26 deletions Source/MochaTool.InteropGen/CodeGen/BaseCodeGenerator.cs

This file was deleted.

188 changes: 114 additions & 74 deletions Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,31 +1,103 @@
using System.CodeDom.Compiler;
using MochaTool.InteropGen.Parsing;
using System.CodeDom.Compiler;
using System.Collections.Immutable;

namespace MochaTool.InteropGen;
namespace MochaTool.InteropGen.CodeGen;

sealed class ManagedCodeGenerator : BaseCodeGenerator
/// <summary>
/// Contains functionality for generating C# code.
/// </summary>
internal static class ManagedCodeGenerator
{
public ManagedCodeGenerator( List<IUnit> units ) : base( units )
/// <summary>
/// The namespace that all generated code will be under.
/// </summary>
private const string Namespace = "Mocha.Glue";

/// <summary>
/// An array containing all using declarations for generated code.
/// </summary>
private static readonly string[] Usings = new[]
{
}

private List<string> GetUsings()
"System.Runtime.InteropServices",
"System.Runtime.Serialization",
"Mocha.Common"
};

/// <summary>
/// The header to be used at the top of generated code.
/// </summary>
private static string Header => $"""
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// InteropGen generated on {DateTime.Now}
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
""";

/// <summary>
/// Generates and returns C# code for a set of <see cref="IUnit"/>s.
/// </summary>
/// <param name="units">An enumerable list of <see cref="IUnit"/>s to generate code for.</param>
/// <returns>C# code representing the set of <see cref="IUnit"/>s passed.</returns>
internal static string GenerateCode( IEnumerable<IUnit> units )
{
return new() { "System.Runtime.InteropServices", "System.Runtime.Serialization", "Mocha.Common" };
}
var (baseTextWriter, writer) = Utils.CreateWriter();

private string GetNamespace()
{
return "Mocha.Glue";
// Write header.
writer.WriteLine( Header );
writer.WriteLine();

// Write using statements.
foreach ( var usingStatement in Usings )
writer.WriteLine( $"using {usingStatement};" );

// Write namespace.
writer.WriteLine();
writer.WriteLine( $"namespace {Namespace};" );
writer.WriteLine();

// Write each unit.
foreach ( var unit in units )
{
switch ( unit )
{
case Class c when c.IsNamespace:
GenerateNamespaceCode( writer, c );
break;
case Class c:
GenerateClassCode( writer, c );
break;
case Struct s:
GenerateStructCode( writer, s );
break;
default:
continue;
}

writer.WriteLine();
}

return baseTextWriter.ToString();
}

private void GenerateClassCode( ref IndentedTextWriter writer, Class sel )
/// <summary>
/// Generates C# code for a class.
/// </summary>
/// <param name="writer">The writer to append the code to.</param>
/// <param name="c">The class to write code for.</param>
private static void GenerateClassCode( IndentedTextWriter writer, Class c )
{
//
// Gather everything we need into nice lists
//
List<string> decls = new();

foreach ( var method in sel.Methods )
foreach ( var method in c.Methods )
{
var returnType = Utils.GetManagedType( method.ReturnType );
var name = method.Name;
Expand Down Expand Up @@ -56,13 +128,13 @@ private void GenerateClassCode( ref IndentedTextWriter writer, Class sel )
// 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.__{sel.Name}_{name}MethodPtr;" );
decls.Add( $"private static {delegateSignature} _{name} = ({delegateSignature})Mocha.Common.Global.UnmanagedArgs.__{c.Name}_{name}MethodPtr;" );
}

//
// Write shit
//
writer.WriteLine( $"public unsafe class {sel.Name} : INativeGlue" );
writer.WriteLine( $"public unsafe class {c.Name} : INativeGlue" );
writer.WriteLine( "{" );
writer.Indent++;

Expand All @@ -71,18 +143,16 @@ private void GenerateClassCode( ref IndentedTextWriter writer, Class sel )
// Decls
writer.WriteLine();
foreach ( var decl in decls )
{
writer.WriteLine( decl );
}
writer.WriteLine();

// Ctor
if ( sel.Methods.Any( x => x.IsConstructor ) )
if ( c.Methods.Any( x => x.IsConstructor ) )
{
var ctor = sel.Methods.First( x => x.IsConstructor );
var ctor = c.Methods.First( x => x.IsConstructor );
var managedCtorArgs = string.Join( ", ", ctor.Parameters.Select( x => $"{Utils.GetManagedType( x.Type )} {x.Name}" ) );

writer.WriteLine( $"public {sel.Name}( {managedCtorArgs} )" );
writer.WriteLine( $"public {c.Name}( {managedCtorArgs} )" );
writer.WriteLine( "{" );
writer.Indent++;

Expand All @@ -94,7 +164,7 @@ private void GenerateClassCode( ref IndentedTextWriter writer, Class sel )
}

// Methods
foreach ( var method in sel.Methods )
foreach ( var method in c.Methods )
{
writer.WriteLine();

Expand Down Expand Up @@ -122,7 +192,7 @@ private void GenerateClassCode( ref IndentedTextWriter writer, Class sel )
writer.Indent++;

// Spin up a MemoryContext instance
writer.WriteLine( $"using var ctx = new MemoryContext( \"{sel.Name}.{name}\" );" );
writer.WriteLine( $"using var ctx = new MemoryContext( \"{c.Name}.{name}\" );" );

//
// Gather function body
Expand All @@ -131,7 +201,7 @@ private void GenerateClassCode( ref IndentedTextWriter writer, Class sel )

// We need to pass the instance in if this is not a static method
if ( !method.IsStatic )
paramsAndInstance = paramsAndInstance.Prepend( new Variable( "NativePtr", "IntPtr" ) ).ToList();
paramsAndInstance = paramsAndInstance.Prepend( new Variable( "NativePtr", "IntPtr" ) ).ToImmutableArray();

// Gather function call arguments. Make sure that we're passing in a pointer for everything
var paramNames = paramsAndInstance.Select( x => "ctx.GetPtr( " + x.Name + " )" );
Expand Down Expand Up @@ -175,30 +245,38 @@ private void GenerateClassCode( ref IndentedTextWriter writer, Class sel )
writer.WriteLine( "}" );
}

private void GenerateStructCode( ref IndentedTextWriter writer, Structure sel )
/// <summary>
/// Generates C# code for a struct.
/// </summary>
/// <param name="writer">The writer to append the code to.</param>
/// <param name="s">The struct to write code for.</param>
private static void GenerateStructCode( IndentedTextWriter writer, Struct s )
{
writer.WriteLine( $"[StructLayout( LayoutKind.Sequential )]" );
writer.WriteLine( $"public struct {sel.Name}" );
writer.WriteLine( $"public struct {s.Name}" );
writer.WriteLine( "{" );
writer.Indent++;

foreach ( var field in sel.Fields )
{
foreach ( var field in s.Fields )
writer.WriteLine( $"public {Utils.GetManagedType( field.Type )} {field.Name};" );
}

writer.Indent--;
writer.WriteLine( "}" );
}

private void GenerateNamespaceCode( ref IndentedTextWriter writer, Class sel )
/// <summary>
/// Generates C# code for a namespace.
/// </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 )
{
//
// Gather everything we need into nice lists
//
List<string> decls = new();

foreach ( var method in sel.Methods )
foreach ( var method in ns.Methods )
{
var returnType = Utils.GetManagedType( method.ReturnType );
var name = method.Name;
Expand All @@ -220,24 +298,22 @@ private void GenerateNamespaceCode( ref IndentedTextWriter writer, Class sel )
// 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.__{sel.Name}_{name}MethodPtr;" );
decls.Add( $"private static {delegateSignature} _{name} = ({delegateSignature})Mocha.Common.Global.UnmanagedArgs.__{ns.Name}_{name}MethodPtr;" );
}

//
// Write shit
//
writer.WriteLine( $"public static unsafe class {sel.Name}" );
writer.WriteLine( $"public static unsafe class {ns.Name}" );
writer.WriteLine( "{" );
writer.Indent++;

writer.WriteLine();
foreach ( var decl in decls )
{
writer.WriteLine( decl );
}

// Methods
foreach ( var method in sel.Methods )
foreach ( var method in ns.Methods )
{
writer.WriteLine();

Expand All @@ -252,10 +328,9 @@ private void GenerateNamespaceCode( ref IndentedTextWriter writer, Class sel )
writer.Indent++;

// Spin up a MemoryContext instance
writer.WriteLine( $"using var ctx = new MemoryContext( \"{sel.Name}.{name}\" );" );
writer.WriteLine( $"using var ctx = new MemoryContext( \"{ns.Name}.{name}\" );" );

var @params = method.Parameters;
var paramNames = @params.Select( x => "ctx.GetPtr( " + x.Name + " )" );
var paramNames = method.Parameters.Select( x => "ctx.GetPtr( " + x.Name + " )" );
var functionCallArgs = string.Join( ", ", paramNames );

if ( returnsPointer )
Expand Down Expand Up @@ -293,39 +368,4 @@ private void GenerateNamespaceCode( ref IndentedTextWriter writer, Class sel )
writer.Indent--;
writer.WriteLine( "}" );
}

public string GenerateManagedCode()
{
var (baseTextWriter, writer) = Utils.CreateWriter();

writer.WriteLine( GetHeader() );
writer.WriteLine();

foreach ( var usingStatement in GetUsings() )
writer.WriteLine( $"using {usingStatement};" );

writer.WriteLine();
writer.WriteLine( $"namespace {GetNamespace()};" );
writer.WriteLine();

foreach ( var unit in Units )
{
if ( unit is Class c )
{
if ( c.IsNamespace )
GenerateNamespaceCode( ref writer, c );
else
GenerateClassCode( ref writer, c );
}

if ( unit is Structure s )
{
GenerateStructCode( ref writer, s );
}

writer.WriteLine();
}

return baseTextWriter.ToString();
}
}
Loading

0 comments on commit 2837fe0

Please sign in to comment.