Browse Source

Uses TSAB.ConvertEnumValue to produce beautified enum member declarations, that reference previously declared enum members.

stash/beautify-enum-member-declarations
Siegfried Pammer 3 years ago
parent
commit
e1663dd544
  1. 11
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/EnumTests.cs
  2. 2
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  3. 70
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs

11
ICSharpCode.Decompiler.Tests/TestCases/Pretty/EnumTests.cs

@ -82,7 +82,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Item1 = 1, Item1 = 1,
Item2 = 2, Item2 = 2,
Item3 = 4, Item3 = 4,
All = 7 All = Item1 | Item2 | Item3
}
[Flags]
public enum SelfReferentialEnum
{
None = 0,
Item1 = 1,
Item2 = Item1,
Item3 = 3
} }
[Flags] [Flags]

2
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -1835,7 +1835,7 @@ namespace ICSharpCode.Decompiler.CSharp
if (constantValue != null) if (constantValue != null)
{ {
long initValue = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, constantValue, false); long initValue = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, constantValue, false);
enumDec.Initializer = typeSystemAstBuilder.ConvertConstantValue(decompilationContext.CurrentTypeDefinition.EnumUnderlyingType, constantValue); enumDec.Initializer = typeSystemAstBuilder.ConvertEnumValue(decompilationContext.CurrentTypeDefinition, initValue, field);
if (enumDec.Initializer is PrimitiveExpression primitive if (enumDec.Initializer is PrimitiveExpression primitive
&& initValue >= 10 && decompileRun.EnumValueDisplayMode == EnumValueDisplayMode.AllHex) && initValue >= 10 && decompileRun.EnumValueDisplayMode == EnumValueDisplayMode.AllHex)
{ {

70
ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs

@ -1199,22 +1199,32 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return type.HasAttribute(KnownAttribute.Flags, inherit: false); return type.HasAttribute(KnownAttribute.Flags, inherit: false);
} }
Expression ConvertEnumValue(IType type, long val) /// <summary>
/// Converts a numeric enum value into its enum member representation, if possible.
/// Uses a series of enum members concatenated by <c>|</c>, if necessary.
/// Returns <c>(EnumType)value</c>, if it fails.
/// <para>
/// If <paramref name="declaringEnumMember"/> is set to a non-<see langword="null"/> value,
/// unqualified references are used and self references are avoided. Also, casts to EnumType are dropped.
/// </para>
/// </summary>
internal Expression ConvertEnumValue(IType type, long val, IField declaringEnumMember = null)
{ {
ITypeDefinition enumDefinition = type.GetDefinition(); ITypeDefinition enumDefinition = type.GetDefinition();
TypeCode enumBaseTypeCode = ReflectionHelper.GetTypeCode(enumDefinition.EnumUnderlyingType); TypeCode enumBaseTypeCode = ReflectionHelper.GetTypeCode(enumDefinition.EnumUnderlyingType);
var fields = enumDefinition.Fields var fields = enumDefinition.Fields
.Select(PrepareConstant) .SelectWithIndex(PrepareConstant)
.Where(f => f.field != null) .Where(f => f.field != null)
.ToArray(); .ToArray();
foreach (var (value, field) in fields) foreach (var (index, value, field, weight) in fields)
{ {
if (value == val) if (value == val && weight == 1)
{ {
var mre = new MemberReferenceExpression(new TypeReferenceExpression(ConvertType(type)), field.Name); if (field == declaringEnumMember)
if (AddResolveResultAnnotations) {
mre.AddAnnotation(new MemberResolveResult(mre.Target.GetResolveResult(), field)); return new PrimitiveExpression(CSharpPrimitiveCast.Cast(enumBaseTypeCode, val, false));
return mre; }
return MakeEnumMemberReference(field);
} }
} }
if (IsFlagsEnum(enumDefinition)) if (IsFlagsEnum(enumDefinition))
@ -1239,14 +1249,14 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
break; break;
} }
Expression negatedExpr = null; Expression negatedExpr = null;
foreach (var (fieldValue, field) in fields.OrderByDescending(f => CalculateHammingWeight(unchecked((ulong)f.value)))) foreach (var (fieldIndex, fieldValue, field, weight) in fields.OrderByDescending(f => f.weight))
{ {
if (fieldValue == 0) if (fieldValue == 0 || field == declaringEnumMember)
continue; // skip None enum value continue; // skip None enum value
if ((fieldValue & enumValue) == fieldValue) if ((fieldValue & enumValue) == fieldValue)
{ {
var fieldExpression = new MemberReferenceExpression(new TypeReferenceExpression(ConvertType(type)), field.Name); var fieldExpression = MakeEnumMemberReference(field);
if (expr == null) if (expr == null)
expr = fieldExpression; expr = fieldExpression;
else else
@ -1256,7 +1266,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
} }
if ((fieldValue & negatedEnumValue) == fieldValue) if ((fieldValue & negatedEnumValue) == fieldValue)
{ {
var fieldExpression = new MemberReferenceExpression(new TypeReferenceExpression(ConvertType(type)), field.Name); var fieldExpression = MakeEnumMemberReference(field);
if (negatedExpr == null) if (negatedExpr == null)
negatedExpr = fieldExpression; negatedExpr = fieldExpression;
else else
@ -1277,16 +1287,24 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return new UnaryOperatorExpression(UnaryOperatorType.BitNot, negatedExpr); return new UnaryOperatorExpression(UnaryOperatorType.BitNot, negatedExpr);
} }
} }
return new CastExpression(ConvertType(type), new PrimitiveExpression(CSharpPrimitiveCast.Cast(enumBaseTypeCode, val, false)));
(long value, IField field) PrepareConstant(IField field) var numericExpression = new PrimitiveExpression(CSharpPrimitiveCast.Cast(enumBaseTypeCode, val, false));
if (declaringEnumMember != null)
{
return numericExpression;
}
return new CastExpression(ConvertType(type), numericExpression);
(int index, long value, IField field, int weight) PrepareConstant(int index, IField field)
{ {
if (!field.IsConst) if (!field.IsConst)
return (-1, null); return default;
object constantValue = field.GetConstantValue(); object constantValue = field.GetConstantValue();
if (constantValue == null) if (constantValue == null)
return (-1, null); return default;
return ((long)CSharpPrimitiveCast.Cast(TypeCode.Int64, constantValue, checkForOverflow: false), field); var value = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, constantValue, checkForOverflow: false);
var weight = CalculateHammingWeight(unchecked((ulong)value));
return (index, value, field, weight);
} }
// see https://en.wikipedia.org/wiki/Hamming_weight // see https://en.wikipedia.org/wiki/Hamming_weight
@ -1301,6 +1319,24 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
x = (x + (x >> 4)) & m4; //put count of each 8 bits into those 8 bits x = (x + (x >> 4)) & m4; //put count of each 8 bits into those 8 bits
return unchecked((int)((x * h01) >> 56)); //returns left 8 bits of x + (x<<8) + (x<<16) + (x<<24) + ... return unchecked((int)((x * h01) >> 56)); //returns left 8 bits of x + (x<<8) + (x<<16) + (x<<24) + ...
} }
Expression MakeEnumMemberReference(IField field)
{
if (declaringEnumMember == null)
{
var mre = new MemberReferenceExpression(new TypeReferenceExpression(ConvertType(type)), field.Name);
if (AddResolveResultAnnotations)
mre.AddAnnotation(new MemberResolveResult(mre.Target.GetResolveResult(), field));
return mre;
}
else
{
var ie = new IdentifierExpression(field.Name);
if (AddResolveResultAnnotations)
ie.AddAnnotation(new MemberResolveResult(null, field));
return ie;
}
}
} }
static bool IsValidFraction(long num, long den) static bool IsValidFraction(long num, long den)

Loading…
Cancel
Save