From 9d92c13c9d99c3d96ff2264c4801cb219e9fef05 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 16 Jun 2013 13:02:24 +0200 Subject: [PATCH] implement display of enum values in debugger tooltips --- .../Debugger.AddIn/TreeModel/ValueNode.cs | 12 ++- src/AddIns/Debugger/Debugger.Core/Value.cs | 7 +- .../Refactoring/TypeSystemAstBuilder.cs | 73 ++++++++++++++++++- 3 files changed, 87 insertions(+), 5 deletions(-) diff --git a/src/AddIns/Debugger/Debugger.AddIn/TreeModel/ValueNode.cs b/src/AddIns/Debugger/Debugger.AddIn/TreeModel/ValueNode.cs index b3d7c75caf..6157580a14 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/TreeModel/ValueNode.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/TreeModel/ValueNode.cs @@ -9,6 +9,8 @@ using System.Linq; using System.Text; using System.Windows.Forms; +using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.CSharp.Refactoring; using Debugger.AddIn.Visualizers; using Debugger.MetaData; using ICSharpCode.Core; @@ -147,14 +149,20 @@ namespace Debugger.AddIn.TreeModel } } else if (val.Type.Kind == TypeKind.Pointer) { fullValue = String.Format("0x{0:X}", val.PointerAddress); - } else if (val.Type.FullName == typeof(string).FullName) { + } else if (val.Type.IsKnownType(KnownTypeCode.String)) { fullValue = '"' + val.InvokeToString(WindowsDebugger.EvalThread).Replace("\n", "\\n").Replace("\t", "\\t").Replace("\r", "\\r").Replace("\0", "\\0").Replace("\b", "\\b").Replace("\a", "\\a").Replace("\f", "\\f").Replace("\v", "\\v").Replace("\"", "\\\"") + '"'; - } else if (val.Type.FullName == typeof(char).FullName) { + } else if (val.Type.IsKnownType(KnownTypeCode.Char)) { fullValue = "'" + val.InvokeToString(WindowsDebugger.EvalThread).Replace("\n", "\\n").Replace("\t", "\\t").Replace("\r", "\\r").Replace("\0", "\\0").Replace("\b", "\\b").Replace("\a", "\\a").Replace("\f", "\\f").Replace("\v", "\\v").Replace("\"", "\\\"") + "'"; } else if ((val.Type.Kind == TypeKind.Class || val.Type.Kind == TypeKind.Struct)) { fullValue = val.FormatByDebuggerDisplayAttribute(WindowsDebugger.EvalThread); if (fullValue == null) fullValue = val.InvokeToString(WindowsDebugger.EvalThread); + } else if (val.Type.Kind == TypeKind.Enum) { + var primitiveValue = val.PrimitiveValue; + var builder = new TypeSystemAstBuilder(); + builder.AlwaysUseShortTypeNames = true; + AstNode node = builder.ConvertConstantValue(val.Type, primitiveValue); + fullValue = node + "=" + primitiveValue; } else { fullValue = val.AsString(); } diff --git a/src/AddIns/Debugger/Debugger.Core/Value.cs b/src/AddIns/Debugger/Debugger.Core/Value.cs index 5b64d2b140..0b1e7dd8ef 100644 --- a/src/AddIns/Debugger/Debugger.Core/Value.cs +++ b/src/AddIns/Debugger/Debugger.Core/Value.cs @@ -289,9 +289,12 @@ namespace Debugger if (this.IsNull) return null; return ((ICorDebugStringValue)this.CorReferenceValue.Dereference()).GetString(); } else { - if (!this.Type.IsPrimitiveType()) + var type = this.Type; + if (type.Kind == TypeKind.Enum) + type = type.GetDefinition().EnumUnderlyingType; + if (!type.IsPrimitiveType()) throw new DebuggerException("Value is not a primitive type"); - return CorGenericValue.GetValue(this.Type.GetDefinition().KnownTypeCode); + return CorGenericValue.GetValue(type.GetDefinition().KnownTypeCode); } } } diff --git a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/TypeSystemAstBuilder.cs b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/TypeSystemAstBuilder.cs index c070af84c5..2d3eaa162c 100644 --- a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/TypeSystemAstBuilder.cs +++ b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/TypeSystemAstBuilder.cs @@ -450,11 +450,82 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring else return new DefaultValueExpression(ConvertType(type)); } else if (type.Kind == TypeKind.Enum) { - return new CastExpression(ConvertType(type), ConvertConstantValue(type.GetDefinition().EnumUnderlyingType, constantValue)); + return ConvertEnumValue(type, constantValue is long ? (long)constantValue : (long)((int)constantValue)); } else { return new PrimitiveExpression(constantValue); } } + + bool IsFlagsEnum(ITypeDefinition type) + { + IType flagsAttributeType = type.Compilation.FindType(typeof(System.FlagsAttribute)); + return type.GetAttribute(flagsAttributeType) != null; + } + + Expression ConvertEnumValue(IType type, long val) + { + ITypeDefinition enumDefinition = type.GetDefinition(); + TypeCode enumBaseTypeCode = ReflectionHelper.GetTypeCode(enumDefinition.EnumUnderlyingType); + foreach (IField field in enumDefinition.Fields) { + if (field.IsConst && object.Equals(CSharpPrimitiveCast.Cast(TypeCode.Int64, field.ConstantValue, false), val)) + return ConvertType(type).Member(field.Name); + } + if (IsFlagsEnum(enumDefinition)) { + long enumValue = val; + Expression expr = null; + long negatedEnumValue = ~val; + // limit negatedEnumValue to the appropriate range + switch (enumBaseTypeCode) { + case TypeCode.Byte: + case TypeCode.SByte: + negatedEnumValue &= byte.MaxValue; + break; + case TypeCode.Int16: + case TypeCode.UInt16: + negatedEnumValue &= ushort.MaxValue; + break; + case TypeCode.Int32: + case TypeCode.UInt32: + negatedEnumValue &= uint.MaxValue; + break; + } + Expression negatedExpr = null; + foreach (IField field in enumDefinition.Fields.Where(fld => fld.IsConst)) { + long fieldValue = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, field.ConstantValue, false); + if (fieldValue == 0) + continue; // skip None enum value + + if ((fieldValue & enumValue) == fieldValue) { + var fieldExpression = ConvertType(type).Member(field.Name); + if (expr == null) + expr = fieldExpression; + else + expr = new BinaryOperatorExpression(expr, BinaryOperatorType.BitwiseOr, fieldExpression); + + enumValue &= ~fieldValue; + } + if ((fieldValue & negatedEnumValue) == fieldValue) { + var fieldExpression = ConvertType(type).Member(field.Name); + if (negatedExpr == null) + negatedExpr = fieldExpression; + else + negatedExpr = new BinaryOperatorExpression(negatedExpr, BinaryOperatorType.BitwiseOr, fieldExpression); + + negatedEnumValue &= ~fieldValue; + } + } + if (enumValue == 0 && expr != null) { + if (!(negatedEnumValue == 0 && negatedExpr != null && negatedExpr.Descendants.Count() < expr.Descendants.Count())) { + return expr; + } + } + if (negatedEnumValue == 0 && negatedExpr != null) { + return new UnaryOperatorExpression(UnaryOperatorType.BitNot, negatedExpr); + } + } + return new PrimitiveExpression(CSharpPrimitiveCast.Cast(enumBaseTypeCode, val, false)).CastTo(ConvertType(type)); + } + #endregion #region Convert Parameter