Skip to content

Commit

Permalink
Merge pull request #889 from microsoft/dev/andarno/metadataUpdate
Browse files Browse the repository at this point in the history
Update metadata
  • Loading branch information
AArnott authored Mar 17, 2023
2 parents abb1b3d + c7449af commit 27f494b
Show file tree
Hide file tree
Showing 10 changed files with 250 additions and 39 deletions.
2 changes: 1 addition & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>

<MetadataVersion>41.0.25-preview</MetadataVersion>
<MetadataVersion>46.0.5-preview</MetadataVersion>
<!-- <DiaMetadataVersion>0.2.185-preview-g7e1e6a442c</DiaMetadataVersion> -->
<ApiDocsVersion>0.1.12-alpha</ApiDocsVersion>

Expand Down
201 changes: 180 additions & 21 deletions src/Microsoft.Windows.CsWin32/Generator.Constant.cs
Original file line number Diff line number Diff line change
Expand Up @@ -158,28 +158,187 @@ internal void RequestConstant(FieldDefinitionHandle fieldDefHandle)
});
}

private static ObjectCreationExpressionSyntax PropertyKeyValue(CustomAttribute propertyKeyAttribute, TypeSyntax type)
private static List<ReadOnlyMemory<char>> SplitConstantArguments(ReadOnlyMemory<char> args)
{
CustomAttributeValue<TypeSyntax> args = propertyKeyAttribute.DecodeValue(CustomAttributeTypeProvider.Instance);
uint a = (uint)args.FixedArguments[0].Value!;
ushort b = (ushort)args.FixedArguments[1].Value!;
ushort c = (ushort)args.FixedArguments[2].Value!;
byte d = (byte)args.FixedArguments[3].Value!;
byte e = (byte)args.FixedArguments[4].Value!;
byte f = (byte)args.FixedArguments[5].Value!;
byte g = (byte)args.FixedArguments[6].Value!;
byte h = (byte)args.FixedArguments[7].Value!;
byte i = (byte)args.FixedArguments[8].Value!;
byte j = (byte)args.FixedArguments[9].Value!;
byte k = (byte)args.FixedArguments[10].Value!;
uint pid = (uint)args.FixedArguments[11].Value!;

return ObjectCreationExpression(type).WithInitializer(
InitializerExpression(SyntaxKind.ObjectInitializerExpression, SeparatedList<ExpressionSyntax>(new[]
List<ReadOnlyMemory<char>> argExpressions = new();

// Recursively parse the arguments, splitting on commas that are not nested within curly brances.
int start = 0;
int depth = 0;
for (int i = 0; i < args.Length; i++)
{
switch (args.Span[i])
{
case '{':
depth++;
break;
case '}':
depth--;
break;
case ',':
if (depth == 0)
{
ReadOnlyMemory<char> arg = args.Slice(start, i - start);

argExpressions.Add(TrimCurlyBraces(arg));
start = i + 1;

// Trim a leading space if present.
if (args.Span[start] == ' ')
{
start++;
i++;
}
}

break;
}
}

if (start < args.Length)
{
argExpressions.Add(TrimCurlyBraces(args.Slice(start)));
}

ReadOnlyMemory<char> TrimCurlyBraces(ReadOnlyMemory<char> arg)
{
return arg.Span[0] == '{' && arg.Span[arg.Length - 1] == '}' ? arg.Slice(1, arg.Length - 2) : arg;
}

return argExpressions;
}

private ObjectCreationExpressionSyntax? CreateConstantViaCtor(List<ReadOnlyMemory<char>> args, TypeSyntax targetType, TypeDefinition targetTypeDef)
{
foreach (MethodDefinitionHandle methodDefHandle in targetTypeDef.GetMethods())
{
MethodDefinition methodDef = this.Reader.GetMethodDefinition(methodDefHandle);
if (this.Reader.StringComparer.Equals(methodDef.Name, ".ctor") && methodDef.GetParameters().Count == args.Count)
{
MethodSignature<TypeHandleInfo> ctorSignature = methodDef.DecodeSignature(SignatureHandleProvider.Instance, null);
var argExpressions = new ArgumentSyntax[args.Count];

for (int i = 0; i < args.Count; i++)
{
TypeHandleInfo parameterTypeInfo = ctorSignature.ParameterTypes[i];
argExpressions[i] = Argument(this.CreateConstant(args[i], parameterTypeInfo));
i++;
}

return ObjectCreationExpression(targetType).AddArgumentListArguments(argExpressions);
}
}

return null;
}

private ObjectCreationExpressionSyntax? CreateConstantByField(List<ReadOnlyMemory<char>> args, TypeSyntax targetType, TypeDefinition targetTypeDef)
{
if (targetTypeDef.GetFields().Count != args.Count)
{
return null;
}

var fieldAssignmentExpressions = new AssignmentExpressionSyntax[args.Count];
int i = 0;
foreach (FieldDefinitionHandle fieldDefHandle in targetTypeDef.GetFields())
{
FieldDefinition fieldDef = this.Reader.GetFieldDefinition(fieldDefHandle);
string fieldName = this.Reader.GetString(fieldDef.Name);
TypeHandleInfo fieldTypeInfo = fieldDef.DecodeSignature(SignatureHandleProvider.Instance, null) with { IsConstantField = true };
fieldAssignmentExpressions[i] = AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
IdentifierName(fieldName),
this.CreateConstant(args[i], fieldTypeInfo));
i++;
}

return ObjectCreationExpression(targetType)
.WithArgumentList(null)
.WithInitializer(InitializerExpression(SyntaxKind.ObjectInitializerExpression, SeparatedList<ExpressionSyntax>()).AddExpressions(fieldAssignmentExpressions));
}

private ExpressionSyntax CreateConstant(ReadOnlyMemory<char> argsAsString, TypeHandleInfo targetType)
{
return targetType switch
{
ArrayTypeHandleInfo { ElementType: PrimitiveTypeHandleInfo { PrimitiveTypeCode: PrimitiveTypeCode.Byte } } pointerType => this.CreateByteArrayConstant(argsAsString),
PrimitiveTypeHandleInfo primitiveType => ToExpressionSyntax(primitiveType.PrimitiveTypeCode, argsAsString),
HandleTypeHandleInfo handleType => this.CreateConstant(argsAsString, targetType.ToTypeSyntax(this.fieldTypeSettings, null).Type, (TypeReferenceHandle)handleType.Handle),
_ => throw new GenerationFailedException($"Unsupported constant type: {targetType}"),
};
}

private ExpressionSyntax CreateConstant(ReadOnlyMemory<char> argsAsString, TypeSyntax targetType, TypeReferenceHandle targetTypeRefHandle)
{
if (!this.TryGetTypeDefHandle(targetTypeRefHandle, out TypeDefinitionHandle targetTypeDefHandle))
{
// Special case for System.Guid.
TypeReference typeRef = this.Reader.GetTypeReference(targetTypeRefHandle);
if (this.Reader.StringComparer.Equals(typeRef.Name, "Guid"))
{
AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, IdentifierName("fmtid"), GuidValue(propertyKeyAttribute)),
AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, IdentifierName("pid"), LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(pid))),
})));
List<ReadOnlyMemory<char>> guidArgs = SplitConstantArguments(argsAsString);
return this.CreateGuidConstant(guidArgs);
}

throw new GenerationFailedException("Unrecognized target type.");
}

this.RequestInteropType(targetTypeDefHandle, this.DefaultContext);
TypeDefinition typeDef = this.Reader.GetTypeDefinition(targetTypeDefHandle);

List<ReadOnlyMemory<char>> args = SplitConstantArguments(argsAsString);

ObjectCreationExpressionSyntax? result =
this.CreateConstantViaCtor(args, targetType, typeDef) ??
this.CreateConstantByField(args, targetType, typeDef);

return result ?? throw new GenerationFailedException($"Unable to construct constant value given {args.Count} fields or constructor arguments.");
}

private ExpressionSyntax CreateByteArrayConstant(ReadOnlyMemory<char> argsAsString)
{
List<ReadOnlyMemory<char>> args = SplitConstantArguments(argsAsString);
TypeSyntax byteTypeSyntax = PredefinedType(Token(SyntaxKind.ByteKeyword));
return CastExpression(
MakeReadOnlySpanOfT(byteTypeSyntax),
ArrayCreationExpression(ArrayType(byteTypeSyntax).AddRankSpecifiers(ArrayRankSpecifier())).WithInitializer(InitializerExpression(SyntaxKind.ArrayInitializerExpression, SeparatedList<ExpressionSyntax>())
.AddExpressions(args.Select(b => ToExpressionSyntax(PrimitiveTypeCode.Byte, b)).ToArray())));
}

private ExpressionSyntax CreateGuidConstant(List<ReadOnlyMemory<char>> guidArgs)
{
if (guidArgs.Count != 11)
{
throw new GenerationFailedException($"Unexpected element count {guidArgs.Count} when constructing a Guid, which requires 11.");
}

var ctorArgs = new SyntaxToken[11]
{
Literal(uint.Parse(guidArgs[0].ToString(), CultureInfo.InvariantCulture)),
Literal(ushort.Parse(guidArgs[1].ToString(), CultureInfo.InvariantCulture)),
Literal(ushort.Parse(guidArgs[2].ToString(), CultureInfo.InvariantCulture)),
Literal(byte.Parse(guidArgs[3].ToString(), CultureInfo.InvariantCulture)),
Literal(byte.Parse(guidArgs[4].ToString(), CultureInfo.InvariantCulture)),
Literal(byte.Parse(guidArgs[5].ToString(), CultureInfo.InvariantCulture)),
Literal(byte.Parse(guidArgs[6].ToString(), CultureInfo.InvariantCulture)),
Literal(byte.Parse(guidArgs[7].ToString(), CultureInfo.InvariantCulture)),
Literal(byte.Parse(guidArgs[8].ToString(), CultureInfo.InvariantCulture)),
Literal(byte.Parse(guidArgs[9].ToString(), CultureInfo.InvariantCulture)),
Literal(byte.Parse(guidArgs[10].ToString(), CultureInfo.InvariantCulture)),
};

return ObjectCreationExpression(GuidTypeSyntax).AddArgumentListArguments(ctorArgs.Select(t => Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, t))).ToArray());
}

private ExpressionSyntax CreateConstant(CustomAttribute constantAttribute, TypeHandleInfo targetType)
{
TypeReferenceHandle targetTypeRefHandle = (TypeReferenceHandle)((HandleTypeHandleInfo)targetType).Handle;
CustomAttributeValue<TypeSyntax> args = constantAttribute.DecodeValue(CustomAttributeTypeProvider.Instance);
return this.CreateConstant(
((string)args.FixedArguments[0].Value!).AsMemory(),
targetType.ToTypeSyntax(this.fieldTypeSettings, null).Type,
targetTypeRefHandle);
}

private FieldDeclarationSyntax DeclareConstant(FieldDefinition fieldDef)
Expand All @@ -193,7 +352,7 @@ private FieldDeclarationSyntax DeclareConstant(FieldDefinition fieldDef)
ExpressionSyntax value =
fieldDef.GetDefaultValue() is { IsNil: false } constantHandle ? ToExpressionSyntax(this.Reader, constantHandle) :
this.FindInteropDecorativeAttribute(customAttributes, nameof(GuidAttribute)) is CustomAttribute guidAttribute ? GuidValue(guidAttribute) :
this.FindInteropDecorativeAttribute(customAttributes, "PropertyKeyAttribute") is CustomAttribute propertyKeyAttribute ? PropertyKeyValue(propertyKeyAttribute, fieldType.Type) :
this.FindInteropDecorativeAttribute(customAttributes, "ConstantAttribute") is CustomAttribute constantAttribute ? this.CreateConstant(constantAttribute, fieldTypeInfo) :
throw new NotSupportedException("Unsupported constant: " + name);
bool requiresUnsafe = false;
if (fieldType.Type is not PredefinedTypeSyntax && value is not ObjectCreationExpressionSyntax)
Expand Down
4 changes: 4 additions & 0 deletions src/Microsoft.Windows.CsWin32/Generator.Handle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,10 @@ public partial class Generator
ExpressionSyntax noerror = MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, ParseName("winmdroot.Foundation.WIN32_ERROR"), IdentifierName("NO_ERROR"));
releaseInvocation = BinaryExpression(SyntaxKind.EqualsExpression, releaseInvocation, noerror);
break;
case "HGLOBAL":
case "HLOCAL":
releaseInvocation = BinaryExpression(SyntaxKind.EqualsExpression, releaseInvocation, DefaultExpression(releaseMethodReturnType.Type));
break;
default:
throw new NotSupportedException($"Return type {identifierName.Identifier.ValueText} on release method {releaseMethod} not supported.");
}
Expand Down
25 changes: 23 additions & 2 deletions src/Microsoft.Windows.CsWin32/SimpleSyntaxFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ internal static class SimpleSyntaxFactory
internal static readonly SyntaxToken SemicolonWithLineFeed = TokenWithLineFeed(SyntaxKind.SemicolonToken);
internal static readonly IdentifierNameSyntax InlineArrayIndexerExtensionsClassName = IdentifierName("InlineArrayIndexerExtensions");
internal static readonly TypeSyntax SafeHandleTypeSyntax = IdentifierName("SafeHandle");
internal static readonly IdentifierNameSyntax GuidTypeSyntax = IdentifierName(nameof(Guid));
internal static readonly IdentifierNameSyntax IntPtrTypeSyntax = IdentifierName(nameof(IntPtr));
internal static readonly IdentifierNameSyntax UIntPtrTypeSyntax = IdentifierName(nameof(UIntPtr));
internal static readonly AttributeSyntax ComImportAttributeSyntax = Attribute(IdentifierName("ComImport"));
Expand Down Expand Up @@ -371,7 +372,7 @@ internal static ObjectCreationExpressionSyntax GuidValue(CustomAttribute guidAtt
byte j = (byte)args.FixedArguments[9].Value!;
byte k = (byte)args.FixedArguments[10].Value!;

return ObjectCreationExpression(IdentifierName(nameof(Guid))).AddArgumentListArguments(
return ObjectCreationExpression(GuidTypeSyntax).AddArgumentListArguments(
Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(ToHex(a), a))),
Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(ToHex(b), b))),
Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(ToHex(c), c))),
Expand Down Expand Up @@ -415,7 +416,7 @@ internal static ExpressionSyntax ToHexExpressionSyntax(MetadataReader reader, Co

ExpressionSyntax UncheckedSignedWrapper(LiteralExpressionSyntax value, SyntaxKind signedType)
{
return assignableToSignedInteger && value.Token.Text.StartsWith("0xF", StringComparison.OrdinalIgnoreCase)
return assignableToSignedInteger && char.ToUpper(value.Token.Text[2]) is '8' or '9' or (>= 'A' and <= 'F')
? UncheckedExpression(CastExpression(PredefinedType(Token(signedType)), value))
: value;
}
Expand Down Expand Up @@ -453,4 +454,24 @@ static ExpressionSyntax FloatExpression(float value)
LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(value));
}
}

internal static ExpressionSyntax ToExpressionSyntax(PrimitiveTypeCode primitiveTypeCode, ReadOnlyMemory<char> valueAsString)
{
string valueAsStringReally = valueAsString.ToString();
return primitiveTypeCode switch
{
PrimitiveTypeCode.Int64 => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(long.Parse(valueAsStringReally, CultureInfo.InvariantCulture))),
PrimitiveTypeCode.Byte => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(byte.Parse(valueAsStringReally, CultureInfo.InvariantCulture))),
PrimitiveTypeCode.SByte => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(sbyte.Parse(valueAsStringReally, CultureInfo.InvariantCulture))),
PrimitiveTypeCode.Int16 => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(short.Parse(valueAsStringReally, CultureInfo.InvariantCulture))),
PrimitiveTypeCode.UInt16 => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(ushort.Parse(valueAsStringReally, CultureInfo.InvariantCulture))),
PrimitiveTypeCode.Int32 => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(int.Parse(valueAsStringReally, CultureInfo.InvariantCulture))),
PrimitiveTypeCode.UInt32 => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(uint.Parse(valueAsStringReally, CultureInfo.InvariantCulture))),
PrimitiveTypeCode.UInt64 => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(ulong.Parse(valueAsStringReally, CultureInfo.InvariantCulture))),
PrimitiveTypeCode.Single => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(float.Parse(valueAsStringReally, CultureInfo.InvariantCulture))),
PrimitiveTypeCode.Double => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(double.Parse(valueAsStringReally, CultureInfo.InvariantCulture))),
PrimitiveTypeCode.String => LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(valueAsStringReally)),
_ => throw new NotSupportedException($"Unrecognized primitive type code: {primitiveTypeCode}."),
};
}
}
2 changes: 1 addition & 1 deletion test/GenerationSandbox.Tests/BasicTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ public void CreateFile()
var path = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
using var fileHandle = PInvoke.CreateFile(
path,
FILE_ACCESS_FLAGS.FILE_GENERIC_WRITE,
(uint)FILE_ACCESS_RIGHTS.FILE_GENERIC_WRITE,
FILE_SHARE_MODE.FILE_SHARE_NONE,
lpSecurityAttributes: default,
FILE_CREATION_DISPOSITION.CREATE_NEW,
Expand Down
4 changes: 2 additions & 2 deletions test/GenerationSandbox.Tests/ComRuntimeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ public void RemotableInterface()
IServiceProvider serviceProvider = (IServiceProvider)shellWindows.FindWindowSW(
PInvoke.CSIDL_DESKTOP,
null,
(int)ShellWindowTypeConstants.SWC_DESKTOP,
ShellWindowTypeConstants.SWC_DESKTOP,
out int hwnd,
(int)ShellWindowFindWindowOptions.SWFO_NEEDDISPATCH);
ShellWindowFindWindowOptions.SWFO_NEEDDISPATCH);
}
}
1 change: 1 addition & 0 deletions test/GenerationSandbox.Tests/NativeMethods.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ CreateFile
CSIDL_DESKTOP
DISPLAYCONFIG_VIDEO_SIGNAL_INFO
EnumWindows
FILE_ACCESS_RIGHTS
GetProcAddress
GetTickCount
GetWindowText
Expand Down
31 changes: 31 additions & 0 deletions test/Microsoft.Windows.CsWin32.Tests/ConstantsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// 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 ConstantsTests : GeneratorTestBase
{
public ConstantsTests(ITestOutputHelper logger)
: base(logger)
{
}

[Theory]
[InlineData("SECURITY_NULL_SID_AUTHORITY")] // SID_IDENTIFIER_AUTHORITY with byte[6] inline array
[InlineData("g_wszStreamBufferRecordingDuration")] // string
[InlineData("HWND_BOTTOM")] // A constant typed as a typedef'd struct
[InlineData("D2D1_DEFAULT_FLATTENING_TOLERANCE")] // a float constant
[InlineData("WIA_CATEGORY_FINISHED_FILE")] // GUID constant
[InlineData("DEVPKEY_MTPBTH_IsConnected")] // DEVPROPKEY constant
[InlineData("PKEY_AudioEndpoint_FormFactor")] // PROPERTYKEY constant
[InlineData("X509_CERT")] // A constant defined as PCSTR
[InlineData("RT_CURSOR")] // PCWSTR constant
[InlineData("HBMMENU_POPUP_RESTORE")] // A HBITMAP handle as a constant

public void InterestingConstants(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();
}
}
Loading

0 comments on commit 27f494b

Please sign in to comment.