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

Use the same method to generate DllImportAttribute in local methods and P/Invoke forwarders #103973

Closed

Conversation

jtschuster
Copy link
Member

We should forward the LibraryImpot.StringMarshalling as DllImport.CharSet to local P/Invokes too, not just forwarded DllImport's.

Copy link
Contributor

Tagging subscribers to this area: @dotnet/interop-contrib
See info in area-owners.md if you want to be subscribed.

@elinor-fung
Copy link
Member

Can we add a test for this?

@elinor-fung
Copy link
Member

elinor-fung commented Jun 25, 2024

Perhaps I'm missing some nuance here, but how do we get into the valid situation where we need this set on the generated local p/invoke and not just the forwarded one?

From what I can tell, we must:

  1. Not just be using a forwarder stub
    • The signature is not blittable, we determined that the target framework is supported, and all types have marshalling info
  2. Be generating a non-blittable local p/invoke such that it needs the CharSet
    • We're not handling marshalling for some parameter/return

Shouldn't we have errored on (2)? Or determined that we didn't have marshalling info in (1), so should forward?

@karelz karelz added this to the 9.0.0 milestone Jun 25, 2024
@jkoritzinsky
Copy link
Member

This is only applicable on the dotnet/runtime-only downlevel support, where we'll generate a source-generated stub for whatever parameters we can and forward whatever parameters we can't marshal (technically we also do this in main, but we emit errors in that case).

@elinor-fung
Copy link
Member

Oh, I thought the dotnet/runtime-only down-level support switched to the direct forwarder if any parameters were ones we can't marshal. And that we always emitted the error if we ended up in the state of generating a stub with parameters we can't marshal.

@jtschuster
Copy link
Member Author

This is only applicable on the dotnet/runtime-only downlevel support, where we'll generate a source-generated stub for whatever parameters we can and forward whatever parameters we can't marshal (technically we also do this in main, but we emit errors in that case).

Is that true? It looks like we have tests that expect a forwarder stub if we can't marshal all the parameters:

// Confirm that if support is missing for any type (like arrays), we fall back to a forwarder even if other types are supported.
{
string code = CodeSnippets.BasicReturnAndParameterByValue("System.Runtime.InteropServices.SafeHandle", "int[]", CodeSnippets.LibraryImportAttributeDeclaration);
yield return new object[] { ID(), code, TestTargetFramework.Net6, true };
yield return new object[] { ID(), code, TestTargetFramework.Core, true };
yield return new object[] { ID(), code, TestTargetFramework.Standard, true };
yield return new object[] { ID(), code, TestTargetFramework.Framework, true };
}

Copy link
Member

@AaronRobinsonMSFT AaronRobinsonMSFT left a comment

Choose a reason for hiding this comment

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

LGTM, but I defer to @elinor-fung and @jkoritzinsky for final sign-off.

@@ -724,7 +785,7 @@ public class Basic { }
await test.RunAsync();
}


Copy link
Member

Choose a reason for hiding this comment

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

Suggested change

@@ -438,37 +438,24 @@ private static MemberDeclarationSyntax PrintForwarderStub(ContainingSyntax userD
{
Debug.Assert(!options.GenerateForwarders, "GenerateForwarders should have already been handled to use a forwarder stub");

// StringMarshalling.Utf16 is the only value that has a supported analogue in DllImportAttribute.CharSet. If it's not Utf16, consider StringMarshalling unset
Copy link
Member

Choose a reason for hiding this comment

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

Unsure if this is worth quibbling about, but CharSet.Ansi on non-Windows is Utf8 if I recall correctly. Based on certain reg keys on Windows it can also be Utf8. I don't know if this makes a difference in this case, but it is something to note.

Copy link
Member

@elinor-fung elinor-fung left a comment

Choose a reason for hiding this comment

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

The change looks good to me. I do want to make sure we understand the down-level support and dotnet/runtime-only downlevel support behaviour/expectations previously commented on though:

This is only applicable on the dotnet/runtime-only downlevel support, where we'll generate a source-generated stub for whatever parameters we can and forward whatever parameters we can't marshal (technically we also do this in main, but we emit errors in that case).

Is that true? It looks like we have tests that expect a forwarder stub if we can't marshal all the parameters:

I think it'd be good to have documentation and tests (or just doc for dotnet/runtime-only, since I don't think we have a way to test that) for the expected behaviour when not all parameters can be marshalled:

  • when do we print a simple forwarder stub versus marshal what we can and forward the rest
  • do we emit a diagnostic on what we can't marshal or just forward silently
  • what is the difference in behaviour for this scenario in down-level and runtime-only down-level support (also non-down-level / current version, but I think we have a decent handle on and coverage of that)

The case of 'we generate a stub that marshals the parameters we can and forwards the remainder, without erroring' was surprising to me.

@@ -438,37 +438,24 @@ private static MemberDeclarationSyntax PrintForwarderStub(ContainingSyntax userD
{
Debug.Assert(!options.GenerateForwarders, "GenerateForwarders should have already been handled to use a forwarder stub");

// StringMarshalling.Utf16 is the only value that has a supported analogue in DllImportAttribute.CharSet. If it's not Utf16, consider StringMarshalling unset
Copy link
Member

Choose a reason for hiding this comment

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

If it's not Utf16, consider StringMarshalling unset

Shouldn't it have errored in this case? I know we have a test around down-level forwarding of StringMarshalling.Utf8 or StringMarshalling.Custom.

public async Task StringMarshallingForwardingNotSupported_ReportsDiagnostic()

Or am I missing something about dotnet/runtime-only down-level versus down-level support?

Copy link
Member Author

Choose a reason for hiding this comment

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

It should error in down-level forwarding of UTF-8 and StringMarshalling.Custom, but this code path is taken in the normal path of LibraryImport in net7+, so we don't want to unconditionally warn here. Ideally this should warn when we hit this path for down-level support with partial marshalling, but we don't right now either.

Copy link
Member

@jkoritzinsky jkoritzinsky left a comment

Choose a reason for hiding this comment

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

LGTM once @elinor-fung's comments are resolved.

@AaronRobinsonMSFT
Copy link
Member

@jtschuster Are we waiting on something specific for this PR to go in?

@stephentoub
Copy link
Member

Was this superceded by #104416?

@AaronRobinsonMSFT
Copy link
Member

Was this superceded by #104416?

Yes. Closing.

@github-actions github-actions bot locked and limited conversation to collaborators Aug 9, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants