From e1663dd54470d0b59eb7bfef2559d49f105c423f Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 27 Jun 2022 16:23:29 +0200 Subject: [PATCH] Uses TSAB.ConvertEnumValue to produce beautified enum member declarations, that reference previously declared enum members. --- .../TestCases/Pretty/EnumTests.cs | 11 ++- .../CSharp/CSharpDecompiler.cs | 2 +- .../CSharp/Syntax/TypeSystemAstBuilder.cs | 70 ++++++++++++++----- 3 files changed, 64 insertions(+), 19 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/EnumTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/EnumTests.cs index f66be670d..afb01b8f7 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/EnumTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/EnumTests.cs @@ -82,7 +82,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Item1 = 1, Item2 = 2, Item3 = 4, - All = 7 + All = Item1 | Item2 | Item3 + } + + [Flags] + public enum SelfReferentialEnum + { + None = 0, + Item1 = 1, + Item2 = Item1, + Item3 = 3 } [Flags] diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 0e9b6ad54..37f6e2355 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -1835,7 +1835,7 @@ namespace ICSharpCode.Decompiler.CSharp if (constantValue != null) { 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 && initValue >= 10 && decompileRun.EnumValueDisplayMode == EnumValueDisplayMode.AllHex) { diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index 9106e7850..29bb98c1d 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -1199,22 +1199,32 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax return type.HasAttribute(KnownAttribute.Flags, inherit: false); } - Expression ConvertEnumValue(IType type, long val) + /// + /// Converts a numeric enum value into its enum member representation, if possible. + /// Uses a series of enum members concatenated by |, if necessary. + /// Returns (EnumType)value, if it fails. + /// + /// If is set to a non- value, + /// unqualified references are used and self references are avoided. Also, casts to EnumType are dropped. + /// + /// + internal Expression ConvertEnumValue(IType type, long val, IField declaringEnumMember = null) { ITypeDefinition enumDefinition = type.GetDefinition(); TypeCode enumBaseTypeCode = ReflectionHelper.GetTypeCode(enumDefinition.EnumUnderlyingType); var fields = enumDefinition.Fields - .Select(PrepareConstant) + .SelectWithIndex(PrepareConstant) .Where(f => f.field != null) .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 (AddResolveResultAnnotations) - mre.AddAnnotation(new MemberResolveResult(mre.Target.GetResolveResult(), field)); - return mre; + if (field == declaringEnumMember) + { + return new PrimitiveExpression(CSharpPrimitiveCast.Cast(enumBaseTypeCode, val, false)); + } + return MakeEnumMemberReference(field); } } if (IsFlagsEnum(enumDefinition)) @@ -1239,14 +1249,14 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax break; } 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 if ((fieldValue & enumValue) == fieldValue) { - var fieldExpression = new MemberReferenceExpression(new TypeReferenceExpression(ConvertType(type)), field.Name); + var fieldExpression = MakeEnumMemberReference(field); if (expr == null) expr = fieldExpression; else @@ -1256,7 +1266,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } if ((fieldValue & negatedEnumValue) == fieldValue) { - var fieldExpression = new MemberReferenceExpression(new TypeReferenceExpression(ConvertType(type)), field.Name); + var fieldExpression = MakeEnumMemberReference(field); if (negatedExpr == null) negatedExpr = fieldExpression; else @@ -1277,16 +1287,24 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax 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) - return (-1, null); + return default; object constantValue = field.GetConstantValue(); if (constantValue == null) - return (-1, null); - return ((long)CSharpPrimitiveCast.Cast(TypeCode.Int64, constantValue, checkForOverflow: false), field); + return default; + 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 @@ -1301,6 +1319,24 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax 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) + ... } + + 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)