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

Dllimport generator build and test fixes #59658

Conversation

jkoritzinsky
Copy link
Member

Build and test fixes for DllImportGenerator.

Marked as draft and targeting main to validate CI. Once CI is validated, will re-target against feature/use-dllimport-generator.

@jkoritzinsky jkoritzinsky added area-System.Runtime.InteropServices NO-MERGE The PR is not ready for merge yet (see discussion for detailed reasons) labels Sep 27, 2021
@@ -8,16 +8,13 @@ internal static partial class Interop
{
internal static partial class Kernel32
{
#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time
// Disabled since GetFullPathNameW takes 'ref's into buffers (the ref parameters don't represent single characters).
Copy link
Member

Choose a reason for hiding this comment

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

What is going to be the recommended way to make these work with GeneratedDllImports?

Copy link
Member Author

Choose a reason for hiding this comment

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

This particular P/Invoke took a dependency on the fact that chars are conditionally blittable, so no copying was done. So far, the Interop team has decided to not consider chars as conditionally blittable.

To make this particular P/Invoke work, there are a few different options:

  1. Update the generator to consider char parameters as blittable when CharSet=CharSet.Unicode.
  2. Update this P/Invoke to take char* parameters instead of ref char and pass in pinned refs.
  3. Update the first parameter to take a string (which will be pinned) or a ReadOnlySpan<char> (using the new Span marshalers) and the second to take a char*, which will be passed without change. As the usage of this method calculates the refs to pass in, from a ReadOnlySpan<char> and a ValueStringBuilder, this would be possible without too much trouble.

Copy link
Member

@elinor-fung elinor-fung Oct 6, 2021

Choose a reason for hiding this comment

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

We did (1) in #59700, so we should be able to switch this and GetLongPathNameW back.

Fine with getting this change in to the feature branch and updating in the main merge PR.

Copy link
Member Author

Choose a reason for hiding this comment

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

With the merge conflicts I had after the property order normalization, I just fixed this as part of resolving those.

…matically excludes these test projects from the .NET Framework test build.
@jkotas
Copy link
Member

jkotas commented Sep 29, 2021

public static bool IsReleaseRuntime => typeof(object).Assembly.GetCustomAttribute().Configuration == "Release";

We do not want to have tests that only work on release flavors of the runtime. We should either fix the non-release runtime; or delete the test and leave the behavior unspecified.


### Semantic changes compared to `DllImportAttribute`

The default value of [`CharSet`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.charset) is runtime/language-defined. In the built-in system, the default value of the `CharSet` property is `CharSet.Ansi`. The P/Invoke source generator makes no assumptions about the `CharSet` if it is not explicitly set on `GeneratedDllImportAttribute`. Marshalling of `char` or `string` requires explicitly specifying marshalling information.
Copy link
Member

Choose a reason for hiding this comment

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

The default value of CharSet is runtime/language-defined

What does it mean for the default value to be "runtime" defined when the code for it is being emitted by the generator?

I assume the generator emits a diagnostic and fails when CharSet would be used and it hasn't been explicitly specified to something other than None?

Copy link
Member

@AaronRobinsonMSFT AaronRobinsonMSFT Sep 29, 2021

Choose a reason for hiding this comment

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

What does it mean for the default value to be "runtime" defined when the code for it is being emitted by the generator?

This is for the DllImportAttribute not for the GeneratedDllImportAttribute. All this is saying is the new system doesn't attempt to make the choice for you and instead expects explicit statement. The CharSet.Auto is basically not supported.

Copy link
Member

Choose a reason for hiding this comment

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

CharSet.Auto is basically not supported

CharSet.Auto is supported (for strings). Not specifying CharSet at all (or specifying CharSet.None) is not supported when marshalling char or string.

Copy link
Member

Choose a reason for hiding this comment

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

Bah. Right. I am confusing the "default" with the concept of specifically setting CharSet.Auto. Apologies.

docs/design/libraries/DllImportGenerator/Compatibility.md Outdated Show resolved Hide resolved
docs/design/libraries/DllImportGenerator/Compatibility.md Outdated Show resolved Hide resolved
docs/design/libraries/DllImportGenerator/Compatibility.md Outdated Show resolved Hide resolved

When converting from native to managed, the built-in system would throw a [`MarshalDirectiveException`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshaldirectiveexception) if the string's length is over 0x7ffffff0. The generated marshalling code will no longer perform this check.

In the built-in system, marshalling a `string` contains an optimization for parameters passed by value to allocate on the stack (instead of through [`AllocCoTaskMem`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshal.alloccotaskmem)) if the string is below a certain length (MAX_PATH). For UTF-16, this optimization was also applied for parameters passed by read-only reference. The generated marshalling code will include this optimization for read-only reference parameters for non-UTF-16 as well.
Copy link
Member

Choose a reason for hiding this comment

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

The generated marshalling code will include this optimization for read-only reference parameters for non-UTF-16 as well.

Why only "readonly reference parameters"? What's an example of something where we could do so but we're choosing not to?

Copy link
Member

Choose a reason for hiding this comment

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

Inadvertent stack corruption. We were leery of supporting the stackalloced solution for all cases so deferred support. This is one of those point in time decisions that can be updated in V2.

Copy link
Member

Choose a reason for hiding this comment

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

Inadvertent stack corruption.

By whom? The generator is generating all of the code in the implementation.

Copy link
Member

Choose a reason for hiding this comment

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

By the callee. Readonly parameters assume and are contracted to be read only on the native side so we assume that here. If the callee does incorrectly write to the buffer it could negatively impact the runtime state and so was deferred until we see a strong use case.

Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Logging.Generators", "..\Microsoft.Extensions.Logging.Abstractions\gen\Microsoft.Extensions.Logging.Generators.Roslyn3.11.csproj", "{E00A72D2-A6C2-4334-92AC-7CF7ECC87F4D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Logging.Generators", "..\Microsoft.Extensions.Logging.Abstractions\gen\Microsoft.Extensions.Logging.Generators.Roslyn4.0.csproj", "{EBEF8790-8095-4FD9-8436-1354B30E1529}"
EndProject
Copy link
Member

Choose a reason for hiding this comment

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

Why are all these .slns being updated with additional projects?

Copy link
Member Author

Choose a reason for hiding this comment

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

I built the slngen.proj project in 27034bc to regenerate all of the solutions since I added new project references for the DllImportGenerator. Looks like someone didn't run it beforehand and this update also updated the solutions with the logging source generator as well.

@@ -4,6 +4,7 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<IncludeRemoteExecutor>true</IncludeRemoteExecutor>
<IgnoreForCI Condition="'$(TargetOS)' == 'Browser'">true</IgnoreForCI>
<EnableDllImportGenerator>true</EnableDllImportGenerator>
Copy link
Member

Choose a reason for hiding this comment

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

Why is this needed? We can't just enable it everywhere?

DLLIMPORTGENANALYZER014 | Usage | Error | RefValuePropertyUnsupported
DLLIMPORTGENANALYZER015 | Interoperability | Disabled | ConvertToGeneratedDllImportAnalyzer
DLLIMPORTGENANALYZER016 | Usage | Error | GenericTypeMustBeClosed
DLLIMPORTGENANALYZER017 | Usage | Warning | GeneratedDllImportContainingTypeMissingModifiers
Copy link
Member

Choose a reason for hiding this comment

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

Will we shift to using the SYSLIB prefix we use in our other generators?
https://github.com/dotnet/runtime/blob/main/docs/project/list-of-diagnostics.md

Copy link
Member

Choose a reason for hiding this comment

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

Also, IIRC the docs earlier stated that everything is an error, but that doesn't map to the severity column here.

namespace Microsoft.Interop.Diagnostics
{
[EventSource(Name = "Microsoft-Interop-SourceGeneration-Events")]
internal sealed class Events : EventSource
Copy link
Member

Choose a reason for hiding this comment

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

Are these still needed? Weren't similar events added to the Roslyn infrastructure for all generators?

private const string GeneratedDllImport = nameof(GeneratedDllImport);
private const string GeneratedDllImportAttribute = nameof(GeneratedDllImportAttribute);

private static readonly Version MinimumSupportedFrameworkVersion = new Version(5, 0);
Copy link
Member

Choose a reason for hiding this comment

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

5 or 6 (or 7)?

@jkoritzinsky
Copy link
Member Author

jkoritzinsky commented Sep 29, 2021

@stephentoub can you please move your feedback to #59579? Once I get a green build, this PR will retarget to the feature/use-dllimport-generator branch (as mentioned in the PR description) and all of the files you have commented on will no longer be in the diff for the PR. Whereas #59579 is the PR that will merge the generator into main, so your feedback will be more useful there.

@jkoritzinsky jkoritzinsky added the NO-REVIEW Experimental/testing PR, do NOT review it label Sep 29, 2021
@stephentoub
Copy link
Member

stephentoub commented Sep 29, 2021

can you please move your feedback to #59579?

I had ~40 comments. I'm not going to recreate them all on the other PR.

There was no indication in the PR description that feedback shouldn't be provided here, this wasn't marked as no review, etc.

If you'd like to address or respond to my feedback elsewhere, that's fine.

@jkoritzinsky
Copy link
Member Author

I explicitly marked it as a draft PR and as No-Merge, which means I didn't want review on it. I honestly thought that we had gotten rid of the NO REVIEW tag with the move to use draft PRs. Also, I explicitly stated in the PR description that I'm going to re-target the PR to a different branch, so most of the commits will not show up.

I only suggest moving the feedback to #59579 to make sure that we don't lose the feedback when I retarget this PR. I don't know what GitHub does when you retarget a PR and the files that have comments are no longer part of the diff.

I've since marked this PR as "NO REVIEW" to make sure that this doesn't happen again. Sorry for the confusion.

…w Mono loads (or more acurately doesn't) the native library component of these tests.
@AaronRobinsonMSFT
Copy link
Member

@stephentoub Lots of good comments here. We will work on addressing them in the integration PR – #59579. A few of the comments are about productization points which we plan to address after the integrating into the product as making it publicly consumable is step 2. There are multiple inconsistencies that we need to address as we make the prototype follow the dotnet/runtime style guidelines. The many design queries we will attempt to answer above and continue that conversation.

@jkoritzinsky
Copy link
Member Author

All of the CI failures are known and tracked with runfo issues, so I'm going to re-target this and mark it ready for review.

CI build of 4d55f3a against main: https://dev.azure.com/dnceng/public/_build/results?buildId=1403599&view=results

@jkoritzinsky jkoritzinsky changed the base branch from main to feature/use-dllimport-generator October 5, 2021 20:15
@jkoritzinsky jkoritzinsky marked this pull request as ready for review October 5, 2021 20:15
@@ -8,16 +8,13 @@ internal static partial class Interop
{
internal static partial class Kernel32
{
#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time
// Disabled since GetFullPathNameW takes 'ref's into buffers (the ref parameters don't represent single characters).
Copy link
Member

@elinor-fung elinor-fung Oct 6, 2021

Choose a reason for hiding this comment

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

We did (1) in #59700, so we should be able to switch this and GetLongPathNameW back.

Fine with getting this change in to the feature branch and updating in the main merge PR.

// As a result, we need to exclude the Mono run here since we build the tests once for CoreCLR and Mono for desktop test runs.
// We should switch this to another mechanism in the future so we don't submit a work item of this assembly that skips every test
// for Mono-on-Desktop-Platforms test runs.
[assembly:ActiveIssue("https://github.com/dotnet/runtime/issues/59815", TestRuntimes.Mono)]
Copy link
Member

Choose a reason for hiding this comment

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

Can we do this via a ProjectExclusions based on RuntimeFlavor in tests.proj? The assembly attribute results in us still sending the test to Helix, running through test discovery for each test to determine that it should not be run, and uploading logs - which seems rather wasteful.

Copy link
Member

Choose a reason for hiding this comment

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

Doh. I completely forgot about that. +1.

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 can't do that because we don't build the libraries tests for each runtime flavor, we use one build for both CoreCLR and Mono on desktop platforms (like Linux and Mac). I tried that originally and it didn't work.

</XmlPeek>
<XmlPeek XmlContent="$(CrossTargetXml)" Query="toolchain-info/compiler-args/text()">
<Output TaskParameter="Result" PropertyName="DnneCompilerUserFlags" />
</XmlPeek>
Copy link
Member

Choose a reason for hiding this comment

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

XmlPeek

Learned about a new task today - not sure I'm happy about it...

@jkoritzinsky jkoritzinsky force-pushed the dllimport-generator-build-fixes branch from cab205b to 00726aa Compare October 6, 2021 16:39
@jkoritzinsky jkoritzinsky merged commit 2dff7cd into dotnet:feature/use-dllimport-generator Oct 6, 2021
@jkoritzinsky jkoritzinsky deleted the dllimport-generator-build-fixes branch October 6, 2021 16:40
@ghost ghost locked as resolved and limited conversation to collaborators Nov 5, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Runtime.InteropServices NO-MERGE The PR is not ready for merge yet (see discussion for detailed reasons) NO-REVIEW Experimental/testing PR, do NOT review it
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants