Skip to content

Commit

Permalink
Generate json with enums instead of C#
Browse files Browse the repository at this point in the history
  • Loading branch information
AArnott committed Mar 8, 2021
1 parent 1245067 commit 7ed8a1d
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 308 deletions.
58 changes: 56 additions & 2 deletions src/Microsoft.Windows.CsWin32/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3426,8 +3426,62 @@ private MarshalAsAttribute ToMarshalAsAttribute(BlobHandle blobHandle)
return ma;
}

private ExpressionSyntax ToExpressionSyntax(Constant constant) => GeneratorUtilities.ToExpressionSyntax(this.mr, constant);
private ExpressionSyntax ToExpressionSyntax(Constant constant)
{
var blobReader = this.mr.GetBlobReader(constant.Value);
return constant.TypeCode switch
{
ConstantTypeCode.Boolean => blobReader.ReadBoolean() ? LiteralExpression(SyntaxKind.TrueLiteralExpression) : LiteralExpression(SyntaxKind.FalseLiteralExpression),
ConstantTypeCode.Char => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(blobReader.ReadChar())),
ConstantTypeCode.SByte => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(blobReader.ReadSByte())),
ConstantTypeCode.Byte => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(blobReader.ReadByte())),
ConstantTypeCode.Int16 => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(blobReader.ReadInt16())),
ConstantTypeCode.UInt16 => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(blobReader.ReadUInt16())),
ConstantTypeCode.Int32 => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(blobReader.ReadInt32())),
ConstantTypeCode.UInt32 => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(blobReader.ReadUInt32())),
ConstantTypeCode.Int64 => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(blobReader.ReadInt64())),
ConstantTypeCode.UInt64 => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(blobReader.ReadUInt64())),
ConstantTypeCode.Single => FloatExpression(blobReader.ReadSingle()),
ConstantTypeCode.Double => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(blobReader.ReadDouble())),
ConstantTypeCode.String => blobReader.ReadConstant(constant.TypeCode) is string value ? LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(value)) : LiteralExpression(SyntaxKind.NullLiteralExpression),
ConstantTypeCode.NullReference => LiteralExpression(SyntaxKind.NullLiteralExpression),
_ => throw new NotSupportedException("ConstantTypeCode not supported: " + constant.TypeCode),
};

private ExpressionSyntax ToHexExpressionSyntax(Constant constant) => GeneratorUtilities.ToHexExpressionSyntax(this.mr, constant);
static ExpressionSyntax FloatExpression(float value)
{
return
float.IsPositiveInfinity(value) ? MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, PredefinedType(Token(SyntaxKind.FloatKeyword)), IdentifierName(nameof(float.PositiveInfinity))) :
float.IsNegativeInfinity(value) ? MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, PredefinedType(Token(SyntaxKind.FloatKeyword)), IdentifierName(nameof(float.NegativeInfinity))) :
float.IsNaN(value) ? MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, PredefinedType(Token(SyntaxKind.FloatKeyword)), IdentifierName(nameof(float.NaN))) :
LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(value));
}
}

private ExpressionSyntax ToHexExpressionSyntax(Constant constant)
{
var blobReader = this.mr.GetBlobReader(constant.Value);
var blobReader2 = this.mr.GetBlobReader(constant.Value);
return constant.TypeCode switch
{
ConstantTypeCode.SByte => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(ToHex(blobReader.ReadSByte()), blobReader2.ReadSByte())),
ConstantTypeCode.Byte => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(ToHex(blobReader.ReadByte()), blobReader2.ReadByte())),
ConstantTypeCode.Int16 => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(ToHex(blobReader.ReadInt16()), blobReader2.ReadInt16())),
ConstantTypeCode.UInt16 => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(ToHex(blobReader.ReadUInt16()), blobReader2.ReadUInt16())),
ConstantTypeCode.Int32 => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(ToHex(blobReader.ReadInt32()), blobReader2.ReadInt32())),
ConstantTypeCode.UInt32 => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(ToHex(blobReader.ReadUInt32()), blobReader2.ReadUInt32())),
ConstantTypeCode.Int64 => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(ToHex(blobReader.ReadInt64()), blobReader2.ReadInt64())),
ConstantTypeCode.UInt64 => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(ToHex(blobReader.ReadUInt64()), blobReader2.ReadUInt64())),
_ => throw new NotSupportedException("ConstantTypeCode not supported: " + constant.TypeCode),
};

unsafe string ToHex<T>(T value)
where T : unmanaged
{
int fullHexLength = sizeof(T) * 2;
string hex = string.Format(CultureInfo.InvariantCulture, "0x{0:X" + fullHexLength + "}", value);
return hex;
}
}
}
}
93 changes: 0 additions & 93 deletions src/Microsoft.Windows.CsWin32/GeneratorUtilities.cs

This file was deleted.

115 changes: 47 additions & 68 deletions src/ScrapeDocs/DocEnum.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,12 @@

namespace ScrapeDocs
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static GeneratorUtilities;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

internal class DocEnum
{
private static readonly AttributeListSyntax FlagsAttributeList = AttributeList().AddAttributes(Attribute(IdentifierName("Flags")));

internal DocEnum(bool isFlags, IReadOnlyDictionary<string, (ulong? Value, string? Doc)> members)
{
this.IsFlags = isFlags;
Expand Down Expand Up @@ -75,83 +69,68 @@ public bool Equals(DocEnum? other)
return true;
}

internal (string Namespace, EnumDeclarationSyntax Enum)? Emit(string name, MetadataReader mr, HashSet<TypeDefinitionHandle> apiClassHandles)
internal string? GetRecommendedName(List<(string MethodName, string ParameterName, string HelpLink, bool IsMethod)> uses)
{
if (this.Members.Count == 2 && this.Members.ContainsKey("TRUE") && this.Members.ContainsKey("FALSE"))
{
return null;
}

PredefinedTypeSyntax? baseType = null;
string? ns = null;

// Look up values for each constant.
var values = new Dictionary<string, ExpressionSyntax>();
foreach (var item in this.Members)
string? enumName = null;
if (uses.Count == 1)
{
bool found = false;
foreach (FieldDefinitionHandle handle in mr.FieldDefinitions)
var oneValue = uses[0];
if (oneValue.ParameterName.Contains("flags", StringComparison.OrdinalIgnoreCase))
{
FieldDefinition fieldDef = mr.GetFieldDefinition(handle);
if (apiClassHandles.Contains(fieldDef.GetDeclaringType()) && mr.StringComparer.Equals(fieldDef.Name, item.Key))
{
found = true;
Constant constant = mr.GetConstant(fieldDef.GetDefaultValue());
values.Add(item.Key, this.IsFlags ? ToHexExpressionSyntax(mr, constant) : ToExpressionSyntax(mr, constant));
baseType ??= ToTypeOfConstant(mr, constant);
ns ??= mr.GetString(mr.GetTypeDefinition(fieldDef.GetDeclaringType()).Namespace);
break;
}
// Only appears in one method, on a parameter named something like "flags".
enumName = $"{oneValue.MethodName}Flags";
}

if (!found)
else
{
// We couldn't find all the constants required.
return null;
enumName = $"{oneValue.MethodName}_{oneValue.ParameterName}Flags";
}
}

if (baseType is null || ns is null)
{
// We don't know all the values.
return null;
}

// Strip the method's declaring interface from the enum name, where applicable.
NameSyntax enumNameSyntax = ParseName(name);
if (enumNameSyntax is QualifiedNameSyntax qname)
else
{
enumNameSyntax = qname.Right;
}

EnumDeclarationSyntax enumDecl = EnumDeclaration(Identifier(enumNameSyntax.ToString()))
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.AddMembers(this.Members
.Select(kv => EnumMemberDeclaration(Identifier(kv.Key)).WithEqualsValue(EqualsValueClause(values[kv.Key]))).ToArray());
string firstName = this.Members.Keys.First();
int commonPrefixLength = firstName.Length;
foreach (string key in this.Members.Keys)
{
commonPrefixLength = Math.Min(commonPrefixLength, GetCommonPrefixLength(key, firstName));
}

if (this.IsFlags)
{
// For flags enums, prefer typing as unsigned integers.
baseType = PredefinedType(Token(baseType.Keyword.Kind() switch
if (commonPrefixLength > 1)
{
SyntaxKind.ShortKeyword => SyntaxKind.UShortKeyword,
SyntaxKind.IntKeyword => SyntaxKind.UIntKeyword,
SyntaxKind.LongKeyword => SyntaxKind.ULongKeyword,
_ => baseType.Keyword.Kind(),
}));
}
int last_ = firstName.LastIndexOf('_', commonPrefixLength - 1);
if (last_ != -1 && last_ != commonPrefixLength - 1)
{
// Trim down to last underscore
commonPrefixLength = last_;
}

if (baseType is not PredefinedTypeSyntax { Keyword: { RawKind: (int)SyntaxKind.IntKeyword } })
{
enumDecl = enumDecl.AddBaseListTypes(SimpleBaseType(baseType));
if (commonPrefixLength > 1 && firstName[commonPrefixLength - 1] == '_')
{
// The enum values share a common prefix suitable to imply a name for the enum.
enumName = firstName.Substring(0, commonPrefixLength - 1);
}
}
}

if (this.IsFlags)
return enumName;
}

private static int GetCommonPrefixLength(ReadOnlySpan<char> first, ReadOnlySpan<char> second)
{
int count = 0;
int minLength = Math.Min(first.Length, second.Length);
for (int i = 0; i < minLength; i++)
{
enumDecl = enumDecl.AddAttributeLists(FlagsAttributeList);
if (first[i] == second[i])
{
count++;
}
else
{
break;
}
}

return (ns, enumDecl);
return count;
}
}
}
Loading

0 comments on commit 7ed8a1d

Please sign in to comment.