-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
Background and Motivation
The Swift programming language has a different ABI, runtime environment, and object model, making it challenging to call from the .NET without the runtime support. Ideally, we want to avoid generating extra wrappers in the objection tools and attempt to directly call all kinds of Swift functions.
For interop developers of managed APIs wrapping native Swift API layers, reconciling the Swift ABI with the existing P/Invoke options is close to intractable. The proposal would be to add built-in support for the Swift ABI via P/Invokes and Reverse P/Invokes through UnmanagedCallConvAttribute
. This would directly help out binding efforts in the MAUI space.
Proposed API
According to the calling convention, the self
context has dedicated registers, and it is always passed through them since it's heavily used. Methods calling other methods on the same object can share the self context.
Here are cases when self
context is passed via register:
- Instance methods on class types: pointer to self
- Class methods: pointer to type metadata (which may be subclass metadata)
- Mutating method on value types: pointer to the value (i.e. value is passed indirectly)
- Non-mutating methods on value types: self may fit in one or more registers, else passed indirectly
- Thick closures, i.e. closures requiring a context: the closure context
Error handling is also handled through registers, so the caller needs to check for error
and throw if necessary. Implementing P/Invoke thunks should simplify registers juggling by using predefined set of registers in these scenarios.
namespace System.Runtime.CompilerServices
{
+ public class CallConvSwift
+ {
+ public CallConvSwift() { }
+ }
}
Based on the design doc we want to introduce types to represent each of the special registers.
+ namespace System.Runtime.InteropServices.Swift
+ {
+ public readonly struct SwiftSelf
+ {
+ public SwiftSelf(IntPtr value)
+ {
+ Value = value;
+ }
+
+ public IntPtr Value { get; }
+ }
+
+ public readonly struct SwiftError
+ {
+ public SwiftError(IntPtr value)
+ {
+ Value = value;
+ }
+
+ public IntPtr Value { get; }
+ }
+ }
Usage Examples
This API would be used in the P/Invoke and Reverse P/Invoke syntax as follows:
using System.Runtime.InteropServices.Swift;
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSwift) })]
[DllImport("SwiftLibrary", EntryPoint = "export")]
public unsafe static extern nint conditionallyThrowError(bool willThrow, SwiftError* error);
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSwift) })]
[DllImport("SwiftLibrary", EntryPoint = "export")]
public static extern nint getMagicNumber(SwiftSelf self);
Using the CallConv*
approach would also permit use in C# unmanaged
function pointers. This would follow the Swift ABI during dispatch. Documentation for the Swift ABI can be found here and its design philosophy here.
/cc @dotnet/jit-contrib @dotnet/interop-contrib @Redth
Metadata
Metadata
Assignees
Labels
Type
Projects
Status