Skip to content

Commit

Permalink
Merge pull request #210 from microsoft/fix208
Browse files Browse the repository at this point in the history
Omit `DefaultDllImportSearchPathsAttribute` generation on older TFMs
  • Loading branch information
AArnott authored Mar 17, 2021
2 parents d2b4902 + 6592a6e commit 7c9a30c
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 29 deletions.
10 changes: 8 additions & 2 deletions src/Microsoft.Windows.CsWin32/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ public class Generator : IDisposable
private readonly bool canCallCreateSpan;
private readonly bool generateSupportedOSPlatformAttributes;
private readonly bool generateSupportedOSPlatformAttributesOnInterfaces; // only supported on net6.0 (https://github.com/dotnet/runtime/pull/48838)
private readonly bool generateDefaultDllImportSearchPathsAttribute;

/// <summary>
/// Initializes a new instance of the <see cref="Generator"/> class.
Expand All @@ -339,6 +340,7 @@ public Generator(Stream metadataLibraryStream, GeneratorOptions? options = null,
this.parseOptions = parseOptions;

this.canCallCreateSpan = this.compilation?.GetTypeByMetadataName(typeof(MemoryMarshal).FullName)?.GetMembers("CreateSpan").Any() is true;
this.generateDefaultDllImportSearchPathsAttribute = this.compilation?.GetTypeByMetadataName(typeof(DefaultDllImportSearchPathsAttribute).FullName) is object;
if (this.compilation?.GetTypeByMetadataName("System.Runtime.Versioning.SupportedOSPlatformAttribute") is { } attribute)
{
this.generateSupportedOSPlatformAttributes = true;
Expand Down Expand Up @@ -2203,8 +2205,7 @@ private void DeclareExternMethod(MethodDefinitionHandle methodDefinitionHandle)

MethodDeclarationSyntax methodDeclaration = MethodDeclaration(
List<AttributeListSyntax>()
.Add(AttributeList().AddAttributes(DllImport(import, moduleName, entrypoint)))
.Add(DefaultDllImportSearchPathsAttributeList),
.Add(AttributeList().AddAttributes(DllImport(import, moduleName, entrypoint))),
modifiers: TokenList(Token(this.Visibility), Token(SyntaxKind.StaticKeyword), Token(SyntaxKind.ExternKeyword)),
returnType.Type,
explicitInterfaceSpecifier: null!,
Expand All @@ -2216,6 +2217,11 @@ private void DeclareExternMethod(MethodDefinitionHandle methodDefinitionHandle)
Token(SyntaxKind.SemicolonToken));
methodDeclaration = returnType.AddReturnMarshalAs(methodDeclaration);

if (this.generateDefaultDllImportSearchPathsAttribute)
{
methodDeclaration = methodDeclaration.AddAttributeLists(DefaultDllImportSearchPathsAttributeList);
}

if (this.GetSupportedOSPlatformAttribute(methodDefinition.GetCustomAttributes()) is AttributeSyntax supportedOSPlatformAttribute)
{
methodDeclaration = methodDeclaration.AddAttributeLists(AttributeList().AddAttributes(supportedOSPlatformAttribute));
Expand Down
59 changes: 32 additions & 27 deletions test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
Expand All @@ -25,8 +26,8 @@ public class GeneratorTests : IDisposable, IAsyncLifetime
private static readonly string FileSeparator = new string('=', 140);
private readonly ITestOutputHelper logger;
private readonly FileStream metadataStream;
private readonly Dictionary<string, CSharpCompilation> starterCompilations = new();
private CSharpCompilation compilation;
private CSharpCompilation net50Compilation;
private CSharpParseOptions parseOptions;
private Generator? generator;

Expand All @@ -41,17 +42,23 @@ public GeneratorTests(ITestOutputHelper logger)

// set in InitializeAsync
this.compilation = null!;
this.net50Compilation = null!;
}

public static IEnumerable<object[]> TFMData =>
new object[][]
{
new object[] { "net40" },
new object[] { "netstandard2.0" },
new object[] { "net5.0" },
};

public async Task InitializeAsync()
{
this.compilation = await this.CreateCompilationAsync(
ReferenceAssemblies.NetStandard.NetStandard20
.AddPackages(ImmutableArray.Create(new PackageIdentity("System.Memory", "4.5.4"))));
this.starterCompilations.Add("net40", await this.CreateCompilationAsync(ReferenceAssemblies.NetFramework.Net40.Default));
this.starterCompilations.Add("netstandard2.0", await this.CreateCompilationAsync(ReferenceAssemblies.NetStandard.NetStandard20.AddPackages(ImmutableArray.Create(new PackageIdentity("System.Memory", "4.5.4")))));
this.starterCompilations.Add("net5.0", await this.CreateCompilationAsync(ReferenceAssemblies.Net.Net50));

this.net50Compilation = await this.CreateCompilationAsync(
ReferenceAssemblies.Net.Net50);
this.compilation = this.starterCompilations["netstandard2.0"];
}

public Task DisposeAsync() => Task.CompletedTask;
Expand All @@ -74,39 +81,37 @@ public void TryGetEnumName(string candidate, string? declaringEnum)
Assert.Equal(declaringEnum, actualDeclaringEnum);
}

[Theory, PairwiseData]
public void SimplestMethod(bool net50)
[Theory]
[MemberData(nameof(TFMData))]
public void SimplestMethod(string tfm)
{
if (net50)
{
this.compilation = this.net50Compilation;
}

this.compilation = this.starterCompilations[tfm];
this.generator = new Generator(this.metadataStream, DefaultTestGeneratorOptions, this.compilation, this.parseOptions);
const string methodName = "GetTickCount";
Assert.True(this.generator.TryGenerateExternMethod(methodName));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();

var generatedMethod = this.FindGeneratedMethod(methodName).Single();
if (net50)
if (tfm == "net5.0")
{
Assert.Contains(generatedMethod.AttributeLists, this.IsAttributePresent);
Assert.Contains(generatedMethod.AttributeLists, al => IsAttributePresent(al, "SupportedOSPlatform"));
}
else
{
Assert.DoesNotContain(generatedMethod.AttributeLists, this.IsAttributePresent);
Assert.DoesNotContain(generatedMethod.AttributeLists, al => IsAttributePresent(al, "SupportedOSPlatform"));
}

if (tfm != "net40")
{
Assert.Contains(generatedMethod.AttributeLists, al => IsAttributePresent(al, "DefaultDllImportSearchPaths"));
}
}

[Theory, PairwiseData]
public void COMInterfaceWithSupportedOSPlatform(bool net50, bool allowMarshaling)
{
if (net50)
{
this.compilation = this.net50Compilation;
}

this.compilation = this.starterCompilations[net50 ? "net5.0" : "netstandard2.0"];
const string typeName = "IInkCursors";
this.generator = new Generator(this.metadataStream, DefaultTestGeneratorOptions with { AllowMarshaling = allowMarshaling }, this.compilation, this.parseOptions);
Assert.True(this.generator.TryGenerateType(typeName));
Expand All @@ -117,11 +122,11 @@ public void COMInterfaceWithSupportedOSPlatform(bool net50, bool allowMarshaling

if (net50 && !allowMarshaling)
{
Assert.Contains(iface.AttributeLists, this.IsAttributePresent);
Assert.Contains(iface.AttributeLists, al => IsAttributePresent(al, "SupportedOSPlatform"));
}
else
{
Assert.DoesNotContain(iface.AttributeLists, this.IsAttributePresent);
Assert.DoesNotContain(iface.AttributeLists, al => IsAttributePresent(al, "SupportedOSPlatform"));
}
}

Expand Down Expand Up @@ -637,7 +642,7 @@ internal static unsafe ref uint ItemRef(this ref MainAVIHeader.__uint_4 @this, i
}
";

this.compilation = this.net50Compilation;
this.compilation = this.starterCompilations["net5.0"];
this.AssertGeneratedType("MainAVIHeader", expected, expectedIndexer);
}

Expand Down Expand Up @@ -683,6 +688,8 @@ private static FileStream OpenMetadata()
return File.OpenRead(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location!)!, "Windows.Win32.winmd"));
}

private static bool IsAttributePresent(AttributeListSyntax al, string attributeName) => al.Attributes.Any(a => a.Name.ToString() == attributeName);

private CSharpCompilation AddGeneratedCode(CSharpCompilation compilation, Generator generator)
{
var compilationUnits = generator.GetCompilationUnits(CancellationToken.None);
Expand Down Expand Up @@ -794,8 +801,6 @@ private void AssertGeneratedType(string apiName, string expectedSyntax, string?
}
}

private bool IsAttributePresent(AttributeListSyntax al) => al.Attributes.Any(a => a.Name.ToString() == "SupportedOSPlatform");

private async Task<CSharpCompilation> CreateCompilationAsync(ReferenceAssemblies references)
{
ImmutableArray<MetadataReference> metadataReferences = await references
Expand Down

0 comments on commit 7c9a30c

Please sign in to comment.