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

Update metadata #889

Merged
merged 5 commits into from
Mar 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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