From 7fdea4073032cef3729af90ff9502d8b9de795dc Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 16:15:01 -0500 Subject: [PATCH 01/51] Refactor units to be readonly --- .../CodeGen/ManagedCodeGenerator.cs | 3 +- .../CodeGen/NativeCodeGenerator.cs | 3 +- Source/MochaTool.InteropGen/Parser.cs | 61 +++++++++++-------- Source/MochaTool.InteropGen/Units/Class.cs | 43 ++++++++++--- Source/MochaTool.InteropGen/Units/Field.cs | 8 +-- Source/MochaTool.InteropGen/Units/IUnit.cs | 14 +++-- Source/MochaTool.InteropGen/Units/Method.cs | 48 ++++++++++++--- .../MochaTool.InteropGen/Units/Structure.cs | 35 ++++++++--- 8 files changed, 155 insertions(+), 60 deletions(-) diff --git a/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs index 6cfb0fa1..d04bf537 100644 --- a/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs +++ b/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs @@ -1,4 +1,5 @@ using System.CodeDom.Compiler; +using System.Collections.Immutable; namespace MochaTool.InteropGen; @@ -131,7 +132,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 + " )" ); diff --git a/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs index 645f06ec..9117b5b4 100644 --- a/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs +++ b/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs @@ -1,4 +1,5 @@ using System.CodeDom.Compiler; +using System.Collections.Immutable; namespace MochaTool.InteropGen; @@ -83,7 +84,7 @@ private void GenerateClassCode( ref IndentedTextWriter writer, Class c ) var args = method.Parameters; if ( !method.IsStatic ) - args = args.Prepend( new Variable( "instance", $"{c.Name}*" ) ).ToList(); + args = args.Prepend( new Variable( "instance", $"{c.Name}*" ) ).ToImmutableArray(); var argStr = string.Join( ", ", args.Select( x => { diff --git a/Source/MochaTool.InteropGen/Parser.cs b/Source/MochaTool.InteropGen/Parser.cs index 05a6904f..1f868547 100644 --- a/Source/MochaTool.InteropGen/Parser.cs +++ b/Source/MochaTool.InteropGen/Parser.cs @@ -1,4 +1,6 @@ using ClangSharp.Interop; +using System.Collections.Immutable; + namespace MochaTool.InteropGen; public static class Parser @@ -66,16 +68,13 @@ bool HasGenerateBindingsAttribute() // Struct / class / namespace // case CXCursorKind.CXCursor_ClassDecl: - units.Add( new Class( cursor.Spelling.ToString() ) ); + units.Add( Class.NewClass( cursor.Spelling.ToString(), ImmutableArray.Empty, ImmutableArray.Empty ) ); break; case CXCursorKind.CXCursor_StructDecl: - units.Add( new Structure( cursor.Spelling.ToString() ) ); + units.Add( Structure.NewStructure( cursor.Spelling.ToString(), ImmutableArray.Empty, ImmutableArray.Empty ) ); break; case CXCursorKind.CXCursor_Namespace: - units.Add( new Class( cursor.Spelling.ToString() ) - { - IsNamespace = true - } ); + units.Add( Class.NewNamespace( cursor.Spelling.ToString(), ImmutableArray.Empty, ImmutableArray.Empty ) ); break; // @@ -88,19 +87,21 @@ bool HasGenerateBindingsAttribute() if ( !HasGenerateBindingsAttribute() ) return CXChildVisitResult.CXChildVisit_Continue; - var oName = cursor.LexicalParent.Spelling.ToString(); - var o = units.FirstOrDefault( x => x.Name == oName ); - var m = new Method( cursor.Spelling.ToString(), cursor.ReturnType.Spelling.ToString() ) - { - IsStatic = cursor.IsStatic - }; - - if ( o == null ) + var ownerName = cursor.LexicalParent.Spelling.ToString(); + var owner = units.FirstOrDefault( x => x.Name == ownerName ); + if ( owner is null ) { Console.WriteLine( "No unit" ); break; } + var name = cursor.Spelling.ToString(); + var returnType = cursor.ReturnType.Spelling.ToString(); + var isStatic = cursor.IsStatic; + var isConstructor = false; + + var parametersBuilder = ImmutableArray.CreateBuilder(); + CXCursorVisitor methodChildVisitor = ( CXCursor cursor, CXCursor parent, void* data ) => { if ( cursor.Kind == CXCursorKind.CXCursor_ParmDecl ) @@ -108,9 +109,7 @@ bool HasGenerateBindingsAttribute() var type = cursor.Type.ToString(); var name = cursor.Spelling.ToString(); - var parameter = new Variable( name, type ); - - m.Parameters.Add( parameter ); + parametersBuilder.Add( new Variable( name, type ) ); } return CXChildVisitResult.CXChildVisit_Recurse; @@ -121,13 +120,23 @@ bool HasGenerateBindingsAttribute() if ( cursor.Kind == CXCursorKind.CXCursor_Constructor ) { // Constructor specific stuff here - m.ReturnType = $"{o.Name}*"; - m.Name = "Ctor"; - m.IsConstructor = true; + name = "Ctor"; + returnType = $"{owner.Name}*"; + isConstructor = true; } if ( cursor.CXXAccessSpecifier == CX_CXXAccessSpecifier.CX_CXXPublic || cursor.Kind == CXCursorKind.CXCursor_FunctionDecl ) - o.Methods.Add( m ); + { + Method method; + if ( isConstructor ) + method = Method.NewConstructor( name, returnType, parametersBuilder.ToImmutable() ); + else + method = Method.NewMethod( name, returnType, isStatic, parametersBuilder.ToImmutable() ); + + var newOwner = owner.WithMethods( owner.Methods.Add( method ) ); + units.Remove( owner ); + units.Add( newOwner ); + } break; } @@ -164,15 +173,17 @@ bool HasGenerateBindingsAttribute() // for ( int i = 0; i < units.Count; i++ ) { - var o = units[i]; - o.Methods = o.Methods.GroupBy( x => x.Name ).Select( x => x.First() ).ToList(); - o.Fields = o.Fields.GroupBy( x => x.Name ).Select( x => x.First() ).ToList(); + var item = units[i]; + item = item.WithFields( item.Fields.GroupBy( x => x.Name ).Select( x => x.First() ).ToImmutableArray() ) + .WithMethods( item.Methods.GroupBy( x => x.Name ).Select( x => x.First() ).ToImmutableArray() ); + + units[i] = item; } // // Remove any units that have no methods or fields // - units = units.Where( x => x.Methods.Count > 0 || x.Fields.Count > 0 ).ToList(); + units = units.Where( x => x.Methods.Length > 0 || x.Fields.Length > 0 ).ToList(); // // Post-processing diff --git a/Source/MochaTool.InteropGen/Units/Class.cs b/Source/MochaTool.InteropGen/Units/Class.cs index 7e436e65..e082dcc0 100644 --- a/Source/MochaTool.InteropGen/Units/Class.cs +++ b/Source/MochaTool.InteropGen/Units/Class.cs @@ -1,22 +1,49 @@ -namespace MochaTool.InteropGen; +using System.Collections.Immutable; + +namespace MochaTool.InteropGen; public struct Class : IUnit { - public Class( string name ) : this() + public string Name { get; } + public bool IsNamespace { get; } + + public ImmutableArray Fields { get; } + public ImmutableArray Methods { get; } + + public Class( string name, bool isNamespace, in ImmutableArray fields, in ImmutableArray methods ) { Name = name; + IsNamespace = isNamespace; + + Fields = fields; + Methods = methods; + } - Fields = new(); - Methods = new(); + public Class WithFields( in ImmutableArray fields ) + { + return new Class( Name, IsNamespace, fields, Methods ); } - public string Name { get; set; } - public List Methods { get; set; } - public List Fields { get; set; } - public bool IsNamespace { get; set; } + public Class WithMethods( in ImmutableArray methods ) + { + return new Class( Name, IsNamespace, Fields, methods ); + } public override string ToString() { return Name; } + + IUnit IUnit.WithFields( in ImmutableArray fields ) => WithFields( fields ); + IUnit IUnit.WithMethods( in ImmutableArray methods ) => WithMethods( methods ); + + public static Class NewClass( string name, in ImmutableArray fields, in ImmutableArray methods ) + { + return new Class( name, false, fields, methods ); + } + + public static Class NewNamespace( string name, in ImmutableArray fields, in ImmutableArray methods ) + { + return new Class( name, true, fields, methods ); + } } diff --git a/Source/MochaTool.InteropGen/Units/Field.cs b/Source/MochaTool.InteropGen/Units/Field.cs index e17a2781..5a71e79e 100644 --- a/Source/MochaTool.InteropGen/Units/Field.cs +++ b/Source/MochaTool.InteropGen/Units/Field.cs @@ -2,15 +2,15 @@ public struct Variable { - public Variable( string name, string type ) : this() + public string Name { get; } + public string Type { get; } + + public Variable( string name, string type ) { Name = name; Type = type; } - public string Name { get; set; } - public string Type { get; set; } - public override string ToString() { return $"{Type} {Name}"; diff --git a/Source/MochaTool.InteropGen/Units/IUnit.cs b/Source/MochaTool.InteropGen/Units/IUnit.cs index 71f88a6c..981bc26e 100644 --- a/Source/MochaTool.InteropGen/Units/IUnit.cs +++ b/Source/MochaTool.InteropGen/Units/IUnit.cs @@ -1,8 +1,14 @@ -namespace MochaTool.InteropGen; +using System.Collections.Immutable; + +namespace MochaTool.InteropGen; public interface IUnit { - public string Name { get; set; } - public List Fields { get; set; } - public List Methods { get; set; } + string Name { get; } + + ImmutableArray Fields { get; } + ImmutableArray Methods { get; } + + IUnit WithFields( in ImmutableArray fields ); + IUnit WithMethods( in ImmutableArray methods ); } diff --git a/Source/MochaTool.InteropGen/Units/Method.cs b/Source/MochaTool.InteropGen/Units/Method.cs index 92ffe8b9..178e1b35 100644 --- a/Source/MochaTool.InteropGen/Units/Method.cs +++ b/Source/MochaTool.InteropGen/Units/Method.cs @@ -1,25 +1,53 @@ -namespace MochaTool.InteropGen; +using System.Collections.Immutable; + +namespace MochaTool.InteropGen; public struct Method { - public Method( string name, string returnType ) + public string Name { get; } + public string ReturnType { get; } + + public bool IsConstructor { get; } = false; + public bool IsDestructor { get; } = false; + public bool IsStatic { get; } = false; + + public ImmutableArray Parameters { get; } + + public Method( string name, string returnType, bool isConstructor, bool isDestructor, bool isStatic, in ImmutableArray parameters ) { Name = name; ReturnType = returnType; - Parameters = new(); - } - public bool IsConstructor { get; set; } = false; - public bool IsDestructor { get; set; } = false; - public bool IsStatic { get; set; } = false; + IsConstructor = isConstructor; + IsDestructor = isDestructor; + IsStatic = isStatic; - public string Name { get; set; } - public string ReturnType { get; set; } - public List Parameters { get; set; } + Parameters = parameters; + } + + public Method WithParameters( in ImmutableArray parameters ) + { + return new( Name, ReturnType, IsConstructor, IsDestructor, IsStatic, parameters ); + } public override string ToString() { var p = string.Join( ", ", Parameters ); return $"{ReturnType} {Name}( {p} )"; } + + public static Method NewConstructor( string name, string returnType, in ImmutableArray parameters ) + { + return new( name, returnType, true, false, false, parameters ); + } + + public static Method NewDestructor( string name, string returnType, in ImmutableArray parameters ) + { + return new( name, returnType, false, true, false, parameters ); + } + + public static Method NewMethod( string name, string returnType, bool isStatic, in ImmutableArray parameters ) + { + return new( name, returnType, false, false, isStatic, parameters ); + } } diff --git a/Source/MochaTool.InteropGen/Units/Structure.cs b/Source/MochaTool.InteropGen/Units/Structure.cs index 4e2f150f..080a377e 100644 --- a/Source/MochaTool.InteropGen/Units/Structure.cs +++ b/Source/MochaTool.InteropGen/Units/Structure.cs @@ -1,21 +1,42 @@ -namespace MochaTool.InteropGen; +using System.Collections.Immutable; + +namespace MochaTool.InteropGen; public struct Structure : IUnit { - public Structure( string name ) : this() + public string Name { get; } + + public ImmutableArray Fields { get; } + public ImmutableArray Methods { get; } + + public Structure( string name, in ImmutableArray fields, in ImmutableArray methods ) { Name = name; - Fields = new(); - Methods = new(); + Fields = fields; + Methods = methods; + } + + public Structure WithFields( in ImmutableArray fields ) + { + return new( Name, fields, Methods ); } - public string Name { get; set; } - public List Methods { get; set; } - public List Fields { get; set; } + public Structure WithMethods( in ImmutableArray methods ) + { + return new( Name, Fields, methods ); + } public override string ToString() { return Name; } + + IUnit IUnit.WithFields( in ImmutableArray fields ) => WithFields( fields ); + IUnit IUnit.WithMethods( in ImmutableArray methods ) => WithMethods( methods ); + + public static Structure NewStructure( string name, in ImmutableArray fields, in ImmutableArray methods ) + { + return new( name, fields, methods ); + } } From dc1eddf5873c3050ac3689c01cf97365c032b848 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 16:16:17 -0500 Subject: [PATCH 02/51] Make units sealed classes As structs they will constantly be boxed in the parser and their use case does not warrant them being structures in the first place. --- Source/MochaTool.InteropGen/Units/Class.cs | 2 +- Source/MochaTool.InteropGen/Units/Field.cs | 2 +- Source/MochaTool.InteropGen/Units/Method.cs | 2 +- Source/MochaTool.InteropGen/Units/Structure.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/MochaTool.InteropGen/Units/Class.cs b/Source/MochaTool.InteropGen/Units/Class.cs index e082dcc0..9233c305 100644 --- a/Source/MochaTool.InteropGen/Units/Class.cs +++ b/Source/MochaTool.InteropGen/Units/Class.cs @@ -2,7 +2,7 @@ namespace MochaTool.InteropGen; -public struct Class : IUnit +public sealed class Class : IUnit { public string Name { get; } public bool IsNamespace { get; } diff --git a/Source/MochaTool.InteropGen/Units/Field.cs b/Source/MochaTool.InteropGen/Units/Field.cs index 5a71e79e..635b29e6 100644 --- a/Source/MochaTool.InteropGen/Units/Field.cs +++ b/Source/MochaTool.InteropGen/Units/Field.cs @@ -1,6 +1,6 @@ namespace MochaTool.InteropGen; -public struct Variable +public sealed class Variable { public string Name { get; } public string Type { get; } diff --git a/Source/MochaTool.InteropGen/Units/Method.cs b/Source/MochaTool.InteropGen/Units/Method.cs index 178e1b35..0f84022c 100644 --- a/Source/MochaTool.InteropGen/Units/Method.cs +++ b/Source/MochaTool.InteropGen/Units/Method.cs @@ -2,7 +2,7 @@ namespace MochaTool.InteropGen; -public struct Method +public sealed class Method { public string Name { get; } public string ReturnType { get; } diff --git a/Source/MochaTool.InteropGen/Units/Structure.cs b/Source/MochaTool.InteropGen/Units/Structure.cs index 080a377e..e8ea96fe 100644 --- a/Source/MochaTool.InteropGen/Units/Structure.cs +++ b/Source/MochaTool.InteropGen/Units/Structure.cs @@ -2,7 +2,7 @@ namespace MochaTool.InteropGen; -public struct Structure : IUnit +public sealed class Structure : IUnit { public string Name { get; } From ec926aea8d11b209d02a08da876c088b06a24dc4 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 16:17:31 -0500 Subject: [PATCH 03/51] Rename Field > Variable --- Source/MochaTool.InteropGen/Units/{Field.cs => Variable.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Source/MochaTool.InteropGen/Units/{Field.cs => Variable.cs} (100%) diff --git a/Source/MochaTool.InteropGen/Units/Field.cs b/Source/MochaTool.InteropGen/Units/Variable.cs similarity index 100% rename from Source/MochaTool.InteropGen/Units/Field.cs rename to Source/MochaTool.InteropGen/Units/Variable.cs From 596f716cb4b5f70525cd02252f609bb4ee0b343c Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 16:17:50 -0500 Subject: [PATCH 04/51] Rename Structure > Struct --- .../CodeGen/ManagedCodeGenerator.cs | 4 ++-- Source/MochaTool.InteropGen/Parser.cs | 2 +- .../Units/{Structure.cs => Struct.cs} | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) rename Source/MochaTool.InteropGen/Units/{Structure.cs => Struct.cs} (61%) diff --git a/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs index d04bf537..d30df55a 100644 --- a/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs +++ b/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs @@ -176,7 +176,7 @@ private void GenerateClassCode( ref IndentedTextWriter writer, Class sel ) writer.WriteLine( "}" ); } - private void GenerateStructCode( ref IndentedTextWriter writer, Structure sel ) + private void GenerateStructCode( ref IndentedTextWriter writer, Struct sel ) { writer.WriteLine( $"[StructLayout( LayoutKind.Sequential )]" ); writer.WriteLine( $"public struct {sel.Name}" ); @@ -319,7 +319,7 @@ public string GenerateManagedCode() GenerateClassCode( ref writer, c ); } - if ( unit is Structure s ) + if ( unit is Struct s ) { GenerateStructCode( ref writer, s ); } diff --git a/Source/MochaTool.InteropGen/Parser.cs b/Source/MochaTool.InteropGen/Parser.cs index 1f868547..d323898a 100644 --- a/Source/MochaTool.InteropGen/Parser.cs +++ b/Source/MochaTool.InteropGen/Parser.cs @@ -71,7 +71,7 @@ bool HasGenerateBindingsAttribute() units.Add( Class.NewClass( cursor.Spelling.ToString(), ImmutableArray.Empty, ImmutableArray.Empty ) ); break; case CXCursorKind.CXCursor_StructDecl: - units.Add( Structure.NewStructure( cursor.Spelling.ToString(), ImmutableArray.Empty, ImmutableArray.Empty ) ); + units.Add( Struct.NewStructure( cursor.Spelling.ToString(), ImmutableArray.Empty, ImmutableArray.Empty ) ); break; case CXCursorKind.CXCursor_Namespace: units.Add( Class.NewNamespace( cursor.Spelling.ToString(), ImmutableArray.Empty, ImmutableArray.Empty ) ); diff --git a/Source/MochaTool.InteropGen/Units/Structure.cs b/Source/MochaTool.InteropGen/Units/Struct.cs similarity index 61% rename from Source/MochaTool.InteropGen/Units/Structure.cs rename to Source/MochaTool.InteropGen/Units/Struct.cs index e8ea96fe..e05c81ac 100644 --- a/Source/MochaTool.InteropGen/Units/Structure.cs +++ b/Source/MochaTool.InteropGen/Units/Struct.cs @@ -2,14 +2,14 @@ namespace MochaTool.InteropGen; -public sealed class Structure : IUnit +public sealed class Struct : IUnit { public string Name { get; } public ImmutableArray Fields { get; } public ImmutableArray Methods { get; } - public Structure( string name, in ImmutableArray fields, in ImmutableArray methods ) + public Struct( string name, in ImmutableArray fields, in ImmutableArray methods ) { Name = name; @@ -17,12 +17,12 @@ public Structure( string name, in ImmutableArray fields, in ImmutableA Methods = methods; } - public Structure WithFields( in ImmutableArray fields ) + public Struct WithFields( in ImmutableArray fields ) { return new( Name, fields, Methods ); } - public Structure WithMethods( in ImmutableArray methods ) + public Struct WithMethods( in ImmutableArray methods ) { return new( Name, Fields, methods ); } @@ -35,7 +35,7 @@ public override string ToString() IUnit IUnit.WithFields( in ImmutableArray fields ) => WithFields( fields ); IUnit IUnit.WithMethods( in ImmutableArray methods ) => WithMethods( methods ); - public static Structure NewStructure( string name, in ImmutableArray fields, in ImmutableArray methods ) + public static Struct NewStructure( string name, in ImmutableArray fields, in ImmutableArray methods ) { return new( name, fields, methods ); } From 4b305bcc486c653c78e056dfd4da7be7d28093f2 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 16:30:09 -0500 Subject: [PATCH 05/51] Fix fields not updating in Parser --- Source/MochaTool.InteropGen/Parser.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Source/MochaTool.InteropGen/Parser.cs b/Source/MochaTool.InteropGen/Parser.cs index d323898a..29ae2cd2 100644 --- a/Source/MochaTool.InteropGen/Parser.cs +++ b/Source/MochaTool.InteropGen/Parser.cs @@ -149,13 +149,15 @@ bool HasGenerateBindingsAttribute() if ( !HasGenerateBindingsAttribute() ) return CXChildVisitResult.CXChildVisit_Continue; - var oName = cursor.LexicalParent.Spelling.ToString(); - var s = units.FirstOrDefault( x => x.Name == oName ); + var ownerName = cursor.LexicalParent.Spelling.ToString(); + var owner = units.FirstOrDefault( x => x.Name == ownerName ); - if ( s == null ) + if ( owner is null ) break; - s.Fields.Add( new Variable( cursor.Spelling.ToString(), cursor.Type.ToString() ) ); + var newOwner = owner.WithFields( owner.Fields.Add( new Variable( cursor.Spelling.ToString(), cursor.Type.ToString() ) ) ); + units.Remove( owner ); + units.Add( newOwner ); break; } From d33482f118e95c47c983300d438d7007b13918d6 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 16:39:35 -0500 Subject: [PATCH 06/51] Minor changes --- Source/MochaTool.InteropGen/Parser.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/MochaTool.InteropGen/Parser.cs b/Source/MochaTool.InteropGen/Parser.cs index 29ae2cd2..93cf2a26 100644 --- a/Source/MochaTool.InteropGen/Parser.cs +++ b/Source/MochaTool.InteropGen/Parser.cs @@ -45,7 +45,7 @@ public unsafe static List GetUnits( string path ) var cursor = unit.Cursor; - CXCursorVisitor cursorVisitor = ( CXCursor cursor, CXCursor parent, void* data ) => + CXChildVisitResult cursorVisitor( CXCursor cursor, CXCursor parent, void* data ) { if ( !cursor.Location.IsFromMainFile ) return CXChildVisitResult.CXChildVisit_Continue; @@ -91,7 +91,7 @@ bool HasGenerateBindingsAttribute() var owner = units.FirstOrDefault( x => x.Name == ownerName ); if ( owner is null ) { - Console.WriteLine( "No unit" ); + Console.WriteLine( $"No unit with name \"{ownerName}\"" ); break; } @@ -102,7 +102,7 @@ bool HasGenerateBindingsAttribute() var parametersBuilder = ImmutableArray.CreateBuilder(); - CXCursorVisitor methodChildVisitor = ( CXCursor cursor, CXCursor parent, void* data ) => + CXChildVisitResult methodChildVisitor( CXCursor cursor, CXCursor parent, void* data ) { if ( cursor.Kind == CXCursorKind.CXCursor_ParmDecl ) { @@ -113,7 +113,7 @@ bool HasGenerateBindingsAttribute() } return CXChildVisitResult.CXChildVisit_Recurse; - }; + } cursor.VisitChildren( methodChildVisitor, default ); @@ -166,7 +166,7 @@ bool HasGenerateBindingsAttribute() } return CXChildVisitResult.CXChildVisit_Recurse; - }; + } cursor.VisitChildren( cursorVisitor, default ); From 6534660baf9247054374b7d2d088729158539c34 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 16:40:32 -0500 Subject: [PATCH 07/51] Add early bail to method parsing --- Source/MochaTool.InteropGen/Parser.cs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/Source/MochaTool.InteropGen/Parser.cs b/Source/MochaTool.InteropGen/Parser.cs index 93cf2a26..e78609f1 100644 --- a/Source/MochaTool.InteropGen/Parser.cs +++ b/Source/MochaTool.InteropGen/Parser.cs @@ -87,6 +87,9 @@ bool HasGenerateBindingsAttribute() if ( !HasGenerateBindingsAttribute() ) return CXChildVisitResult.CXChildVisit_Continue; + if ( cursor.CXXAccessSpecifier != CX_CXXAccessSpecifier.CX_CXXPublic && cursor.Kind != CXCursorKind.CXCursor_FunctionDecl ) + break; + var ownerName = cursor.LexicalParent.Spelling.ToString(); var owner = units.FirstOrDefault( x => x.Name == ownerName ); if ( owner is null ) @@ -124,19 +127,15 @@ CXChildVisitResult methodChildVisitor( CXCursor cursor, CXCursor parent, void* d returnType = $"{owner.Name}*"; isConstructor = true; } + Method method; + if ( isConstructor ) + method = Method.NewConstructor( name, returnType, parametersBuilder.ToImmutable() ); + else + method = Method.NewMethod( name, returnType, isStatic, parametersBuilder.ToImmutable() ); - if ( cursor.CXXAccessSpecifier == CX_CXXAccessSpecifier.CX_CXXPublic || cursor.Kind == CXCursorKind.CXCursor_FunctionDecl ) - { - Method method; - if ( isConstructor ) - method = Method.NewConstructor( name, returnType, parametersBuilder.ToImmutable() ); - else - method = Method.NewMethod( name, returnType, isStatic, parametersBuilder.ToImmutable() ); - - var newOwner = owner.WithMethods( owner.Methods.Add( method ) ); - units.Remove( owner ); - units.Add( newOwner ); - } + var newOwner = owner.WithMethods( owner.Methods.Add( method ) ); + units.Remove( owner ); + units.Add( newOwner ); break; } From 957141fcf94eba2eaca25ff7d49555bb2f5647e6 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 16:40:44 -0500 Subject: [PATCH 08/51] Move constructor logic --- Source/MochaTool.InteropGen/Parser.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/MochaTool.InteropGen/Parser.cs b/Source/MochaTool.InteropGen/Parser.cs index e78609f1..3593de6d 100644 --- a/Source/MochaTool.InteropGen/Parser.cs +++ b/Source/MochaTool.InteropGen/Parser.cs @@ -105,6 +105,13 @@ bool HasGenerateBindingsAttribute() var parametersBuilder = ImmutableArray.CreateBuilder(); + if ( cursor.Kind == CXCursorKind.CXCursor_Constructor ) + { + name = "Ctor"; + returnType = owner.Name + '*'; + isConstructor = true; + } + CXChildVisitResult methodChildVisitor( CXCursor cursor, CXCursor parent, void* data ) { if ( cursor.Kind == CXCursorKind.CXCursor_ParmDecl ) @@ -120,13 +127,6 @@ CXChildVisitResult methodChildVisitor( CXCursor cursor, CXCursor parent, void* d cursor.VisitChildren( methodChildVisitor, default ); - if ( cursor.Kind == CXCursorKind.CXCursor_Constructor ) - { - // Constructor specific stuff here - name = "Ctor"; - returnType = $"{owner.Name}*"; - isConstructor = true; - } Method method; if ( isConstructor ) method = Method.NewConstructor( name, returnType, parametersBuilder.ToImmutable() ); From 2302bb46ac83b0c637fc119f300a27ab8f0e32b0 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 17:02:39 -0500 Subject: [PATCH 09/51] Add destructor method parsing --- Source/MochaTool.InteropGen/Parser.cs | 34 +++++++++++++++++++-------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/Source/MochaTool.InteropGen/Parser.cs b/Source/MochaTool.InteropGen/Parser.cs index 3593de6d..b786ac93 100644 --- a/Source/MochaTool.InteropGen/Parser.cs +++ b/Source/MochaTool.InteropGen/Parser.cs @@ -98,10 +98,11 @@ bool HasGenerateBindingsAttribute() break; } - var name = cursor.Spelling.ToString(); - var returnType = cursor.ReturnType.Spelling.ToString(); - var isStatic = cursor.IsStatic; - var isConstructor = false; + string name; + string returnType; + bool isStatic; + bool isConstructor; + bool isDestructor; var parametersBuilder = ImmutableArray.CreateBuilder(); @@ -109,7 +110,25 @@ bool HasGenerateBindingsAttribute() { name = "Ctor"; returnType = owner.Name + '*'; + isStatic = false; isConstructor = true; + isDestructor = false; + } + else if ( cursor.Kind == CXCursorKind.CXCursor_Destructor ) + { + name = "DeCtor"; + returnType = '~' + owner.Name; + isStatic = false; + isConstructor = false; + isDestructor = true; + } + else + { + name = cursor.Spelling.ToString(); + returnType = cursor.ReturnType.Spelling.ToString(); + isStatic = cursor.IsStatic; + isConstructor = false; + isDestructor = false; } CXChildVisitResult methodChildVisitor( CXCursor cursor, CXCursor parent, void* data ) @@ -127,12 +146,7 @@ CXChildVisitResult methodChildVisitor( CXCursor cursor, CXCursor parent, void* d cursor.VisitChildren( methodChildVisitor, default ); - Method method; - if ( isConstructor ) - method = Method.NewConstructor( name, returnType, parametersBuilder.ToImmutable() ); - else - method = Method.NewMethod( name, returnType, isStatic, parametersBuilder.ToImmutable() ); - + var method = new Method( name, returnType, isConstructor, isDestructor, isStatic, parametersBuilder.ToImmutable() ); var newOwner = owner.WithMethods( owner.Methods.Add( method ) ); units.Remove( owner ); units.Add( newOwner ); From ee9732fb034895d58214ecebb8deb9b4ff502dc7 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 17:33:32 -0500 Subject: [PATCH 10/51] Move HasGenerateBindingsAttribute to extension method --- .../MochaTool.InteropGen/CXCursorExtensions.cs | 18 ++++++++++++++++++ Source/MochaTool.InteropGen/Parser.cs | 16 ++-------------- 2 files changed, 20 insertions(+), 14 deletions(-) create mode 100644 Source/MochaTool.InteropGen/CXCursorExtensions.cs diff --git a/Source/MochaTool.InteropGen/CXCursorExtensions.cs b/Source/MochaTool.InteropGen/CXCursorExtensions.cs new file mode 100644 index 00000000..80d6ea40 --- /dev/null +++ b/Source/MochaTool.InteropGen/CXCursorExtensions.cs @@ -0,0 +1,18 @@ +using ClangSharp.Interop; + +namespace MochaTool.InteropGen; + +internal static class CXCursorExtensions +{ + internal static bool HasGenerateBindingsAttribute( this CXCursor cursor ) + { + if ( !cursor.HasAttrs ) + return false; + + var attr = cursor.GetAttr( 0 ); + if ( attr.Spelling.CString != "generate_bindings" ) + return false; + + return true; + } +} diff --git a/Source/MochaTool.InteropGen/Parser.cs b/Source/MochaTool.InteropGen/Parser.cs index b786ac93..1017a5d4 100644 --- a/Source/MochaTool.InteropGen/Parser.cs +++ b/Source/MochaTool.InteropGen/Parser.cs @@ -50,18 +50,6 @@ CXChildVisitResult cursorVisitor( CXCursor cursor, CXCursor parent, void* data ) if ( !cursor.Location.IsFromMainFile ) return CXChildVisitResult.CXChildVisit_Continue; - bool HasGenerateBindingsAttribute() - { - if ( !cursor.HasAttrs ) - return false; - - var attr = cursor.GetAttr( 0 ); - if ( attr.Spelling.CString != "generate_bindings" ) - return false; - - return true; - } - switch ( cursor.Kind ) { // @@ -84,7 +72,7 @@ bool HasGenerateBindingsAttribute() case CXCursorKind.CXCursor_CXXMethod: case CXCursorKind.CXCursor_FunctionDecl: { - if ( !HasGenerateBindingsAttribute() ) + if ( !cursor.HasGenerateBindingsAttribute() ) return CXChildVisitResult.CXChildVisit_Continue; if ( cursor.CXXAccessSpecifier != CX_CXXAccessSpecifier.CX_CXXPublic && cursor.Kind != CXCursorKind.CXCursor_FunctionDecl ) @@ -159,7 +147,7 @@ CXChildVisitResult methodChildVisitor( CXCursor cursor, CXCursor parent, void* d // case CXCursorKind.CXCursor_FieldDecl: { - if ( !HasGenerateBindingsAttribute() ) + if ( !cursor.HasGenerateBindingsAttribute() ) return CXChildVisitResult.CXChildVisit_Continue; var ownerName = cursor.LexicalParent.Spelling.ToString(); From dcb74992c42da70041322a7b47090cc95480a577 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 17:33:46 -0500 Subject: [PATCH 11/51] Minor changes --- Source/MochaTool.InteropGen/Parser.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/MochaTool.InteropGen/Parser.cs b/Source/MochaTool.InteropGen/Parser.cs index 1017a5d4..2b8ba7b7 100644 --- a/Source/MochaTool.InteropGen/Parser.cs +++ b/Source/MochaTool.InteropGen/Parser.cs @@ -32,12 +32,12 @@ private static string[] GetLaunchArgs() public unsafe static List GetUnits( string path ) { - List units = new(); + var units = new List(); using var index = CXIndex.Create(); using var unit = CXTranslationUnit.Parse( index, path, s_launchArgs, ReadOnlySpan.Empty, CXTranslationUnit_Flags.CXTranslationUnit_None ); - for ( int i = 0; i < unit.NumDiagnostics; ++i ) + for ( var i = 0; i < unit.NumDiagnostics; ++i ) { var diagnostics = unit.GetDiagnostic( (uint)i ); Console.WriteLine( $"{diagnostics.Format( CXDiagnostic.DefaultDisplayOptions )}" ); From 83605a1febfcd5bcaf570a9bd12636626a1e80c7 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 17:38:35 -0500 Subject: [PATCH 12/51] Make Parser.GetUnits return IEnumerable instead --- Source/MochaTool.InteropGen/CodeGen/BaseCodeGenerator.cs | 4 ++-- Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs | 2 +- Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs | 2 +- Source/MochaTool.InteropGen/Parser.cs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/MochaTool.InteropGen/CodeGen/BaseCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/BaseCodeGenerator.cs index 7587c39c..f022ba23 100644 --- a/Source/MochaTool.InteropGen/CodeGen/BaseCodeGenerator.cs +++ b/Source/MochaTool.InteropGen/CodeGen/BaseCodeGenerator.cs @@ -2,9 +2,9 @@ internal class BaseCodeGenerator { - protected List Units { get; } = new(); + protected IEnumerable Units { get; } - public BaseCodeGenerator( List units ) + public BaseCodeGenerator( IEnumerable units ) { Units = units; } diff --git a/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs index d30df55a..1b7a2215 100644 --- a/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs +++ b/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs @@ -5,7 +5,7 @@ namespace MochaTool.InteropGen; sealed class ManagedCodeGenerator : BaseCodeGenerator { - public ManagedCodeGenerator( List units ) : base( units ) + public ManagedCodeGenerator( IEnumerable units ) : base( units ) { } diff --git a/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs index 9117b5b4..c0e8b765 100644 --- a/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs +++ b/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs @@ -5,7 +5,7 @@ namespace MochaTool.InteropGen; sealed class NativeCodeGenerator : BaseCodeGenerator { - public NativeCodeGenerator( List units ) : base( units ) + public NativeCodeGenerator( IEnumerable units ) : base( units ) { } diff --git a/Source/MochaTool.InteropGen/Parser.cs b/Source/MochaTool.InteropGen/Parser.cs index 2b8ba7b7..a68c3b79 100644 --- a/Source/MochaTool.InteropGen/Parser.cs +++ b/Source/MochaTool.InteropGen/Parser.cs @@ -30,7 +30,7 @@ private static string[] GetLaunchArgs() return args.ToArray(); } - public unsafe static List GetUnits( string path ) + public unsafe static IEnumerable GetUnits( string path ) { var units = new List(); From d9de12521b91dba53ed78cc11864298f358b4d7e Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 17:38:53 -0500 Subject: [PATCH 13/51] Make BaseCodeGenerator.GetHeader a static property --- .../CodeGen/BaseCodeGenerator.cs | 25 ++++++++----------- .../CodeGen/ManagedCodeGenerator.cs | 2 +- .../CodeGen/NativeCodeGenerator.cs | 2 +- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/Source/MochaTool.InteropGen/CodeGen/BaseCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/BaseCodeGenerator.cs index f022ba23..6edabde6 100644 --- a/Source/MochaTool.InteropGen/CodeGen/BaseCodeGenerator.cs +++ b/Source/MochaTool.InteropGen/CodeGen/BaseCodeGenerator.cs @@ -9,18 +9,15 @@ public BaseCodeGenerator( IEnumerable units ) Units = units; } - protected string GetHeader() - { - return $""" - //------------------------------------------------------------------------------ - // - // 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. - // - //------------------------------------------------------------------------------ - """; - } + protected static string Header => $""" + //------------------------------------------------------------------------------ + // + // 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. + // + //------------------------------------------------------------------------------ + """; } diff --git a/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs index 1b7a2215..8a86d3f0 100644 --- a/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs +++ b/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs @@ -299,7 +299,7 @@ public string GenerateManagedCode() { var (baseTextWriter, writer) = Utils.CreateWriter(); - writer.WriteLine( GetHeader() ); + writer.WriteLine( Header ); writer.WriteLine(); foreach ( var usingStatement in GetUsings() ) diff --git a/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs index c0e8b765..ed771253 100644 --- a/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs +++ b/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs @@ -13,7 +13,7 @@ public string GenerateNativeCode( string headerPath ) { var (baseTextWriter, writer) = Utils.CreateWriter(); - writer.WriteLine( GetHeader() ); + writer.WriteLine( Header ); writer.WriteLine(); writer.WriteLine( "#pragma once" ); From b3921bd4c124f72e3cbf45d5d3443c7df3ef5f5e Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 18:16:19 -0500 Subject: [PATCH 14/51] Minor change --- Source/MochaTool.InteropGen/Program.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/MochaTool.InteropGen/Program.cs b/Source/MochaTool.InteropGen/Program.cs index d4dce2f7..c95111a7 100644 --- a/Source/MochaTool.InteropGen/Program.cs +++ b/Source/MochaTool.InteropGen/Program.cs @@ -2,7 +2,6 @@ public static class Program { - internal static List s_generatedPaths { get; set; } = new(); internal static List s_units { get; set; } = new(); internal static List s_files { get; set; } = new(); From e9f1ace51f437a5823c023e35fbe662c320a6b8a Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 18:31:03 -0500 Subject: [PATCH 15/51] Update ThreadDispatcher to Mocha.Common version --- .../MochaTool.InteropGen/ThreadDispatcher.cs | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/Source/MochaTool.InteropGen/ThreadDispatcher.cs b/Source/MochaTool.InteropGen/ThreadDispatcher.cs index ee0cfeb7..f29fca8d 100644 --- a/Source/MochaTool.InteropGen/ThreadDispatcher.cs +++ b/Source/MochaTool.InteropGen/ThreadDispatcher.cs @@ -1,24 +1,31 @@ -namespace MochaTool.InteropGen; +namespace Mocha.Common; public class ThreadDispatcher { public delegate void ThreadCallback( List threadQueue ); + public delegate Task AsyncThreadCallback( List threadQueue ); - private int _threadCount = 16; + public bool IsComplete => _threadsCompleted >= _threadCount; + + private int _threadCount = (int)Math.Ceiling( Environment.ProcessorCount * 0.75 ); private int _threadsCompleted = 0; - public bool IsComplete => _threadsCompleted == _threadCount; public ThreadDispatcher( ThreadCallback threadStart, List queue ) { - var batchSize = queue.Count / _threadCount; + Setup( queue, threadQueue => threadStart( threadQueue ) ); + } - if ( batchSize == 0 ) - return; // Bail to avoid division by zero + public ThreadDispatcher( AsyncThreadCallback threadStart, List queue ) + { + Setup( queue, threadQueue => threadStart( threadQueue ).Wait() ); + } - var remainder = queue.Count % _threadCount; + private void Setup( List queue, Action> threadStart ) + { + var batchSize = queue.Count / _threadCount - 1; - if ( remainder != 0 ) - batchSize++; + if ( batchSize == 0 ) + return; // Bail to avoid division by zero var batched = queue .Select( ( Value, Index ) => new { Value, Index } ) @@ -26,8 +33,7 @@ public ThreadDispatcher( ThreadCallback threadStart, List queue ) .Select( g => g.Select( p => p.Value ).ToList() ) .ToList(); - if ( batched.Count < _threadCount ) - _threadCount = batched.Count; // Min. 1 per thread + _threadCount = batched.Count; for ( int i = 0; i < batched.Count; i++ ) { @@ -35,7 +41,6 @@ public ThreadDispatcher( ThreadCallback threadStart, List queue ) var thread = new Thread( () => { threadStart( threadQueue ); - _threadsCompleted++; } ); From 2c1d8827adf070b3c3b429bf5b794fc732c5bf51 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 18:31:16 -0500 Subject: [PATCH 16/51] Drop sleep timer on waiting for thread dispatcher --- Source/MochaTool.InteropGen/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/MochaTool.InteropGen/Program.cs b/Source/MochaTool.InteropGen/Program.cs index c95111a7..ff6a90ac 100644 --- a/Source/MochaTool.InteropGen/Program.cs +++ b/Source/MochaTool.InteropGen/Program.cs @@ -68,7 +68,7 @@ private static void Parse( string baseDir ) // Wait for all threads to finish... while ( !dispatcher.IsComplete ) - Thread.Sleep( 500 ); + Thread.Sleep( 1 ); } private static void WriteManagedStruct( string baseDir, ref List<(string Name, Method method)> methods ) From 36c919ba277bd7aa4e940f19c7281049577db657 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 18:32:09 -0500 Subject: [PATCH 17/51] Tweak ThreadDispatcher namespace --- Source/MochaTool.InteropGen/ThreadDispatcher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/MochaTool.InteropGen/ThreadDispatcher.cs b/Source/MochaTool.InteropGen/ThreadDispatcher.cs index f29fca8d..6fc99860 100644 --- a/Source/MochaTool.InteropGen/ThreadDispatcher.cs +++ b/Source/MochaTool.InteropGen/ThreadDispatcher.cs @@ -1,4 +1,4 @@ -namespace Mocha.Common; +namespace MochaTool.InteropGen; public class ThreadDispatcher { From c9c26645284df7b7ec19f28d346e12b02a6f51c0 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 16:15:01 -0500 Subject: [PATCH 18/51] Refactor units to be readonly --- .../CodeGen/ManagedCodeGenerator.cs | 3 +- .../CodeGen/NativeCodeGenerator.cs | 3 +- Source/MochaTool.InteropGen/Parser.cs | 61 +++++++++++-------- Source/MochaTool.InteropGen/Units/Class.cs | 43 ++++++++++--- Source/MochaTool.InteropGen/Units/Field.cs | 8 +-- Source/MochaTool.InteropGen/Units/IUnit.cs | 14 +++-- Source/MochaTool.InteropGen/Units/Method.cs | 48 ++++++++++++--- .../MochaTool.InteropGen/Units/Structure.cs | 35 ++++++++--- 8 files changed, 155 insertions(+), 60 deletions(-) diff --git a/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs index 6cfb0fa1..d04bf537 100644 --- a/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs +++ b/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs @@ -1,4 +1,5 @@ using System.CodeDom.Compiler; +using System.Collections.Immutable; namespace MochaTool.InteropGen; @@ -131,7 +132,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 + " )" ); diff --git a/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs index 645f06ec..9117b5b4 100644 --- a/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs +++ b/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs @@ -1,4 +1,5 @@ using System.CodeDom.Compiler; +using System.Collections.Immutable; namespace MochaTool.InteropGen; @@ -83,7 +84,7 @@ private void GenerateClassCode( ref IndentedTextWriter writer, Class c ) var args = method.Parameters; if ( !method.IsStatic ) - args = args.Prepend( new Variable( "instance", $"{c.Name}*" ) ).ToList(); + args = args.Prepend( new Variable( "instance", $"{c.Name}*" ) ).ToImmutableArray(); var argStr = string.Join( ", ", args.Select( x => { diff --git a/Source/MochaTool.InteropGen/Parser.cs b/Source/MochaTool.InteropGen/Parser.cs index 05a6904f..1f868547 100644 --- a/Source/MochaTool.InteropGen/Parser.cs +++ b/Source/MochaTool.InteropGen/Parser.cs @@ -1,4 +1,6 @@ using ClangSharp.Interop; +using System.Collections.Immutable; + namespace MochaTool.InteropGen; public static class Parser @@ -66,16 +68,13 @@ bool HasGenerateBindingsAttribute() // Struct / class / namespace // case CXCursorKind.CXCursor_ClassDecl: - units.Add( new Class( cursor.Spelling.ToString() ) ); + units.Add( Class.NewClass( cursor.Spelling.ToString(), ImmutableArray.Empty, ImmutableArray.Empty ) ); break; case CXCursorKind.CXCursor_StructDecl: - units.Add( new Structure( cursor.Spelling.ToString() ) ); + units.Add( Structure.NewStructure( cursor.Spelling.ToString(), ImmutableArray.Empty, ImmutableArray.Empty ) ); break; case CXCursorKind.CXCursor_Namespace: - units.Add( new Class( cursor.Spelling.ToString() ) - { - IsNamespace = true - } ); + units.Add( Class.NewNamespace( cursor.Spelling.ToString(), ImmutableArray.Empty, ImmutableArray.Empty ) ); break; // @@ -88,19 +87,21 @@ bool HasGenerateBindingsAttribute() if ( !HasGenerateBindingsAttribute() ) return CXChildVisitResult.CXChildVisit_Continue; - var oName = cursor.LexicalParent.Spelling.ToString(); - var o = units.FirstOrDefault( x => x.Name == oName ); - var m = new Method( cursor.Spelling.ToString(), cursor.ReturnType.Spelling.ToString() ) - { - IsStatic = cursor.IsStatic - }; - - if ( o == null ) + var ownerName = cursor.LexicalParent.Spelling.ToString(); + var owner = units.FirstOrDefault( x => x.Name == ownerName ); + if ( owner is null ) { Console.WriteLine( "No unit" ); break; } + var name = cursor.Spelling.ToString(); + var returnType = cursor.ReturnType.Spelling.ToString(); + var isStatic = cursor.IsStatic; + var isConstructor = false; + + var parametersBuilder = ImmutableArray.CreateBuilder(); + CXCursorVisitor methodChildVisitor = ( CXCursor cursor, CXCursor parent, void* data ) => { if ( cursor.Kind == CXCursorKind.CXCursor_ParmDecl ) @@ -108,9 +109,7 @@ bool HasGenerateBindingsAttribute() var type = cursor.Type.ToString(); var name = cursor.Spelling.ToString(); - var parameter = new Variable( name, type ); - - m.Parameters.Add( parameter ); + parametersBuilder.Add( new Variable( name, type ) ); } return CXChildVisitResult.CXChildVisit_Recurse; @@ -121,13 +120,23 @@ bool HasGenerateBindingsAttribute() if ( cursor.Kind == CXCursorKind.CXCursor_Constructor ) { // Constructor specific stuff here - m.ReturnType = $"{o.Name}*"; - m.Name = "Ctor"; - m.IsConstructor = true; + name = "Ctor"; + returnType = $"{owner.Name}*"; + isConstructor = true; } if ( cursor.CXXAccessSpecifier == CX_CXXAccessSpecifier.CX_CXXPublic || cursor.Kind == CXCursorKind.CXCursor_FunctionDecl ) - o.Methods.Add( m ); + { + Method method; + if ( isConstructor ) + method = Method.NewConstructor( name, returnType, parametersBuilder.ToImmutable() ); + else + method = Method.NewMethod( name, returnType, isStatic, parametersBuilder.ToImmutable() ); + + var newOwner = owner.WithMethods( owner.Methods.Add( method ) ); + units.Remove( owner ); + units.Add( newOwner ); + } break; } @@ -164,15 +173,17 @@ bool HasGenerateBindingsAttribute() // for ( int i = 0; i < units.Count; i++ ) { - var o = units[i]; - o.Methods = o.Methods.GroupBy( x => x.Name ).Select( x => x.First() ).ToList(); - o.Fields = o.Fields.GroupBy( x => x.Name ).Select( x => x.First() ).ToList(); + var item = units[i]; + item = item.WithFields( item.Fields.GroupBy( x => x.Name ).Select( x => x.First() ).ToImmutableArray() ) + .WithMethods( item.Methods.GroupBy( x => x.Name ).Select( x => x.First() ).ToImmutableArray() ); + + units[i] = item; } // // Remove any units that have no methods or fields // - units = units.Where( x => x.Methods.Count > 0 || x.Fields.Count > 0 ).ToList(); + units = units.Where( x => x.Methods.Length > 0 || x.Fields.Length > 0 ).ToList(); // // Post-processing diff --git a/Source/MochaTool.InteropGen/Units/Class.cs b/Source/MochaTool.InteropGen/Units/Class.cs index 7e436e65..e082dcc0 100644 --- a/Source/MochaTool.InteropGen/Units/Class.cs +++ b/Source/MochaTool.InteropGen/Units/Class.cs @@ -1,22 +1,49 @@ -namespace MochaTool.InteropGen; +using System.Collections.Immutable; + +namespace MochaTool.InteropGen; public struct Class : IUnit { - public Class( string name ) : this() + public string Name { get; } + public bool IsNamespace { get; } + + public ImmutableArray Fields { get; } + public ImmutableArray Methods { get; } + + public Class( string name, bool isNamespace, in ImmutableArray fields, in ImmutableArray methods ) { Name = name; + IsNamespace = isNamespace; + + Fields = fields; + Methods = methods; + } - Fields = new(); - Methods = new(); + public Class WithFields( in ImmutableArray fields ) + { + return new Class( Name, IsNamespace, fields, Methods ); } - public string Name { get; set; } - public List Methods { get; set; } - public List Fields { get; set; } - public bool IsNamespace { get; set; } + public Class WithMethods( in ImmutableArray methods ) + { + return new Class( Name, IsNamespace, Fields, methods ); + } public override string ToString() { return Name; } + + IUnit IUnit.WithFields( in ImmutableArray fields ) => WithFields( fields ); + IUnit IUnit.WithMethods( in ImmutableArray methods ) => WithMethods( methods ); + + public static Class NewClass( string name, in ImmutableArray fields, in ImmutableArray methods ) + { + return new Class( name, false, fields, methods ); + } + + public static Class NewNamespace( string name, in ImmutableArray fields, in ImmutableArray methods ) + { + return new Class( name, true, fields, methods ); + } } diff --git a/Source/MochaTool.InteropGen/Units/Field.cs b/Source/MochaTool.InteropGen/Units/Field.cs index e17a2781..5a71e79e 100644 --- a/Source/MochaTool.InteropGen/Units/Field.cs +++ b/Source/MochaTool.InteropGen/Units/Field.cs @@ -2,15 +2,15 @@ public struct Variable { - public Variable( string name, string type ) : this() + public string Name { get; } + public string Type { get; } + + public Variable( string name, string type ) { Name = name; Type = type; } - public string Name { get; set; } - public string Type { get; set; } - public override string ToString() { return $"{Type} {Name}"; diff --git a/Source/MochaTool.InteropGen/Units/IUnit.cs b/Source/MochaTool.InteropGen/Units/IUnit.cs index 71f88a6c..981bc26e 100644 --- a/Source/MochaTool.InteropGen/Units/IUnit.cs +++ b/Source/MochaTool.InteropGen/Units/IUnit.cs @@ -1,8 +1,14 @@ -namespace MochaTool.InteropGen; +using System.Collections.Immutable; + +namespace MochaTool.InteropGen; public interface IUnit { - public string Name { get; set; } - public List Fields { get; set; } - public List Methods { get; set; } + string Name { get; } + + ImmutableArray Fields { get; } + ImmutableArray Methods { get; } + + IUnit WithFields( in ImmutableArray fields ); + IUnit WithMethods( in ImmutableArray methods ); } diff --git a/Source/MochaTool.InteropGen/Units/Method.cs b/Source/MochaTool.InteropGen/Units/Method.cs index 92ffe8b9..178e1b35 100644 --- a/Source/MochaTool.InteropGen/Units/Method.cs +++ b/Source/MochaTool.InteropGen/Units/Method.cs @@ -1,25 +1,53 @@ -namespace MochaTool.InteropGen; +using System.Collections.Immutable; + +namespace MochaTool.InteropGen; public struct Method { - public Method( string name, string returnType ) + public string Name { get; } + public string ReturnType { get; } + + public bool IsConstructor { get; } = false; + public bool IsDestructor { get; } = false; + public bool IsStatic { get; } = false; + + public ImmutableArray Parameters { get; } + + public Method( string name, string returnType, bool isConstructor, bool isDestructor, bool isStatic, in ImmutableArray parameters ) { Name = name; ReturnType = returnType; - Parameters = new(); - } - public bool IsConstructor { get; set; } = false; - public bool IsDestructor { get; set; } = false; - public bool IsStatic { get; set; } = false; + IsConstructor = isConstructor; + IsDestructor = isDestructor; + IsStatic = isStatic; - public string Name { get; set; } - public string ReturnType { get; set; } - public List Parameters { get; set; } + Parameters = parameters; + } + + public Method WithParameters( in ImmutableArray parameters ) + { + return new( Name, ReturnType, IsConstructor, IsDestructor, IsStatic, parameters ); + } public override string ToString() { var p = string.Join( ", ", Parameters ); return $"{ReturnType} {Name}( {p} )"; } + + public static Method NewConstructor( string name, string returnType, in ImmutableArray parameters ) + { + return new( name, returnType, true, false, false, parameters ); + } + + public static Method NewDestructor( string name, string returnType, in ImmutableArray parameters ) + { + return new( name, returnType, false, true, false, parameters ); + } + + public static Method NewMethod( string name, string returnType, bool isStatic, in ImmutableArray parameters ) + { + return new( name, returnType, false, false, isStatic, parameters ); + } } diff --git a/Source/MochaTool.InteropGen/Units/Structure.cs b/Source/MochaTool.InteropGen/Units/Structure.cs index 4e2f150f..080a377e 100644 --- a/Source/MochaTool.InteropGen/Units/Structure.cs +++ b/Source/MochaTool.InteropGen/Units/Structure.cs @@ -1,21 +1,42 @@ -namespace MochaTool.InteropGen; +using System.Collections.Immutable; + +namespace MochaTool.InteropGen; public struct Structure : IUnit { - public Structure( string name ) : this() + public string Name { get; } + + public ImmutableArray Fields { get; } + public ImmutableArray Methods { get; } + + public Structure( string name, in ImmutableArray fields, in ImmutableArray methods ) { Name = name; - Fields = new(); - Methods = new(); + Fields = fields; + Methods = methods; + } + + public Structure WithFields( in ImmutableArray fields ) + { + return new( Name, fields, Methods ); } - public string Name { get; set; } - public List Methods { get; set; } - public List Fields { get; set; } + public Structure WithMethods( in ImmutableArray methods ) + { + return new( Name, Fields, methods ); + } public override string ToString() { return Name; } + + IUnit IUnit.WithFields( in ImmutableArray fields ) => WithFields( fields ); + IUnit IUnit.WithMethods( in ImmutableArray methods ) => WithMethods( methods ); + + public static Structure NewStructure( string name, in ImmutableArray fields, in ImmutableArray methods ) + { + return new( name, fields, methods ); + } } From 0ed975a2f63fc71c2829021399d159126cc327c5 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 16:16:17 -0500 Subject: [PATCH 19/51] Make units sealed classes As structs they will constantly be boxed in the parser and their use case does not warrant them being structures in the first place. --- Source/MochaTool.InteropGen/Units/Class.cs | 2 +- Source/MochaTool.InteropGen/Units/Field.cs | 2 +- Source/MochaTool.InteropGen/Units/Method.cs | 2 +- Source/MochaTool.InteropGen/Units/Structure.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/MochaTool.InteropGen/Units/Class.cs b/Source/MochaTool.InteropGen/Units/Class.cs index e082dcc0..9233c305 100644 --- a/Source/MochaTool.InteropGen/Units/Class.cs +++ b/Source/MochaTool.InteropGen/Units/Class.cs @@ -2,7 +2,7 @@ namespace MochaTool.InteropGen; -public struct Class : IUnit +public sealed class Class : IUnit { public string Name { get; } public bool IsNamespace { get; } diff --git a/Source/MochaTool.InteropGen/Units/Field.cs b/Source/MochaTool.InteropGen/Units/Field.cs index 5a71e79e..635b29e6 100644 --- a/Source/MochaTool.InteropGen/Units/Field.cs +++ b/Source/MochaTool.InteropGen/Units/Field.cs @@ -1,6 +1,6 @@ namespace MochaTool.InteropGen; -public struct Variable +public sealed class Variable { public string Name { get; } public string Type { get; } diff --git a/Source/MochaTool.InteropGen/Units/Method.cs b/Source/MochaTool.InteropGen/Units/Method.cs index 178e1b35..0f84022c 100644 --- a/Source/MochaTool.InteropGen/Units/Method.cs +++ b/Source/MochaTool.InteropGen/Units/Method.cs @@ -2,7 +2,7 @@ namespace MochaTool.InteropGen; -public struct Method +public sealed class Method { public string Name { get; } public string ReturnType { get; } diff --git a/Source/MochaTool.InteropGen/Units/Structure.cs b/Source/MochaTool.InteropGen/Units/Structure.cs index 080a377e..e8ea96fe 100644 --- a/Source/MochaTool.InteropGen/Units/Structure.cs +++ b/Source/MochaTool.InteropGen/Units/Structure.cs @@ -2,7 +2,7 @@ namespace MochaTool.InteropGen; -public struct Structure : IUnit +public sealed class Structure : IUnit { public string Name { get; } From ae59d0c4a660793424d07b15b82461ff596917ae Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 16:17:31 -0500 Subject: [PATCH 20/51] Rename Field > Variable --- Source/MochaTool.InteropGen/Units/{Field.cs => Variable.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Source/MochaTool.InteropGen/Units/{Field.cs => Variable.cs} (100%) diff --git a/Source/MochaTool.InteropGen/Units/Field.cs b/Source/MochaTool.InteropGen/Units/Variable.cs similarity index 100% rename from Source/MochaTool.InteropGen/Units/Field.cs rename to Source/MochaTool.InteropGen/Units/Variable.cs From 065d8410793ca9b0d42209fa398300639fff8a37 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 16:17:50 -0500 Subject: [PATCH 21/51] Rename Structure > Struct --- .../CodeGen/ManagedCodeGenerator.cs | 4 ++-- Source/MochaTool.InteropGen/Parser.cs | 2 +- .../Units/{Structure.cs => Struct.cs} | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) rename Source/MochaTool.InteropGen/Units/{Structure.cs => Struct.cs} (61%) diff --git a/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs index d04bf537..d30df55a 100644 --- a/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs +++ b/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs @@ -176,7 +176,7 @@ private void GenerateClassCode( ref IndentedTextWriter writer, Class sel ) writer.WriteLine( "}" ); } - private void GenerateStructCode( ref IndentedTextWriter writer, Structure sel ) + private void GenerateStructCode( ref IndentedTextWriter writer, Struct sel ) { writer.WriteLine( $"[StructLayout( LayoutKind.Sequential )]" ); writer.WriteLine( $"public struct {sel.Name}" ); @@ -319,7 +319,7 @@ public string GenerateManagedCode() GenerateClassCode( ref writer, c ); } - if ( unit is Structure s ) + if ( unit is Struct s ) { GenerateStructCode( ref writer, s ); } diff --git a/Source/MochaTool.InteropGen/Parser.cs b/Source/MochaTool.InteropGen/Parser.cs index 1f868547..d323898a 100644 --- a/Source/MochaTool.InteropGen/Parser.cs +++ b/Source/MochaTool.InteropGen/Parser.cs @@ -71,7 +71,7 @@ bool HasGenerateBindingsAttribute() units.Add( Class.NewClass( cursor.Spelling.ToString(), ImmutableArray.Empty, ImmutableArray.Empty ) ); break; case CXCursorKind.CXCursor_StructDecl: - units.Add( Structure.NewStructure( cursor.Spelling.ToString(), ImmutableArray.Empty, ImmutableArray.Empty ) ); + units.Add( Struct.NewStructure( cursor.Spelling.ToString(), ImmutableArray.Empty, ImmutableArray.Empty ) ); break; case CXCursorKind.CXCursor_Namespace: units.Add( Class.NewNamespace( cursor.Spelling.ToString(), ImmutableArray.Empty, ImmutableArray.Empty ) ); diff --git a/Source/MochaTool.InteropGen/Units/Structure.cs b/Source/MochaTool.InteropGen/Units/Struct.cs similarity index 61% rename from Source/MochaTool.InteropGen/Units/Structure.cs rename to Source/MochaTool.InteropGen/Units/Struct.cs index e8ea96fe..e05c81ac 100644 --- a/Source/MochaTool.InteropGen/Units/Structure.cs +++ b/Source/MochaTool.InteropGen/Units/Struct.cs @@ -2,14 +2,14 @@ namespace MochaTool.InteropGen; -public sealed class Structure : IUnit +public sealed class Struct : IUnit { public string Name { get; } public ImmutableArray Fields { get; } public ImmutableArray Methods { get; } - public Structure( string name, in ImmutableArray fields, in ImmutableArray methods ) + public Struct( string name, in ImmutableArray fields, in ImmutableArray methods ) { Name = name; @@ -17,12 +17,12 @@ public Structure( string name, in ImmutableArray fields, in ImmutableA Methods = methods; } - public Structure WithFields( in ImmutableArray fields ) + public Struct WithFields( in ImmutableArray fields ) { return new( Name, fields, Methods ); } - public Structure WithMethods( in ImmutableArray methods ) + public Struct WithMethods( in ImmutableArray methods ) { return new( Name, Fields, methods ); } @@ -35,7 +35,7 @@ public override string ToString() IUnit IUnit.WithFields( in ImmutableArray fields ) => WithFields( fields ); IUnit IUnit.WithMethods( in ImmutableArray methods ) => WithMethods( methods ); - public static Structure NewStructure( string name, in ImmutableArray fields, in ImmutableArray methods ) + public static Struct NewStructure( string name, in ImmutableArray fields, in ImmutableArray methods ) { return new( name, fields, methods ); } From 3b0083a8d48fa1ba7e65941cd9c76b4b1ba51c08 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 16:30:09 -0500 Subject: [PATCH 22/51] Fix fields not updating in Parser --- Source/MochaTool.InteropGen/Parser.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Source/MochaTool.InteropGen/Parser.cs b/Source/MochaTool.InteropGen/Parser.cs index d323898a..29ae2cd2 100644 --- a/Source/MochaTool.InteropGen/Parser.cs +++ b/Source/MochaTool.InteropGen/Parser.cs @@ -149,13 +149,15 @@ bool HasGenerateBindingsAttribute() if ( !HasGenerateBindingsAttribute() ) return CXChildVisitResult.CXChildVisit_Continue; - var oName = cursor.LexicalParent.Spelling.ToString(); - var s = units.FirstOrDefault( x => x.Name == oName ); + var ownerName = cursor.LexicalParent.Spelling.ToString(); + var owner = units.FirstOrDefault( x => x.Name == ownerName ); - if ( s == null ) + if ( owner is null ) break; - s.Fields.Add( new Variable( cursor.Spelling.ToString(), cursor.Type.ToString() ) ); + var newOwner = owner.WithFields( owner.Fields.Add( new Variable( cursor.Spelling.ToString(), cursor.Type.ToString() ) ) ); + units.Remove( owner ); + units.Add( newOwner ); break; } From f056e3eaa44fbe4ea03d226851a427becaab1b4d Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 16:39:35 -0500 Subject: [PATCH 23/51] Minor changes --- Source/MochaTool.InteropGen/Parser.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/MochaTool.InteropGen/Parser.cs b/Source/MochaTool.InteropGen/Parser.cs index 29ae2cd2..93cf2a26 100644 --- a/Source/MochaTool.InteropGen/Parser.cs +++ b/Source/MochaTool.InteropGen/Parser.cs @@ -45,7 +45,7 @@ public unsafe static List GetUnits( string path ) var cursor = unit.Cursor; - CXCursorVisitor cursorVisitor = ( CXCursor cursor, CXCursor parent, void* data ) => + CXChildVisitResult cursorVisitor( CXCursor cursor, CXCursor parent, void* data ) { if ( !cursor.Location.IsFromMainFile ) return CXChildVisitResult.CXChildVisit_Continue; @@ -91,7 +91,7 @@ bool HasGenerateBindingsAttribute() var owner = units.FirstOrDefault( x => x.Name == ownerName ); if ( owner is null ) { - Console.WriteLine( "No unit" ); + Console.WriteLine( $"No unit with name \"{ownerName}\"" ); break; } @@ -102,7 +102,7 @@ bool HasGenerateBindingsAttribute() var parametersBuilder = ImmutableArray.CreateBuilder(); - CXCursorVisitor methodChildVisitor = ( CXCursor cursor, CXCursor parent, void* data ) => + CXChildVisitResult methodChildVisitor( CXCursor cursor, CXCursor parent, void* data ) { if ( cursor.Kind == CXCursorKind.CXCursor_ParmDecl ) { @@ -113,7 +113,7 @@ bool HasGenerateBindingsAttribute() } return CXChildVisitResult.CXChildVisit_Recurse; - }; + } cursor.VisitChildren( methodChildVisitor, default ); @@ -166,7 +166,7 @@ bool HasGenerateBindingsAttribute() } return CXChildVisitResult.CXChildVisit_Recurse; - }; + } cursor.VisitChildren( cursorVisitor, default ); From f0b6440d8aa4b5f6f4206359c8e0acb9a961822b Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 16:40:32 -0500 Subject: [PATCH 24/51] Add early bail to method parsing --- Source/MochaTool.InteropGen/Parser.cs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/Source/MochaTool.InteropGen/Parser.cs b/Source/MochaTool.InteropGen/Parser.cs index 93cf2a26..e78609f1 100644 --- a/Source/MochaTool.InteropGen/Parser.cs +++ b/Source/MochaTool.InteropGen/Parser.cs @@ -87,6 +87,9 @@ bool HasGenerateBindingsAttribute() if ( !HasGenerateBindingsAttribute() ) return CXChildVisitResult.CXChildVisit_Continue; + if ( cursor.CXXAccessSpecifier != CX_CXXAccessSpecifier.CX_CXXPublic && cursor.Kind != CXCursorKind.CXCursor_FunctionDecl ) + break; + var ownerName = cursor.LexicalParent.Spelling.ToString(); var owner = units.FirstOrDefault( x => x.Name == ownerName ); if ( owner is null ) @@ -124,19 +127,15 @@ CXChildVisitResult methodChildVisitor( CXCursor cursor, CXCursor parent, void* d returnType = $"{owner.Name}*"; isConstructor = true; } + Method method; + if ( isConstructor ) + method = Method.NewConstructor( name, returnType, parametersBuilder.ToImmutable() ); + else + method = Method.NewMethod( name, returnType, isStatic, parametersBuilder.ToImmutable() ); - if ( cursor.CXXAccessSpecifier == CX_CXXAccessSpecifier.CX_CXXPublic || cursor.Kind == CXCursorKind.CXCursor_FunctionDecl ) - { - Method method; - if ( isConstructor ) - method = Method.NewConstructor( name, returnType, parametersBuilder.ToImmutable() ); - else - method = Method.NewMethod( name, returnType, isStatic, parametersBuilder.ToImmutable() ); - - var newOwner = owner.WithMethods( owner.Methods.Add( method ) ); - units.Remove( owner ); - units.Add( newOwner ); - } + var newOwner = owner.WithMethods( owner.Methods.Add( method ) ); + units.Remove( owner ); + units.Add( newOwner ); break; } From 203f0706529ce0de6699f29df4469d5e5c8dc20c Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 16:40:44 -0500 Subject: [PATCH 25/51] Move constructor logic --- Source/MochaTool.InteropGen/Parser.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/MochaTool.InteropGen/Parser.cs b/Source/MochaTool.InteropGen/Parser.cs index e78609f1..3593de6d 100644 --- a/Source/MochaTool.InteropGen/Parser.cs +++ b/Source/MochaTool.InteropGen/Parser.cs @@ -105,6 +105,13 @@ bool HasGenerateBindingsAttribute() var parametersBuilder = ImmutableArray.CreateBuilder(); + if ( cursor.Kind == CXCursorKind.CXCursor_Constructor ) + { + name = "Ctor"; + returnType = owner.Name + '*'; + isConstructor = true; + } + CXChildVisitResult methodChildVisitor( CXCursor cursor, CXCursor parent, void* data ) { if ( cursor.Kind == CXCursorKind.CXCursor_ParmDecl ) @@ -120,13 +127,6 @@ CXChildVisitResult methodChildVisitor( CXCursor cursor, CXCursor parent, void* d cursor.VisitChildren( methodChildVisitor, default ); - if ( cursor.Kind == CXCursorKind.CXCursor_Constructor ) - { - // Constructor specific stuff here - name = "Ctor"; - returnType = $"{owner.Name}*"; - isConstructor = true; - } Method method; if ( isConstructor ) method = Method.NewConstructor( name, returnType, parametersBuilder.ToImmutable() ); From 321f958d5ec1cc915f5f1888d91301580bec3b53 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 17:02:39 -0500 Subject: [PATCH 26/51] Add destructor method parsing --- Source/MochaTool.InteropGen/Parser.cs | 34 +++++++++++++++++++-------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/Source/MochaTool.InteropGen/Parser.cs b/Source/MochaTool.InteropGen/Parser.cs index 3593de6d..b786ac93 100644 --- a/Source/MochaTool.InteropGen/Parser.cs +++ b/Source/MochaTool.InteropGen/Parser.cs @@ -98,10 +98,11 @@ bool HasGenerateBindingsAttribute() break; } - var name = cursor.Spelling.ToString(); - var returnType = cursor.ReturnType.Spelling.ToString(); - var isStatic = cursor.IsStatic; - var isConstructor = false; + string name; + string returnType; + bool isStatic; + bool isConstructor; + bool isDestructor; var parametersBuilder = ImmutableArray.CreateBuilder(); @@ -109,7 +110,25 @@ bool HasGenerateBindingsAttribute() { name = "Ctor"; returnType = owner.Name + '*'; + isStatic = false; isConstructor = true; + isDestructor = false; + } + else if ( cursor.Kind == CXCursorKind.CXCursor_Destructor ) + { + name = "DeCtor"; + returnType = '~' + owner.Name; + isStatic = false; + isConstructor = false; + isDestructor = true; + } + else + { + name = cursor.Spelling.ToString(); + returnType = cursor.ReturnType.Spelling.ToString(); + isStatic = cursor.IsStatic; + isConstructor = false; + isDestructor = false; } CXChildVisitResult methodChildVisitor( CXCursor cursor, CXCursor parent, void* data ) @@ -127,12 +146,7 @@ CXChildVisitResult methodChildVisitor( CXCursor cursor, CXCursor parent, void* d cursor.VisitChildren( methodChildVisitor, default ); - Method method; - if ( isConstructor ) - method = Method.NewConstructor( name, returnType, parametersBuilder.ToImmutable() ); - else - method = Method.NewMethod( name, returnType, isStatic, parametersBuilder.ToImmutable() ); - + var method = new Method( name, returnType, isConstructor, isDestructor, isStatic, parametersBuilder.ToImmutable() ); var newOwner = owner.WithMethods( owner.Methods.Add( method ) ); units.Remove( owner ); units.Add( newOwner ); From d0a3cba2c2b6e58a2b0902ac2210b8478dd1365e Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 17:33:32 -0500 Subject: [PATCH 27/51] Move HasGenerateBindingsAttribute to extension method --- .../MochaTool.InteropGen/CXCursorExtensions.cs | 18 ++++++++++++++++++ Source/MochaTool.InteropGen/Parser.cs | 16 ++-------------- 2 files changed, 20 insertions(+), 14 deletions(-) create mode 100644 Source/MochaTool.InteropGen/CXCursorExtensions.cs diff --git a/Source/MochaTool.InteropGen/CXCursorExtensions.cs b/Source/MochaTool.InteropGen/CXCursorExtensions.cs new file mode 100644 index 00000000..80d6ea40 --- /dev/null +++ b/Source/MochaTool.InteropGen/CXCursorExtensions.cs @@ -0,0 +1,18 @@ +using ClangSharp.Interop; + +namespace MochaTool.InteropGen; + +internal static class CXCursorExtensions +{ + internal static bool HasGenerateBindingsAttribute( this CXCursor cursor ) + { + if ( !cursor.HasAttrs ) + return false; + + var attr = cursor.GetAttr( 0 ); + if ( attr.Spelling.CString != "generate_bindings" ) + return false; + + return true; + } +} diff --git a/Source/MochaTool.InteropGen/Parser.cs b/Source/MochaTool.InteropGen/Parser.cs index b786ac93..1017a5d4 100644 --- a/Source/MochaTool.InteropGen/Parser.cs +++ b/Source/MochaTool.InteropGen/Parser.cs @@ -50,18 +50,6 @@ CXChildVisitResult cursorVisitor( CXCursor cursor, CXCursor parent, void* data ) if ( !cursor.Location.IsFromMainFile ) return CXChildVisitResult.CXChildVisit_Continue; - bool HasGenerateBindingsAttribute() - { - if ( !cursor.HasAttrs ) - return false; - - var attr = cursor.GetAttr( 0 ); - if ( attr.Spelling.CString != "generate_bindings" ) - return false; - - return true; - } - switch ( cursor.Kind ) { // @@ -84,7 +72,7 @@ bool HasGenerateBindingsAttribute() case CXCursorKind.CXCursor_CXXMethod: case CXCursorKind.CXCursor_FunctionDecl: { - if ( !HasGenerateBindingsAttribute() ) + if ( !cursor.HasGenerateBindingsAttribute() ) return CXChildVisitResult.CXChildVisit_Continue; if ( cursor.CXXAccessSpecifier != CX_CXXAccessSpecifier.CX_CXXPublic && cursor.Kind != CXCursorKind.CXCursor_FunctionDecl ) @@ -159,7 +147,7 @@ CXChildVisitResult methodChildVisitor( CXCursor cursor, CXCursor parent, void* d // case CXCursorKind.CXCursor_FieldDecl: { - if ( !HasGenerateBindingsAttribute() ) + if ( !cursor.HasGenerateBindingsAttribute() ) return CXChildVisitResult.CXChildVisit_Continue; var ownerName = cursor.LexicalParent.Spelling.ToString(); From bef236cdd3e7d022ab7c8456b0e193a41ebd0b2a Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 17:33:46 -0500 Subject: [PATCH 28/51] Minor changes --- Source/MochaTool.InteropGen/Parser.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/MochaTool.InteropGen/Parser.cs b/Source/MochaTool.InteropGen/Parser.cs index 1017a5d4..2b8ba7b7 100644 --- a/Source/MochaTool.InteropGen/Parser.cs +++ b/Source/MochaTool.InteropGen/Parser.cs @@ -32,12 +32,12 @@ private static string[] GetLaunchArgs() public unsafe static List GetUnits( string path ) { - List units = new(); + var units = new List(); using var index = CXIndex.Create(); using var unit = CXTranslationUnit.Parse( index, path, s_launchArgs, ReadOnlySpan.Empty, CXTranslationUnit_Flags.CXTranslationUnit_None ); - for ( int i = 0; i < unit.NumDiagnostics; ++i ) + for ( var i = 0; i < unit.NumDiagnostics; ++i ) { var diagnostics = unit.GetDiagnostic( (uint)i ); Console.WriteLine( $"{diagnostics.Format( CXDiagnostic.DefaultDisplayOptions )}" ); From c068836fad83f1f001990c76f92bf7e5dd5c7790 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 17:38:35 -0500 Subject: [PATCH 29/51] Make Parser.GetUnits return IEnumerable instead --- Source/MochaTool.InteropGen/CodeGen/BaseCodeGenerator.cs | 4 ++-- Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs | 2 +- Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs | 2 +- Source/MochaTool.InteropGen/Parser.cs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/MochaTool.InteropGen/CodeGen/BaseCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/BaseCodeGenerator.cs index 7587c39c..f022ba23 100644 --- a/Source/MochaTool.InteropGen/CodeGen/BaseCodeGenerator.cs +++ b/Source/MochaTool.InteropGen/CodeGen/BaseCodeGenerator.cs @@ -2,9 +2,9 @@ internal class BaseCodeGenerator { - protected List Units { get; } = new(); + protected IEnumerable Units { get; } - public BaseCodeGenerator( List units ) + public BaseCodeGenerator( IEnumerable units ) { Units = units; } diff --git a/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs index d30df55a..1b7a2215 100644 --- a/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs +++ b/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs @@ -5,7 +5,7 @@ namespace MochaTool.InteropGen; sealed class ManagedCodeGenerator : BaseCodeGenerator { - public ManagedCodeGenerator( List units ) : base( units ) + public ManagedCodeGenerator( IEnumerable units ) : base( units ) { } diff --git a/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs index 9117b5b4..c0e8b765 100644 --- a/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs +++ b/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs @@ -5,7 +5,7 @@ namespace MochaTool.InteropGen; sealed class NativeCodeGenerator : BaseCodeGenerator { - public NativeCodeGenerator( List units ) : base( units ) + public NativeCodeGenerator( IEnumerable units ) : base( units ) { } diff --git a/Source/MochaTool.InteropGen/Parser.cs b/Source/MochaTool.InteropGen/Parser.cs index 2b8ba7b7..a68c3b79 100644 --- a/Source/MochaTool.InteropGen/Parser.cs +++ b/Source/MochaTool.InteropGen/Parser.cs @@ -30,7 +30,7 @@ private static string[] GetLaunchArgs() return args.ToArray(); } - public unsafe static List GetUnits( string path ) + public unsafe static IEnumerable GetUnits( string path ) { var units = new List(); From 564114882cd7a4841b0ad5175782f16566850c2a Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 17:38:53 -0500 Subject: [PATCH 30/51] Make BaseCodeGenerator.GetHeader a static property --- .../CodeGen/BaseCodeGenerator.cs | 25 ++++++++----------- .../CodeGen/ManagedCodeGenerator.cs | 2 +- .../CodeGen/NativeCodeGenerator.cs | 2 +- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/Source/MochaTool.InteropGen/CodeGen/BaseCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/BaseCodeGenerator.cs index f022ba23..6edabde6 100644 --- a/Source/MochaTool.InteropGen/CodeGen/BaseCodeGenerator.cs +++ b/Source/MochaTool.InteropGen/CodeGen/BaseCodeGenerator.cs @@ -9,18 +9,15 @@ public BaseCodeGenerator( IEnumerable units ) Units = units; } - protected string GetHeader() - { - return $""" - //------------------------------------------------------------------------------ - // - // 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. - // - //------------------------------------------------------------------------------ - """; - } + protected static string Header => $""" + //------------------------------------------------------------------------------ + // + // 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. + // + //------------------------------------------------------------------------------ + """; } diff --git a/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs index 1b7a2215..8a86d3f0 100644 --- a/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs +++ b/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs @@ -299,7 +299,7 @@ public string GenerateManagedCode() { var (baseTextWriter, writer) = Utils.CreateWriter(); - writer.WriteLine( GetHeader() ); + writer.WriteLine( Header ); writer.WriteLine(); foreach ( var usingStatement in GetUsings() ) diff --git a/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs index c0e8b765..ed771253 100644 --- a/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs +++ b/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs @@ -13,7 +13,7 @@ public string GenerateNativeCode( string headerPath ) { var (baseTextWriter, writer) = Utils.CreateWriter(); - writer.WriteLine( GetHeader() ); + writer.WriteLine( Header ); writer.WriteLine(); writer.WriteLine( "#pragma once" ); From e14e7913613ab4c2eee70a7e54b6af6b35ba5f95 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 18:16:19 -0500 Subject: [PATCH 31/51] Minor change --- Source/MochaTool.InteropGen/Program.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/MochaTool.InteropGen/Program.cs b/Source/MochaTool.InteropGen/Program.cs index d4dce2f7..c95111a7 100644 --- a/Source/MochaTool.InteropGen/Program.cs +++ b/Source/MochaTool.InteropGen/Program.cs @@ -2,7 +2,6 @@ public static class Program { - internal static List s_generatedPaths { get; set; } = new(); internal static List s_units { get; set; } = new(); internal static List s_files { get; set; } = new(); From bb6eb459517ac63f1d7daba64d1d8b5c0851439c Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 18:31:03 -0500 Subject: [PATCH 32/51] Update ThreadDispatcher to Mocha.Common version --- .../MochaTool.InteropGen/ThreadDispatcher.cs | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/Source/MochaTool.InteropGen/ThreadDispatcher.cs b/Source/MochaTool.InteropGen/ThreadDispatcher.cs index ee0cfeb7..f29fca8d 100644 --- a/Source/MochaTool.InteropGen/ThreadDispatcher.cs +++ b/Source/MochaTool.InteropGen/ThreadDispatcher.cs @@ -1,24 +1,31 @@ -namespace MochaTool.InteropGen; +namespace Mocha.Common; public class ThreadDispatcher { public delegate void ThreadCallback( List threadQueue ); + public delegate Task AsyncThreadCallback( List threadQueue ); - private int _threadCount = 16; + public bool IsComplete => _threadsCompleted >= _threadCount; + + private int _threadCount = (int)Math.Ceiling( Environment.ProcessorCount * 0.75 ); private int _threadsCompleted = 0; - public bool IsComplete => _threadsCompleted == _threadCount; public ThreadDispatcher( ThreadCallback threadStart, List queue ) { - var batchSize = queue.Count / _threadCount; + Setup( queue, threadQueue => threadStart( threadQueue ) ); + } - if ( batchSize == 0 ) - return; // Bail to avoid division by zero + public ThreadDispatcher( AsyncThreadCallback threadStart, List queue ) + { + Setup( queue, threadQueue => threadStart( threadQueue ).Wait() ); + } - var remainder = queue.Count % _threadCount; + private void Setup( List queue, Action> threadStart ) + { + var batchSize = queue.Count / _threadCount - 1; - if ( remainder != 0 ) - batchSize++; + if ( batchSize == 0 ) + return; // Bail to avoid division by zero var batched = queue .Select( ( Value, Index ) => new { Value, Index } ) @@ -26,8 +33,7 @@ public ThreadDispatcher( ThreadCallback threadStart, List queue ) .Select( g => g.Select( p => p.Value ).ToList() ) .ToList(); - if ( batched.Count < _threadCount ) - _threadCount = batched.Count; // Min. 1 per thread + _threadCount = batched.Count; for ( int i = 0; i < batched.Count; i++ ) { @@ -35,7 +41,6 @@ public ThreadDispatcher( ThreadCallback threadStart, List queue ) var thread = new Thread( () => { threadStart( threadQueue ); - _threadsCompleted++; } ); From f203dade3d5b48348b1aa2f05d9a56401a050d58 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 18:31:16 -0500 Subject: [PATCH 33/51] Drop sleep timer on waiting for thread dispatcher --- Source/MochaTool.InteropGen/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/MochaTool.InteropGen/Program.cs b/Source/MochaTool.InteropGen/Program.cs index c95111a7..ff6a90ac 100644 --- a/Source/MochaTool.InteropGen/Program.cs +++ b/Source/MochaTool.InteropGen/Program.cs @@ -68,7 +68,7 @@ private static void Parse( string baseDir ) // Wait for all threads to finish... while ( !dispatcher.IsComplete ) - Thread.Sleep( 500 ); + Thread.Sleep( 1 ); } private static void WriteManagedStruct( string baseDir, ref List<(string Name, Method method)> methods ) From 73fc8a75c029bc2a2aaa2315a81266f1389827d7 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 9 Mar 2023 18:32:09 -0500 Subject: [PATCH 34/51] Tweak ThreadDispatcher namespace --- Source/MochaTool.InteropGen/ThreadDispatcher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/MochaTool.InteropGen/ThreadDispatcher.cs b/Source/MochaTool.InteropGen/ThreadDispatcher.cs index f29fca8d..6fc99860 100644 --- a/Source/MochaTool.InteropGen/ThreadDispatcher.cs +++ b/Source/MochaTool.InteropGen/ThreadDispatcher.cs @@ -1,4 +1,4 @@ -namespace Mocha.Common; +namespace MochaTool.InteropGen; public class ThreadDispatcher { From 9eece4e91c1a3eba92dd94fdba3a909199e35cf8 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Fri, 10 Mar 2023 08:31:09 -0500 Subject: [PATCH 35/51] Refactor code generators --- .../CodeGen/BaseCodeGenerator.cs | 23 ---- .../CodeGen/ManagedCodeGenerator.cs | 102 +++++++++--------- .../CodeGen/NativeCodeGenerator.cs | 30 ++++-- Source/MochaTool.InteropGen/Program.cs | 8 +- 4 files changed, 75 insertions(+), 88 deletions(-) delete mode 100644 Source/MochaTool.InteropGen/CodeGen/BaseCodeGenerator.cs diff --git a/Source/MochaTool.InteropGen/CodeGen/BaseCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/BaseCodeGenerator.cs deleted file mode 100644 index 6edabde6..00000000 --- a/Source/MochaTool.InteropGen/CodeGen/BaseCodeGenerator.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace MochaTool.InteropGen; - -internal class BaseCodeGenerator -{ - protected IEnumerable Units { get; } - - public BaseCodeGenerator( IEnumerable units ) - { - Units = units; - } - - protected static string Header => $""" - //------------------------------------------------------------------------------ - // - // 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. - // - //------------------------------------------------------------------------------ - """; -} diff --git a/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs index 8a86d3f0..d3d46f54 100644 --- a/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs +++ b/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs @@ -3,23 +3,64 @@ namespace MochaTool.InteropGen; -sealed class ManagedCodeGenerator : BaseCodeGenerator +internal static class ManagedCodeGenerator { - public ManagedCodeGenerator( IEnumerable units ) : base( units ) + private const string Namespace = "Mocha.Glue"; + private static readonly string[] Usings = new[] { - } - - private List GetUsings() + "System.Runtime.InteropServices", + "System.Runtime.Serialization", + "Mocha.Common" + }; + + private static string Header => $""" + //------------------------------------------------------------------------------ + // + // 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. + // + //------------------------------------------------------------------------------ + """; + + internal static string GenerateCode( IEnumerable units ) { - return new() { "System.Runtime.InteropServices", "System.Runtime.Serialization", "Mocha.Common" }; - } + var (baseTextWriter, writer) = Utils.CreateWriter(); - private string GetNamespace() - { - return "Mocha.Glue"; + writer.WriteLine( Header ); + writer.WriteLine(); + + foreach ( var usingStatement in Usings ) + writer.WriteLine( $"using {usingStatement};" ); + + writer.WriteLine(); + writer.WriteLine( $"namespace {Namespace};" ); + writer.WriteLine(); + + foreach ( var unit in units ) + { + if ( unit is Class c ) + { + if ( c.IsNamespace ) + GenerateNamespaceCode( writer, c ); + else + GenerateClassCode( writer, c ); + } + + if ( unit is Struct s ) + { + GenerateStructCode( writer, s ); + } + + writer.WriteLine(); + } + + return baseTextWriter.ToString(); } - private void GenerateClassCode( ref IndentedTextWriter writer, Class sel ) + private static void GenerateClassCode( IndentedTextWriter writer, Class sel ) { // // Gather everything we need into nice lists @@ -176,7 +217,7 @@ private void GenerateClassCode( ref IndentedTextWriter writer, Class sel ) writer.WriteLine( "}" ); } - private void GenerateStructCode( ref IndentedTextWriter writer, Struct sel ) + private static void GenerateStructCode( IndentedTextWriter writer, Struct sel ) { writer.WriteLine( $"[StructLayout( LayoutKind.Sequential )]" ); writer.WriteLine( $"public struct {sel.Name}" ); @@ -192,7 +233,7 @@ private void GenerateStructCode( ref IndentedTextWriter writer, Struct sel ) writer.WriteLine( "}" ); } - private void GenerateNamespaceCode( ref IndentedTextWriter writer, Class sel ) + private static void GenerateNamespaceCode( IndentedTextWriter writer, Class sel ) { // // Gather everything we need into nice lists @@ -294,39 +335,4 @@ private void GenerateNamespaceCode( ref IndentedTextWriter writer, Class sel ) writer.Indent--; writer.WriteLine( "}" ); } - - public string GenerateManagedCode() - { - var (baseTextWriter, writer) = Utils.CreateWriter(); - - writer.WriteLine( Header ); - 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 Struct s ) - { - GenerateStructCode( ref writer, s ); - } - - writer.WriteLine(); - } - - return baseTextWriter.ToString(); - } } diff --git a/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs index ed771253..78e6ffa8 100644 --- a/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs +++ b/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs @@ -3,13 +3,21 @@ namespace MochaTool.InteropGen; -sealed class NativeCodeGenerator : BaseCodeGenerator +internal static class NativeCodeGenerator { - public NativeCodeGenerator( IEnumerable units ) : base( units ) - { - } - - public string GenerateNativeCode( string headerPath ) + private static string Header => $""" + //------------------------------------------------------------------------------ + // + // 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. + // + //------------------------------------------------------------------------------ + """; + + internal static string GenerateCode( string headerPath, IEnumerable units ) { var (baseTextWriter, writer) = Utils.CreateWriter(); @@ -21,14 +29,14 @@ public string GenerateNativeCode( string headerPath ) writer.WriteLine(); - foreach ( var unit in Units ) + foreach ( var unit in units ) { if ( unit is Class c ) { if ( c.IsNamespace ) - GenerateNamespaceCode( ref writer, c ); + GenerateNamespaceCode( writer, c ); else - GenerateClassCode( ref writer, c ); + GenerateClassCode( writer, c ); } writer.WriteLine(); @@ -37,7 +45,7 @@ public string GenerateNativeCode( string headerPath ) return baseTextWriter.ToString(); } - private void GenerateNamespaceCode( ref IndentedTextWriter writer, Class c ) + private static void GenerateNamespaceCode( IndentedTextWriter writer, Class c ) { foreach ( var method in c.Methods ) { @@ -77,7 +85,7 @@ private void GenerateNamespaceCode( ref IndentedTextWriter writer, Class c ) } } - private void GenerateClassCode( ref IndentedTextWriter writer, Class c ) + private static void GenerateClassCode( IndentedTextWriter writer, Class c ) { foreach ( var method in c.Methods ) { diff --git a/Source/MochaTool.InteropGen/Program.cs b/Source/MochaTool.InteropGen/Program.cs index ff6a90ac..28ba87e7 100644 --- a/Source/MochaTool.InteropGen/Program.cs +++ b/Source/MochaTool.InteropGen/Program.cs @@ -12,15 +12,11 @@ private static void ProcessHeader( string baseDir, string path ) var units = Parser.GetUnits( path ); var fileName = Path.GetFileNameWithoutExtension( path ); - var managedGenerator = new ManagedCodeGenerator( units ); - var managedCode = managedGenerator.GenerateManagedCode(); + var managedCode = ManagedCodeGenerator.GenerateCode( units ); File.WriteAllText( $"{baseDir}Mocha.Common\\Glue\\{fileName}.generated.cs", managedCode ); - var nativeGenerator = new NativeCodeGenerator( units ); var relativePath = Path.GetRelativePath( $"{baseDir}/Mocha.Host/", path ); - var nativeCode = nativeGenerator.GenerateNativeCode( relativePath ); - - Console.WriteLine( $"{baseDir}Mocha.Host\\generated\\{fileName}.generated.h" ); + var nativeCode = NativeCodeGenerator.GenerateCode( relativePath, units ); File.WriteAllText( $"{baseDir}Mocha.Host\\generated\\{fileName}.generated.h", nativeCode ); s_files.Add( fileName ); From 7b77b44b0d3f02f0683c2089f6c574f6d7f7afaf Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Fri, 10 Mar 2023 08:32:02 -0500 Subject: [PATCH 36/51] Remove refs on reference type parameters --- Source/MochaTool.InteropGen/Program.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Source/MochaTool.InteropGen/Program.cs b/Source/MochaTool.InteropGen/Program.cs index 28ba87e7..cb7141c9 100644 --- a/Source/MochaTool.InteropGen/Program.cs +++ b/Source/MochaTool.InteropGen/Program.cs @@ -23,7 +23,7 @@ private static void ProcessHeader( string baseDir, string path ) s_units.AddRange( units ); } - private static void QueueDirectory( ref List queue, string directory ) + private static void QueueDirectory( List queue, string directory ) { foreach ( var file in Directory.GetFiles( directory ) ) { @@ -34,17 +34,17 @@ private static void QueueDirectory( ref List queue, string directory ) if ( !fileContents.Contains( "GENERATE_BINDINGS", StringComparison.CurrentCultureIgnoreCase ) ) continue; // Fast early bail - QueueFile( ref queue, file ); + QueueFile( queue, file ); } } foreach ( var subDirectory in Directory.GetDirectories( directory ) ) { - QueueDirectory( ref queue, subDirectory ); + QueueDirectory( queue, subDirectory ); } } - private static void QueueFile( ref List queue, string path ) + private static void QueueFile( List queue, string path ) { queue.Add( path ); } @@ -52,7 +52,7 @@ private static void QueueFile( ref List queue, string path ) private static void Parse( string baseDir ) { List queue = new(); - QueueDirectory( ref queue, baseDir ); + QueueDirectory( queue, baseDir ); var dispatcher = new ThreadDispatcher( ( files ) => { @@ -67,7 +67,7 @@ private static void Parse( string baseDir ) Thread.Sleep( 1 ); } - private static void WriteManagedStruct( string baseDir, ref List<(string Name, Method method)> methods ) + private static void WriteManagedStruct( string baseDir, List<(string Name, Method method)> methods ) { var (baseManagedStructWriter, managedStructWriter) = Utils.CreateWriter(); @@ -93,7 +93,7 @@ private static void WriteManagedStruct( string baseDir, ref List<(string Name, M File.WriteAllText( $"{baseDir}/Mocha.Common/Glue/UnmanagedArgs.cs", baseManagedStructWriter.ToString() ); } - private static void WriteNativeStruct( string baseDir, ref List<(string Name, Method method)> methods ) + private static void WriteNativeStruct( string baseDir, List<(string Name, Method method)> methods ) { var (baseNativeStructWriter, nativeStructWriter) = Utils.CreateWriter(); @@ -190,8 +190,8 @@ public static void Main( string[] args ) // // Write files // - WriteManagedStruct( baseDir, ref methods ); - WriteNativeStruct( baseDir, ref methods ); + WriteManagedStruct( baseDir, methods ); + WriteNativeStruct( baseDir, methods ); WriteNativeIncludes( baseDir ); // Track time & output total duration From b594a593f976cd42655ad5e0429aad9f7e5aeebd Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Fri, 10 Mar 2023 09:19:05 -0500 Subject: [PATCH 37/51] Lots of cleanup --- .../CodeGen/ManagedCodeGenerator.cs | 24 ++- .../CodeGen/NativeCodeGenerator.cs | 42 ++--- Source/MochaTool.InteropGen/Program.cs | 153 +++++++++--------- 3 files changed, 100 insertions(+), 119 deletions(-) diff --git a/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs index d3d46f54..6baa362e 100644 --- a/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs +++ b/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs @@ -41,17 +41,19 @@ internal static string GenerateCode( IEnumerable units ) foreach ( var unit in units ) { - if ( unit is Class c ) + switch ( unit ) { - if ( c.IsNamespace ) + case Class c when c.IsNamespace: GenerateNamespaceCode( writer, c ); - else + break; + case Class c: GenerateClassCode( writer, c ); - } - - if ( unit is Struct s ) - { - GenerateStructCode( writer, s ); + break; + case Struct s: + GenerateStructCode( writer, s ); + break; + default: + continue; } writer.WriteLine(); @@ -113,9 +115,7 @@ private static void GenerateClassCode( IndentedTextWriter writer, Class sel ) // Decls writer.WriteLine(); foreach ( var decl in decls ) - { writer.WriteLine( decl ); - } writer.WriteLine(); // Ctor @@ -225,9 +225,7 @@ private static void GenerateStructCode( IndentedTextWriter writer, Struct sel ) writer.Indent++; foreach ( var field in sel.Fields ) - { writer.WriteLine( $"public {Utils.GetManagedType( field.Type )} {field.Name};" ); - } writer.Indent--; writer.WriteLine( "}" ); @@ -274,9 +272,7 @@ private static void GenerateNamespaceCode( IndentedTextWriter writer, Class sel writer.WriteLine(); foreach ( var decl in decls ) - { writer.WriteLine( decl ); - } // Methods foreach ( var method in sel.Methods ) diff --git a/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs index 78e6ffa8..72467e9b 100644 --- a/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs +++ b/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs @@ -31,13 +31,13 @@ internal static string GenerateCode( string headerPath, IEnumerable units foreach ( var unit in units ) { - if ( unit is Class c ) - { - if ( c.IsNamespace ) - GenerateNamespaceCode( writer, c ); - else - GenerateClassCode( writer, c ); - } + if ( unit is not Class c ) + continue; + + if ( c.IsNamespace ) + GenerateNamespaceCode( writer, c ); + else + GenerateClassCode( writer, c ); writer.WriteLine(); } @@ -54,25 +54,23 @@ private static void GenerateNamespaceCode( IndentedTextWriter writer, Class c ) var argStr = string.Join( ", ", args.Select( x => { if ( x.Type == "std::string" ) - { return $"const char* {x.Name}"; - } return $"{x.Type} {x.Name}"; } ) ); var signature = $"extern \"C\" inline {method.ReturnType} __{c.Name}_{method.Name}( {argStr} )"; var body = ""; - var @params = string.Join( ", ", method.Parameters.Select( x => x.Name ) ); + var parameters = string.Join( ", ", method.Parameters.Select( x => x.Name ) ); var accessor = $"{c.Name}::"; if ( method.ReturnType == "void" ) - body += $"{accessor}{method.Name}( {@params} );"; + body += $"{accessor}{method.Name}( {parameters} );"; else if ( method.ReturnType == "std::string" ) - body += $"std::string text = {accessor}{method.Name}( {@params} );\r\nconst char* cstr = text.c_str();\r\nchar* dup = _strdup(cstr);\r\nreturn dup;"; + body += $"std::string text = {accessor}{method.Name}( {parameters} );\r\nconst char* cstr = text.c_str();\r\nchar* dup = _strdup(cstr);\r\nreturn dup;"; else - body += $"return {accessor}{method.Name}( {@params} );"; + body += $"return {accessor}{method.Name}( {parameters} );"; writer.WriteLine( signature ); writer.WriteLine( "{" ); @@ -97,35 +95,29 @@ private static void GenerateClassCode( IndentedTextWriter writer, Class c ) var argStr = string.Join( ", ", args.Select( x => { if ( x.Type == "std::string" ) - { return $"const char* {x.Name}"; - } return $"{x.Type} {x.Name}"; } ) ); var signature = $"extern \"C\" inline {method.ReturnType} __{c.Name}_{method.Name}( {argStr} )"; var body = ""; - var @params = string.Join( ", ", method.Parameters.Select( x => x.Name ) ); + var parameters = string.Join( ", ", method.Parameters.Select( x => x.Name ) ); if ( method.IsConstructor ) - { - body += $"return new {c.Name}( {@params} );"; - } + body += $"return new {c.Name}( {parameters} );"; else if ( method.IsDestructor ) - { - body += $"instance->~{c.Name}( {@params} );"; - } + body += $"instance->~{c.Name}( {parameters} );"; else { var accessor = method.IsStatic ? $"{c.Name}::" : "instance->"; if ( method.ReturnType == "void" ) - body += $"{accessor}{method.Name}( {@params} );"; + body += $"{accessor}{method.Name}( {parameters} );"; else if ( method.ReturnType == "std::string" ) - body += $"std::string text = {accessor}{method.Name}( {@params} );\r\nconst char* cstr = text.c_str();\r\nchar* dup = _strdup(cstr);\r\nreturn dup;"; + body += $"std::string text = {accessor}{method.Name}( {parameters} );\r\nconst char* cstr = text.c_str();\r\nchar* dup = _strdup(cstr);\r\nreturn dup;"; else - body += $"return {accessor}{method.Name}( {@params} );"; + body += $"return {accessor}{method.Name}( {parameters} );"; } writer.WriteLine( signature ); diff --git a/Source/MochaTool.InteropGen/Program.cs b/Source/MochaTool.InteropGen/Program.cs index cb7141c9..7ba36f3d 100644 --- a/Source/MochaTool.InteropGen/Program.cs +++ b/Source/MochaTool.InteropGen/Program.cs @@ -5,48 +5,49 @@ public static class Program internal static List s_units { get; set; } = new(); internal static List s_files { get; set; } = new(); - private static void ProcessHeader( string baseDir, string path ) + public static void Main( string[] args ) { - Console.WriteLine( $"Processing header {path}..." ); + var baseDir = args[0]; + var start = DateTime.Now; - var units = Parser.GetUnits( path ); - var fileName = Path.GetFileNameWithoutExtension( path ); + Console.WriteLine( "Generating C# <--> C++ interop code..." ); - var managedCode = ManagedCodeGenerator.GenerateCode( units ); - File.WriteAllText( $"{baseDir}Mocha.Common\\Glue\\{fileName}.generated.cs", managedCode ); + // + // Prep + // + DeleteExistingFiles( baseDir ); + Parse( baseDir ); - var relativePath = Path.GetRelativePath( $"{baseDir}/Mocha.Host/", path ); - var nativeCode = NativeCodeGenerator.GenerateCode( relativePath, units ); - File.WriteAllText( $"{baseDir}Mocha.Host\\generated\\{fileName}.generated.h", nativeCode ); + // + // Expand methods out into list of (method name, method) + // + var methods = s_units.OfType().SelectMany( unit => unit.Methods, ( unit, method ) => (unit.Name, method) ).ToList(); - s_files.Add( fileName ); - s_units.AddRange( units ); + // + // Write files + // + WriteManagedStruct( baseDir, methods ); + WriteNativeStruct( baseDir, methods ); + WriteNativeIncludes( baseDir ); + + // Track time & output total duration + var end = DateTime.Now; + var totalTime = end - start; + Console.WriteLine( $"-- Took {totalTime.TotalSeconds} seconds." ); } - private static void QueueDirectory( List queue, string directory ) + private static void DeleteExistingFiles( string baseDir ) { - foreach ( var file in Directory.GetFiles( directory ) ) - { - if ( file.EndsWith( ".h" ) && !file.EndsWith( ".generated.h" ) ) - { - var fileContents = File.ReadAllText( file ); - - if ( !fileContents.Contains( "GENERATE_BINDINGS", StringComparison.CurrentCultureIgnoreCase ) ) - continue; // Fast early bail - - QueueFile( queue, file ); - } - } + var destCsDir = $"{baseDir}\\Mocha.Common\\Glue\\"; + var destHeaderDir = $"{baseDir}\\Mocha.Host\\generated\\"; - foreach ( var subDirectory in Directory.GetDirectories( directory ) ) - { - QueueDirectory( queue, subDirectory ); - } - } + if ( Directory.Exists( destHeaderDir ) ) + Directory.Delete( destHeaderDir, true ); + if ( Directory.Exists( destCsDir ) ) + Directory.Delete( destCsDir, true ); - private static void QueueFile( List queue, string path ) - { - queue.Add( path ); + Directory.CreateDirectory( destHeaderDir ); + Directory.CreateDirectory( destCsDir ); } private static void Parse( string baseDir ) @@ -54,12 +55,10 @@ private static void Parse( string baseDir ) List queue = new(); QueueDirectory( queue, baseDir ); - var dispatcher = new ThreadDispatcher( ( files ) => + var dispatcher = new ThreadDispatcher( async ( files ) => { foreach ( var path in files ) - { - ProcessHeader( baseDir, path ); - } + await ProcessHeaderAsync( baseDir, path ); }, queue ); // Wait for all threads to finish... @@ -71,11 +70,11 @@ private static void WriteManagedStruct( string baseDir, List<(string Name, Metho { var (baseManagedStructWriter, managedStructWriter) = Utils.CreateWriter(); - managedStructWriter.WriteLine( $"using System.Runtime.InteropServices;" ); + managedStructWriter.WriteLine( "using System.Runtime.InteropServices;" ); managedStructWriter.WriteLine(); - managedStructWriter.WriteLine( $"[StructLayout( LayoutKind.Sequential )]" ); - managedStructWriter.WriteLine( $"public struct UnmanagedArgs" ); - managedStructWriter.WriteLine( $"{{" ); + managedStructWriter.WriteLine( "[StructLayout( LayoutKind.Sequential )]" ); + managedStructWriter.WriteLine( "public struct UnmanagedArgs" ); + managedStructWriter.WriteLine( '{' ); managedStructWriter.Indent++; @@ -87,7 +86,7 @@ private static void WriteManagedStruct( string baseDir, List<(string Name, Metho managedStructWriter.Indent--; - managedStructWriter.WriteLine( $"}}" ); + managedStructWriter.WriteLine( '}' ); managedStructWriter.Dispose(); File.WriteAllText( $"{baseDir}/Mocha.Common/Glue/UnmanagedArgs.cs", baseManagedStructWriter.ToString() ); @@ -102,7 +101,7 @@ private static void WriteNativeStruct( string baseDir, List<(string Name, Method nativeStructWriter.WriteLine( "#include \"InteropList.generated.h\"" ); nativeStructWriter.WriteLine(); nativeStructWriter.WriteLine( "struct UnmanagedArgs" ); - nativeStructWriter.WriteLine( $"{{" ); + nativeStructWriter.WriteLine( '{' ); nativeStructWriter.Indent++; nativeStructWriter.WriteLine( "void* __Root;" ); @@ -112,11 +111,11 @@ private static void WriteNativeStruct( string baseDir, List<(string Name, Method nativeStructWriter.WriteLine(); nativeStructWriter.Indent--; - nativeStructWriter.WriteLine( $"}};" ); + nativeStructWriter.WriteLine( "};" ); nativeStructWriter.WriteLine(); nativeStructWriter.WriteLine( "inline UnmanagedArgs args" ); - nativeStructWriter.WriteLine( $"{{" ); + nativeStructWriter.WriteLine( '{' ); nativeStructWriter.Indent++; nativeStructWriter.WriteLine( "Root::GetInstance()," ); @@ -126,10 +125,10 @@ private static void WriteNativeStruct( string baseDir, List<(string Name, Method nativeStructWriter.WriteLine(); nativeStructWriter.Indent--; - nativeStructWriter.WriteLine( $"}};" ); + nativeStructWriter.WriteLine( "};" ); nativeStructWriter.WriteLine(); - nativeStructWriter.WriteLine( $"#endif // __GENERATED_UNMANAGED_ARGS_H" ); + nativeStructWriter.WriteLine( "#endif // __GENERATED_UNMANAGED_ARGS_H" ); nativeStructWriter.Dispose(); File.WriteAllText( $"{baseDir}Mocha.Host\\generated\\UnmanagedArgs.generated.h", baseNativeStructWriter.ToString() ); @@ -155,48 +154,42 @@ private static void WriteNativeIncludes( string baseDir ) File.WriteAllText( $"{baseDir}Mocha.Host\\generated\\InteropList.generated.h", baseNativeListWriter.ToString() ); } - private static void DeleteExistingFiles( string baseDir ) + private static async Task ProcessHeaderAsync( string baseDir, string path ) { - var destCsDir = $"{baseDir}\\Mocha.Common\\Glue\\"; - var destHeaderDir = $"{baseDir}\\Mocha.Host\\generated\\"; + Console.WriteLine( $"Processing header {path}..." ); - if ( Directory.Exists( destHeaderDir ) ) - Directory.Delete( destHeaderDir, true ); - if ( Directory.Exists( destCsDir ) ) - Directory.Delete( destCsDir, true ); + var units = Parser.GetUnits( path ); + var fileName = Path.GetFileNameWithoutExtension( path ); - Directory.CreateDirectory( destHeaderDir ); - Directory.CreateDirectory( destCsDir ); - } + var managedCode = ManagedCodeGenerator.GenerateCode( units ); - public static void Main( string[] args ) - { - var baseDir = args[0]; - var start = DateTime.Now; + var relativePath = Path.GetRelativePath( $"{baseDir}/Mocha.Host/", path ); + var nativeCode = NativeCodeGenerator.GenerateCode( relativePath, units ); - Console.WriteLine( "Generating C# <--> C++ interop code..." ); + var csTask = File.WriteAllTextAsync( $"{baseDir}Mocha.Common\\Glue\\{fileName}.generated.cs", managedCode ); + var nativeTask = File.WriteAllTextAsync( $"{baseDir}Mocha.Host\\generated\\{fileName}.generated.h", nativeCode ); - // - // Prep - // - DeleteExistingFiles( baseDir ); - Parse( baseDir ); + await Task.WhenAll( csTask, nativeTask ); - // - // Expand methods out into list of (method name, method) - // - var methods = s_units.OfType().SelectMany( unit => unit.Methods, ( unit, method ) => (unit.Name, method) ).ToList(); + s_files.Add( fileName ); + s_units.AddRange( units ); + } - // - // Write files - // - WriteManagedStruct( baseDir, methods ); - WriteNativeStruct( baseDir, methods ); - WriteNativeIncludes( baseDir ); + private static void QueueDirectory( ICollection queue, string directory ) + { + foreach ( var file in Directory.GetFiles( directory ) ) + { + if ( !file.EndsWith( ".h" ) || file.EndsWith( ".generated.h" ) ) + continue; - // Track time & output total duration - var end = DateTime.Now; - var totalTime = end - start; - Console.WriteLine( $"-- Took {totalTime.TotalSeconds} seconds." ); + var fileContents = File.ReadAllText( file ); + if ( !fileContents.Contains( "GENERATE_BINDINGS", StringComparison.CurrentCultureIgnoreCase ) ) + continue; // Fast early bail + + queue.Add( file ); + } + + foreach ( var subDirectory in Directory.GetDirectories( directory ) ) + QueueDirectory( queue, subDirectory ); } } From d5bdcf1df33af30dc49962884d8098fe35329d23 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Mon, 13 Mar 2023 11:02:32 -0400 Subject: [PATCH 38/51] Internalize/privatize everything --- Source/MochaTool.InteropGen/Parser.cs | 13 +++++++--- Source/MochaTool.InteropGen/Program.cs | 4 +-- .../MochaTool.InteropGen/ThreadDispatcher.cs | 12 ++++----- Source/MochaTool.InteropGen/Units/Class.cs | 12 ++++----- Source/MochaTool.InteropGen/Units/IUnit.cs | 2 +- Source/MochaTool.InteropGen/Units/Method.cs | 25 +++++++++---------- Source/MochaTool.InteropGen/Units/Struct.cs | 10 ++++---- Source/MochaTool.InteropGen/Units/Variable.cs | 8 +++--- Source/MochaTool.InteropGen/Utils.cs | 8 +++--- Source/MochaTool.InteropGen/VcxprojParser.cs | 2 +- 10 files changed, 51 insertions(+), 45 deletions(-) diff --git a/Source/MochaTool.InteropGen/Parser.cs b/Source/MochaTool.InteropGen/Parser.cs index a68c3b79..4681a70d 100644 --- a/Source/MochaTool.InteropGen/Parser.cs +++ b/Source/MochaTool.InteropGen/Parser.cs @@ -3,7 +3,7 @@ namespace MochaTool.InteropGen; -public static class Parser +internal static class Parser { /// /// Cached launch arguments so that we don't have to regenerate them every time @@ -30,7 +30,7 @@ private static string[] GetLaunchArgs() return args.ToArray(); } - public unsafe static IEnumerable GetUnits( string path ) + internal unsafe static IEnumerable GetUnits( string path ) { var units = new List(); @@ -134,7 +134,14 @@ CXChildVisitResult methodChildVisitor( CXCursor cursor, CXCursor parent, void* d cursor.VisitChildren( methodChildVisitor, default ); - var method = new Method( name, returnType, isConstructor, isDestructor, isStatic, parametersBuilder.ToImmutable() ); + Method method; + if ( isConstructor ) + method = Method.NewConstructor( name, returnType, parametersBuilder.ToImmutable() ); + else if ( isDestructor ) + method = Method.NewDestructor( name, returnType, parametersBuilder.ToImmutable() ); + else + method = Method.NewMethod( name, returnType, isStatic, parametersBuilder.ToImmutable() ); + var newOwner = owner.WithMethods( owner.Methods.Add( method ) ); units.Remove( owner ); units.Add( newOwner ); diff --git a/Source/MochaTool.InteropGen/Program.cs b/Source/MochaTool.InteropGen/Program.cs index 7ba36f3d..53398eb4 100644 --- a/Source/MochaTool.InteropGen/Program.cs +++ b/Source/MochaTool.InteropGen/Program.cs @@ -2,8 +2,8 @@ public static class Program { - internal static List s_units { get; set; } = new(); - internal static List s_files { get; set; } = new(); + private static List s_units { get; set; } = new(); + private static List s_files { get; set; } = new(); public static void Main( string[] args ) { diff --git a/Source/MochaTool.InteropGen/ThreadDispatcher.cs b/Source/MochaTool.InteropGen/ThreadDispatcher.cs index 6fc99860..2c46e44f 100644 --- a/Source/MochaTool.InteropGen/ThreadDispatcher.cs +++ b/Source/MochaTool.InteropGen/ThreadDispatcher.cs @@ -1,21 +1,21 @@ namespace MochaTool.InteropGen; -public class ThreadDispatcher +internal class ThreadDispatcher { - public delegate void ThreadCallback( List threadQueue ); - public delegate Task AsyncThreadCallback( List threadQueue ); + internal delegate void ThreadCallback( List threadQueue ); + internal delegate Task AsyncThreadCallback( List threadQueue ); - public bool IsComplete => _threadsCompleted >= _threadCount; + internal bool IsComplete => _threadsCompleted >= _threadCount; private int _threadCount = (int)Math.Ceiling( Environment.ProcessorCount * 0.75 ); private int _threadsCompleted = 0; - public ThreadDispatcher( ThreadCallback threadStart, List queue ) + internal ThreadDispatcher( ThreadCallback threadStart, List queue ) { Setup( queue, threadQueue => threadStart( threadQueue ) ); } - public ThreadDispatcher( AsyncThreadCallback threadStart, List queue ) + internal ThreadDispatcher( AsyncThreadCallback threadStart, List queue ) { Setup( queue, threadQueue => threadStart( threadQueue ).Wait() ); } diff --git a/Source/MochaTool.InteropGen/Units/Class.cs b/Source/MochaTool.InteropGen/Units/Class.cs index 9233c305..48acc488 100644 --- a/Source/MochaTool.InteropGen/Units/Class.cs +++ b/Source/MochaTool.InteropGen/Units/Class.cs @@ -2,7 +2,7 @@ namespace MochaTool.InteropGen; -public sealed class Class : IUnit +internal sealed class Class : IUnit { public string Name { get; } public bool IsNamespace { get; } @@ -10,7 +10,7 @@ public sealed class Class : IUnit public ImmutableArray Fields { get; } public ImmutableArray Methods { get; } - public Class( string name, bool isNamespace, in ImmutableArray fields, in ImmutableArray methods ) + private Class( string name, bool isNamespace, in ImmutableArray fields, in ImmutableArray methods ) { Name = name; IsNamespace = isNamespace; @@ -19,12 +19,12 @@ public Class( string name, bool isNamespace, in ImmutableArray fields, Methods = methods; } - public Class WithFields( in ImmutableArray fields ) + internal Class WithFields( in ImmutableArray fields ) { return new Class( Name, IsNamespace, fields, Methods ); } - public Class WithMethods( in ImmutableArray methods ) + internal Class WithMethods( in ImmutableArray methods ) { return new Class( Name, IsNamespace, Fields, methods ); } @@ -37,12 +37,12 @@ public override string ToString() IUnit IUnit.WithFields( in ImmutableArray fields ) => WithFields( fields ); IUnit IUnit.WithMethods( in ImmutableArray methods ) => WithMethods( methods ); - public static Class NewClass( string name, in ImmutableArray fields, in ImmutableArray methods ) + internal static Class NewClass( string name, in ImmutableArray fields, in ImmutableArray methods ) { return new Class( name, false, fields, methods ); } - public static Class NewNamespace( string name, in ImmutableArray fields, in ImmutableArray methods ) + internal static Class NewNamespace( string name, in ImmutableArray fields, in ImmutableArray methods ) { return new Class( name, true, fields, methods ); } diff --git a/Source/MochaTool.InteropGen/Units/IUnit.cs b/Source/MochaTool.InteropGen/Units/IUnit.cs index 981bc26e..58a129b8 100644 --- a/Source/MochaTool.InteropGen/Units/IUnit.cs +++ b/Source/MochaTool.InteropGen/Units/IUnit.cs @@ -2,7 +2,7 @@ namespace MochaTool.InteropGen; -public interface IUnit +internal interface IUnit { string Name { get; } diff --git a/Source/MochaTool.InteropGen/Units/Method.cs b/Source/MochaTool.InteropGen/Units/Method.cs index 0f84022c..e4990c78 100644 --- a/Source/MochaTool.InteropGen/Units/Method.cs +++ b/Source/MochaTool.InteropGen/Units/Method.cs @@ -2,18 +2,18 @@ namespace MochaTool.InteropGen; -public sealed class Method +internal sealed class Method { - public string Name { get; } - public string ReturnType { get; } + internal string Name { get; } + internal string ReturnType { get; } - public bool IsConstructor { get; } = false; - public bool IsDestructor { get; } = false; - public bool IsStatic { get; } = false; + internal bool IsConstructor { get; } = false; + internal bool IsDestructor { get; } = false; + internal bool IsStatic { get; } = false; - public ImmutableArray Parameters { get; } + internal ImmutableArray Parameters { get; } - public Method( string name, string returnType, bool isConstructor, bool isDestructor, bool isStatic, in ImmutableArray parameters ) + private Method( string name, string returnType, bool isConstructor, bool isDestructor, bool isStatic, in ImmutableArray parameters ) { Name = name; ReturnType = returnType; @@ -24,8 +24,7 @@ public Method( string name, string returnType, bool isConstructor, bool isDestru Parameters = parameters; } - - public Method WithParameters( in ImmutableArray parameters ) + internal Method WithParameters( in ImmutableArray parameters ) { return new( Name, ReturnType, IsConstructor, IsDestructor, IsStatic, parameters ); } @@ -36,17 +35,17 @@ public override string ToString() return $"{ReturnType} {Name}( {p} )"; } - public static Method NewConstructor( string name, string returnType, in ImmutableArray parameters ) + internal static Method NewConstructor( string name, string returnType, in ImmutableArray parameters ) { return new( name, returnType, true, false, false, parameters ); } - public static Method NewDestructor( string name, string returnType, in ImmutableArray parameters ) + internal static Method NewDestructor( string name, string returnType, in ImmutableArray parameters ) { return new( name, returnType, false, true, false, parameters ); } - public static Method NewMethod( string name, string returnType, bool isStatic, in ImmutableArray parameters ) + internal static Method NewMethod( string name, string returnType, bool isStatic, in ImmutableArray parameters ) { return new( name, returnType, false, false, isStatic, parameters ); } diff --git a/Source/MochaTool.InteropGen/Units/Struct.cs b/Source/MochaTool.InteropGen/Units/Struct.cs index e05c81ac..28bad670 100644 --- a/Source/MochaTool.InteropGen/Units/Struct.cs +++ b/Source/MochaTool.InteropGen/Units/Struct.cs @@ -2,14 +2,14 @@ namespace MochaTool.InteropGen; -public sealed class Struct : IUnit +internal sealed class Struct : IUnit { public string Name { get; } public ImmutableArray Fields { get; } public ImmutableArray Methods { get; } - public Struct( string name, in ImmutableArray fields, in ImmutableArray methods ) + private Struct( string name, in ImmutableArray fields, in ImmutableArray methods ) { Name = name; @@ -17,12 +17,12 @@ public Struct( string name, in ImmutableArray fields, in ImmutableArra Methods = methods; } - public Struct WithFields( in ImmutableArray fields ) + internal Struct WithFields( in ImmutableArray fields ) { return new( Name, fields, Methods ); } - public Struct WithMethods( in ImmutableArray methods ) + internal Struct WithMethods( in ImmutableArray methods ) { return new( Name, Fields, methods ); } @@ -35,7 +35,7 @@ public override string ToString() IUnit IUnit.WithFields( in ImmutableArray fields ) => WithFields( fields ); IUnit IUnit.WithMethods( in ImmutableArray methods ) => WithMethods( methods ); - public static Struct NewStructure( string name, in ImmutableArray fields, in ImmutableArray methods ) + internal static Struct NewStructure( string name, in ImmutableArray fields, in ImmutableArray methods ) { return new( name, fields, methods ); } diff --git a/Source/MochaTool.InteropGen/Units/Variable.cs b/Source/MochaTool.InteropGen/Units/Variable.cs index 635b29e6..7134d56b 100644 --- a/Source/MochaTool.InteropGen/Units/Variable.cs +++ b/Source/MochaTool.InteropGen/Units/Variable.cs @@ -1,11 +1,11 @@ namespace MochaTool.InteropGen; -public sealed class Variable +internal sealed class Variable { - public string Name { get; } - public string Type { get; } + internal string Name { get; } + internal string Type { get; } - public Variable( string name, string type ) + internal Variable( string name, string type ) { Name = name; Type = type; diff --git a/Source/MochaTool.InteropGen/Utils.cs b/Source/MochaTool.InteropGen/Utils.cs index 77f528d1..ac62a084 100644 --- a/Source/MochaTool.InteropGen/Utils.cs +++ b/Source/MochaTool.InteropGen/Utils.cs @@ -2,15 +2,15 @@ namespace MochaTool.InteropGen; -static class Utils +internal static class Utils { - public static bool IsPointer( string nativeType ) + internal static bool IsPointer( string nativeType ) { var managedType = GetManagedType( nativeType ); return nativeType.Trim().EndsWith( "*" ) && managedType != "string" && managedType != "IntPtr"; } - public static string GetManagedType( string nativeType ) + internal static string GetManagedType( string nativeType ) { // Trim whitespace from beginning / end (if it exists) nativeType = nativeType.Trim(); @@ -77,7 +77,7 @@ public static string GetManagedType( string nativeType ) return nativeType; } - public static (StringWriter StringWriter, IndentedTextWriter TextWriter) CreateWriter() + internal static (StringWriter StringWriter, IndentedTextWriter TextWriter) CreateWriter() { var baseTextWriter = new StringWriter(); diff --git a/Source/MochaTool.InteropGen/VcxprojParser.cs b/Source/MochaTool.InteropGen/VcxprojParser.cs index 4ddaef8f..a7da67b5 100644 --- a/Source/MochaTool.InteropGen/VcxprojParser.cs +++ b/Source/MochaTool.InteropGen/VcxprojParser.cs @@ -31,7 +31,7 @@ private static string GetNodeContents( XmlNode root, string xpath, XmlNamespaceM /// This currently only supports x64-windows, so any different includes for other platforms /// will not be reflected here. /// - public static List ParseIncludes( string path ) + internal static List ParseIncludes( string path ) { XmlDocument doc = new XmlDocument(); doc.Load( path ); From 282e839fede3466c146e6d7a5062b9dd1425e4e1 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Mon, 13 Mar 2023 11:03:38 -0400 Subject: [PATCH 39/51] Documentation pass on Unit items --- Source/MochaTool.InteropGen/Units/Class.cs | 41 +++++++++++++ Source/MochaTool.InteropGen/Units/IUnit.cs | 22 +++++++ Source/MochaTool.InteropGen/Units/Method.cs | 59 +++++++++++++++++++ Source/MochaTool.InteropGen/Units/Struct.cs | 32 ++++++++++ Source/MochaTool.InteropGen/Units/Variable.cs | 15 +++++ 5 files changed, 169 insertions(+) diff --git a/Source/MochaTool.InteropGen/Units/Class.cs b/Source/MochaTool.InteropGen/Units/Class.cs index 48acc488..7a8b6b5c 100644 --- a/Source/MochaTool.InteropGen/Units/Class.cs +++ b/Source/MochaTool.InteropGen/Units/Class.cs @@ -2,14 +2,28 @@ namespace MochaTool.InteropGen; +/// +/// Represents a class or namespace in C++. +/// internal sealed class Class : IUnit { + /// public string Name { get; } + /// public bool IsNamespace { get; } + /// public ImmutableArray Fields { get; } + /// public ImmutableArray Methods { get; } + /// + /// Initializes a new instance of . + /// + /// The name of the class or namespace. + /// Whether or not it is a class or namespace. + /// All of the fields that are contained. + /// All of the methods that are contained. private Class( string name, bool isNamespace, in ImmutableArray fields, in ImmutableArray methods ) { Name = name; @@ -19,29 +33,56 @@ private Class( string name, bool isNamespace, in ImmutableArray fields Methods = methods; } + /// + /// Returns a new instance of the with the fields given. + /// + /// The new fields to place in the instance. + /// A new instance of the with the fields given. internal Class WithFields( in ImmutableArray fields ) { return new Class( Name, IsNamespace, fields, Methods ); } + /// + /// Returns a new instance of the with the methods given. + /// + /// The new methods to place in the instance. + /// A new instance of the with the methods given. internal Class WithMethods( in ImmutableArray methods ) { return new Class( Name, IsNamespace, Fields, methods ); } + /// public override string ToString() { return Name; } + /// IUnit IUnit.WithFields( in ImmutableArray fields ) => WithFields( fields ); + /// IUnit IUnit.WithMethods( in ImmutableArray methods ) => WithMethods( methods ); + /// + /// Returns a new instance of . + /// + /// The name of the class. + /// The fields contained in the class. + /// The methods contained in the class. + /// A new instance of . internal static Class NewClass( string name, in ImmutableArray fields, in ImmutableArray methods ) { return new Class( name, false, fields, methods ); } + /// + /// Returns a new instance of as a namespace. + /// + /// The name of the namespace. + /// The fields contained in the namespace. + /// The methods contained in the namespace. + /// A new instance of as a namespace. internal static Class NewNamespace( string name, in ImmutableArray fields, in ImmutableArray methods ) { return new Class( name, true, fields, methods ); diff --git a/Source/MochaTool.InteropGen/Units/IUnit.cs b/Source/MochaTool.InteropGen/Units/IUnit.cs index 58a129b8..ee915883 100644 --- a/Source/MochaTool.InteropGen/Units/IUnit.cs +++ b/Source/MochaTool.InteropGen/Units/IUnit.cs @@ -2,13 +2,35 @@ namespace MochaTool.InteropGen; +/// +/// Defines a container for fields and methods defined in C++. +/// internal interface IUnit { + /// + /// The name of the . + /// string Name { get; } + /// + /// All of the fields contained in the . + /// ImmutableArray Fields { get; } + /// + /// All of the methods contained in the . + /// ImmutableArray Methods { get; } + /// + /// Returns a new instance of the with the fields given. + /// + /// The new fields to place in the instance. + /// A new instance of the with the fields given. IUnit WithFields( in ImmutableArray fields ); + /// + /// Returns a new instance of the with the methods given. + /// + /// The new methods to place in the instance. + /// A new instance of the with the methods given. IUnit WithMethods( in ImmutableArray methods ); } diff --git a/Source/MochaTool.InteropGen/Units/Method.cs b/Source/MochaTool.InteropGen/Units/Method.cs index e4990c78..119549c1 100644 --- a/Source/MochaTool.InteropGen/Units/Method.cs +++ b/Source/MochaTool.InteropGen/Units/Method.cs @@ -2,17 +2,47 @@ namespace MochaTool.InteropGen; +/// +/// Represents a method in C++. +/// internal sealed class Method { + /// + /// The name of the method. + /// internal string Name { get; } + /// + /// The literal string containing the return type of the method. + /// internal string ReturnType { get; } + /// + /// Whether or not the method is a constructor. + /// internal bool IsConstructor { get; } = false; + /// + /// Whether or not the method is a destructor. + /// internal bool IsDestructor { get; } = false; + /// + /// Whether or not the method is static. + /// internal bool IsStatic { get; } = false; + /// + /// An array of all the parameters in the method. + /// internal ImmutableArray Parameters { get; } + /// + /// Initializes a new instance of . + /// + /// The name of the method. + /// The literal string containing the return type of the method. + /// Whether or not the method is a constructor. + /// Whether or not the method is a destructor. + /// Whether or not the method is static. + /// An array of all the parameters in the method. private Method( string name, string returnType, bool isConstructor, bool isDestructor, bool isStatic, in ImmutableArray parameters ) { Name = name; @@ -24,27 +54,56 @@ private Method( string name, string returnType, bool isConstructor, bool isDestr Parameters = parameters; } + + /// + /// Returns a new instance of the with the parameters given. + /// + /// The new fields to place in the instance. + /// A new instance of the with the parameters given. internal Method WithParameters( in ImmutableArray parameters ) { return new( Name, ReturnType, IsConstructor, IsDestructor, IsStatic, parameters ); } + /// public override string ToString() { var p = string.Join( ", ", Parameters ); return $"{ReturnType} {Name}( {p} )"; } + /// + /// Returns a new instance of as a constructor. + /// + /// The name of the method. + /// The literal string containing the return type of the method. + /// An array of all the parameters in the method. + /// A new instance of as a constructor. internal static Method NewConstructor( string name, string returnType, in ImmutableArray parameters ) { return new( name, returnType, true, false, false, parameters ); } + /// + /// Returns a new instance of as a destructor. + /// + /// The name of the method. + /// The literal string containing the return type of the method. + /// An array of all the parameters in the method. + /// A new instance of as a destructor. internal static Method NewDestructor( string name, string returnType, in ImmutableArray parameters ) { return new( name, returnType, false, true, false, parameters ); } + /// + /// Returns a new instance of . + /// + /// The name of the method. + /// The literal string containing the return type of the method. + /// Whether or not the method is static. + /// An array of all the parameters in the method. + /// A new instance of . internal static Method NewMethod( string name, string returnType, bool isStatic, in ImmutableArray parameters ) { return new( name, returnType, false, false, isStatic, parameters ); diff --git a/Source/MochaTool.InteropGen/Units/Struct.cs b/Source/MochaTool.InteropGen/Units/Struct.cs index 28bad670..7543692f 100644 --- a/Source/MochaTool.InteropGen/Units/Struct.cs +++ b/Source/MochaTool.InteropGen/Units/Struct.cs @@ -2,13 +2,25 @@ namespace MochaTool.InteropGen; +/// +/// Represents a struct in C++. +/// internal sealed class Struct : IUnit { + /// public string Name { get; } + /// public ImmutableArray Fields { get; } + /// public ImmutableArray Methods { get; } + /// + /// Initializes a new instance of . + /// + /// The name of the struct. + /// The fields contained in the struct. + /// The methods contained in the struct. private Struct( string name, in ImmutableArray fields, in ImmutableArray methods ) { Name = name; @@ -17,24 +29,44 @@ private Struct( string name, in ImmutableArray fields, in ImmutableArr Methods = methods; } + /// + /// Returns a new instance of the with the fields given. + /// + /// The new fields to place in the instance. + /// A new instance of the with the fields given. internal Struct WithFields( in ImmutableArray fields ) { return new( Name, fields, Methods ); } + /// + /// Returns a new instance of the with the methods given. + /// + /// The new methods to place in the instance. + /// A new instance of the with the methods given. internal Struct WithMethods( in ImmutableArray methods ) { return new( Name, Fields, methods ); } + /// public override string ToString() { return Name; } + /// IUnit IUnit.WithFields( in ImmutableArray fields ) => WithFields( fields ); + /// IUnit IUnit.WithMethods( in ImmutableArray methods ) => WithMethods( methods ); + /// + /// Returns a new instance of . + /// + /// The name of the struct. + /// The fields contained in the struct. + /// The methods contained in the struct. + /// A new instance of . internal static Struct NewStructure( string name, in ImmutableArray fields, in ImmutableArray methods ) { return new( name, fields, methods ); diff --git a/Source/MochaTool.InteropGen/Units/Variable.cs b/Source/MochaTool.InteropGen/Units/Variable.cs index 7134d56b..a3e86d63 100644 --- a/Source/MochaTool.InteropGen/Units/Variable.cs +++ b/Source/MochaTool.InteropGen/Units/Variable.cs @@ -1,16 +1,31 @@ namespace MochaTool.InteropGen; +/// +/// Represents a variable in C++. This can be a field, parameter, etc. +/// internal sealed class Variable { + /// + /// The name of the variable. + /// internal string Name { get; } + /// + /// The literal string containing the type of the variable. + /// internal string Type { get; } + /// + /// Initializes a new instance of . + /// + /// The name of the variable. + /// The literal string containing the type of the variable. internal Variable( string name, string type ) { Name = name; Type = type; } + /// public override string ToString() { return $"{Type} {Name}"; From 93ff2bce62dc29148f14d827d460e8eeaa652385 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Mon, 13 Mar 2023 11:03:52 -0400 Subject: [PATCH 40/51] Cleanup VcxprojParser --- Source/MochaTool.InteropGen/VcxprojParser.cs | 26 +++++++++----------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/Source/MochaTool.InteropGen/VcxprojParser.cs b/Source/MochaTool.InteropGen/VcxprojParser.cs index a7da67b5..b45cba7f 100644 --- a/Source/MochaTool.InteropGen/VcxprojParser.cs +++ b/Source/MochaTool.InteropGen/VcxprojParser.cs @@ -11,19 +11,6 @@ internal static class VcxprojParser private const string ExternalIncludePath = "/rs:Project/rs:PropertyGroup[@Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\"]/rs:ExternalIncludePath"; private const string IncludePath = "/rs:Project/rs:PropertyGroup[@Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\"]/rs:IncludePath"; - private static string GetNodeContents( XmlNode root, string xpath, XmlNamespaceManager namespaceManager ) - { - var nodeList = root.SelectNodes( xpath, namespaceManager ); - if ( nodeList?.Count == 0 || nodeList?[0] == null ) - throw new Exception( "Couldn't find IncludePath!" ); - -#pragma warning disable CS8602 // Dereference of a possibly null reference. - var includeStr = nodeList[0].InnerText; -#pragma warning restore CS8602 // Dereference of a possibly null reference. - - return includeStr; - } - /// /// Parse the include list from a vcxproj file. /// @@ -39,7 +26,7 @@ internal static List ParseIncludes( string path ) XmlNamespaceManager namespaceManager = new XmlNamespaceManager( doc.NameTable ); namespaceManager.AddNamespace( "rs", "http://schemas.microsoft.com/developer/msbuild/2003" ); - if ( doc.DocumentElement == null ) + if ( doc.DocumentElement is null ) throw new Exception( "Failed to parse root node!" ); XmlNode root = doc.DocumentElement; @@ -87,4 +74,15 @@ internal static List ParseIncludes( string path ) return parsedIncludes; } + + private static string GetNodeContents( XmlNode root, string xpath, XmlNamespaceManager namespaceManager ) + { + var nodeList = root.SelectNodes( xpath, namespaceManager ); + if ( nodeList?.Count == 0 || nodeList?[0] is null ) + throw new Exception( "Couldn't find IncludePath!" ); + + var includeStr = nodeList[0]!.InnerText; + + return includeStr; + } } From becc7a1ec7063d2c4ac9a9bb3d0bc0e174f716cd Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Wed, 15 Mar 2023 12:59:39 -0400 Subject: [PATCH 41/51] Documentation pass --- .../CXCursorExtensions.cs | 8 +++ Source/MochaTool.InteropGen/Parser.cs | 54 +++++++++------ Source/MochaTool.InteropGen/Program.cs | 67 ++++++++++++++++--- Source/MochaTool.InteropGen/Utils.cs | 17 +++++ Source/MochaTool.InteropGen/VcxprojParser.cs | 3 + 5 files changed, 120 insertions(+), 29 deletions(-) diff --git a/Source/MochaTool.InteropGen/CXCursorExtensions.cs b/Source/MochaTool.InteropGen/CXCursorExtensions.cs index 80d6ea40..186107be 100644 --- a/Source/MochaTool.InteropGen/CXCursorExtensions.cs +++ b/Source/MochaTool.InteropGen/CXCursorExtensions.cs @@ -2,8 +2,16 @@ namespace MochaTool.InteropGen; +/// +/// Contains extension methods for the . +/// internal static class CXCursorExtensions { + /// + /// Returns whether or not the current item the cursor is over has the "generate_bindings" attribute on it. + /// + /// The cursor to check. + /// Whether or not the current item the cursor is over has the "generate_bindings" attribute on it. internal static bool HasGenerateBindingsAttribute( this CXCursor cursor ) { if ( !cursor.HasAttrs ) diff --git a/Source/MochaTool.InteropGen/Parser.cs b/Source/MochaTool.InteropGen/Parser.cs index 4681a70d..55ce78e2 100644 --- a/Source/MochaTool.InteropGen/Parser.cs +++ b/Source/MochaTool.InteropGen/Parser.cs @@ -3,33 +3,21 @@ namespace MochaTool.InteropGen; +/// +/// Contains all parsing functionality for C++ header files. +/// internal static class Parser { /// /// Cached launch arguments so that we don't have to regenerate them every time /// - private static string[] s_launchArgs = GetLaunchArgs(); - private static string[] GetLaunchArgs() - { - // Generate includes from vcxproj - var includeDirs = VcxprojParser.ParseIncludes( "../Mocha.Host/Mocha.Host.vcxproj" ); - - var args = new List - { - "-x", - "c++", - "-fparse-all-comments", - "-std=c++20", - "-DVK_NO_PROTOTYPES", - "-DNOMINMAX", - "-DVK_USE_PLATFORM_WIN32_KHR" - }; - - args.AddRange( includeDirs.Select( x => "-I" + x ) ); - - return args.ToArray(); - } + private static readonly string[] s_launchArgs = GetLaunchArgs(); + /// + /// Parses a header file and returns all of the s contained inside. + /// + /// The absolute path to the header file to parse. + /// All of the s contained inside the header file. internal unsafe static IEnumerable GetUnits( string path ) { var units = new List(); @@ -213,4 +201,28 @@ CXChildVisitResult methodChildVisitor( CXCursor cursor, CXCursor parent, void* d return units; } + /// + /// Returns a compiled array of launch arguments to pass to the C++ parser. + /// + /// A compiled array of launch arguments to pass to the C++ parser. + private static string[] GetLaunchArgs() + { + // Generate includes from vcxproj + var includeDirs = VcxprojParser.ParseIncludes( "../Mocha.Host/Mocha.Host.vcxproj" ); + + var args = new List + { + "-x", + "c++", + "-fparse-all-comments", + "-std=c++20", + "-DVK_NO_PROTOTYPES", + "-DNOMINMAX", + "-DVK_USE_PLATFORM_WIN32_KHR" + }; + + args.AddRange( includeDirs.Select( x => "-I" + x ) ); + + return args.ToArray(); + } } diff --git a/Source/MochaTool.InteropGen/Program.cs b/Source/MochaTool.InteropGen/Program.cs index 53398eb4..2a6ca997 100644 --- a/Source/MochaTool.InteropGen/Program.cs +++ b/Source/MochaTool.InteropGen/Program.cs @@ -1,10 +1,23 @@ namespace MochaTool.InteropGen; +/// +/// The main entry point to the IntropGen program. +/// public static class Program { - private static List s_units { get; set; } = new(); - private static List s_files { get; set; } = new(); - + /// + /// Contains all of the parsed units to generate bindings for. + /// + private static readonly List s_units = new(); + /// + /// Contains all of the files that need to be generated. + /// + private static readonly List s_files = new(); + + /// + /// The entry point to the program. + /// + /// The command-line arguments given to the program. public static void Main( string[] args ) { var baseDir = args[0]; @@ -36,6 +49,10 @@ public static void Main( string[] args ) Console.WriteLine( $"-- Took {totalTime.TotalSeconds} seconds." ); } + /// + /// Deletes and re-creates the generated file directories. + /// + /// The base directory that contains the source projects. private static void DeleteExistingFiles( string baseDir ) { var destCsDir = $"{baseDir}\\Mocha.Common\\Glue\\"; @@ -50,11 +67,17 @@ private static void DeleteExistingFiles( string baseDir ) Directory.CreateDirectory( destCsDir ); } + /// + /// Parses all header files in the Mocha.Host project for interop generation. + /// + /// The base directory that contains the source projects. private static void Parse( string baseDir ) { - List queue = new(); QueueDirectory( queue, baseDir ); + // Find and queue all of the header files to parse. + var queue = new List(); + // Dispatch jobs to parse all files. var dispatcher = new ThreadDispatcher( async ( files ) => { foreach ( var path in files ) @@ -66,7 +89,12 @@ private static void Parse( string baseDir ) Thread.Sleep( 1 ); } - private static void WriteManagedStruct( string baseDir, List<(string Name, Method method)> methods ) + /// + /// Writes the C# unmanaged arguments. + /// + /// The base directory that contains the source projects. + /// An enumerable list of all of the methods to write in the struct. + private static void WriteManagedStruct( string baseDir, IEnumerable<(string Name, Method method)> methods ) { var (baseManagedStructWriter, managedStructWriter) = Utils.CreateWriter(); @@ -92,7 +120,12 @@ private static void WriteManagedStruct( string baseDir, List<(string Name, Metho File.WriteAllText( $"{baseDir}/Mocha.Common/Glue/UnmanagedArgs.cs", baseManagedStructWriter.ToString() ); } - private static void WriteNativeStruct( string baseDir, List<(string Name, Method method)> methods ) + /// + /// Writes the C++ unmanaged arguments. + /// + /// The base directory that contains the source projects. + /// An enumerable list of all of the methods to write in the struct. + private static void WriteNativeStruct( string baseDir, IEnumerable<(string Name, Method method)> methods ) { var (baseNativeStructWriter, nativeStructWriter) = Utils.CreateWriter(); @@ -134,6 +167,10 @@ private static void WriteNativeStruct( string baseDir, List<(string Name, Method File.WriteAllText( $"{baseDir}Mocha.Host\\generated\\UnmanagedArgs.generated.h", baseNativeStructWriter.ToString() ); } + /// + /// Writes the C++ includes for the host project. + /// + /// The base directory that contains the source projects. private static void WriteNativeIncludes( string baseDir ) { var (baseNativeListWriter, nativeListWriter) = Utils.CreateWriter(); @@ -154,27 +191,41 @@ private static void WriteNativeIncludes( string baseDir ) File.WriteAllText( $"{baseDir}Mocha.Host\\generated\\InteropList.generated.h", baseNativeListWriter.ToString() ); } + /// + /// Parses a header file and generates its C# and C++ interop code. + /// + /// The base directory that contains the source projects. + /// + /// A task that represents the asynchronous operation. private static async Task ProcessHeaderAsync( string baseDir, string path ) { Console.WriteLine( $"Processing header {path}..." ); + // Parse header. var units = Parser.GetUnits( path ); - var fileName = Path.GetFileNameWithoutExtension( path ); + // Generate interop code. var managedCode = ManagedCodeGenerator.GenerateCode( units ); - var relativePath = Path.GetRelativePath( $"{baseDir}/Mocha.Host/", path ); var nativeCode = NativeCodeGenerator.GenerateCode( relativePath, units ); + // Write interop code. + var fileName = Path.GetFileNameWithoutExtension( path ); var csTask = File.WriteAllTextAsync( $"{baseDir}Mocha.Common\\Glue\\{fileName}.generated.cs", managedCode ); var nativeTask = File.WriteAllTextAsync( $"{baseDir}Mocha.Host\\generated\\{fileName}.generated.h", nativeCode ); + // Wait for writing to finish. await Task.WhenAll( csTask, nativeTask ); s_files.Add( fileName ); s_units.AddRange( units ); } + /// + /// Searches the directory for any header files that should be parsed. + /// + /// The queue collection to append to. + /// The absolute path to the directory to search for files. private static void QueueDirectory( ICollection queue, string directory ) { foreach ( var file in Directory.GetFiles( directory ) ) diff --git a/Source/MochaTool.InteropGen/Utils.cs b/Source/MochaTool.InteropGen/Utils.cs index ac62a084..07b24934 100644 --- a/Source/MochaTool.InteropGen/Utils.cs +++ b/Source/MochaTool.InteropGen/Utils.cs @@ -2,14 +2,27 @@ namespace MochaTool.InteropGen; +/// +/// Contains a number of utility methods. +/// internal static class Utils { + /// + /// Returns whether or not the string represents a pointer. + /// + /// The native type to check. + /// Whether or not the string represents a pointer. internal static bool IsPointer( string nativeType ) { var managedType = GetManagedType( nativeType ); return nativeType.Trim().EndsWith( "*" ) && managedType != "string" && managedType != "IntPtr"; } + /// + /// Returns the C# version of a native type. + /// + /// The native type to check. + /// The C# verison of a native type. internal static string GetManagedType( string nativeType ) { // Trim whitespace from beginning / end (if it exists) @@ -77,6 +90,10 @@ internal static string GetManagedType( string nativeType ) return nativeType; } + /// + /// Creates and returns the text writer for writing formatted files. + /// + /// The created text writer. internal static (StringWriter StringWriter, IndentedTextWriter TextWriter) CreateWriter() { var baseTextWriter = new StringWriter(); diff --git a/Source/MochaTool.InteropGen/VcxprojParser.cs b/Source/MochaTool.InteropGen/VcxprojParser.cs index b45cba7f..34538290 100644 --- a/Source/MochaTool.InteropGen/VcxprojParser.cs +++ b/Source/MochaTool.InteropGen/VcxprojParser.cs @@ -2,6 +2,9 @@ namespace MochaTool.InteropGen; +/// +/// Contains functionality for parsing vcxproj files. +/// internal static class VcxprojParser { // Note that these paths only work for the windows x64 platforms right now. From 04bcd3b3648e457ad016bca473527196845cb85d Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Wed, 15 Mar 2023 13:00:46 -0400 Subject: [PATCH 42/51] Switch to ILogger instead of Console writing --- Source/MochaTool.InteropGen/Global.cs | 26 +++++++ .../MochaTool.InteropGen/ILoggerExtensions.cs | 68 +++++++++++++++++++ .../MochaTool.InteropGen.csproj | 3 + Source/MochaTool.InteropGen/Parser.cs | 22 +++++- Source/MochaTool.InteropGen/Program.cs | 21 +++--- Source/MochaTool.InteropGen/StopwatchLog.cs | 43 ++++++++++++ 6 files changed, 169 insertions(+), 14 deletions(-) create mode 100644 Source/MochaTool.InteropGen/Global.cs create mode 100644 Source/MochaTool.InteropGen/ILoggerExtensions.cs create mode 100644 Source/MochaTool.InteropGen/StopwatchLog.cs diff --git a/Source/MochaTool.InteropGen/Global.cs b/Source/MochaTool.InteropGen/Global.cs new file mode 100644 index 00000000..9049ac6e --- /dev/null +++ b/Source/MochaTool.InteropGen/Global.cs @@ -0,0 +1,26 @@ +global using static MochaTool.InteropGen.Global; +using Microsoft.Extensions.Logging; + +namespace MochaTool.InteropGen; + +/// +/// Contains globally used items in the project. +/// +internal static class Global +{ + /// + /// The instance of to use when logging. + /// + internal static readonly ILogger Log; + + /// + /// Initializes the instance. + /// + static Global() + { + using var factory = LoggerFactory.Create( builder => builder + .AddConsole() + .SetMinimumLevel( LogLevel.Information ) ); + Log = factory.CreateLogger( "InteropGen" ); + } +} diff --git a/Source/MochaTool.InteropGen/ILoggerExtensions.cs b/Source/MochaTool.InteropGen/ILoggerExtensions.cs new file mode 100644 index 00000000..1c5188a7 --- /dev/null +++ b/Source/MochaTool.InteropGen/ILoggerExtensions.cs @@ -0,0 +1,68 @@ +using Microsoft.Extensions.Logging; + +namespace MochaTool.InteropGen; + +/// +/// Contains extension methods for s. +/// +internal static partial class ILoggerExtensions +{ + /// + /// Logs the first message to the user. + /// + /// The instance to log to. + [LoggerMessage( EventId = 0, + Level = LogLevel.Information, + Message = "Generating C# <--> C++ interop code..." )] + internal static partial void LogIntro( this ILogger logger ); + + /// + /// Logs a timed operation to the user. + /// + /// The instance to log to. + /// The name of the timed operation. + /// The time in seconds that it took to complete the operation. + [LoggerMessage( EventId = 1, + Message = "{name} took {seconds} seconds." )] + internal static partial void ReportTime( this ILogger logger, LogLevel logLevel, string name, double seconds ); + + /// + /// Logs to the user that a header is being processed by the parser. + /// + /// The instance to log to. + /// The absolute path to the header file being processed. + [LoggerMessage( EventId = 2, + Level = LogLevel.Debug, + Message = "Processing header {path}..." )] + internal static partial void ProcessingHeader( this ILogger logger, string path ); + + /// + /// Logs a fatal C++ diagnostic to the user. + /// + /// The instance to log to. + /// The diagnostic to show. + [LoggerMessage( EventId = 3, + Level = LogLevel.Warning, + Message = "{diagnostic}" )] + internal static partial void FatalDiagnostic( this ILogger logger, string diagnostic ); + + /// + /// Logs an error C++ diagnostic to the user. + /// + /// The instance to log to. + /// The diagnostic to show. + [LoggerMessage( EventId = 4, + Level = LogLevel.Warning, + Message = "{diagnostic}" )] + internal static partial void ErrorDiagnostic( this ILogger logger, string diagnostic ); + + /// + /// Logs a warning C++ diagnostic to the user. + /// + /// The instance to log to. + /// The diagnostic to show. + [LoggerMessage( EventId = 5, + Level = LogLevel.Warning, + Message = "{diagnostic}" )] + internal static partial void WarnDiagnostic( this ILogger logger, string diagnostic ); +} diff --git a/Source/MochaTool.InteropGen/MochaTool.InteropGen.csproj b/Source/MochaTool.InteropGen/MochaTool.InteropGen.csproj index 1ff8a9e9..8a1951e0 100644 --- a/Source/MochaTool.InteropGen/MochaTool.InteropGen.csproj +++ b/Source/MochaTool.InteropGen/MochaTool.InteropGen.csproj @@ -15,6 +15,9 @@ + + + diff --git a/Source/MochaTool.InteropGen/Parser.cs b/Source/MochaTool.InteropGen/Parser.cs index 55ce78e2..7707bc07 100644 --- a/Source/MochaTool.InteropGen/Parser.cs +++ b/Source/MochaTool.InteropGen/Parser.cs @@ -1,4 +1,5 @@ using ClangSharp.Interop; +using Microsoft.Extensions.Logging; using System.Collections.Immutable; namespace MochaTool.InteropGen; @@ -25,10 +26,25 @@ internal unsafe static IEnumerable GetUnits( string path ) using var index = CXIndex.Create(); using var unit = CXTranslationUnit.Parse( index, path, s_launchArgs, ReadOnlySpan.Empty, CXTranslationUnit_Flags.CXTranslationUnit_None ); - for ( var i = 0; i < unit.NumDiagnostics; ++i ) + // Only start walking diagnostics if logging is enabled to the minimum level. + if ( Log.IsEnabled( LogLevel.Warning ) ) { - var diagnostics = unit.GetDiagnostic( (uint)i ); - Console.WriteLine( $"{diagnostics.Format( CXDiagnostic.DefaultDisplayOptions )}" ); + for ( var i = 0; i < unit.NumDiagnostics; i++ ) + { + var diagnostics = unit.GetDiagnostic( (uint)i ); + switch ( diagnostics.Severity ) + { + case CXDiagnosticSeverity.CXDiagnostic_Fatal: + Log.FatalDiagnostic( diagnostics.Format( CXDiagnostic.DefaultDisplayOptions ).CString ); + break; + case CXDiagnosticSeverity.CXDiagnostic_Error: + Log.ErrorDiagnostic( diagnostics.Format( CXDiagnostic.DefaultDisplayOptions ).CString ); + break; + case CXDiagnosticSeverity.CXDiagnostic_Warning: + Log.WarnDiagnostic( diagnostics.Format( CXDiagnostic.DefaultDisplayOptions ).CString ); + break; + } + } } var cursor = unit.Cursor; diff --git a/Source/MochaTool.InteropGen/Program.cs b/Source/MochaTool.InteropGen/Program.cs index 2a6ca997..22f6d1fb 100644 --- a/Source/MochaTool.InteropGen/Program.cs +++ b/Source/MochaTool.InteropGen/Program.cs @@ -1,4 +1,6 @@ -namespace MochaTool.InteropGen; +using Microsoft.Extensions.Logging; + +namespace MochaTool.InteropGen; /// /// The main entry point to the IntropGen program. @@ -20,16 +22,18 @@ public static class Program /// The command-line arguments given to the program. public static void Main( string[] args ) { - var baseDir = args[0]; - var start = DateTime.Now; + using var _totalTime = new StopwatchLog( "InteropGen", LogLevel.Information ); - Console.WriteLine( "Generating C# <--> C++ interop code..." ); + var baseDir = args[0]; + Log.LogIntro(); // // Prep // DeleteExistingFiles( baseDir ); - Parse( baseDir ); + + using ( var _parseTime = new StopwatchLog( "Parsing" ) ) + Parse( baseDir ); // // Expand methods out into list of (method name, method) @@ -42,11 +46,6 @@ public static void Main( string[] args ) WriteManagedStruct( baseDir, methods ); WriteNativeStruct( baseDir, methods ); WriteNativeIncludes( baseDir ); - - // Track time & output total duration - var end = DateTime.Now; - var totalTime = end - start; - Console.WriteLine( $"-- Took {totalTime.TotalSeconds} seconds." ); } /// @@ -199,7 +198,7 @@ private static void WriteNativeIncludes( string baseDir ) /// A task that represents the asynchronous operation. private static async Task ProcessHeaderAsync( string baseDir, string path ) { - Console.WriteLine( $"Processing header {path}..." ); + Log.ProcessingHeader( path ); // Parse header. var units = Parser.GetUnits( path ); diff --git a/Source/MochaTool.InteropGen/StopwatchLog.cs b/Source/MochaTool.InteropGen/StopwatchLog.cs new file mode 100644 index 00000000..2a3a11d4 --- /dev/null +++ b/Source/MochaTool.InteropGen/StopwatchLog.cs @@ -0,0 +1,43 @@ +using Microsoft.Extensions.Logging; +using System.Diagnostics; + +namespace MochaTool.InteropGen; + +/// +/// Represents a scoped logger for recording time taken to complete an operation. +/// +internal readonly struct StopwatchLog : IDisposable +{ + /// + /// The name of the operation being completed. + /// + private readonly string name = "Unknown"; + /// + /// The level at which to log the operation. + /// + private readonly LogLevel logLevel = LogLevel.Debug; + /// + /// The timestamp at which the operation started. + /// + private readonly long startTimestamp = Stopwatch.GetTimestamp(); + + /// + /// Initializes a new instance of . + /// + /// The name of the operation being completed. + /// The level at which to log the operation. + /// The timestamp at which the operation started. + internal StopwatchLog( string name, LogLevel logLevel = LogLevel.Debug, long? startTimestamp = null ) + { + this.name = name; + this.logLevel = logLevel; + this.startTimestamp = startTimestamp ?? Stopwatch.GetTimestamp(); + } + + /// + public void Dispose() + { + // Log the time taken to complete the operation. + Log.ReportTime( logLevel, name, Stopwatch.GetElapsedTime( startTimestamp ).TotalSeconds ); + } +} From 316fce7499d6be0b68f94aa8924a5f57019a3211 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Wed, 15 Mar 2023 13:01:34 -0400 Subject: [PATCH 43/51] Cleanup --- Source/MochaTool.InteropGen/Parser.cs | 232 ++++++++++--------- Source/MochaTool.InteropGen/Program.cs | 4 +- Source/MochaTool.InteropGen/Utils.cs | 6 +- Source/MochaTool.InteropGen/VcxprojParser.cs | 12 +- 4 files changed, 128 insertions(+), 126 deletions(-) diff --git a/Source/MochaTool.InteropGen/Parser.cs b/Source/MochaTool.InteropGen/Parser.cs index 7707bc07..e0d715e5 100644 --- a/Source/MochaTool.InteropGen/Parser.cs +++ b/Source/MochaTool.InteropGen/Parser.cs @@ -47,8 +47,6 @@ internal unsafe static IEnumerable GetUnits( string path ) } } - var cursor = unit.Cursor; - CXChildVisitResult cursorVisitor( CXCursor cursor, CXCursor parent, void* data ) { if ( !cursor.Location.IsFromMainFile ) @@ -75,112 +73,19 @@ CXChildVisitResult cursorVisitor( CXCursor cursor, CXCursor parent, void* data ) case CXCursorKind.CXCursor_Constructor: case CXCursorKind.CXCursor_CXXMethod: case CXCursorKind.CXCursor_FunctionDecl: - { - if ( !cursor.HasGenerateBindingsAttribute() ) - return CXChildVisitResult.CXChildVisit_Continue; - - if ( cursor.CXXAccessSpecifier != CX_CXXAccessSpecifier.CX_CXXPublic && cursor.Kind != CXCursorKind.CXCursor_FunctionDecl ) - break; - - var ownerName = cursor.LexicalParent.Spelling.ToString(); - var owner = units.FirstOrDefault( x => x.Name == ownerName ); - if ( owner is null ) - { - Console.WriteLine( $"No unit with name \"{ownerName}\"" ); - break; - } - - string name; - string returnType; - bool isStatic; - bool isConstructor; - bool isDestructor; - - var parametersBuilder = ImmutableArray.CreateBuilder(); - - if ( cursor.Kind == CXCursorKind.CXCursor_Constructor ) - { - name = "Ctor"; - returnType = owner.Name + '*'; - isStatic = false; - isConstructor = true; - isDestructor = false; - } - else if ( cursor.Kind == CXCursorKind.CXCursor_Destructor ) - { - name = "DeCtor"; - returnType = '~' + owner.Name; - isStatic = false; - isConstructor = false; - isDestructor = true; - } - else - { - name = cursor.Spelling.ToString(); - returnType = cursor.ReturnType.Spelling.ToString(); - isStatic = cursor.IsStatic; - isConstructor = false; - isDestructor = false; - } - - CXChildVisitResult methodChildVisitor( CXCursor cursor, CXCursor parent, void* data ) - { - if ( cursor.Kind == CXCursorKind.CXCursor_ParmDecl ) - { - var type = cursor.Type.ToString(); - var name = cursor.Spelling.ToString(); - - parametersBuilder.Add( new Variable( name, type ) ); - } - - return CXChildVisitResult.CXChildVisit_Recurse; - } - - cursor.VisitChildren( methodChildVisitor, default ); - - Method method; - if ( isConstructor ) - method = Method.NewConstructor( name, returnType, parametersBuilder.ToImmutable() ); - else if ( isDestructor ) - method = Method.NewDestructor( name, returnType, parametersBuilder.ToImmutable() ); - else - method = Method.NewMethod( name, returnType, isStatic, parametersBuilder.ToImmutable() ); - - var newOwner = owner.WithMethods( owner.Methods.Add( method ) ); - units.Remove( owner ); - units.Add( newOwner ); - - break; - } + return VisitMethod( cursor, units ); // // Field // case CXCursorKind.CXCursor_FieldDecl: - { - if ( !cursor.HasGenerateBindingsAttribute() ) - return CXChildVisitResult.CXChildVisit_Continue; - - var ownerName = cursor.LexicalParent.Spelling.ToString(); - var owner = units.FirstOrDefault( x => x.Name == ownerName ); - - if ( owner is null ) - break; - - var newOwner = owner.WithFields( owner.Fields.Add( new Variable( cursor.Spelling.ToString(), cursor.Type.ToString() ) ) ); - units.Remove( owner ); - units.Add( newOwner ); - break; - } - - default: - break; + return VisitField( cursor, units ); } return CXChildVisitResult.CXChildVisit_Recurse; } - cursor.VisitChildren( cursorVisitor, default ); + unit.Cursor.VisitChildren( cursorVisitor, default ); // // Remove all items with duplicate names @@ -199,24 +104,123 @@ CXChildVisitResult methodChildVisitor( CXCursor cursor, CXCursor parent, void* d // units = units.Where( x => x.Methods.Length > 0 || x.Fields.Length > 0 ).ToList(); - // - // Post-processing - // - //foreach ( var o in units ) - //{ - // // Create a default constructor if one wasn't already defined - // if ( !o.Methods.Any( x => x.IsConstructor ) && o is not Class { IsNamespace: true } ) - // { - // Console.WriteLine( $"Creating default ctor for {o.Name}" ); - // o.Methods.Add( new Method( "Ctor", $"{o.Name}*" ) - // { - // IsConstructor = true - // } ); - // } - //} - return units; } + + /// + /// The visitor method for walking a method declaration. + /// + /// The cursor that is traversing the method. + /// The collection to fetch method owners from. + /// The next action the cursor should take in traversal. + private static unsafe CXChildVisitResult VisitMethod( in CXCursor cursor, ICollection units ) + { + // Early bails. + if ( !cursor.HasGenerateBindingsAttribute() ) + return CXChildVisitResult.CXChildVisit_Continue; + if ( cursor.CXXAccessSpecifier != CX_CXXAccessSpecifier.CX_CXXPublic && cursor.Kind != CXCursorKind.CXCursor_FunctionDecl ) + return CXChildVisitResult.CXChildVisit_Continue; + + // Verify that the method has an owner. + var ownerName = cursor.LexicalParent.Spelling.ToString(); + var owner = units.FirstOrDefault( x => x.Name == ownerName ); + if ( owner is null ) + return CXChildVisitResult.CXChildVisit_Continue; + + string name; + string returnType; + bool isStatic; + bool isConstructor; + bool isDestructor; + + var parametersBuilder = ImmutableArray.CreateBuilder(); + // We're traversing a constructor. + if ( cursor.Kind == CXCursorKind.CXCursor_Constructor ) + { + name = "Ctor"; + returnType = owner.Name + '*'; + isStatic = false; + isConstructor = true; + isDestructor = false; + } + // We're traversing a destructor. + else if ( cursor.Kind == CXCursorKind.CXCursor_Destructor ) + { + name = "DeCtor"; + returnType = '~' + owner.Name; + isStatic = false; + isConstructor = false; + isDestructor = true; + } + // We're traversing a standard method. + else + { + name = cursor.Spelling.ToString(); + returnType = cursor.ReturnType.Spelling.ToString(); + isStatic = cursor.IsStatic; + isConstructor = false; + isDestructor = false; + } + + // Visitor for parameter delcarations. + CXChildVisitResult methodChildVisitor( CXCursor cursor, CXCursor parent, void* data ) + { + if ( cursor.Kind != CXCursorKind.CXCursor_ParmDecl ) + return CXChildVisitResult.CXChildVisit_Continue; + + var name = cursor.Spelling.ToString(); + var type = cursor.Type.ToString(); + + parametersBuilder.Add( new Variable( name, type ) ); + + return CXChildVisitResult.CXChildVisit_Recurse; + } + + cursor.VisitChildren( methodChildVisitor, default ); + + // Construct the method. + Method method; + if ( isConstructor ) + method = Method.NewConstructor( name, returnType, parametersBuilder.ToImmutable() ); + else if ( isDestructor ) + method = Method.NewDestructor( name, returnType, parametersBuilder.ToImmutable() ); + else + method = Method.NewMethod( name, returnType, isStatic, parametersBuilder.ToImmutable() ); + + // Update owner with new method. + var newOwner = owner.WithMethods( owner.Methods.Add( method ) ); + units.Remove( owner ); + units.Add( newOwner ); + + return CXChildVisitResult.CXChildVisit_Continue; + } + + /// + /// The visitor method for walking a field declaration. + /// + /// The cursor that is traversing the method. + /// The collection to fetch method owners from. + /// The next action the cursor should take in traversal. + private static CXChildVisitResult VisitField( in CXCursor cursor, ICollection units ) + { + // Early bail. + if ( !cursor.HasGenerateBindingsAttribute() ) + return CXChildVisitResult.CXChildVisit_Continue; + + // Verify that the field has an owner. + var ownerName = cursor.LexicalParent.Spelling.ToString(); + var owner = units.FirstOrDefault( x => x.Name == ownerName ); + if ( owner is null ) + return CXChildVisitResult.CXChildVisit_Recurse; + + // Update owner with new field. + var newOwner = owner.WithFields( owner.Fields.Add( new Variable( cursor.Spelling.ToString(), cursor.Type.ToString() ) ) ); + units.Remove( owner ); + units.Add( newOwner ); + + return CXChildVisitResult.CXChildVisit_Recurse; + } + /// /// Returns a compiled array of launch arguments to pass to the C++ parser. /// diff --git a/Source/MochaTool.InteropGen/Program.cs b/Source/MochaTool.InteropGen/Program.cs index 22f6d1fb..f7257176 100644 --- a/Source/MochaTool.InteropGen/Program.cs +++ b/Source/MochaTool.InteropGen/Program.cs @@ -54,8 +54,8 @@ public static void Main( string[] args ) /// The base directory that contains the source projects. private static void DeleteExistingFiles( string baseDir ) { - var destCsDir = $"{baseDir}\\Mocha.Common\\Glue\\"; - var destHeaderDir = $"{baseDir}\\Mocha.Host\\generated\\"; + var destCsDir = $"{baseDir}\\Mocha.Common\\Glue"; + var destHeaderDir = $"{baseDir}\\Mocha.Host\\generated"; if ( Directory.Exists( destHeaderDir ) ) Directory.Delete( destHeaderDir, true ); diff --git a/Source/MochaTool.InteropGen/Utils.cs b/Source/MochaTool.InteropGen/Utils.cs index 07b24934..073ba679 100644 --- a/Source/MochaTool.InteropGen/Utils.cs +++ b/Source/MochaTool.InteropGen/Utils.cs @@ -69,17 +69,17 @@ internal static string GetManagedType( string nativeType ) return GetManagedType( nativeType[0..^1] ); // Check if the native type is in the lookup table - if ( lookupTable.ContainsKey( nativeType ) ) + if ( s_lookupTable.TryGetValue( nativeType, out var value ) ) { // Bonus: Emit a compiler warning if the native type is std::string if ( nativeType == "std::string" ) { // There's a better API that does this but I can't remember what it is // TODO: Show position of the warning (line number, file name) - Console.WriteLine( "warning IG0001: std::string is not supported in managed code. Use a C string instead." ); + Log.WarnDiagnostic( "warning IG0001: std::string is not supported in managed code. Use a C string instead." ); } - return lookupTable[nativeType]; + return value; } // Check if the native type is a pointer diff --git a/Source/MochaTool.InteropGen/VcxprojParser.cs b/Source/MochaTool.InteropGen/VcxprojParser.cs index 34538290..92068165 100644 --- a/Source/MochaTool.InteropGen/VcxprojParser.cs +++ b/Source/MochaTool.InteropGen/VcxprojParser.cs @@ -23,18 +23,18 @@ internal static class VcxprojParser /// internal static List ParseIncludes( string path ) { - XmlDocument doc = new XmlDocument(); + var doc = new XmlDocument(); doc.Load( path ); - XmlNamespaceManager namespaceManager = new XmlNamespaceManager( doc.NameTable ); + var namespaceManager = new XmlNamespaceManager( doc.NameTable ); namespaceManager.AddNamespace( "rs", "http://schemas.microsoft.com/developer/msbuild/2003" ); if ( doc.DocumentElement is null ) throw new Exception( "Failed to parse root node!" ); - XmlNode root = doc.DocumentElement; + var root = doc.DocumentElement; - List includes = new(); + var includes = new List(); // Select Project -> PropertyGroup -> ExternalIncludePath { @@ -60,7 +60,7 @@ internal static List ParseIncludes( string path ) { "ExternalIncludePath", "" } }; - List parsedIncludes = new(); + var parsedIncludes = new List(); // Simple find-and-replace for macros and environment variables foreach ( var include in includes ) @@ -68,9 +68,7 @@ internal static List ParseIncludes( string path ) var processedInclude = include; foreach ( var environmentVariable in environmentVariables ) - { processedInclude = processedInclude.Replace( $"$({environmentVariable.Key})", environmentVariable.Value ); - } parsedIncludes.Add( processedInclude ); } From ef57a227024d0d9b97e84ee3e0dd7988a79bcc7f Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Wed, 15 Mar 2023 13:01:49 -0400 Subject: [PATCH 44/51] Fix launchSettings.json working directory --- Source/MochaTool.InteropGen/Properties/launchSettings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/MochaTool.InteropGen/Properties/launchSettings.json b/Source/MochaTool.InteropGen/Properties/launchSettings.json index 4443d61e..32179963 100644 --- a/Source/MochaTool.InteropGen/Properties/launchSettings.json +++ b/Source/MochaTool.InteropGen/Properties/launchSettings.json @@ -3,7 +3,7 @@ "Run InteropGen": { "commandName": "Project", "commandLineArgs": "$(SolutionDir)", - "workingDirectory": "F:\\Projects\\mocha-native\\Source\\MochaTool.InteropGen" + "workingDirectory": "$(ProjectDir)" } } } \ No newline at end of file From c0cde0108d9c17172540b13ca119f1da6def6273 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Wed, 15 Mar 2023 13:02:04 -0400 Subject: [PATCH 45/51] Initialize lookup table once statically --- Source/MochaTool.InteropGen/Utils.cs | 66 ++++++++++++++-------------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/Source/MochaTool.InteropGen/Utils.cs b/Source/MochaTool.InteropGen/Utils.cs index 073ba679..a476fb13 100644 --- a/Source/MochaTool.InteropGen/Utils.cs +++ b/Source/MochaTool.InteropGen/Utils.cs @@ -7,6 +7,40 @@ namespace MochaTool.InteropGen; /// internal static class Utils { + /// + /// Used as a lookup table for mapping native types to managed ones. + /// + private static readonly Dictionary s_lookupTable = new() + { + // Native type Managed type + //------------------------------- + { "void", "void" }, + { "uint32_t", "uint" }, + { "int32_t", "int" }, + { "size_t", "uint" }, + + { "char**", "ref string" }, + { "char **", "ref string" }, + { "char*", "string" }, + { "char *", "string" }, + { "void*", "IntPtr" }, + { "void *", "IntPtr" }, + + // STL + { "std::string", "/* UNSUPPORTED */ string" }, + + // GLM + { "glm::vec2", "Vector2" }, + { "glm::vec3", "Vector3" }, + { "glm::mat4", "Matrix4x4" }, + { "glm::quat", "Rotation" }, + + // Custom + { "Quaternion", "Rotation" }, + { "InteropStruct", "IInteropArray" }, + { "Handle", "uint" } + }; + /// /// Returns whether or not the string represents a pointer. /// @@ -32,38 +66,6 @@ internal static string GetManagedType( string nativeType ) if ( nativeType.StartsWith( "const" ) ) nativeType = nativeType[5..].Trim(); - // Create a dictionary to hold the mapping between native and managed types - var lookupTable = new Dictionary() - { - // Native type Managed type - //------------------------------- - { "void", "void" }, - { "uint32_t", "uint" }, - { "int32_t", "int" }, - { "size_t", "uint" }, - - { "char**", "ref string" }, - { "char **", "ref string" }, - { "char*", "string" }, - { "char *", "string" }, - { "void*", "IntPtr" }, - { "void *", "IntPtr" }, - - // STL - { "std::string", "/* UNSUPPORTED */ string" }, - - // GLM - { "glm::vec2", "Vector2" }, - { "glm::vec3", "Vector3" }, - { "glm::mat4", "Matrix4x4" }, - { "glm::quat", "Rotation" }, - - // Custom - { "Quaternion", "Rotation" }, - { "InteropStruct", "IInteropArray" }, - { "Handle", "uint" } - }; - // Check if the native type is a reference if ( nativeType.EndsWith( "&" ) ) return GetManagedType( nativeType[0..^1] ); From 2af966f0c2f1ce1b11797434d8cb8bb6d9974677 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Wed, 15 Mar 2023 13:02:26 -0400 Subject: [PATCH 46/51] Only search Mocha.Host project for headers --- Source/MochaTool.InteropGen/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/MochaTool.InteropGen/Program.cs b/Source/MochaTool.InteropGen/Program.cs index f7257176..4647d4c4 100644 --- a/Source/MochaTool.InteropGen/Program.cs +++ b/Source/MochaTool.InteropGen/Program.cs @@ -72,9 +72,9 @@ private static void DeleteExistingFiles( string baseDir ) /// The base directory that contains the source projects. private static void Parse( string baseDir ) { - QueueDirectory( queue, baseDir ); // Find and queue all of the header files to parse. var queue = new List(); + QueueDirectory( queue, baseDir + "\\Mocha.Host" ); // Dispatch jobs to parse all files. var dispatcher = new ThreadDispatcher( async ( files ) => From e477302c8f78d75385c9913225e590afdfd41e8f Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Wed, 15 Mar 2023 13:02:41 -0400 Subject: [PATCH 47/51] Filter files by *.h instead of grabbing all --- Source/MochaTool.InteropGen/Program.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/MochaTool.InteropGen/Program.cs b/Source/MochaTool.InteropGen/Program.cs index 4647d4c4..6982bf5f 100644 --- a/Source/MochaTool.InteropGen/Program.cs +++ b/Source/MochaTool.InteropGen/Program.cs @@ -227,9 +227,9 @@ private static async Task ProcessHeaderAsync( string baseDir, string path ) /// The absolute path to the directory to search for files. private static void QueueDirectory( ICollection queue, string directory ) { - foreach ( var file in Directory.GetFiles( directory ) ) + foreach ( var file in Directory.GetFiles( directory, "*.h" ) ) { - if ( !file.EndsWith( ".h" ) || file.EndsWith( ".generated.h" ) ) + if ( file.EndsWith( ".generated.h" ) ) continue; var fileContents = File.ReadAllText( file ); From e64351794f28cd2ab7e485309295cd27031ca8e1 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 16 Mar 2023 20:07:31 -0400 Subject: [PATCH 48/51] Document code generators --- .../CodeGen/ManagedCodeGenerator.cs | 76 +++++++++++++----- .../CodeGen/NativeCodeGenerator.cs | 78 ++++++++++++------- 2 files changed, 106 insertions(+), 48 deletions(-) diff --git a/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs index 6baa362e..21563b6c 100644 --- a/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs +++ b/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs @@ -3,9 +3,19 @@ namespace MochaTool.InteropGen; +/// +/// Contains functionality for generating C# code. +/// internal static class ManagedCodeGenerator { + /// + /// The namespace that all generated code will be under. + /// private const string Namespace = "Mocha.Glue"; + + /// + /// An array containing all using declarations for generated code. + /// private static readonly string[] Usings = new[] { "System.Runtime.InteropServices", @@ -13,6 +23,9 @@ internal static class ManagedCodeGenerator "Mocha.Common" }; + /// + /// The header to be used at the top of generated code. + /// private static string Header => $""" //------------------------------------------------------------------------------ // @@ -25,20 +38,29 @@ internal static class ManagedCodeGenerator //------------------------------------------------------------------------------ """; + /// + /// Generates and returns C# code for a set of s. + /// + /// An enumerable list of s to generate code for. + /// C# code representing the set of s passed. internal static string GenerateCode( IEnumerable units ) { var (baseTextWriter, writer) = Utils.CreateWriter(); + // 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 ) @@ -62,14 +84,19 @@ internal static string GenerateCode( IEnumerable units ) return baseTextWriter.ToString(); } - private static void GenerateClassCode( IndentedTextWriter writer, Class sel ) + /// + /// Generates C# code for a class. + /// + /// The writer to append the code to. + /// The class to write code for. + private static void GenerateClassCode( IndentedTextWriter writer, Class c ) { // // Gather everything we need into nice lists // List decls = new(); - foreach ( var method in sel.Methods ) + foreach ( var method in c.Methods ) { var returnType = Utils.GetManagedType( method.ReturnType ); var name = method.Name; @@ -100,13 +127,13 @@ private static void GenerateClassCode( 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++; @@ -119,12 +146,12 @@ private static void GenerateClassCode( IndentedTextWriter writer, Class sel ) 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++; @@ -136,7 +163,7 @@ private static void GenerateClassCode( IndentedTextWriter writer, Class sel ) } // Methods - foreach ( var method in sel.Methods ) + foreach ( var method in c.Methods ) { writer.WriteLine(); @@ -164,7 +191,7 @@ private static void GenerateClassCode( 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 @@ -217,28 +244,38 @@ private static void GenerateClassCode( IndentedTextWriter writer, Class sel ) writer.WriteLine( "}" ); } - private static void GenerateStructCode( IndentedTextWriter writer, Struct sel ) + /// + /// Generates C# code for a struct. + /// + /// The writer to append the code to. + /// The struct to write code for. + 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 static void GenerateNamespaceCode( IndentedTextWriter writer, Class sel ) + /// + /// Generates C# code for a namespace. + /// + /// The writer to append the code to. + /// The namespace to write code for. + private static void GenerateNamespaceCode( IndentedTextWriter writer, Class ns ) { // // Gather everything we need into nice lists // List decls = new(); - foreach ( var method in sel.Methods ) + foreach ( var method in ns.Methods ) { var returnType = Utils.GetManagedType( method.ReturnType ); var name = method.Name; @@ -260,13 +297,13 @@ private static void GenerateNamespaceCode( 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++; @@ -275,7 +312,7 @@ private static void GenerateNamespaceCode( IndentedTextWriter writer, Class sel writer.WriteLine( decl ); // Methods - foreach ( var method in sel.Methods ) + foreach ( var method in ns.Methods ) { writer.WriteLine(); @@ -290,10 +327,9 @@ private static void GenerateNamespaceCode( 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 ) diff --git a/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs index 72467e9b..4127b0f1 100644 --- a/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs +++ b/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs @@ -3,8 +3,14 @@ namespace MochaTool.InteropGen; +/// +/// Contains functionality for generating C++ code. +/// internal static class NativeCodeGenerator { + /// + /// The header to be used at the top of generated code. + /// private static string Header => $""" //------------------------------------------------------------------------------ // @@ -17,6 +23,12 @@ internal static class NativeCodeGenerator //------------------------------------------------------------------------------ """; + /// + /// Generates and returns C++ code for a set of s. + /// + /// The path to the header file that contained the units. + /// An enumerable list of s to generate code for. + /// C++ code representing the set of s passed. internal static string GenerateCode( string headerPath, IEnumerable units ) { var (baseTextWriter, writer) = Utils.CreateWriter(); @@ -45,12 +57,20 @@ internal static string GenerateCode( string headerPath, IEnumerable units return baseTextWriter.ToString(); } - private static void GenerateNamespaceCode( IndentedTextWriter writer, Class c ) + /// + /// Generates C++ code for a class. + /// + /// The writer to append the code to. + /// The class to write code for. + private static void GenerateClassCode( IndentedTextWriter writer, Class c ) { foreach ( var method in c.Methods ) { var args = method.Parameters; + if ( !method.IsStatic ) + args = args.Prepend( new Variable( "instance", $"{c.Name}*" ) ).ToImmutableArray(); + var argStr = string.Join( ", ", args.Select( x => { if ( x.Type == "std::string" ) @@ -63,14 +83,21 @@ private static void GenerateNamespaceCode( IndentedTextWriter writer, Class c ) var body = ""; var parameters = string.Join( ", ", method.Parameters.Select( x => x.Name ) ); - var accessor = $"{c.Name}::"; - - if ( method.ReturnType == "void" ) - body += $"{accessor}{method.Name}( {parameters} );"; - else if ( method.ReturnType == "std::string" ) - body += $"std::string text = {accessor}{method.Name}( {parameters} );\r\nconst char* cstr = text.c_str();\r\nchar* dup = _strdup(cstr);\r\nreturn dup;"; + if ( method.IsConstructor ) + body += $"return new {c.Name}( {parameters} );"; + else if ( method.IsDestructor ) + body += $"instance->~{c.Name}( {parameters} );"; else - body += $"return {accessor}{method.Name}( {parameters} );"; + { + var accessor = method.IsStatic ? $"{c.Name}::" : "instance->"; + + if ( method.ReturnType == "void" ) + body += $"{accessor}{method.Name}( {parameters} );"; + else if ( method.ReturnType == "std::string" ) + body += $"std::string text = {accessor}{method.Name}( {parameters} );\r\nconst char* cstr = text.c_str();\r\nchar* dup = _strdup(cstr);\r\nreturn dup;"; + else + body += $"return {accessor}{method.Name}( {parameters} );"; + } writer.WriteLine( signature ); writer.WriteLine( "{" ); @@ -83,15 +110,17 @@ private static void GenerateNamespaceCode( IndentedTextWriter writer, Class c ) } } - private static void GenerateClassCode( IndentedTextWriter writer, Class c ) + /// + /// Generates C++ code for a namespace. + /// + /// The writer to append the code to. + /// The namespace to write code for. + private static void GenerateNamespaceCode( IndentedTextWriter writer, Class ns ) { - foreach ( var method in c.Methods ) + foreach ( var method in ns.Methods ) { var args = method.Parameters; - if ( !method.IsStatic ) - args = args.Prepend( new Variable( "instance", $"{c.Name}*" ) ).ToImmutableArray(); - var argStr = string.Join( ", ", args.Select( x => { if ( x.Type == "std::string" ) @@ -100,25 +129,18 @@ private static void GenerateClassCode( IndentedTextWriter writer, Class c ) return $"{x.Type} {x.Name}"; } ) ); - var signature = $"extern \"C\" inline {method.ReturnType} __{c.Name}_{method.Name}( {argStr} )"; + var signature = $"extern \"C\" inline {method.ReturnType} __{ns.Name}_{method.Name}( {argStr} )"; var body = ""; var parameters = string.Join( ", ", method.Parameters.Select( x => x.Name ) ); - if ( method.IsConstructor ) - body += $"return new {c.Name}( {parameters} );"; - else if ( method.IsDestructor ) - body += $"instance->~{c.Name}( {parameters} );"; - else - { - var accessor = method.IsStatic ? $"{c.Name}::" : "instance->"; + var accessor = $"{ns.Name}::"; - if ( method.ReturnType == "void" ) - body += $"{accessor}{method.Name}( {parameters} );"; - else if ( method.ReturnType == "std::string" ) - body += $"std::string text = {accessor}{method.Name}( {parameters} );\r\nconst char* cstr = text.c_str();\r\nchar* dup = _strdup(cstr);\r\nreturn dup;"; - else - body += $"return {accessor}{method.Name}( {parameters} );"; - } + if ( method.ReturnType == "void" ) + body += $"{accessor}{method.Name}( {parameters} );"; + else if ( method.ReturnType == "std::string" ) + body += $"std::string text = {accessor}{method.Name}( {parameters} );\r\nconst char* cstr = text.c_str();\r\nchar* dup = _strdup(cstr);\r\nreturn dup;"; + else + body += $"return {accessor}{method.Name}( {parameters} );"; writer.WriteLine( signature ); writer.WriteLine( "{" ); From ec7ba13648a87d322b0d355a506d9663a37f66c6 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 16 Mar 2023 20:07:54 -0400 Subject: [PATCH 49/51] Move extensions to separate namespace --- .../{ => Extensions}/CXCursorExtensions.cs | 2 +- .../MochaTool.InteropGen/{ => Extensions}/ILoggerExtensions.cs | 2 +- Source/MochaTool.InteropGen/Parser.cs | 1 + Source/MochaTool.InteropGen/Program.cs | 1 + Source/MochaTool.InteropGen/StopwatchLog.cs | 1 + Source/MochaTool.InteropGen/Utils.cs | 3 ++- 6 files changed, 7 insertions(+), 3 deletions(-) rename Source/MochaTool.InteropGen/{ => Extensions}/CXCursorExtensions.cs (94%) rename Source/MochaTool.InteropGen/{ => Extensions}/ILoggerExtensions.cs (98%) diff --git a/Source/MochaTool.InteropGen/CXCursorExtensions.cs b/Source/MochaTool.InteropGen/Extensions/CXCursorExtensions.cs similarity index 94% rename from Source/MochaTool.InteropGen/CXCursorExtensions.cs rename to Source/MochaTool.InteropGen/Extensions/CXCursorExtensions.cs index 186107be..0d385ad2 100644 --- a/Source/MochaTool.InteropGen/CXCursorExtensions.cs +++ b/Source/MochaTool.InteropGen/Extensions/CXCursorExtensions.cs @@ -1,6 +1,6 @@ using ClangSharp.Interop; -namespace MochaTool.InteropGen; +namespace MochaTool.InteropGen.Extensions; /// /// Contains extension methods for the . diff --git a/Source/MochaTool.InteropGen/ILoggerExtensions.cs b/Source/MochaTool.InteropGen/Extensions/ILoggerExtensions.cs similarity index 98% rename from Source/MochaTool.InteropGen/ILoggerExtensions.cs rename to Source/MochaTool.InteropGen/Extensions/ILoggerExtensions.cs index 1c5188a7..0a0056b9 100644 --- a/Source/MochaTool.InteropGen/ILoggerExtensions.cs +++ b/Source/MochaTool.InteropGen/Extensions/ILoggerExtensions.cs @@ -1,6 +1,6 @@ using Microsoft.Extensions.Logging; -namespace MochaTool.InteropGen; +namespace MochaTool.InteropGen.Extensions; /// /// Contains extension methods for s. diff --git a/Source/MochaTool.InteropGen/Parser.cs b/Source/MochaTool.InteropGen/Parser.cs index e0d715e5..53887de0 100644 --- a/Source/MochaTool.InteropGen/Parser.cs +++ b/Source/MochaTool.InteropGen/Parser.cs @@ -1,5 +1,6 @@ using ClangSharp.Interop; using Microsoft.Extensions.Logging; +using MochaTool.InteropGen.Extensions; using System.Collections.Immutable; namespace MochaTool.InteropGen; diff --git a/Source/MochaTool.InteropGen/Program.cs b/Source/MochaTool.InteropGen/Program.cs index 6982bf5f..c0012534 100644 --- a/Source/MochaTool.InteropGen/Program.cs +++ b/Source/MochaTool.InteropGen/Program.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Logging; +using MochaTool.InteropGen.Extensions; namespace MochaTool.InteropGen; diff --git a/Source/MochaTool.InteropGen/StopwatchLog.cs b/Source/MochaTool.InteropGen/StopwatchLog.cs index 2a3a11d4..e9ae7f09 100644 --- a/Source/MochaTool.InteropGen/StopwatchLog.cs +++ b/Source/MochaTool.InteropGen/StopwatchLog.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Logging; +using MochaTool.InteropGen.Extensions; using System.Diagnostics; namespace MochaTool.InteropGen; diff --git a/Source/MochaTool.InteropGen/Utils.cs b/Source/MochaTool.InteropGen/Utils.cs index a476fb13..e7153969 100644 --- a/Source/MochaTool.InteropGen/Utils.cs +++ b/Source/MochaTool.InteropGen/Utils.cs @@ -1,4 +1,5 @@ -using System.CodeDom.Compiler; +using MochaTool.InteropGen.Extensions; +using System.CodeDom.Compiler; namespace MochaTool.InteropGen; From 7c791ab124896c8827dc3d4cba06b02427613015 Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 16 Mar 2023 20:09:00 -0400 Subject: [PATCH 50/51] Move code generators to separate namespace --- Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs | 2 +- Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs | 2 +- Source/MochaTool.InteropGen/Program.cs | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs index 21563b6c..797d3012 100644 --- a/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs +++ b/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs @@ -1,7 +1,7 @@ using System.CodeDom.Compiler; using System.Collections.Immutable; -namespace MochaTool.InteropGen; +namespace MochaTool.InteropGen.CodeGen; /// /// Contains functionality for generating C# code. diff --git a/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs index 4127b0f1..4a500b6b 100644 --- a/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs +++ b/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs @@ -1,7 +1,7 @@ using System.CodeDom.Compiler; using System.Collections.Immutable; -namespace MochaTool.InteropGen; +namespace MochaTool.InteropGen.CodeGen; /// /// Contains functionality for generating C++ code. diff --git a/Source/MochaTool.InteropGen/Program.cs b/Source/MochaTool.InteropGen/Program.cs index c0012534..2accfeae 100644 --- a/Source/MochaTool.InteropGen/Program.cs +++ b/Source/MochaTool.InteropGen/Program.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Logging; +using MochaTool.InteropGen.CodeGen; using MochaTool.InteropGen.Extensions; namespace MochaTool.InteropGen; From 3e0fdce27ea2f79a466b73d0cbb5dcc95aa6ca1e Mon Sep 17 00:00:00 2001 From: peter-r-g Date: Thu, 16 Mar 2023 20:11:04 -0400 Subject: [PATCH 51/51] Move parsing items to separate namespace --- Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs | 3 ++- Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs | 3 ++- Source/MochaTool.InteropGen/{Units => Parsing}/Class.cs | 2 +- Source/MochaTool.InteropGen/{Units => Parsing}/IUnit.cs | 2 +- Source/MochaTool.InteropGen/{Units => Parsing}/Method.cs | 2 +- Source/MochaTool.InteropGen/{ => Parsing}/Parser.cs | 2 +- Source/MochaTool.InteropGen/{Units => Parsing}/Struct.cs | 2 +- Source/MochaTool.InteropGen/{Units => Parsing}/Variable.cs | 2 +- Source/MochaTool.InteropGen/{ => Parsing}/VcxprojParser.cs | 2 +- Source/MochaTool.InteropGen/Program.cs | 1 + 10 files changed, 12 insertions(+), 9 deletions(-) rename Source/MochaTool.InteropGen/{Units => Parsing}/Class.cs (98%) rename Source/MochaTool.InteropGen/{Units => Parsing}/IUnit.cs (96%) rename Source/MochaTool.InteropGen/{Units => Parsing}/Method.cs (99%) rename Source/MochaTool.InteropGen/{ => Parsing}/Parser.cs (99%) rename Source/MochaTool.InteropGen/{Units => Parsing}/Struct.cs (98%) rename Source/MochaTool.InteropGen/{Units => Parsing}/Variable.cs (94%) rename Source/MochaTool.InteropGen/{ => Parsing}/VcxprojParser.cs (98%) diff --git a/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs index 797d3012..eeb6a576 100644 --- a/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs +++ b/Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs @@ -1,4 +1,5 @@ -using System.CodeDom.Compiler; +using MochaTool.InteropGen.Parsing; +using System.CodeDom.Compiler; using System.Collections.Immutable; namespace MochaTool.InteropGen.CodeGen; diff --git a/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs b/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs index 4a500b6b..5721ae72 100644 --- a/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs +++ b/Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs @@ -1,4 +1,5 @@ -using System.CodeDom.Compiler; +using MochaTool.InteropGen.Parsing; +using System.CodeDom.Compiler; using System.Collections.Immutable; namespace MochaTool.InteropGen.CodeGen; diff --git a/Source/MochaTool.InteropGen/Units/Class.cs b/Source/MochaTool.InteropGen/Parsing/Class.cs similarity index 98% rename from Source/MochaTool.InteropGen/Units/Class.cs rename to Source/MochaTool.InteropGen/Parsing/Class.cs index 7a8b6b5c..0ec9c643 100644 --- a/Source/MochaTool.InteropGen/Units/Class.cs +++ b/Source/MochaTool.InteropGen/Parsing/Class.cs @@ -1,6 +1,6 @@ using System.Collections.Immutable; -namespace MochaTool.InteropGen; +namespace MochaTool.InteropGen.Parsing; /// /// Represents a class or namespace in C++. diff --git a/Source/MochaTool.InteropGen/Units/IUnit.cs b/Source/MochaTool.InteropGen/Parsing/IUnit.cs similarity index 96% rename from Source/MochaTool.InteropGen/Units/IUnit.cs rename to Source/MochaTool.InteropGen/Parsing/IUnit.cs index ee915883..ad8b68fb 100644 --- a/Source/MochaTool.InteropGen/Units/IUnit.cs +++ b/Source/MochaTool.InteropGen/Parsing/IUnit.cs @@ -1,6 +1,6 @@ using System.Collections.Immutable; -namespace MochaTool.InteropGen; +namespace MochaTool.InteropGen.Parsing; /// /// Defines a container for fields and methods defined in C++. diff --git a/Source/MochaTool.InteropGen/Units/Method.cs b/Source/MochaTool.InteropGen/Parsing/Method.cs similarity index 99% rename from Source/MochaTool.InteropGen/Units/Method.cs rename to Source/MochaTool.InteropGen/Parsing/Method.cs index 119549c1..8b9540cc 100644 --- a/Source/MochaTool.InteropGen/Units/Method.cs +++ b/Source/MochaTool.InteropGen/Parsing/Method.cs @@ -1,6 +1,6 @@ using System.Collections.Immutable; -namespace MochaTool.InteropGen; +namespace MochaTool.InteropGen.Parsing; /// /// Represents a method in C++. diff --git a/Source/MochaTool.InteropGen/Parser.cs b/Source/MochaTool.InteropGen/Parsing/Parser.cs similarity index 99% rename from Source/MochaTool.InteropGen/Parser.cs rename to Source/MochaTool.InteropGen/Parsing/Parser.cs index 53887de0..14810da4 100644 --- a/Source/MochaTool.InteropGen/Parser.cs +++ b/Source/MochaTool.InteropGen/Parsing/Parser.cs @@ -3,7 +3,7 @@ using MochaTool.InteropGen.Extensions; using System.Collections.Immutable; -namespace MochaTool.InteropGen; +namespace MochaTool.InteropGen.Parsing; /// /// Contains all parsing functionality for C++ header files. diff --git a/Source/MochaTool.InteropGen/Units/Struct.cs b/Source/MochaTool.InteropGen/Parsing/Struct.cs similarity index 98% rename from Source/MochaTool.InteropGen/Units/Struct.cs rename to Source/MochaTool.InteropGen/Parsing/Struct.cs index 7543692f..d48fd6ba 100644 --- a/Source/MochaTool.InteropGen/Units/Struct.cs +++ b/Source/MochaTool.InteropGen/Parsing/Struct.cs @@ -1,6 +1,6 @@ using System.Collections.Immutable; -namespace MochaTool.InteropGen; +namespace MochaTool.InteropGen.Parsing; /// /// Represents a struct in C++. diff --git a/Source/MochaTool.InteropGen/Units/Variable.cs b/Source/MochaTool.InteropGen/Parsing/Variable.cs similarity index 94% rename from Source/MochaTool.InteropGen/Units/Variable.cs rename to Source/MochaTool.InteropGen/Parsing/Variable.cs index a3e86d63..60c5e1e8 100644 --- a/Source/MochaTool.InteropGen/Units/Variable.cs +++ b/Source/MochaTool.InteropGen/Parsing/Variable.cs @@ -1,4 +1,4 @@ -namespace MochaTool.InteropGen; +namespace MochaTool.InteropGen.Parsing; /// /// Represents a variable in C++. This can be a field, parameter, etc. diff --git a/Source/MochaTool.InteropGen/VcxprojParser.cs b/Source/MochaTool.InteropGen/Parsing/VcxprojParser.cs similarity index 98% rename from Source/MochaTool.InteropGen/VcxprojParser.cs rename to Source/MochaTool.InteropGen/Parsing/VcxprojParser.cs index 92068165..d7317fca 100644 --- a/Source/MochaTool.InteropGen/VcxprojParser.cs +++ b/Source/MochaTool.InteropGen/Parsing/VcxprojParser.cs @@ -1,6 +1,6 @@ using System.Xml; -namespace MochaTool.InteropGen; +namespace MochaTool.InteropGen.Parsing; /// /// Contains functionality for parsing vcxproj files. diff --git a/Source/MochaTool.InteropGen/Program.cs b/Source/MochaTool.InteropGen/Program.cs index 2accfeae..6e815d58 100644 --- a/Source/MochaTool.InteropGen/Program.cs +++ b/Source/MochaTool.InteropGen/Program.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.Logging; using MochaTool.InteropGen.CodeGen; using MochaTool.InteropGen.Extensions; +using MochaTool.InteropGen.Parsing; namespace MochaTool.InteropGen;