Skip to content

Conversation

kotlarmilos
Copy link
Member

Description

This PR improves the implementation of the Foundation.Data and Swift.String projections by adding methods required by CryptoKit and by using a struct for ToString callback context.

Additionally, the CryptoKit tests have been updated. The Foundation.cs has been removed, and CryptoKit.cs has been refactored to align more closely with the tooling output.

Out of scope

This PR doesn't fully replace CryptoKit.cs with generated bindings. The follow-up work will be done as outlined in #2999.

Fixes #2992

@kotlarmilos kotlarmilos self-assigned this Feb 13, 2025
@kotlarmilos kotlarmilos added the area-SwiftBindings Swift bindings for .NET label Feb 13, 2025
{
fixed (void* _payloadPtr = &this)
{
metadata.ValueWitnessTable->InitializeWithCopy((void*)swiftDest, (void*)_payloadPtr, metadata);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a broader question (not just Data): In MarshalToSwift we call InitializeWithCopy, when copying the content to a native memory buffer. But in the NewFromPayload (and in the constructor) we make a payload copy with just "memcpy". The question is why is it OK to do a simple memory copy when bringing the data into managed but it's not OK to do it when creating a native memory copy? It feels inconsistent.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've discussed this offline and it seems that the existing copy constructor is not valid.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry I was not able to be there for the discussion. Do we have some kind of writeup which describes the outcomes? We'll probably need some kind of rules to follow in other projections as well.

Copy link
Member Author

@kotlarmilos kotlarmilos Feb 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, sorry for the vague response -- I was waiting for this PR to streamline the feedback: #3008

Copy link

@jkurdek jkurdek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work!

Comment on lines +36 to +39
_protocolConformanceSymbols = new Dictionary<Type, string> {
{ typeof(ISwiftDataProtocol), "$s10Foundation4DataVAA0B8ProtocolAAMc" },
{ typeof(ISwiftContiguousBytes), "$s10Foundation4DataVAA15ContiguousBytesAAMc" },
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side note (definitely not this PR): This induces a cost to just touching the Data type - it's basically a startup cost. But there's no good reason for it - this is data which is hardcoded on the type. What if we encoded this in "code". Basically instead generating code which fills dictionary - generate a switch statement. For smallish number of cases the perf is probably better for switch anyway and there's 0 startup cost (with AOT it's true 0, with JIT it's as lazy as it can be).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, thanks. Added to #2990.

{
private long _flags;
private IntPtr _object;

private static nuint _payloadSize = SwiftObjectHelper<Data>.GetTypeMetadata().Size;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since I'm nit picking and posting unrelated comments: This is another "startup cost" - this leads to several things:

  • It gets the metadata (PInvoke)
  • caches it
  • Gets the size (another PInvoke)

And all of this will happen the first time anything just touches the Data type. While in reality we only need this when the type is instantiated (or maybe even later on).

Additionally I just noticed that this doesn't seem to be used anywhere... ???

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should only add this when SwiftIndirectResult is used, since it appears to be the only case. I've added this to. Added to #2990.

Copy link
Member

@vitek-karas vitek-karas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The projection looks good to me - thanks a lot.

@kotlarmilos kotlarmilos merged commit 4095751 into dotnet:feature/swift-bindings Feb 19, 2025
6 checks passed
{
private long _flags;
private IntPtr _object;

private static nuint _payloadSize = SwiftObjectHelper<Data>.GetTypeMetadata().Size;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: dotnet/runtime naming convention is to prefix static fields with s_ - in case you would like to be consistent with it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, we will update the generated and manual bindings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-SwiftBindings Swift bindings for .NET
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants