Skip to content

Commit

Permalink
Merge pull request #923 from microsoft/fix913
Browse files Browse the repository at this point in the history
Recognize `MemorySizeAttribute` in metadata to improve friendly overloads
  • Loading branch information
AArnott authored Apr 28, 2023
2 parents 477c087 + d781dc3 commit f8b5498
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 0 deletions.
8 changes: 8 additions & 0 deletions src/Microsoft.Windows.CsWin32/Generator.FriendlyOverloads.cs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,14 @@ private IEnumerable<MethodDeclarationSyntax> DeclareFriendlyOverloads(MethodDefi
sizeParamIndex = nativeArrayInfo.CountParamIndex;
sizeConst = nativeArrayInfo.CountConst;
}
else if (externParam.Type is PointerTypeSyntax { ElementType: PredefinedTypeSyntax { Keyword.RawKind: (int)SyntaxKind.ByteKeyword } } && this.FindInteropDecorativeAttribute(param.GetCustomAttributes(), MemorySizeAttribute) is CustomAttribute att2)
{
// A very special case as documented in https://github.com/microsoft/win32metadata/issues/1555
// where MemorySizeAttribute is applied to byte* parameters to indicate the size of the buffer.
isArray = true;
MemorySize memorySize = DecodeMemorySizeAttribute(att2);
sizeParamIndex = memorySize.BytesParamIndex;
}

IdentifierNameSyntax localName = IdentifierName(origName + "Local");
if (isArray)
Expand Down
1 change: 1 addition & 0 deletions src/Microsoft.Windows.CsWin32/Generator.Invariants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public partial class Generator
{
internal const string InteropDecorationNamespace = "Windows.Win32.Foundation.Metadata";
internal const string NativeArrayInfoAttribute = "NativeArrayInfoAttribute";
internal const string MemorySizeAttribute = "MemorySizeAttribute";
internal const string RAIIFreeAttribute = "RAIIFreeAttribute";
internal const string DoNotReleaseAttribute = "DoNotReleaseAttribute";
internal const string GlobalNamespacePrefix = "global::";
Expand Down
14 changes: 14 additions & 0 deletions src/Microsoft.Windows.CsWin32/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,15 @@ private static NativeArrayInfo DecodeNativeArrayInfoAttribute(CustomAttribute na
};
}

private static MemorySize DecodeMemorySizeAttribute(CustomAttribute memorySizeAttribute)
{
CustomAttributeValue<TypeSyntax> args = memorySizeAttribute.DecodeValue(CustomAttributeTypeProvider.Instance);
return new MemorySize
{
BytesParamIndex = (short?)args.NamedArguments.FirstOrDefault(a => a.Name == "BytesParamIndex").Value,
};
}

private bool TryGetRenamedMethod(string methodName, [NotNullWhen(true)] out string? newName)
{
if (this.WideCharOnly && IsWideFunction(methodName))
Expand Down Expand Up @@ -1421,6 +1430,11 @@ internal struct NativeArrayInfo
internal int? CountConst { get; init; }
}

internal struct MemorySize
{
internal short? BytesParamIndex { get; init; }
}

private class DirectiveTriviaRemover : CSharpSyntaxRewriter
{
internal static readonly DirectiveTriviaRemover Instance = new();
Expand Down
6 changes: 6 additions & 0 deletions test/GenerationSandbox.Tests/GeneratedForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,10 @@ private static void PROCESS_BASIC_INFORMATION_PebBaseAddressIsPointer()
PEB_unmanaged* p = null;
info.PebBaseAddress = p;
}

private static void WriteFile()
{
uint written = 0;
PInvoke.WriteFile((SafeHandle?)null, new byte[2], &written, (NativeOverlapped*)null);
}
}
38 changes: 38 additions & 0 deletions test/Microsoft.Windows.CsWin32.Tests/FriendlyOverloadTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

public class FriendlyOverloadTests : GeneratorTestBase
{
public FriendlyOverloadTests(ITestOutputHelper logger)
: base(logger)
{
}

[Fact]
public void WriteFile()
{
const string name = "WriteFile";
this.Generate(name);
Assert.Contains(this.FindGeneratedMethod(name), m => m.ParameterList.Parameters.Count == 4);
}

[Fact]
public void SHGetFileInfo()
{
// This method uses MemorySize but for determining the size of a struct that another parameter points to.
// We cannot know the size of that, since it may be a v1 struct, a v2 struct, etc.
// So assert that no overload has fewer parameters.
const string name = "SHGetFileInfo";
this.Generate(name);
Assert.All(this.FindGeneratedMethod(name), m => Assert.Equal(5, m.ParameterList.Parameters.Count));
}

private void Generate(string name)
{
this.compilation = this.compilation.WithOptions(this.compilation.Options.WithPlatform(Platform.X64));
this.generator = this.CreateGenerator();
Assert.True(this.generator.TryGenerate(name, CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
}
}

0 comments on commit f8b5498

Please sign in to comment.