Browse Source

Fix #2448: Decompiler shows some enum values as hexdecimal instead of decimal

pull/2566/head
Siegfried Pammer 4 years ago
parent
commit
ec6a9afc57
  1. 8
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs
  2. 2
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributeSamples.cs
  3. 10
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.cs
  4. 10
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes2.cs
  5. 10
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/EnumTests.cs
  6. 45
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  7. 14
      ICSharpCode.Decompiler/DecompileRun.cs

8
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs

@ -26,10 +26,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
[Flags] [Flags]
private enum MyEnum private enum MyEnum
{ {
None = 0x0, None = 0,
One = 0x1, One = 1,
Two = 0x2, Two = 2,
Four = 0x4 Four = 4
} }
public enum ShortEnum : short public enum ShortEnum : short

2
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributeSamples.cs

@ -37,7 +37,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.CustomAttributeSamples
[Flags] [Flags]
public enum EnumWithFlagsAttribute public enum EnumWithFlagsAttribute
{ {
None = 0x0 None = 0
} }
[AttributeUsage(AttributeTargets.All)] [AttributeUsage(AttributeTargets.All)]

10
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.cs

@ -27,11 +27,11 @@ namespace CustomAttributes
public enum EnumWithFlag public enum EnumWithFlag
{ {
All = 0xF, All = 0xF,
None = 0x0, None = 0,
Item1 = 0x1, Item1 = 1,
Item2 = 0x2, Item2 = 2,
Item3 = 0x4, Item3 = 4,
Item4 = 0x8 Item4 = 8
} }
[AttributeUsage(AttributeTargets.All)] [AttributeUsage(AttributeTargets.All)]
public class MyAttribute : Attribute public class MyAttribute : Attribute

10
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes2.cs

@ -7,11 +7,11 @@ namespace CustomAttributes2
public enum EnumWithFlag public enum EnumWithFlag
{ {
All = 0xF, All = 0xF,
None = 0x0, None = 0,
Item1 = 0x1, Item1 = 1,
Item2 = 0x2, Item2 = 2,
Item3 = 0x4, Item3 = 4,
Item4 = 0x8 Item4 = 8
} }
[AttributeUsage(AttributeTargets.All)] [AttributeUsage(AttributeTargets.All)]
public class MyAttribute : Attribute public class MyAttribute : Attribute

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

@ -58,11 +58,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
[Flags] [Flags]
public enum SimpleFlagsEnum public enum SimpleFlagsEnum
{ {
None = 0x0, None = 0,
Item1 = 0x1, Item1 = 1,
Item2 = 0x2, Item2 = 2,
Item3 = 0x4, Item3 = 4,
All = 0x7 All = 7
} }
[Flags] [Flags]

45
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -1264,6 +1264,11 @@ namespace ICSharpCode.Decompiler.CSharp
typeDecl.Members.Add(nestedType); typeDecl.Members.Add(nestedType);
} }
} }
decompileRun.EnumValueDisplayMode = typeDef.Kind == TypeKind.Enum
? DetectBestEnumValueDisplayMode(typeDef, module.PEFile)
: null;
// With C# 9 records, the relative order of fields and properties matters: // With C# 9 records, the relative order of fields and properties matters:
IEnumerable<IMember> fieldsAndProperties = recordDecompiler?.FieldsAndProperties IEnumerable<IMember> fieldsAndProperties = recordDecompiler?.FieldsAndProperties
?? typeDef.Fields.Concat<IMember>(typeDef.Properties); ?? typeDef.Fields.Concat<IMember>(typeDef.Properties);
@ -1331,7 +1336,7 @@ namespace ICSharpCode.Decompiler.CSharp
} }
if (typeDecl.ClassType == ClassType.Enum) if (typeDecl.ClassType == ClassType.Enum)
{ {
switch (DetectBestEnumValueDisplayMode(typeDef, module.PEFile)) switch (decompileRun.EnumValueDisplayMode)
{ {
case EnumValueDisplayMode.FirstOnly: case EnumValueDisplayMode.FirstOnly:
foreach (var enumMember in typeDecl.Members.OfType<EnumMemberDeclaration>().Skip(1)) foreach (var enumMember in typeDecl.Members.OfType<EnumMemberDeclaration>().Skip(1))
@ -1350,11 +1355,13 @@ namespace ICSharpCode.Decompiler.CSharp
} }
break; break;
case EnumValueDisplayMode.All: case EnumValueDisplayMode.All:
case EnumValueDisplayMode.AllHex:
// nothing needs to be changed. // nothing needs to be changed.
break; break;
default: default:
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
} }
decompileRun.EnumValueDisplayMode = null;
} }
return typeDecl; return typeDecl;
} }
@ -1369,21 +1376,14 @@ namespace ICSharpCode.Decompiler.CSharp
} }
} }
enum EnumValueDisplayMode
{
None,
All,
FirstOnly
}
EnumValueDisplayMode DetectBestEnumValueDisplayMode(ITypeDefinition typeDef, PEFile module) EnumValueDisplayMode DetectBestEnumValueDisplayMode(ITypeDefinition typeDef, PEFile module)
{ {
if (settings.AlwaysShowEnumMemberValues)
return EnumValueDisplayMode.All;
if (typeDef.HasAttribute(KnownAttribute.Flags, inherit: false)) if (typeDef.HasAttribute(KnownAttribute.Flags, inherit: false))
return EnumValueDisplayMode.All; return EnumValueDisplayMode.AllHex;
bool first = true; bool first = true;
long firstValue = 0, previousValue = 0; long firstValue = 0, previousValue = 0;
bool allPowersOfTwo = true;
bool allConsecutive = true;
foreach (var field in typeDef.Fields) foreach (var field in typeDef.Fields)
{ {
if (MemberIsHidden(module, field.MetadataToken, settings)) if (MemberIsHidden(module, field.MetadataToken, settings))
@ -1392,17 +1392,35 @@ namespace ICSharpCode.Decompiler.CSharp
if (constantValue == null) if (constantValue == null)
continue; continue;
long currentValue = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, constantValue, false); long currentValue = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, constantValue, false);
allConsecutive = allConsecutive && (first || previousValue + 1 == currentValue);
// N & (N - 1) == 0, iff N is a power of 2, for all N != 0.
// We define that 0 is a power of 2 in the context of enum values.
allPowersOfTwo = allPowersOfTwo && unchecked(currentValue & (currentValue - 1)) == 0;
if (first) if (first)
{ {
firstValue = currentValue; firstValue = currentValue;
first = false; first = false;
} }
else if (previousValue + 1 != currentValue) else if (!allConsecutive && !allPowersOfTwo)
{ {
// We already know that the values are neither consecutive nor all powers of 2,
// so we can abort, and just display all values as-is.
return EnumValueDisplayMode.All; return EnumValueDisplayMode.All;
} }
previousValue = currentValue; previousValue = currentValue;
} }
if (allPowersOfTwo && previousValue > 2)
{
// If all values are powers of 2, display all enum values, but use hex.
return EnumValueDisplayMode.AllHex;
}
if (settings.AlwaysShowEnumMemberValues)
{
// The user always wants to see all enum values, but we know hex is not necessary.
return EnumValueDisplayMode.All;
}
// We know that all values are consecutive, so if the first value is not 0
// display the first enum value only.
return firstValue == 0 ? EnumValueDisplayMode.None : EnumValueDisplayMode.FirstOnly; return firstValue == 0 ? EnumValueDisplayMode.None : EnumValueDisplayMode.FirstOnly;
} }
@ -1712,8 +1730,7 @@ namespace ICSharpCode.Decompiler.CSharp
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.ConvertConstantValue(decompilationContext.CurrentTypeDefinition.EnumUnderlyingType, constantValue);
if (enumDec.Initializer is PrimitiveExpression primitive if (enumDec.Initializer is PrimitiveExpression primitive
&& initValue >= 0 && (decompilationContext.CurrentTypeDefinition.HasAttribute(KnownAttribute.Flags) && initValue >= 10 && decompileRun.EnumValueDisplayMode == EnumValueDisplayMode.AllHex)
|| (initValue > 9 && (unchecked(initValue & (initValue - 1)) == 0 || unchecked(initValue & (initValue + 1)) == 0))))
{ {
primitive.Format = LiteralFormat.HexadecimalNumber; primitive.Format = LiteralFormat.HexadecimalNumber;
} }

14
ICSharpCode.Decompiler/DecompileRun.cs

@ -12,8 +12,8 @@ namespace ICSharpCode.Decompiler
{ {
internal class DecompileRun internal class DecompileRun
{ {
public HashSet<string> DefinedSymbols { get; private set; } = new HashSet<string>(); public HashSet<string> DefinedSymbols { get; } = new HashSet<string>();
public HashSet<string> Namespaces { get; private set; } = new HashSet<string>(); public HashSet<string> Namespaces { get; } = new HashSet<string>();
public CancellationToken CancellationToken { get; set; } public CancellationToken CancellationToken { get; set; }
public DecompilerSettings Settings { get; } public DecompilerSettings Settings { get; }
public IDocumentationProvider DocumentationProvider { get; set; } public IDocumentationProvider DocumentationProvider { get; set; }
@ -47,5 +47,15 @@ namespace ICSharpCode.Decompiler
} }
return usingScope; return usingScope;
} }
public EnumValueDisplayMode? EnumValueDisplayMode { get; set; }
}
enum EnumValueDisplayMode
{
None,
All,
AllHex,
FirstOnly
} }
} }

Loading…
Cancel
Save