Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update parser and emitter to support nested Swift types #2548

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

26 changes: 26 additions & 0 deletions src/Swift.Bindings/src/Emitter/StringCSharpEmitter/ClassEmitter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.CodeDom.Compiler;

namespace BindingsGeneration
{
public partial class StringCSharpEmitter : ICSharpEmitter
{
/// <summary>
/// Emits a class declaration.
/// </summary>
/// <param name="writer">The IndentedTextWriter instance.</param>
/// <param name="moduleDecl">The module declaration.</param>
/// <param name="decl">The class declaration.</param>
private void EmitClass(IndentedTextWriter writer, ModuleDecl moduleDecl, ClassDecl classDecl)
{
writer.WriteLine($"public unsafe class {classDecl.Name} {{");
writer.Indent++;
foreach (BaseDecl baseDecl in classDecl.Declarations)
EmitBaseDecl(writer, moduleDecl, baseDecl);
writer.Indent--;
writer.WriteLine("}");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.CodeDom.Compiler;

namespace BindingsGeneration
{
public partial class StringCSharpEmitter : ICSharpEmitter
{
/// <summary>
/// Emits the method declaration.
/// </summary>
/// <param name="writer">The IndentedTextWriter instance.</param>
/// <param name="moduleDecl">The module declaration.</param>
/// <param name="decl">The method declaration.</param>
private void EmitMethod(IndentedTextWriter writer, ModuleDecl moduleDecl, MethodDecl methodDecl)
{
var accessModifier = methodDecl.RequireMarshalling ? "internal" : "public";

// Write the P/Invoke attribute and method signature
writer.WriteLine("[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSwift) })]");
writer.WriteLine($"[DllImport(\"{moduleDecl.Name}.dylib\", EntryPoint = \"{methodDecl.MangledName}\")]");
writer.Write($"{accessModifier} static extern {methodDecl.Signature.First().TypeIdentifier} {GetMethodName(methodDecl)}(");
EmitMethodParams(writer, methodDecl.Signature.Skip(1));
writer.WriteLine($");");

// Emit the wrapper method if marshalling is required
if (methodDecl.RequireMarshalling)
{
writer.Write($"public {(methodDecl.IsStatic ? "static" : "")} {methodDecl.Signature.First().TypeIdentifier} {methodDecl.Name}(");
EmitMethodParams(writer, methodDecl.Signature.Skip(1));
writer.WriteLine($")");
writer.WriteLine($"{{");
writer.Indent++;

string returnPrefix = methodDecl.Signature.First().TypeIdentifier != "void" ? "return " : "";
writer.WriteLine($"{returnPrefix}{PInvokePrefix}{methodDecl.Name}({GetMethodArguments(methodDecl)});");

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

/// <summary>
/// Constructs the method name with optional prefix based on PInvoke requirement.
/// </summary>
private string GetMethodName(MethodDecl decl)
{
return decl.RequireMarshalling ? $"{PInvokePrefix}{decl.Name}" : decl.Name;
}

/// <summary>
/// Formats method arguments for invocation.
/// </summary>
private string GetMethodArguments(MethodDecl decl)
{
return string.Join(", ", decl.Signature.Skip(1).Select(p => p.Name));
}

/// <summary>
/// Emits parameters for the method.
/// </summary>
private void EmitMethodParams(IndentedTextWriter writer, IEnumerable<TypeDecl> parameters)
{
writer.Write(string.Join(", ", parameters.Select(p => $"{p.TypeIdentifier} {p.Name}")));
}
}
}
101 changes: 101 additions & 0 deletions src/Swift.Bindings/src/Emitter/StringCSharpEmitter/ModuleEmitter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.CodeDom.Compiler;
using Swift.Runtime;

namespace BindingsGeneration
{
/// <summary>
/// Represents an string-based C# emitter.
/// </summary>
public partial class StringCSharpEmitter : ICSharpEmitter
{
// Literals in generated source
private const string PInvokePrefix = "PIfunc_";

// Private properties
private readonly string _outputDirectory;
private readonly TypeDatabase _typeDatabase;
private readonly int _verbose;

/// <summary>
/// Initializes a new instance of the <see cref="StringCSharpEmitter"/> class.
/// </summary>
public StringCSharpEmitter(string outputDirectory, TypeDatabase typeDatabase, int verbose = 0)
{
_outputDirectory = outputDirectory;
_typeDatabase = typeDatabase;
_verbose = verbose;
}

/// <summary>
/// Emits a C# module based on the module declaration.
/// </summary>
/// <param name="moduleDecl">The module declaration.</param>
public void EmitModule(ModuleDecl moduleDecl)
{
var sw = new StringWriter();
IndentedTextWriter writer = new(sw);
var generatedNamespace = $"{moduleDecl.Name}Bindings";

foreach (var dependency in moduleDecl.Dependencies)
writer.WriteLine($"using {dependency};");

writer.WriteLine();
writer.WriteLine($"namespace {generatedNamespace}");
writer.WriteLine("{");
writer.Indent++;

// Emit top-level methods
if (moduleDecl.Declarations.OfType<MethodDecl>().Any())
{
writer.WriteLine($"public class {moduleDecl.Name}");
writer.WriteLine("{");
writer.Indent++;
foreach(MethodDecl methodDecl in moduleDecl.Declarations.OfType<MethodDecl>())
{
EmitMethod(writer, moduleDecl, methodDecl);
writer.WriteLine();
}
writer.Indent--;
writer.WriteLine("}");
writer.WriteLine();
}

// Emit top-level types
foreach (BaseDecl baseDecl in moduleDecl.Declarations.Where(d => !(d is MethodDecl)))
{
EmitBaseDecl(writer, moduleDecl, baseDecl);
writer.WriteLine();
}

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

string csOutputPath = Path.Combine(_outputDirectory, $"{generatedNamespace}.cs");
using (StreamWriter outputFile = new StreamWriter(csOutputPath))
{
outputFile.Write(sw.ToString());
}
}

/// <summary>
/// Emits a base declaration.
/// </summary>
/// <param name="writer">The IndentedTextWriter instance.</param>
/// <param name="moduleDecl">The module declaration.</param>
/// <param name="decl">The base declaration.</param>
private void EmitBaseDecl(IndentedTextWriter writer, ModuleDecl moduleDecl, BaseDecl decl)
{
if (decl is StructDecl structDecl)
EmitStruct(writer, moduleDecl, structDecl);
else if (decl is ClassDecl classDecl)
EmitClass(writer, moduleDecl, classDecl);
else if (decl is MethodDecl methodDecl)
EmitMethod(writer, moduleDecl, methodDecl);
else
throw new NotImplementedException($"Unsupported declaration type: {decl.GetType().Name}");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.CodeDom.Compiler;

namespace BindingsGeneration
{
public partial class StringCSharpEmitter : ICSharpEmitter
{
/// <summary>
/// Emits a struct declaration.
/// </summary>
/// <param name="writer">The IndentedTextWriter instance.</param>
/// <param name="moduleDecl">The module declaration.</param>
/// <param name="decl">The struct declaration.</param>
private void EmitStruct(IndentedTextWriter writer, ModuleDecl moduleDecl, StructDecl structDecl)
{
writer.WriteLine($"public unsafe struct {structDecl.Name} {{");
writer.Indent++;
foreach (BaseDecl baseDecl in structDecl.Declarations)
EmitBaseDecl(writer, moduleDecl, baseDecl);
writer.Indent--;
writer.WriteLine("}");
}
}
}
21 changes: 21 additions & 0 deletions src/Swift.Bindings/src/Model/BaseDecl.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace BindingsGeneration
{
/// <summary>
/// Represents a base declaration.
/// </summary>
public record BaseDecl
{
/// <summary>
/// Name of the struct.
/// </summary>
public required string Name { get; set; }

/// <summary>
/// Declarations within the base declaration.
/// </summary>
public required List<BaseDecl> Declarations { get; set; }
}
}
Loading