Skip to content

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

Closed
jtschuster wants to merge 4 commits into
dotnet:mainfrom
jtschuster:LibraryImportForwarderAddCharSet
Closed

Use the same method to generate DllImportAttribute in local methods and P/Invoke forwarders#103973
jtschuster wants to merge 4 commits into
dotnet:mainfrom
jtschuster:LibraryImportForwarderAddCharSet

Conversation

@jtschuster

Copy link
Copy Markdown
Member

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

@dotnet-policy-service

Copy link
Copy Markdown
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
Copy Markdown
Member

Can we add a test for this?

@elinor-fung

elinor-fung commented Jun 25, 2024

Copy link
Copy Markdown
Member

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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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 };
}

@AaronRobinsonMSFT AaronRobinsonMSFT left a comment

Copy link
Copy Markdown
Member

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.

}


Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change

{
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
Copy Markdown
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.

@elinor-fung elinor-fung left a comment

Copy link
Copy Markdown
Member

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.

{
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
Copy Markdown
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
Copy Markdown
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.

@jkoritzinsky jkoritzinsky left a comment

Copy link
Copy Markdown
Member

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
Copy Markdown
Member

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

@stephentoub

Copy link
Copy Markdown
Member

Was this superceded by #104416?

@AaronRobinsonMSFT

Copy link
Copy Markdown
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