diff --git a/src/ExpressionEvaluator/CSharp/Test/ResultProvider/TupleTests.cs b/src/ExpressionEvaluator/CSharp/Test/ResultProvider/TupleTests.cs index b9aad54d8de2c67c84bb9891a870328f762d806e..07b601e69ae799c675169e6a5bbc048de923ceca 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ResultProvider/TupleTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ResultProvider/TupleTests.cs @@ -217,7 +217,7 @@ class C } [Fact] - public void LongTuple() + public void LongTuple_NoNames() { var source = @"class C @@ -247,25 +247,55 @@ public void LongTuple() "o._17", DkmEvaluationResultFlags.Expandable)); children = GetChildren(children[0], inspectionContext); - Assert.Equal(18, children.Length); - var child = children[children.Length - 1]; - Verify(child, - EvalResult( - "Rest", - "(0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011)", - "(short, short, short, short, short, short, short, short, short, short)", - "o._17.Rest", - DkmEvaluationResultFlags.Expandable)); - children = GetChildren(child, inspectionContext); - Assert.Equal(11, children.Length); - child = children[children.Length - 1]; - Verify(child, + Verify(children, + EvalResult("Item1", "0x0001", "short", "o._17.Item1"), + EvalResult("Item2", "0x0002", "short", "o._17.Item2"), + EvalResult("Item3", "0x0003", "short", "o._17.Item3"), + EvalResult("Item4", "0x0004", "short", "o._17.Item4"), + EvalResult("Item5", "0x0005", "short", "o._17.Item5"), + EvalResult("Item6", "0x0006", "short", "o._17.Item6"), + EvalResult("Item7", "0x0007", "short", "o._17.Item7"), + EvalResult("Item8", "0x0008", "short", "o._17.Rest.Item1"), + EvalResult("Item9", "0x0009", "short", "o._17.Rest.Item2"), + EvalResult("Item10", "0x000a", "short", "o._17.Rest.Item3"), + EvalResult("Item11", "0x000b", "short", "o._17.Rest.Item4"), + EvalResult("Item12", "0x000c", "short", "o._17.Rest.Item5"), + EvalResult("Item13", "0x000d", "short", "o._17.Rest.Item6"), + EvalResult("Item14", "0x000e", "short", "o._17.Rest.Item7"), + EvalResult("Item15", "0x000f", "short", "o._17.Rest.Rest.Item1"), + EvalResult("Item16", "0x0010", "short", "o._17.Rest.Rest.Item2"), + EvalResult("Item17", "0x0011", "short", "o._17.Rest.Rest.Item3"), EvalResult( - "Rest", - "(0x000f, 0x0010, 0x0011)", - "(short, short, short)", - "o._17.Rest.Rest", - DkmEvaluationResultFlags.Expandable)); + "Raw View", + "(0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011)", + "(short, short, short, short, short, short, short, short, short, short, short, short, short, short, short, short, short)", + "o._17, raw", + DkmEvaluationResultFlags.Expandable | DkmEvaluationResultFlags.ReadOnly)); + children = GetChildren(children[children.Length - 1], inspectionContext); + Verify(children, + EvalResult("Item1", "0x0001", "short", "o._17.Item1"), + EvalResult("Item2", "0x0002", "short", "o._17.Item2"), + EvalResult("Item3", "0x0003", "short", "o._17.Item3"), + EvalResult("Item4", "0x0004", "short", "o._17.Item4"), + EvalResult("Item5", "0x0005", "short", "o._17.Item5"), + EvalResult("Item6", "0x0006", "short", "o._17.Item6"), + EvalResult("Item7", "0x0007", "short", "o._17.Item7"), + EvalResult("Rest", "(0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011)", "(short, short, short, short, short, short, short, short, short, short)", "o._17.Rest, raw", DkmEvaluationResultFlags.Expandable)); + children = GetChildren(children[children.Length - 1], inspectionContext); + Verify(children, + EvalResult("Item1", "0x0008", "short", "o._17.Rest.Item1"), + EvalResult("Item2", "0x0009", "short", "o._17.Rest.Item2"), + EvalResult("Item3", "0x000a", "short", "o._17.Rest.Item3"), + EvalResult("Item4", "0x000b", "short", "o._17.Rest.Item4"), + EvalResult("Item5", "0x000c", "short", "o._17.Rest.Item5"), + EvalResult("Item6", "0x000d", "short", "o._17.Rest.Item6"), + EvalResult("Item7", "0x000e", "short", "o._17.Rest.Item7"), + EvalResult("Rest", "(0x000f, 0x0010, 0x0011)", "(short, short, short)", "o._17.Rest.Rest, raw", DkmEvaluationResultFlags.Expandable)); + children = GetChildren(children[children.Length - 1], inspectionContext); + Verify(children, + EvalResult("Item1", "0x000f", "short", "o._17.Rest.Rest.Item1"), + EvalResult("Item2", "0x0010", "short", "o._17.Rest.Rest.Item2"), + EvalResult("Item3", "0x0011", "short", "o._17.Rest.Rest.Item3")); } } @@ -349,21 +379,19 @@ class C var moreChildren = GetChildren(children[0]); Verify(moreChildren, EvalResult("A", "1", "int", "o.F.Item1"), - EvalResult("Item1", "1", "int", "o.F.Item1")); + EvalResult("Raw View", "{(int, int)}", "(int A, int B)", "o.F, raw", DkmEvaluationResultFlags.Expandable | DkmEvaluationResultFlags.ReadOnly)); moreChildren = GetChildren(children[1]); Verify(moreChildren); moreChildren = GetChildren(children[2]); Verify(moreChildren, EvalResult("Item1", "1", "int", "o.H.Item1"), EvalResult("B", "2", "int", "o.H.Item2"), - EvalResult("Item2", "2", "int", "o.H.Item2"), EvalResult("Item3", "3", "int", "o.H.Item3"), EvalResult("D", "4", "int", "o.H.Item4"), - EvalResult("Item4", "4", "int", "o.H.Item4"), EvalResult("Item5", "5", "int", "o.H.Item5"), EvalResult("F", "6", "int", "o.H.Item6"), - EvalResult("Item6", "6", "int", "o.H.Item6"), - EvalResult("Item7", "7", "int", "o.H.Item7")); + EvalResult("Item7", "7", "int", "o.H.Item7"), + EvalResult("Raw View", "{(int, int, int, int, int, int, int, int, int)}", "(int, int B, int, int D, int, int F, int, int H, int)", "o.H, raw", DkmEvaluationResultFlags.Expandable | DkmEvaluationResultFlags.ReadOnly)); } } @@ -677,15 +705,13 @@ class C moreChildren = GetChildren(moreChildren[0]); Verify(moreChildren, EvalResult("X", "null", "dynamic {object}", "o.G.F.Item1"), - EvalResult("Item1", "null", "dynamic {object}", "o.G.F.Item1"), EvalResult("Y", "(null, {B<(object, object)>.S})", "(object E, B<(object F, dynamic G)>.S H) {(object, B<(object, object)>.S)}", "o.G.F.Item2", DkmEvaluationResultFlags.Expandable), - EvalResult("Item2", "(null, {B<(object, object)>.S})", "(object E, B<(object F, dynamic G)>.S H) {(object, B<(object, object)>.S)}", "o.G.F.Item2", DkmEvaluationResultFlags.Expandable)); - moreChildren = GetChildren(moreChildren[3]); + EvalResult("Raw View", "(null, (null, {B<(object, object)>.S}))", "(dynamic X, (object E, B<(object F, dynamic G)>.S H) Y) {(object, (object, B<(object, object)>.S))}", "o.G.F, raw", DkmEvaluationResultFlags.Expandable | DkmEvaluationResultFlags.ReadOnly)); + moreChildren = GetChildren(moreChildren[1]); Verify(moreChildren, EvalResult("E", "null", "object", "o.G.F.Item2.Item1"), - EvalResult("Item1", "null", "object", "o.G.F.Item2.Item1"), EvalResult("H", "{B<(object, object)>.S}", "B<(object F, dynamic G)>.S {B<(object, object)>.S}", "o.G.F.Item2.Item2"), - EvalResult("Item2", "{B<(object, object)>.S}", "B<(object F, dynamic G)>.S {B<(object, object)>.S}", "o.G.F.Item2.Item2")); + EvalResult("Raw View", "(null, {B<(object, object)>.S})", "(object E, B<(object F, dynamic G)>.S H) {(object, B<(object, object)>.S)}", "o.G.F.Item2, raw", DkmEvaluationResultFlags.Expandable | DkmEvaluationResultFlags.ReadOnly)); } } @@ -821,14 +847,17 @@ public void InvalidElementName() children = GetChildren(children[0]); Verify(children, EvalResult("Item2", "null", "object", "o.F.Item1"), - EvalResult("Item1", "null", "object", "o.F.Item1"), EvalResult("struct { }", "null", "object", "o.F.Item2"), + EvalResult("Raw View", "(null, null)", "(object Item2, object struct { })", "o.F, raw", DkmEvaluationResultFlags.Expandable | DkmEvaluationResultFlags.ReadOnly)); + children = GetChildren(children[children.Length - 1]); + Verify(children, + EvalResult("Item1", "null", "object", "o.F.Item1"), EvalResult("Item2", "null", "object", "o.F.Item2")); } } [Fact] - public void LongTuple_Expansion() + public void LongTuple_ElementNames() { // Define in IL to include tuple element names // for the Rest elements. @@ -862,50 +891,96 @@ public void LongTuple_Expansion() children = GetChildren(children[0]); Verify(children, EvalResult("One", "0", "int", "o.F.Item1"), - EvalResult("Item1", "0", "int", "o.F.Item1"), EvalResult("Two", "0", "int", "o.F.Item2"), - EvalResult("Item2", "0", "int", "o.F.Item2"), EvalResult("Item3", "0", "int", "o.F.Item3"), EvalResult("Item4", "0", "int", "o.F.Item4"), EvalResult("Five", "0", "int", "o.F.Item5"), - EvalResult("Item5", "0", "int", "o.F.Item5"), EvalResult("Six", "0", "int", "o.F.Item6"), - EvalResult("Item6", "0", "int", "o.F.Item6"), EvalResult("Item7", "0", "int", "o.F.Item7"), EvalResult("Item8", "0", "int", "o.F.Rest.Item1"), EvalResult("Nine", "0", "int", "o.F.Rest.Item2"), - EvalResult("Item9", "0", "int", "o.F.Rest.Item2"), EvalResult("Ten", "0", "int", "o.F.Rest.Item3"), - EvalResult("Item10", "0", "int", "o.F.Rest.Item3"), EvalResult("Item11", "0", "int", "o.F.Rest.Item4"), EvalResult("Item12", "0", "int", "o.F.Rest.Item5"), EvalResult("Thirteen", "0", "int", "o.F.Rest.Item6"), - EvalResult("Item13", "0", "int", "o.F.Rest.Item6"), EvalResult("Fourteen", "0", "int", "o.F.Rest.Item7"), - EvalResult("Item14", "0", "int", "o.F.Rest.Item7"), EvalResult("Item15", "0", "int", "o.F.Rest.Rest.Item1"), - EvalResult("Rest", "(0, 0, 0, 0, 0, 0, 0, 0)", "(int, int Seventeen, int Eighteen, int, int, int TwentyOne, int TwentyTwo, int)", "o.F.Rest", DkmEvaluationResultFlags.Expandable)); + EvalResult( + "Raw View", + "(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)", "(int One, int Two, int, int, int Five, int Six, int, int, int Nine, int Ten, int, int, int Thirteen, int Fourteen, int)", + "o.F, raw", + DkmEvaluationResultFlags.Expandable | DkmEvaluationResultFlags.ReadOnly)); + children = GetChildren(children[children.Length - 1]); + Verify(children, + EvalResult("Item1", "0", "int", "o.F.Item1"), + EvalResult("Item2", "0", "int", "o.F.Item2"), + EvalResult("Item3", "0", "int", "o.F.Item3"), + EvalResult("Item4", "0", "int", "o.F.Item4"), + EvalResult("Item5", "0", "int", "o.F.Item5"), + EvalResult("Item6", "0", "int", "o.F.Item6"), + EvalResult("Item7", "0", "int", "o.F.Item7"), + EvalResult("Rest", "(0, 0, 0, 0, 0, 0, 0, 0)", "(int, int Seventeen, int Eighteen, int, int, int TwentyOne, int TwentyTwo, int)", "o.F.Rest, raw", DkmEvaluationResultFlags.Expandable)); children = GetChildren(children[children.Length - 1]); Verify(children, EvalResult("Item1", "0", "int", "o.F.Rest.Item1"), - EvalResult("Seventeen", "0", "int", "o.F.Rest.Item2"), EvalResult("Item2", "0", "int", "o.F.Rest.Item2"), - EvalResult("Eighteen", "0", "int", "o.F.Rest.Item3"), EvalResult("Item3", "0", "int", "o.F.Rest.Item3"), EvalResult("Item4", "0", "int", "o.F.Rest.Item4"), EvalResult("Item5", "0", "int", "o.F.Rest.Item5"), - EvalResult("TwentyOne", "0", "int", "o.F.Rest.Item6"), EvalResult("Item6", "0", "int", "o.F.Rest.Item6"), - EvalResult("TwentyTwo", "0", "int", "o.F.Rest.Item7"), EvalResult("Item7", "0", "int", "o.F.Rest.Item7"), - EvalResult("Item8", "0", "int", "o.F.Rest.Rest.Item1"), - EvalResult("Rest", "{System.ValueTuple}", "System.ValueTuple", "o.F.Rest.Rest", DkmEvaluationResultFlags.Expandable)); + EvalResult("Rest", "{System.ValueTuple}", "System.ValueTuple", "o.F.Rest.Rest, raw", DkmEvaluationResultFlags.Expandable)); children = GetChildren(children[children.Length - 1]); Verify(children, EvalResult("Item1", "0", "int", "o.F.Rest.Rest.Item1")); } } + [Fact] + public void RawView() + { + var source = +@"class C +{ + (int A, int, (int C, int D) E, int, int, int H, int, int J) T = (1, 2, (3, 4), 5, 6, 7, 8, 9); +}"; + var assembly0 = GenerateTupleAssembly(); + var reference0 = AssemblyMetadata.CreateFromImage(assembly0).GetReference(); + var compilation1 = CSharpTestBaseBase.CreateCompilationWithMscorlib(source, references: new[] { reference0 }); + var assembly1 = compilation1.EmitToArray(); + var runtime = new DkmClrRuntimeInstance(ReflectionUtilities.GetMscorlib(ReflectionUtilities.Load(assembly0), ReflectionUtilities.Load(assembly1))); + using (runtime.Load()) + { + var inspectionContext = CreateDkmInspectionContext(DkmEvaluationFlags.ShowValueRaw); + var type = runtime.GetType("C"); + var value = type.Instantiate(); + var evalResult = FormatResult("o", value, inspectionContext: inspectionContext); + Verify(evalResult, + EvalResult("o", "{C}", "C", "o, raw", DkmEvaluationResultFlags.Expandable)); + var children = GetChildren(evalResult, inspectionContext); + Verify(children, + EvalResult( + "T", + "(1, 2, (3, 4), 5, 6, 7, 8, 9)", + "(int A, int, (int C, int D) E, int, int, int H, int, int J)", + "o.T, raw", + DkmEvaluationResultFlags.Expandable)); + children = GetChildren(children[0], inspectionContext); + Verify(children, + EvalResult("Item1", "1", "int", "o.T.Item1, raw"), + EvalResult("Item2", "2", "int", "o.T.Item2, raw"), + EvalResult("Item3", "(3, 4)", "(int C, int D)", "o.T.Item3, raw", DkmEvaluationResultFlags.Expandable), + EvalResult("Item4", "5", "int", "o.T.Item4, raw"), + EvalResult("Item5", "6", "int", "o.T.Item5, raw"), + EvalResult("Item6", "7", "int", "o.T.Item6, raw"), + EvalResult("Item7", "8", "int", "o.T.Item7, raw"), + EvalResult("Rest", "{System.ValueTuple}", "System.ValueTuple", "o.T.Rest, raw", DkmEvaluationResultFlags.Expandable)); + children = GetChildren(children[7], inspectionContext); + Verify(children, + EvalResult("Item1", "9", "int", "o.T.Rest.Item1, raw")); + } + } + [Fact] public void Keywords() { @@ -1075,10 +1150,10 @@ public void Exception() children = GetChildren(children[0]); Verify(children, EvalResult("A", "1", "object {int}", "o.F.Item1"), - EvalResult("Item1", "1", "object {int}", "o.F.Item1"), EvalResult("Item2", "'o.F.Item2' threw an exception of type 'System.InvalidOperationException'", "int {System.InvalidOperationException}", "o.F.Item2", DkmEvaluationResultFlags.Expandable | DkmEvaluationResultFlags.ExceptionThrown), - EvalResult("Item3", "3", "int", "o.F.Item3")); - children = GetChildren(children[2]); + EvalResult("Item3", "3", "int", "o.F.Item3"), + EvalResult("Raw View", "(1, {System.InvalidOperationException: Unable to evaluate}, 3)", "(object A, int, int)", "o.F, raw", DkmEvaluationResultFlags.Expandable | DkmEvaluationResultFlags.ReadOnly)); + children = GetChildren(children[1]); Assert.True(children.Length > 0); Assert.Null(children[0].FullName); // FullName null for members of thrown Exception. } @@ -1119,6 +1194,10 @@ class E : System.Exception Verify(children, EvalResult("Item1", "1", "int", null), EvalResult("B", "2", "int", null), + EvalResult("Raw View", "(1, 2)", "(int, int B)", null, DkmEvaluationResultFlags.Expandable | DkmEvaluationResultFlags.ReadOnly)); + children = GetChildren(children[children.Length - 1]); + Verify(children, + EvalResult("Item1", "1", "int", null), EvalResult("Item2", "2", "int", null)); } } diff --git a/src/ExpressionEvaluator/Core/Source/ResultProvider/Expansion/TupleExpansion.cs b/src/ExpressionEvaluator/Core/Source/ResultProvider/Expansion/TupleExpansion.cs index 39133b1cf89f6a128c979bc86c643a867180fcb5..54f736ab6c453f699b4a7a43eb3789fd803fdaa9 100644 --- a/src/ExpressionEvaluator/Core/Source/ResultProvider/Expansion/TupleExpansion.cs +++ b/src/ExpressionEvaluator/Core/Source/ResultProvider/Expansion/TupleExpansion.cs @@ -14,24 +14,32 @@ namespace Microsoft.CodeAnalysis.ExpressionEvaluator { internal sealed class TupleExpansion : Expansion { - internal static TupleExpansion CreateExpansion(DkmClrValue value, TypeAndCustomInfo declaredTypeAndInfo, int cardinality) + internal static TupleExpansion CreateExpansion( + DkmInspectionContext inspectionContext, + TypeAndCustomInfo declaredTypeAndInfo, + DkmClrValue value, + int cardinality) { if (value.IsNull) { // No expansion. return null; } - return new TupleExpansion(new TypeAndCustomInfo(value.Type, declaredTypeAndInfo.Info), cardinality); + + bool useRawView = (inspectionContext.EvaluationFlags & DkmEvaluationFlags.ShowValueRaw) != 0; + return new TupleExpansion(new TypeAndCustomInfo(value.Type, declaredTypeAndInfo.Info), cardinality, useRawView); } private readonly TypeAndCustomInfo _typeAndInfo; private readonly int _cardinality; - private ReadOnlyCollection _lazyFields; + private readonly bool _useRawView; + private Fields _lazyFields; - private TupleExpansion(TypeAndCustomInfo typeAndInfo, int cardinality) + private TupleExpansion(TypeAndCustomInfo typeAndInfo, int cardinality, bool useRawView) { _typeAndInfo = typeAndInfo; _cardinality = cardinality; + _useRawView = useRawView; } internal override void GetRows( @@ -46,19 +54,30 @@ private TupleExpansion(TypeAndCustomInfo typeAndInfo, int cardinality) ref int index) { var fields = GetFields(); + var defaultView = fields.DefaultView; int startIndex2; int count2; - GetIntersection(startIndex, count, index, fields.Count, out startIndex2, out count2); + GetIntersection(startIndex, count, index, defaultView.Count, out startIndex2, out count2); int offset = startIndex2 - index; for (int i = 0; i < count2; i++) { - var row = GetMemberRow(resultProvider, inspectionContext, value, fields[i + offset], parent); + var row = GetMemberRow(resultProvider, inspectionContext, value, defaultView[i + offset], parent, _cardinality); rows.Add(row); } - index += fields.Count; + index += defaultView.Count; + + if (fields.IncludeRawView) + { + if (InRange(startIndex, count, index)) + { + rows.Add(this.CreateRawViewRow(resultProvider, inspectionContext, parent, value)); + } + + index++; + } } private static EvalResult GetMemberRow( @@ -66,7 +85,8 @@ private TupleExpansion(TypeAndCustomInfo typeAndInfo, int cardinality) DkmInspectionContext inspectionContext, DkmClrValue value, Field field, - EvalResultDataItem parent) + EvalResultDataItem parent, + int cardinality) { var fullNameProvider = resultProvider.FullNameProvider; var parentFullName = parent.ChildFullNamePrefix; @@ -94,19 +114,56 @@ private TupleExpansion(TypeAndCustomInfo typeAndInfo, int cardinality) field, parentFullName, out fullName); + var name = field.Name; + var typeDeclaringMemberAndInfo = default(TypeAndCustomInfo); + var declaredTypeAndInfo = field.FieldTypeAndInfo; + var flags = fieldValue.EvalFlags; + var formatSpecifiers = Formatter.NoFormatSpecifiers; + + if (field.IsRest) + { + var displayValue = fieldValue.GetValueString(inspectionContext, Formatter.NoFormatSpecifiers); + var displayType = ResultProvider.GetTypeName( + inspectionContext, + fieldValue, + declaredTypeAndInfo.ClrType, + declaredTypeAndInfo.Info, + isPointerDereference: false); + var expansion = new TupleExpansion(declaredTypeAndInfo, cardinality - (TypeHelpers.TupleFieldRestPosition - 1), useRawView: true); + return new EvalResult( + ExpansionKind.Explicit, + name, + typeDeclaringMemberAndInfo, + declaredTypeAndInfo, + useDebuggerDisplay: false, + value: fieldValue, + displayValue: displayValue, + expansion: expansion, + childShouldParenthesize: false, + fullName: fullName, + childFullNamePrefixOpt: flags.Includes(DkmEvaluationResultFlags.ExceptionThrown) ? null : fullName, + formatSpecifiers: Formatter.AddFormatSpecifier(formatSpecifiers, "raw"), + category: DkmEvaluationResultCategory.Other, + flags: flags, + editableValue: null, + inspectionContext: inspectionContext, + displayName: name, + displayType: displayType); + } + return resultProvider.CreateDataItem( inspectionContext, - field.Name, - typeDeclaringMemberAndInfo: default(TypeAndCustomInfo), - declaredTypeAndInfo: field.FieldTypeAndInfo, + name, + typeDeclaringMemberAndInfo: typeDeclaringMemberAndInfo, + declaredTypeAndInfo: declaredTypeAndInfo, value: fieldValue, useDebuggerDisplay: false, expansionFlags: ExpansionFlags.All, childShouldParenthesize: false, fullName: fullName, - formatSpecifiers: Formatter.NoFormatSpecifiers, + formatSpecifiers: formatSpecifiers, category: DkmEvaluationResultCategory.Other, - flags: fieldValue.EvalFlags, + flags: flags, evalFlags: DkmEvaluationFlags.None); } @@ -150,13 +207,15 @@ private sealed class Field internal readonly FieldInfo FieldInfo; // type field internal readonly string Name; internal readonly Field Parent; // parent Rest field, if any + internal readonly bool IsRest; internal Field( TypeAndCustomInfo declaringTypeAndInfo, TypeAndCustomInfo fieldTypeAndInfo, FieldInfo fieldInfo, string name, - Field parent) + Field parent, + bool isRest) { Debug.Assert(declaringTypeAndInfo.ClrType != null); Debug.Assert(fieldTypeAndInfo.ClrType != null); @@ -171,30 +230,43 @@ private sealed class Field FieldInfo = fieldInfo; Name = name; Parent = parent; + IsRest = isRest; + } + } + + private sealed class Fields + { + internal readonly ReadOnlyCollection DefaultView; + internal readonly bool IncludeRawView; + + internal Fields(ReadOnlyCollection defaultView, bool includeRawView) + { + DefaultView = defaultView; + IncludeRawView = includeRawView; } } - private ReadOnlyCollection GetFields() + private Fields GetFields() { if (_lazyFields == null) { - _lazyFields = GetFields(_typeAndInfo, _cardinality); + _lazyFields = GetFields(_typeAndInfo, _cardinality, _useRawView); } return _lazyFields; } - private static ReadOnlyCollection GetFields(TypeAndCustomInfo declaringTypeAndInfo, int cardinality) + private static Fields GetFields(TypeAndCustomInfo declaringTypeAndInfo, int cardinality, bool useRawView) { Debug.Assert(declaringTypeAndInfo.Type.GetTupleCardinalityIfAny() == cardinality); var appDomain = declaringTypeAndInfo.ClrType.AppDomain; - var customTypeInfoMap = CustomTypeInfoTypeArgumentMap.Create(declaringTypeAndInfo); var tupleElementNames = customTypeInfoMap.TupleElementNames; var builder = ArrayBuilder.GetInstance(); Field parent = null; int offset = 0; + bool includeRawView = false; while (true) { @@ -211,17 +283,24 @@ private static ReadOnlyCollection GetFields(TypeAndCustomInfo declaringTy } var fieldTypeAndInfo = GetTupleFieldTypeAndInfo(appDomain, field, customTypeInfoMap); - var name = CustomTypeInfo.GetTupleElementNameIfAny(tupleElementNames, offset + index); - if (name != null) + if (!useRawView) { - builder.Add(new Field(declaringTypeAndInfo, fieldTypeAndInfo, field, name, parent)); + var name = CustomTypeInfo.GetTupleElementNameIfAny(tupleElementNames, offset + index); + if (name != null) + { + includeRawView = true; + builder.Add(new Field(declaringTypeAndInfo, fieldTypeAndInfo, field, name, parent, isRest: false)); + continue; + } } + builder.Add(new Field( declaringTypeAndInfo, fieldTypeAndInfo, field, (offset == 0) ? fieldName : TypeHelpers.GetTupleFieldName(offset + index), - parent)); + parent, + isRest: false)); } cardinality -= n; @@ -238,23 +317,21 @@ private static ReadOnlyCollection GetFields(TypeAndCustomInfo declaringTy } var restTypeAndInfo = GetTupleFieldTypeAndInfo(appDomain, rest, customTypeInfoMap); - parent = new Field(declaringTypeAndInfo, restTypeAndInfo, rest, TypeHelpers.TupleFieldRestName, parent); - declaringTypeAndInfo = restTypeAndInfo; - offset += TypeHelpers.TupleFieldRestPosition - 1; - } + var restField = new Field(declaringTypeAndInfo, restTypeAndInfo, rest, TypeHelpers.TupleFieldRestName, parent, isRest: true); - // If there were any nested ValueTuples, - // add the Rest field of the outermost. - if (parent != null) - { - while (parent.Parent != null) + if (useRawView) { - parent = parent.Parent; + builder.Add(restField); + break; } - builder.Add(parent); + + includeRawView = true; + parent = restField; + declaringTypeAndInfo = restTypeAndInfo; + offset += TypeHelpers.TupleFieldRestPosition - 1; } - return builder.ToImmutableAndFree(); + return new Fields(builder.ToImmutableAndFree(), includeRawView); } private static TypeAndCustomInfo GetTupleFieldTypeAndInfo( @@ -268,5 +345,41 @@ private static ReadOnlyCollection GetFields(TypeAndCustomInfo declaringTy var fieldTypeInfo = customTypeInfoMap.SubstituteCustomTypeInfo(fieldDef.FieldType, null); return new TypeAndCustomInfo(fieldType, fieldTypeInfo); } + + private EvalResult CreateRawViewRow( + ResultProvider resultProvider, + DkmInspectionContext inspectionContext, + EvalResultDataItem parent, + DkmClrValue value) + { + var displayName = Resources.RawView; + var displayValue = value.GetValueString(inspectionContext, Formatter.NoFormatSpecifiers); + var displayType = ResultProvider.GetTypeName( + inspectionContext, + value, + _typeAndInfo.ClrType, + _typeAndInfo.Info, + isPointerDereference: false); + var expansion = new TupleExpansion(_typeAndInfo, _cardinality, useRawView: true); + return new EvalResult( + ExpansionKind.Explicit, + displayName, + default(TypeAndCustomInfo), + _typeAndInfo, + useDebuggerDisplay: false, + value: value, + displayValue: displayValue, + expansion: expansion, + childShouldParenthesize: parent.ChildShouldParenthesize, + fullName: parent.FullNameWithoutFormatSpecifiers, + childFullNamePrefixOpt: parent.ChildFullNamePrefix, + formatSpecifiers: Formatter.AddFormatSpecifier(parent.FormatSpecifiers, "raw"), + category: DkmEvaluationResultCategory.Data, + flags: DkmEvaluationResultFlags.ReadOnly, + editableValue: null, + inspectionContext: inspectionContext, + displayName: displayName, + displayType: displayType); + } } } diff --git a/src/ExpressionEvaluator/Core/Source/ResultProvider/Helpers/EvalResultDataItem.cs b/src/ExpressionEvaluator/Core/Source/ResultProvider/Helpers/EvalResultDataItem.cs index 4aa24e0b9e7ab22eb297cf6a15228028f05abfc0..def9125f705ed9b8e777d860601c06740659c8d0 100644 --- a/src/ExpressionEvaluator/Core/Source/ResultProvider/Helpers/EvalResultDataItem.cs +++ b/src/ExpressionEvaluator/Core/Source/ResultProvider/Helpers/EvalResultDataItem.cs @@ -66,6 +66,7 @@ protected override void OnClose() internal enum ExpansionKind { Default, + Explicit, // All interesting fields set explicitly including DisplayName, DisplayValue, DisplayType. DynamicView, Error, NativeView, @@ -85,7 +86,9 @@ internal sealed class EvalResult public readonly TypeAndCustomInfo DeclaredTypeAndInfo; public readonly bool UseDebuggerDisplay; public readonly DkmClrValue Value; + public readonly string DisplayName; public readonly string DisplayValue; // overrides the "Value" text displayed for certain kinds of DataItems (errors, invalid pointer dereferences, etc)...not to be confused with DebuggerDisplayAttribute Value... + public readonly string DisplayType; public readonly Expansion Expansion; public readonly bool ChildShouldParenthesize; public readonly string FullNameWithoutFormatSpecifiers; @@ -149,7 +152,9 @@ public EvalResult(string name, string errorMessage, DkmInspectionContext inspect DkmEvaluationResultCategory category, DkmEvaluationResultFlags flags, string editableValue, - DkmInspectionContext inspectionContext) + DkmInspectionContext inspectionContext, + string displayName = null, + string displayType = null) { Debug.Assert(name != null); Debug.Assert(formatSpecifiers != null); @@ -171,6 +176,8 @@ public EvalResult(string name, string errorMessage, DkmInspectionContext inspect this.Flags = flags | GetFlags(value, inspectionContext) | ((expansion == null) ? DkmEvaluationResultFlags.None : DkmEvaluationResultFlags.Expandable); this.Expansion = expansion; this.InspectionContext = inspectionContext; + this.DisplayName = displayName; + this.DisplayType = displayType; } internal EvalResultDataItem ToDataItem() diff --git a/src/ExpressionEvaluator/Core/Source/ResultProvider/ResultProvider.cs b/src/ExpressionEvaluator/Core/Source/ResultProvider/ResultProvider.cs index 3c8b95301b1b10835ab0c03eaeb20b44eee1c634..8f75f6f78d03c382f35cfb0f84b791ab8d5982cf 100644 --- a/src/ExpressionEvaluator/Core/Source/ResultProvider/ResultProvider.cs +++ b/src/ExpressionEvaluator/Core/Source/ResultProvider/ResultProvider.cs @@ -240,6 +240,25 @@ private void CreateEvaluationResultAndContinue(EvalResult result, WorkList workL { switch (result.Kind) { + case ExpansionKind.Explicit: + completionRoutine(DkmSuccessEvaluationResult.Create( + inspectionContext, + stackFrame, + Name: result.DisplayName, + FullName: result.FullName, + Flags: result.Flags, + Value: result.DisplayValue, + EditableValue: result.EditableValue, + Type: result.DisplayType, + Category: DkmEvaluationResultCategory.Data, + Access: DkmEvaluationResultAccessType.None, + StorageType: DkmEvaluationResultStorageType.None, + TypeModifierFlags: DkmEvaluationResultTypeModifierFlags.None, + Address: result.Value.Address, + CustomUIVisualizers: null, + ExternalModules: null, + DataItem: result.ToDataItem())); + break; case ExpansionKind.Error: completionRoutine(DkmFailedEvaluationResult.Create( inspectionContext, @@ -465,14 +484,19 @@ private void CreateEvaluationResultAndContinue(EvalResult result, WorkList workL /// The qualified name (i.e. including containing types and namespaces) of a named, pointer, /// or array type followed by the qualified name of the actual runtime type, if provided. /// - private static string GetTypeName(DkmInspectionContext inspectionContext, DkmClrValue value, DkmClrType declaredType, DkmClrCustomTypeInfo declaredTypeInfo, ExpansionKind kind) + internal static string GetTypeName( + DkmInspectionContext inspectionContext, + DkmClrValue value, + DkmClrType declaredType, + DkmClrCustomTypeInfo declaredTypeInfo, + bool isPointerDereference) { var declaredLmrType = declaredType.GetLmrType(); var runtimeType = value.Type; var declaredTypeName = inspectionContext.GetTypeName(declaredType, declaredTypeInfo, Formatter.NoFormatSpecifiers); // Include the runtime type if distinct. if (!declaredLmrType.IsPointer && - (kind != ExpansionKind.PointerDereference) && + !isPointerDereference && (!declaredLmrType.IsNullable() || value.EvalFlags.Includes(DkmEvaluationResultFlags.ExceptionThrown))) { // Generate the declared type name without tuple element names. @@ -831,7 +855,7 @@ private static string GetTypeName(DkmInspectionContext inspectionContext, DkmClr display = value.GetValueString(inspectionContext, Formatter.NoFormatSpecifiers); } - var typeName = displayType ?? GetTypeName(inspectionContext, value, declaredType, declaredTypeInfo, result.Kind); + var typeName = displayType ?? GetTypeName(inspectionContext, value, declaredType, declaredTypeInfo, result.Kind == ExpansionKind.PointerDereference); return CreateEvaluationResult(inspectionContext, value, name, typeName, display, result); } @@ -951,7 +975,7 @@ private static string GetTypeName(DkmInspectionContext inspectionContext, DkmClr int cardinality; if (runtimeType.IsTupleCompatible(out cardinality)) { - return TupleExpansion.CreateExpansion(value, declaredTypeAndInfo, cardinality); + return TupleExpansion.CreateExpansion(inspectionContext, declaredTypeAndInfo, value, cardinality); } return MemberExpansion.CreateExpansion(inspectionContext, declaredTypeAndInfo, value, flags, TypeHelpers.IsVisibleMember, this); diff --git a/src/ExpressionEvaluator/VisualBasic/Test/ResultProvider/TupleTests.vb b/src/ExpressionEvaluator/VisualBasic/Test/ResultProvider/TupleTests.vb index 1fbb4955003d904109f805ff8355ab56dbe29b74..7e3eb5c2575819b4de1662d443c4e83ab2e4e3ed 100644 --- a/src/ExpressionEvaluator/VisualBasic/Test/ResultProvider/TupleTests.vb +++ b/src/ExpressionEvaluator/VisualBasic/Test/ResultProvider/TupleTests.vb @@ -16,7 +16,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator.UnitTests Inherits VisualBasicResultProviderTestBase - Public Sub LongTuple() + Public Sub LongTuple_NoNames() Const source = "Class C Private _17 As (Short, Short, Short, Short, Short, Short, Short, Short, Short, Short, Short, Short, Short, Short, Short, Short, Short) = @@ -32,7 +32,7 @@ End Class" Dim value = type.Instantiate() Dim result = FormatResult("o", value) Verify(result, - EvalResult("o", "{C}", "C", "o", DkmEvaluationResultFlags.Expandable)) + EvalResult("o", "{C}", "C", "o", DkmEvaluationResultFlags.Expandable)) Dim children = GetChildren(result) Verify(children, EvalResult( @@ -42,25 +42,55 @@ End Class" "o._17", DkmEvaluationResultFlags.Expandable)) children = GetChildren(children(0)) - Assert.Equal(18, children.Length) - Dim child = children(children.Length - 1) - Verify(child, - EvalResult( - "Rest", - "(8, 9, 10, 11, 12, 13, 14, 15, 16, 17)", - "(Short, Short, Short, Short, Short, Short, Short, Short, Short, Short)", - "o._17.Rest", - DkmEvaluationResultFlags.Expandable)) - children = GetChildren(child) - Assert.Equal(11, children.Length) - child = children(children.Length - 1) - Verify(child, + Verify(children, + EvalResult("Item1", "1", "Short", "o._17.Item1"), + EvalResult("Item2", "2", "Short", "o._17.Item2"), + EvalResult("Item3", "3", "Short", "o._17.Item3"), + EvalResult("Item4", "4", "Short", "o._17.Item4"), + EvalResult("Item5", "5", "Short", "o._17.Item5"), + EvalResult("Item6", "6", "Short", "o._17.Item6"), + EvalResult("Item7", "7", "Short", "o._17.Item7"), + EvalResult("Item8", "8", "Short", "o._17.Rest.Item1"), + EvalResult("Item9", "9", "Short", "o._17.Rest.Item2"), + EvalResult("Item10", "10", "Short", "o._17.Rest.Item3"), + EvalResult("Item11", "11", "Short", "o._17.Rest.Item4"), + EvalResult("Item12", "12", "Short", "o._17.Rest.Item5"), + EvalResult("Item13", "13", "Short", "o._17.Rest.Item6"), + EvalResult("Item14", "14", "Short", "o._17.Rest.Item7"), + EvalResult("Item15", "15", "Short", "o._17.Rest.Rest.Item1"), + EvalResult("Item16", "16", "Short", "o._17.Rest.Rest.Item2"), + EvalResult("Item17", "17", "Short", "o._17.Rest.Rest.Item3"), EvalResult( - "Rest", - "(15, 16, 17)", - "(Short, Short, Short)", - "o._17.Rest.Rest", - DkmEvaluationResultFlags.Expandable)) + "Raw View", + "(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17)", + "(Short, Short, Short, Short, Short, Short, Short, Short, Short, Short, Short, Short, Short, Short, Short, Short, Short)", + "o._17, raw", + DkmEvaluationResultFlags.Expandable Or DkmEvaluationResultFlags.ReadOnly)) + children = GetChildren(children(children.Length - 1)) + Verify(children, + EvalResult("Item1", "1", "Short", "o._17.Item1"), + EvalResult("Item2", "2", "Short", "o._17.Item2"), + EvalResult("Item3", "3", "Short", "o._17.Item3"), + EvalResult("Item4", "4", "Short", "o._17.Item4"), + EvalResult("Item5", "5", "Short", "o._17.Item5"), + EvalResult("Item6", "6", "Short", "o._17.Item6"), + EvalResult("Item7", "7", "Short", "o._17.Item7"), + EvalResult("Rest", "(8, 9, 10, 11, 12, 13, 14, 15, 16, 17)", "(Short, Short, Short, Short, Short, Short, Short, Short, Short, Short)", "o._17.Rest, raw", DkmEvaluationResultFlags.Expandable)) + children = GetChildren(children(children.Length - 1)) + Verify(children, + EvalResult("Item1", "8", "Short", "o._17.Rest.Item1"), + EvalResult("Item2", "9", "Short", "o._17.Rest.Item2"), + EvalResult("Item3", "10", "Short", "o._17.Rest.Item3"), + EvalResult("Item4", "11", "Short", "o._17.Rest.Item4"), + EvalResult("Item5", "12", "Short", "o._17.Rest.Item5"), + EvalResult("Item6", "13", "Short", "o._17.Rest.Item6"), + EvalResult("Item7", "14", "Short", "o._17.Rest.Item7"), + EvalResult("Rest", "(15, 16, 17)", "(Short, Short, Short)", "o._17.Rest.Rest, raw", DkmEvaluationResultFlags.Expandable)) + children = GetChildren(children(children.Length - 1)) + Verify(children, + EvalResult("Item1", "15", "Short", "o._17.Rest.Rest.Item1"), + EvalResult("Item2", "16", "Short", "o._17.Rest.Rest.Item2"), + EvalResult("Item3", "17", "Short", "o._17.Rest.Rest.Item3")) End Using End Sub @@ -136,15 +166,13 @@ class C moreChildren = GetChildren(moreChildren(0)) Verify(moreChildren, EvalResult("X", "Nothing", "Object", "o.G.F.Item1"), - EvalResult("Item1", "Nothing", "Object", "o.G.F.Item1"), EvalResult("Y", "(Nothing, {B(Of (Object, Object)).S})", "(E As Object, H As B(Of (F As Object, G As Object)).S)", "o.G.F.Item2", DkmEvaluationResultFlags.Expandable), - EvalResult("Item2", "(Nothing, {B(Of (Object, Object)).S})", "(E As Object, H As B(Of (F As Object, G As Object)).S)", "o.G.F.Item2", DkmEvaluationResultFlags.Expandable)) - moreChildren = GetChildren(moreChildren(3)) + EvalResult("Raw View", "(Nothing, (Nothing, {B(Of (Object, Object)).S}))", "(X As Object, Y As (E As Object, H As B(Of (F As Object, G As Object)).S))", "o.G.F, raw", DkmEvaluationResultFlags.Expandable Or DkmEvaluationResultFlags.ReadOnly)) + moreChildren = GetChildren(moreChildren(1)) Verify(moreChildren, EvalResult("E", "Nothing", "Object", "o.G.F.Item2.Item1"), - EvalResult("Item1", "Nothing", "Object", "o.G.F.Item2.Item1"), EvalResult("H", "{B(Of (Object, Object)).S}", "B(Of (F As Object, G As Object)).S", "o.G.F.Item2.Item2"), - EvalResult("Item2", "{B(Of (Object, Object)).S}", "B(Of (F As Object, G As Object)).S", "o.G.F.Item2.Item2")) + EvalResult("Raw View", "(Nothing, {B(Of (Object, Object)).S})", "(E As Object, H As B(Of (F As Object, G As Object)).S)", "o.G.F.Item2, raw", DkmEvaluationResultFlags.Expandable Or DkmEvaluationResultFlags.ReadOnly)) End Using End Sub