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

Add .NET Swift interop tooling components and layout #312

Closed
37 changes: 25 additions & 12 deletions proposed/swift-interop.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,33 +152,46 @@ If possible, Swift tuples should be represented as `ValueTuple`s in .NET. If thi

#### Projection Tooling Components

The projection tooling should be split into these components:
The projection tooling will be based on the [Binding Tools for Swift](https://github.com/xamarin/binding-tools-for-swift) (BTfS). The BTfS contains a set of tools that can consume a compiled Apple Swift library and generate wrappers (bindings) that allow it to be surfaced as a .NET library. This tool will be implemented as a self-hosted .NET CLI tool, automating the generation of wrappers by parsing the Swift library interface. It generates C# bindings that can be utilized as a .NET library and Swift bindings that C# bindings invoke in cases where direct P/Invoke into Swift library is not possible.
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved

The projection tooling should be split into these components.

##### Importing Swift into .NET
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved

1. A tool that takes in a `.swiftinterface` file or Swift sources and produces C# code.
2. A library that provides the basic support for Swift interop that the generated code builds on.
3. User tooling to easily generate Swift projections for a given set of `.framework`s.
- This tooling would build a higher-level interface on top of the tool in item 1 that is more user-friendly and project-system-integrated.
4. (optional) A NuGet package, possibly referencable by `FrameworkReference` or automatically included when targeting macOS, Mac Catalyst, iOS, or tvOS platforms that exposes the platform APIs for each `.framework` that is exposed from Swift to .NET.
- This would be required to provide a single source of truth for Swift types so they can be exposed across an assembly boundary.
Importing Swift into .NET is done through bindings generated by the following components:
- `Demangler`: This component analyzes a source Swift library and maps entry points to mangled names. It consumes a source Swift library and creates `TLDefinition` objects that contain type, mangled name, demangled name, module name, and offset within a module.
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved
- `Swiftmodule parser`: This component aggregates the public API. It takes in a `.swiftmodule` file generated by the Swift compiler, similar to a C header file, and combines it with the `TLDefinition` objects to generate a module declaration.
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved
- `WrappingCompiler` and `OverrideBuilder`: These components generate Swift wrappers for cases where direct P/Invoke from C# into Swift is not possible.
- `NewClassCompiler`: This component generates C# bindings based on the generated `TLDefinition` and module declarations. It should implement type mapping and register lowering. Key considerations include:
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved
- Splitting out tuples into individual parameters
- Determining the return buffer size using the value witness table for the struct type
- Managing enum/struct arguments through registers vs. passing by reference
- Error handling
- `XamGlue`: This component is a library that provides basic support for Swift interop that the generated code builds on. It includes bindings for common built-in types like arrays and dictionaries. It provides a single source of truth for Swift types so that they can be exposed across an assembly boundary. This can be a NuGet package, possibly referencable by `FrameworkReference` or automatically included when targeting macOS, Mac Catalyst, iOS, or tvOS platforms that exposes the platform APIs for each `.framework` that is exposed from Swift to .NET.
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved
- `Tom-Swifty`: This component orchestrates other components and allows users to easily generate Swift projections for a given set of `.framework`s. It presents an interface on top of the tool that is user-friendly and project-system-integrated.
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved

The flow starts with the source Swift library, which is processed by the Demangler to generate `TLDefinition` objects. Then, the `Swiftmodule parser` aggregates the public API from `.swiftmodule` file and generates a module declaration. Both the `TLDefinition` objects and module declaration are then used as inputs for two distinct components: the `WrappingCompiler` and `OverrideBuilder`. These components generate Swift wrappers source code. The wrappers source code is subsequently processed again by the `Demangler` and `Swiftmodule parser` leading to the generation of wrapper library `TLDefinition` objects and its module declaration. Finally, `TLDefinition` and module declarations, both for the source Swift library and Swift wrappers, are consumed by the `NewClassCompiler` to generate C# bindings.

##### Exporting .NET to Swift
##### Exporting .NET to Swift

There are two components to exporting .NET to Swift: Implementing existing Swift types in .NET and passing instances of those types to Swift, and exposing novel types from .NET code to Swift code to be created from Swift. Exposing novel types from .NET code to Swift code is considered out of scope at this time.

For implementing existing Swift types in .NET, we will require one of the following tooling options:

1. A Roslyn source generator to generate any supporting code needed to produce any required metadata, such as type metadata and witness tables, to pass instances of Swift-type-implementing .NET types defined in the current project to Swift.
2. An IL-post-processing tool to generate the supporting code and metadata from the compiled assembly.
2. An IL-post-processing tool to generate the supporting code and metadata from the compiled assembly.

If we were to use an IL-post-processing tool here, we would break Hot Reload in assemblies that implement Swift types, even for .NET-only code, due to introducing new tokens that the Hot Reload "client" (aka Roslyn) does not know about. As a result, we should prefer the Roslyn source generator approach.
If we were to use an IL-post-processing tool here, we would break Hot Reload in assemblies that implement Swift types, even for .NET-only code, due to introducing new tokens that the Hot Reload "client" (aka Roslyn) does not know about. As a result, we should prefer the Roslyn source generator approach. The existing tool [supports exporting .NET types to Swift](https://github.com/xamarin/binding-tools-for-swift/blob/main/docs/FutureIdeas.md#exposing-c-to-swift) that can work as a source generator.

For a C# class that can be inherited gets implemented in C#, the tool generates a mirror class in Swift and overrides it with another class that vectors all the calls through a simulated vtable. This class implements the constructor and overrides the virtual methods by sending calls to a function pointer which ends up in C#.

### Distribution

The calling convention work will be implemented by the .NET runtimes in dotnet/runtime.
The calling convention work will be implemented by the .NET runtimes in dotnet/runtime. The projection tooling will be implemented and shipped as part of the Xamarin publishing infrastructure. The projection tooling will be accessible as a .NET CLI tool and distributed as a NuGet package. It should either be automatically included as part of the TPMs for Apple platforms or should be easily referencable.
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved

### Validation

The projection tooling will not ship as part of the runtime. It should be available as a separate NuGet package, possibly as a .NET CLI tool package. The projections should either be included automatically as part of the TPMs for macOS, iOS, and tvOS, or should be easily referenceable.
The interop will be showcased through MAUI samples. List of libraries and MAUI samples to support in .NET 9 are outlined in https://github.com/dotnet/runtime/issues/95636.

## Q & A

Expand Down
Loading