Skip to content

Commit

Permalink
Merge pull request #586 from microsoft/fix585
Browse files Browse the repository at this point in the history
Add interop helpers to BOOLEAN
  • Loading branch information
AArnott authored Jun 15, 2022
2 parents 38e016d + d66e7cc commit a79abbd
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 161 deletions.
92 changes: 2 additions & 90 deletions src/Microsoft.Windows.CsWin32/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3763,11 +3763,6 @@ private ClassDeclarationSyntax DeclareCocreatableClass(TypeDefinition typeDef)
private StructDeclarationSyntax DeclareTypeDefStruct(TypeDefinition typeDef, TypeDefinitionHandle typeDefHandle)
{
IdentifierNameSyntax name = IdentifierName(this.Reader.GetString(typeDef.Name));
if (name.Identifier.ValueText == "BOOL")
{
return this.DeclareTypeDefBOOLStruct(typeDef);
}

bool isHandle = name.Identifier.ValueText == "HGDIOBJ";
foreach (CustomAttributeHandle attHandle in typeDef.GetCustomAttributes())
{
Expand Down Expand Up @@ -3870,6 +3865,8 @@ private StructDeclarationSyntax DeclareTypeDefStruct(TypeDefinition typeDef, Typ
break;
case "HRESULT":
case "NTSTATUS":
case "BOOL":
case "BOOLEAN":
members = members.AddRange(this.ExtractMembersFromTemplate(name.Identifier.ValueText));
break;
default:
Expand Down Expand Up @@ -4145,91 +4142,6 @@ private MethodDeclarationSyntax CreateAsSpanMethodOverValueAndLength(TypeSyntax
.WithLeadingTrivia(StrAsSpanComment);
}

private StructDeclarationSyntax DeclareTypeDefBOOLStruct(TypeDefinition typeDef)
{
IdentifierNameSyntax name = IdentifierName("BOOL");

FieldDefinition fieldDef = this.Reader.GetFieldDefinition(typeDef.GetFields().Single());
CustomAttributeHandleCollection fieldAttributes = fieldDef.GetCustomAttributes();
IdentifierNameSyntax fieldName = IdentifierName("value");
VariableDeclaratorSyntax fieldDeclarator = VariableDeclarator(fieldName.Identifier);
(TypeSyntax FieldType, SyntaxList<MemberDeclarationSyntax> AdditionalMembers, AttributeSyntax? MarshalAs) fieldInfo =
this.ReinterpretFieldType(fieldDef, fieldDef.DecodeSignature(SignatureHandleProvider.Instance, null).ToTypeSyntax(this.fieldTypeSettings, fieldAttributes).Type, fieldAttributes, this.DefaultContext);
SyntaxList<MemberDeclarationSyntax> members = List<MemberDeclarationSyntax>();

FieldDeclarationSyntax fieldSyntax = FieldDeclaration(
VariableDeclaration(fieldInfo.FieldType).AddVariables(fieldDeclarator))
.AddModifiers(TokenWithSpace(SyntaxKind.PrivateKeyword), TokenWithSpace(SyntaxKind.ReadOnlyKeyword));
members = members.Add(fieldSyntax);
MemberAccessExpressionSyntax fieldAccessExpression = MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, ThisExpression(), IdentifierName("value"));

// Add property accessor
members = members.Add(PropertyDeclaration(PredefinedType(TokenWithSpace(SyntaxKind.IntKeyword)), "Value")
.WithExpressionBody(ArrowExpressionClause(fieldAccessExpression)).WithSemicolonToken(SemicolonWithLineFeed)
.AddModifiers(TokenWithSpace(this.Visibility)));

// unsafe BOOL(bool value) => this.value = *(sbyte*)&value;
IdentifierNameSyntax valueParameter = IdentifierName("value");
ExpressionSyntax boolToSByte = PrefixUnaryExpression(
SyntaxKind.PointerIndirectionExpression,
CastExpression(
PointerType(PredefinedType(TokenWithNoSpace(SyntaxKind.SByteKeyword))),
PrefixUnaryExpression(SyntaxKind.AddressOfExpression, valueParameter)));
members = members.Add(ConstructorDeclaration(name.Identifier)
.AddModifiers(TokenWithSpace(this.Visibility), TokenWithSpace(SyntaxKind.UnsafeKeyword))
.AddParameterListParameters(Parameter(valueParameter.Identifier).WithType(PredefinedType(TokenWithSpace(SyntaxKind.BoolKeyword))))
.WithExpressionBody(ArrowExpressionClause(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, fieldAccessExpression, boolToSByte).WithOperatorToken(TokenWithSpaces(SyntaxKind.EqualsToken))))
.WithSemicolonToken(SemicolonWithLineFeed));

// BOOL(int value) => this.value = value;
members = members.Add(ConstructorDeclaration(name.Identifier)
.AddModifiers(TokenWithSpace(this.Visibility))
.AddParameterListParameters(Parameter(valueParameter.Identifier).WithType(PredefinedType(TokenWithSpace(SyntaxKind.IntKeyword))))
.WithExpressionBody(ArrowExpressionClause(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, fieldAccessExpression, valueParameter).WithOperatorToken(TokenWithSpaces(SyntaxKind.EqualsToken))))
.WithSemicolonToken(SemicolonWithLineFeed));

// public unsafe static implicit operator bool(BOOL value)
// {
// sbyte v = checked((sbyte)value.value);
// return *(bool*)&v;
// }
IdentifierNameSyntax localVarName = IdentifierName("v");
ExpressionSyntax sbyteToBool = PrefixUnaryExpression(
SyntaxKind.PointerIndirectionExpression,
CastExpression(
PointerType(PredefinedType(TokenWithNoSpace(SyntaxKind.BoolKeyword))),
PrefixUnaryExpression(SyntaxKind.AddressOfExpression, localVarName)));
BlockSyntax? implicitBOOLtoBoolBody = Block().AddStatements(
LocalDeclarationStatement(VariableDeclaration(PredefinedType(Token(SyntaxKind.SByteKeyword)))).AddDeclarationVariables(
VariableDeclarator(localVarName.Identifier).WithInitializer(EqualsValueClause(CheckedExpression(SyntaxKind.CheckedExpression, CastExpression(PredefinedType(Token(SyntaxKind.SByteKeyword)), MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, valueParameter, fieldName)))))),
ReturnStatement(sbyteToBool));
members = members.Add(ConversionOperatorDeclaration(Token(SyntaxKind.ImplicitKeyword), PredefinedType(Token(SyntaxKind.BoolKeyword)))
.AddParameterListParameters(Parameter(valueParameter.Identifier).WithType(name.WithTrailingTrivia(TriviaList(Space))))
.WithBody(implicitBOOLtoBoolBody)
.AddModifiers(TokenWithSpace(SyntaxKind.PublicKeyword), TokenWithSpace(SyntaxKind.StaticKeyword), TokenWithSpace(SyntaxKind.UnsafeKeyword))); // operators MUST be public

// public static implicit operator BOOL(bool value) => new BOOL(value);
members = members.Add(ConversionOperatorDeclaration(Token(SyntaxKind.ImplicitKeyword), name)
.AddParameterListParameters(Parameter(valueParameter.Identifier).WithType(PredefinedType(TokenWithSpace(SyntaxKind.BoolKeyword))))
.WithExpressionBody(ArrowExpressionClause(ObjectCreationExpression(name).AddArgumentListArguments(Argument(valueParameter))))
.AddModifiers(TokenWithSpace(SyntaxKind.PublicKeyword), TokenWithSpace(SyntaxKind.StaticKeyword)) // operators MUST be public
.WithSemicolonToken(SemicolonWithLineFeed));

// public static explicit operator BOOL(int value) => new BOOL(value);
members = members.Add(ConversionOperatorDeclaration(Token(SyntaxKind.ExplicitKeyword), name)
.AddParameterListParameters(Parameter(valueParameter.Identifier).WithType(PredefinedType(TokenWithSpace(SyntaxKind.IntKeyword))))
.WithExpressionBody(ArrowExpressionClause(ObjectCreationExpression(name).AddArgumentListArguments(Argument(valueParameter))))
.AddModifiers(TokenWithSpace(SyntaxKind.PublicKeyword), TokenWithSpace(SyntaxKind.StaticKeyword)) // operators MUST be public
.WithSemicolonToken(SemicolonWithLineFeed));

StructDeclarationSyntax result = StructDeclaration(name.Identifier)
.WithMembers(members)
.WithModifiers(TokenList(TokenWithSpace(this.Visibility), TokenWithSpace(SyntaxKind.ReadOnlyKeyword), TokenWithSpace(SyntaxKind.PartialKeyword)));

result = this.AddApiDocumentation(name.Identifier.ValueText, result);
return result;
}

private EnumDeclarationSyntax DeclareEnum(TypeDefinition typeDef)
{
bool flagsEnum = false;
Expand Down
11 changes: 11 additions & 0 deletions src/Microsoft.Windows.CsWin32/templates/BOOL.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
partial struct BOOL
{
internal unsafe BOOL(bool value) => this.Value = *(sbyte*)&value;
public static unsafe implicit operator bool(BOOL value)
{
sbyte v = checked((sbyte)value.Value);
return *(bool*)&v;
}

public static implicit operator BOOL(bool value) => new BOOL(value);
}
11 changes: 11 additions & 0 deletions src/Microsoft.Windows.CsWin32/templates/BOOLEAN.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
partial struct BOOLEAN
{
internal unsafe BOOLEAN(bool value) => this.Value = *(byte*)&value;
public static unsafe implicit operator bool(BOOLEAN value)
{
byte v = checked((byte)value.Value);
return *(bool*)&v;
}

public static implicit operator BOOLEAN(bool value) => new BOOLEAN(value);
}
53 changes: 0 additions & 53 deletions test/GenerationSandbox.Tests/BasicTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,48 +51,6 @@ public void DISPLAYCONFIG_VIDEO_SIGNAL_INFO_Test()
//// Assert.Equal(-2140340211, hr.Value);
////}

[Fact]
public void Bool()
{
BOOL b = true;
bool b2 = b;
Assert.True(b);
Assert.True(b2);

Assert.False(default(BOOL));
}

[Theory]
[InlineData(3)]
[InlineData(-1)]
public void NotLossyConversionBetweenBoolAndBOOL(int ordinal)
{
BOOL nativeBool = new BOOL(ordinal);
bool managedBool = nativeBool;
BOOL roundtrippedNativeBool = managedBool;
Assert.Equal(nativeBool, roundtrippedNativeBool);
}

[Theory]
[InlineData(3)]
[InlineData(-1)]
public void NotLossyConversionBetweenBoolAndBOOL_Ctors(int ordinal)
{
BOOL nativeBool = new BOOL(ordinal);
bool managedBool = nativeBool;
BOOL roundtrippedNativeBool = new BOOL(managedBool);
Assert.Equal(nativeBool, roundtrippedNativeBool);
}

[Fact]
public void BOOLEqualsComparesExactValue()
{
BOOL b1 = new BOOL(1);
BOOL b2 = new BOOL(2);
Assert.Equal(b1, b1);
Assert.NotEqual(b1, b2);
}

[Fact]
public void BSTR_ToString()
{
Expand Down Expand Up @@ -183,17 +141,6 @@ public void HANDLE_OverridesEqualityOperator()
Assert.False(handle5 == handle8);
}

[Fact]
public void BOOL_OverridesEqualityOperator()
{
var @true = new BOOL(true);
var @false = new BOOL(false);
Assert.True(@true == new BOOL(true));
Assert.False(@true != new BOOL(true));
Assert.True(@true != @false);
Assert.False(@true == @false);
}

[Fact]
public void CreateFile()
{
Expand Down
60 changes: 60 additions & 0 deletions test/GenerationSandbox.Tests/BoolTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Windows.Win32.Foundation;

public class BoolTests
{
[Fact]
public void Bool()
{
BOOL b = true;
bool b2 = b;
Assert.True(b);
Assert.True(b2);

Assert.False(default(BOOL));
}

[Theory]
[InlineData(3)]
[InlineData(-1)]
public void NotLossyConversionBetweenBoolAndBOOL(int ordinal)
{
BOOL nativeBool = new BOOL(ordinal);
bool managedBool = nativeBool;
BOOL roundtrippedNativeBool = managedBool;
Assert.Equal(nativeBool, roundtrippedNativeBool);
}

[Theory]
[InlineData(3)]
[InlineData(-1)]
public void NotLossyConversionBetweenBoolAndBOOL_Ctors(int ordinal)
{
BOOL nativeBool = new BOOL(ordinal);
bool managedBool = nativeBool;
BOOL roundtrippedNativeBool = new BOOL(managedBool);
Assert.Equal(nativeBool, roundtrippedNativeBool);
}

[Fact]
public void BOOLEqualsComparesExactValue()
{
BOOL b1 = new BOOL(1);
BOOL b2 = new BOOL(2);
Assert.Equal(b1, b1);
Assert.NotEqual(b1, b2);
}

[Fact]
public void BOOL_OverridesEqualityOperator()
{
var @true = new BOOL(true);
var @false = new BOOL(false);
Assert.True(@true == new BOOL(true));
Assert.False(@true != new BOOL(true));
Assert.True(@true != @false);
Assert.False(@true == @false);
}
}
60 changes: 60 additions & 0 deletions test/GenerationSandbox.Tests/BooleanTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Windows.Win32.Foundation;

public class BooleanTests
{
[Fact]
public void Boolean()
{
BOOLEAN b = true;
bool b2 = b;
Assert.True(b);
Assert.True(b2);

Assert.False(default(BOOLEAN));
}

[Theory]
[InlineData(3)]
[InlineData(0xff)]
public void NotLossyConversionBetweenBoolAndBOOLEAN(byte ordinal)
{
BOOLEAN nativeBool = new BOOLEAN(ordinal);
bool managedBool = nativeBool;
BOOLEAN roundtrippedNativeBool = managedBool;
Assert.Equal(nativeBool, roundtrippedNativeBool);
}

[Theory]
[InlineData(3)]
[InlineData(0xff)]
public void NotLossyConversionBetweenBoolAndBOOLEAN_Ctors(byte ordinal)
{
BOOLEAN nativeBool = new BOOLEAN(ordinal);
bool managedBool = nativeBool;
BOOLEAN roundtrippedNativeBool = new BOOLEAN(managedBool);
Assert.Equal(nativeBool, roundtrippedNativeBool);
}

[Fact]
public void BOOLEANEqualsComparesExactValue()
{
BOOLEAN b1 = new BOOLEAN(1);
BOOLEAN b2 = new BOOLEAN(2);
Assert.Equal(b1, b1);
Assert.NotEqual(b1, b2);
}

[Fact]
public void BOOLEAN_OverridesEqualityOperator()
{
var @true = new BOOLEAN(true);
var @false = new BOOLEAN(false);
Assert.True(@true == new BOOLEAN(true));
Assert.False(@true != new BOOLEAN(true));
Assert.True(@true != @false);
Assert.False(@true == @false);
}
}
1 change: 1 addition & 0 deletions test/GenerationSandbox.Tests/NativeMethods.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
BOOL
BOOLEAN
CHAR
CreateCursor
CreateFile
Expand Down
Loading

0 comments on commit a79abbd

Please sign in to comment.