-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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 experimental Swift bindings for CryptoKit #108318
Closed
kotlarmilos
wants to merge
9
commits into
dotnet:main
from
kotlarmilos:improvement/cryptokit-bindings
Closed
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
c7ab93f
Add C# CryptoKit bindings and remove Swift wrapper
kotlarmilos acb1756
Use CryptographicException for error handling
kotlarmilos eed991e
Remove DynamicLibraryLoader and use NativeLibrary
kotlarmilos 55c459e
Reorder Data members
jkurdek c8d73f7
Refactor tests to have C#-like surface and move Swift-specific featur…
kotlarmilos 2e05e17
Update bindings to follow coding guidelines
kotlarmilos 9b1b2a9
Check for errors in GetConformanceDescriptor
kotlarmilos 302fdcf
Add IDisposable for non-frozen types in CryptoKit
kotlarmilos ad43d2d
Retrieve layout size from value witness table
kotlarmilos File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
494 changes: 494 additions & 0 deletions
494
src/libraries/Common/src/Interop/OSX/Swift.Runtime/CryptoKit.cs
Large diffs are not rendered by default.
Oops, something went wrong.
278 changes: 278 additions & 0 deletions
278
src/libraries/Common/src/Interop/OSX/Swift.Runtime/Foundation.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,278 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
using System.Runtime.CompilerServices; | ||
using System.Runtime.InteropServices; | ||
using System.Reflection; | ||
|
||
#pragma warning disable CS3016 // Arrays as attribute arguments are not CLS Compliant | ||
#pragma warning disable SYSLIB1051 | ||
#pragma warning disable IDE0060 | ||
|
||
namespace Swift | ||
{ | ||
/// <summary> | ||
/// Represents a Swift type in C#. | ||
/// </summary> | ||
internal unsafe interface ISwiftObject | ||
{ | ||
public static abstract void* Metadata { get; } | ||
} | ||
|
||
// <summary> | ||
// Represents Swift UnsafePointer in C#. | ||
// </summary> | ||
internal readonly unsafe struct UnsafePointer<T> where T : unmanaged | ||
{ | ||
private readonly T* _pointee; | ||
public UnsafePointer(T* pointee) | ||
{ | ||
this._pointee = pointee; | ||
} | ||
|
||
public T* Pointee => _pointee; | ||
|
||
public static implicit operator T*(UnsafePointer<T> pointer) => pointer.Pointee; | ||
|
||
public static implicit operator UnsafePointer<T>(T* pointee) => new(pointee); | ||
} | ||
|
||
// <summary> | ||
// Represents Swift UnsafeMutablePointer in C#. | ||
// </summary> | ||
internal readonly unsafe struct UnsafeMutablePointer<T> where T : unmanaged | ||
{ | ||
private readonly T* _pointee; | ||
public UnsafeMutablePointer(T* pointee) | ||
{ | ||
_pointee = pointee; | ||
} | ||
|
||
public T* Pointee => _pointee; | ||
|
||
public static implicit operator T*(UnsafeMutablePointer<T> pointer) => pointer.Pointee; | ||
|
||
public static implicit operator UnsafeMutablePointer<T>(T* pointee) => new(pointee); | ||
} | ||
|
||
// <summary> | ||
// Represents Swift UnsafeRawPointer in C#. | ||
// </summary> | ||
internal readonly unsafe struct UnsafeRawPointer | ||
{ | ||
private readonly void* _pointee; | ||
public UnsafeRawPointer(void* pointee) | ||
{ | ||
_pointee = pointee; | ||
} | ||
|
||
public void* Pointee => _pointee; | ||
|
||
public static implicit operator void*(UnsafeRawPointer pointer) => pointer.Pointee; | ||
|
||
public static implicit operator UnsafeRawPointer(void* pointee) => new(pointee); | ||
} | ||
|
||
// <summary> | ||
// Represents Swift UnsafeMutableRawPointer in C#. | ||
// </summary> | ||
internal readonly unsafe struct UnsafeMutableRawPointer | ||
{ | ||
private readonly void* _pointee; | ||
public UnsafeMutableRawPointer(void* pointee) | ||
{ | ||
_pointee = pointee; | ||
} | ||
|
||
public void* Pointee => _pointee; | ||
|
||
public static implicit operator void*(UnsafeMutableRawPointer pointer) => pointer.Pointee; | ||
|
||
public static implicit operator UnsafeMutableRawPointer(void* pointee) => new(pointee); | ||
} | ||
|
||
// <summary> | ||
// Represents Swift UnsafeBufferPointer in C#. | ||
// </summary> | ||
internal readonly unsafe struct UnsafeBufferPointer<T> where T : unmanaged | ||
{ | ||
private readonly T* _baseAddress; | ||
private readonly nint _count; | ||
public UnsafeBufferPointer(T* baseAddress, nint count) | ||
{ | ||
_baseAddress = baseAddress; | ||
_count = count; | ||
} | ||
|
||
public T* BaseAddress => _baseAddress; | ||
public nint Count => _count; | ||
} | ||
|
||
// <summary> | ||
// Represents Swift UnsafeMutableBufferPointer in C#. | ||
// </summary> | ||
internal readonly unsafe struct UnsafeMutableBufferPointer<T> where T : unmanaged | ||
{ | ||
private readonly T* _baseAddress; | ||
private readonly nint _count; | ||
public UnsafeMutableBufferPointer(T* baseAddress, nint count) | ||
{ | ||
_baseAddress = baseAddress; | ||
_count = count; | ||
} | ||
|
||
public T* BaseAddress => _baseAddress; | ||
public nint Count => _count; | ||
} | ||
|
||
// <summary> | ||
// Represents Swift Foundation.Data in C#. | ||
// </summary> | ||
[StructLayout(LayoutKind.Sequential, Size = 16)] | ||
[InlineArray(16)] | ||
internal unsafe partial struct Data : ISwiftObject | ||
{ | ||
private byte _payload; | ||
|
||
internal unsafe Data(UnsafeRawPointer pointer, nint count) | ||
{ | ||
this = Foundation.PInvoke_Data_InitWithBytes(pointer, count); | ||
} | ||
|
||
internal byte Payload => _payload; | ||
|
||
internal readonly nint Count => Foundation.PInvoke_Data_GetCount(this); | ||
|
||
internal unsafe void CopyBytes(UnsafeMutablePointer<byte> buffer, nint count) | ||
{ | ||
Foundation.PInvoke_Data_CopyBytes(buffer, count, this); | ||
} | ||
|
||
public static void* Metadata => Foundation.PInvoke_Data_GetMetadata(); | ||
} | ||
|
||
/// <summary> | ||
/// Represents Swift Foundation.DataProtocol in C#. | ||
/// </summary> | ||
internal unsafe interface IDataProtocol | ||
{ | ||
public static void* GetConformanceDescriptor => Runtime.GetConformanceDescriptor("$s10Foundation4DataVAA0B8ProtocolAAMc"); | ||
} | ||
|
||
/// <summary> | ||
/// Represents Swift Foundation.ContiguousBytes in C#. | ||
/// </summary> | ||
internal unsafe interface IContiguousBytes | ||
{ | ||
public static void* GetConformanceDescriptor => Runtime.GetConformanceDescriptor("$s10Foundation4DataVAA15ContiguousBytesAAMc"); | ||
} | ||
|
||
/// <summary> | ||
/// Swift Foundation PInvoke methods in C#. | ||
/// </summary> | ||
internal static partial class Foundation | ||
{ | ||
internal const string Path = "/System/Library/Frameworks/Foundation.framework/Foundation"; | ||
|
||
[LibraryImport(Path, EntryPoint = "$s10Foundation4DataV5bytes5countACSV_SitcfC")] | ||
[UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] | ||
internal static unsafe partial Data PInvoke_Data_InitWithBytes(UnsafeRawPointer pointer, nint count); | ||
|
||
[LibraryImport(Path, EntryPoint = "$s10Foundation4DataV5countSivg")] | ||
[UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] | ||
internal static unsafe partial nint PInvoke_Data_GetCount(Data data); | ||
|
||
[LibraryImport(Path, EntryPoint = "$s10Foundation4DataV9copyBytes2to5countySpys5UInt8VG_SitF")] | ||
[UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] | ||
internal static unsafe partial void PInvoke_Data_CopyBytes(UnsafeMutablePointer<byte> buffer, nint count, Data data); | ||
|
||
[LibraryImport(Path, EntryPoint = "swift_getWitnessTable")] | ||
[UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] | ||
internal static unsafe partial void* PInvoke_Swift_GetWitnessTable(void* conformanceDescriptor, void* typeMetadata, void* instantiationArgs); | ||
|
||
[LibraryImport(Path, EntryPoint = "$s10Foundation4DataVMa")] | ||
[UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] | ||
internal static unsafe partial void* PInvoke_Data_GetMetadata(); | ||
} | ||
|
||
/// <summary> | ||
/// Swift runtime helper methods in C#. | ||
/// </summary> | ||
internal static partial class Runtime | ||
{ | ||
/// <summary> | ||
/// https://github.com/apple/swift/blob/main/include/swift/ABI/MetadataValues.h#L117 | ||
/// </summary> | ||
[Flags] | ||
public enum ValueWitnessFlags | ||
{ | ||
AlignmentMask = 0x0000FFFF, | ||
IsNonPOD = 0x00010000, | ||
IsNonInline = 0x00020000, | ||
HasSpareBits = 0x00080000, | ||
IsNonBitwiseTakable = 0x00100000, | ||
HasEnumWitnesses = 0x00200000, | ||
Incomplete = 0x00400000, | ||
} | ||
|
||
/// <summary> | ||
/// See https://github.com/apple/swift/blob/main/include/swift/ABI/ValueWitness.def | ||
/// </summary> | ||
[StructLayout (LayoutKind.Sequential)] | ||
public ref struct ValueWitnessTable | ||
{ | ||
public IntPtr InitializeBufferWithCopyOfBuffer; | ||
public IntPtr Destroy; | ||
public IntPtr InitWithCopy; | ||
public IntPtr AssignWithCopy; | ||
public IntPtr InitWithTake; | ||
public IntPtr AssignWithTake; | ||
public IntPtr GetEnumTagSinglePayload; | ||
public IntPtr StoreEnumTagSinglePayload; | ||
private IntPtr _Size; | ||
private IntPtr _Stride; | ||
public ValueWitnessFlags Flags; | ||
public uint ExtraInhabitantCount; | ||
public int Size => _Size.ToInt32(); | ||
public int Stride => _Stride.ToInt32(); | ||
public int Alignment => (int)((Flags & ValueWitnessFlags.AlignmentMask) + 1); | ||
public bool IsNonPOD => Flags.HasFlag (ValueWitnessFlags.IsNonPOD); | ||
public bool IsNonBitwiseTakable => Flags.HasFlag (ValueWitnessFlags.IsNonBitwiseTakable); | ||
public bool HasExtraInhabitants => ExtraInhabitantCount != 0; | ||
} | ||
|
||
internal static unsafe void* GetMetadata<T>(T type) where T: ISwiftObject | ||
{ | ||
return T.Metadata; | ||
} | ||
|
||
internal static unsafe void* GetValueWitnessTable(void* metadata) | ||
{ | ||
void* valueWitnessTable = (void*)Marshal.ReadIntPtr((IntPtr)metadata, -IntPtr.Size); | ||
return valueWitnessTable; | ||
} | ||
|
||
internal static unsafe void* GetConformanceDescriptor(string symbol) | ||
{ | ||
IntPtr handle = IntPtr.Zero; | ||
try | ||
{ | ||
handle = NativeLibrary.Load(Foundation.Path); | ||
void* conformanceDescriptor = NativeLibrary.GetExport(handle, symbol).ToPointer(); | ||
return conformanceDescriptor; | ||
} | ||
catch (Exception ex) | ||
{ | ||
throw new InvalidOperationException($"Failed to get conformance descriptor for symbol: {symbol}", ex); | ||
} | ||
finally | ||
{ | ||
if (handle != IntPtr.Zero) | ||
{ | ||
NativeLibrary.Free(handle); | ||
} | ||
} | ||
} | ||
} | ||
} |
39 changes: 0 additions & 39 deletions
39
src/libraries/Common/src/Interop/OSX/Swift.Runtime/UnsafeBufferPointer.cs
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pointers all need some revision. Swift has very specific semantics that surround pointers that are used to ensure that they play nicely with the swift memory model. It's is not OK to initialize the pointer value to a
T *
.Swift unmanaged pointers have the following states:
On top of that, allocation and deallocation should always be owned by Swift and not by us.
Imagine this scenario in Swift:
If we call this from C# with a non-swift allocated and initialized UnsafeMutablePointer, we're in a load of trouble.
Similarly, in the mutable versions of these,
Pointee
should be get/set, but we should call swift to make the changes because the Swift runtime will take care of using the appropriate witness table methods to ensure that reference counting works properly.