private static DkmClrType GetEnumerableType(DkmClrValue value) { Debug.Assert(!value.IsError()); if (value.IsNull) { return null; } var valueType = value.Type.GetLmrType(); // Do not support Results View for strings // or arrays. (Matches legacy EE.) if (valueType.IsString() || valueType.IsArray) { return null; } var enumerableType = valueType.GetIEnumerableImplementationIfAny(); if (enumerableType == null) { return null; } return DkmClrType.Create(value.Type.AppDomain, enumerableType); }
internal static DynamicViewExpansion CreateExpansion(DkmInspectionContext inspectionContext, DkmClrValue value, ResultProvider resultProvider) { if (value.IsError() || value.IsNull || value.HasExceptionThrown()) { return null; } var type = value.Type.GetLmrType(); if (!(type.IsComObject() || type.IsIDynamicMetaObjectProvider())) { return null; } var proxyValue = value.InstantiateDynamicViewProxy(inspectionContext); Debug.Assert((proxyValue == null) || (!proxyValue.IsNull && !proxyValue.IsError() && !proxyValue.HasExceptionThrown())); // InstantiateDynamicViewProxy may return null (if required assembly is missing, for instance). if (proxyValue == null) { return null; } // Expansion is based on the 'DynamicMetaObjectProviderDebugView.Items' property. var proxyType = proxyValue.Type; var itemsMemberExpansion = RootHiddenExpansion.CreateExpansion( proxyType.GetMemberByName("Items"), DynamicFlagsMap.Create(new TypeAndCustomInfo(proxyType))); return new DynamicViewExpansion(proxyValue, itemsMemberExpansion); }
internal static EvalResult CreateResultsOnlyRow( DkmInspectionContext inspectionContext, string name, string fullName, ReadOnlyCollection<string> formatSpecifiers, DkmClrType declaredType, DkmClrCustomTypeInfo declaredTypeInfo, DkmClrValue value, ResultProvider resultProvider) { string errorMessage; if (value.IsError()) { errorMessage = (string)value.HostObjectValue; } else if (value.HasExceptionThrown()) { errorMessage = value.GetExceptionMessage(inspectionContext, name); } else { var enumerableType = GetEnumerableType(value); if (enumerableType != null) { var expansion = CreateExpansion(inspectionContext, value, enumerableType, resultProvider); if (expansion != null) { return expansion.CreateResultsViewRow( inspectionContext, name, fullName, formatSpecifiers, new TypeAndCustomInfo(declaredType, declaredTypeInfo), value, includeResultsFormatSpecifier: true, fullNameProvider: resultProvider.FullNameProvider); } errorMessage = Resources.ResultsViewNoSystemCore; } else { errorMessage = Resources.ResultsViewNotEnumerable; } } Debug.Assert(errorMessage != null); return new EvalResult(name, errorMessage, inspectionContext); }
internal static DkmEvaluationResult CreateResultsOnly( string name, DkmClrType declaredType, DkmClrValue value, EvalResultDataItem parent, Formatter formatter) { string errorMessage; if (value.IsError()) { errorMessage = (string)value.HostObjectValue; } else if (value.HasExceptionThrown(parent)) { errorMessage = value.GetExceptionMessage(name, formatter); } else { var enumerableType = GetEnumerableType(value); if (enumerableType != null) { var expansion = CreateExpansion(value, enumerableType, formatter); if (expansion != null) { return expansion.CreateEvaluationResult(name, parent, formatter); } errorMessage = Resources.ResultsViewNoSystemCore; } else { errorMessage = Resources.ResultsViewNotEnumerable; } } Debug.Assert(errorMessage != null); return DkmFailedEvaluationResult.Create( InspectionContext: value.InspectionContext, StackFrame: value.StackFrame, Name: name, FullName: null, ErrorMessage: errorMessage, Flags: DkmEvaluationResultFlags.None, Type: null, DataItem: null); }
internal string GetEditableValue(DkmClrValue value, DkmInspectionContext inspectionContext) { if (value.IsError()) { return null; } if (value.EvalFlags.Includes(DkmEvaluationResultFlags.ReadOnly)) { return null; } var type = value.Type.GetLmrType(); if (type.IsEnum) { return this.GetValueString(value, inspectionContext, ObjectDisplayOptions.None, GetValueFlags.IncludeTypeName); } else if (type.IsDecimal()) { return this.GetValueString(value, inspectionContext, ObjectDisplayOptions.IncludeTypeSuffix, GetValueFlags.None); } // The legacy EE didn't special-case strings or chars (when ",nq" was used, // you had to manually add quotes when editing) but it makes sense to // always automatically quote (non-null) strings and chars when editing. else if (type.IsString()) { if (!value.IsNull) { return this.GetValueString(value, inspectionContext, ObjectDisplayOptions.UseQuotes, GetValueFlags.None); } } else if (type.IsCharacter()) { return this.GetValueStringForCharacter(value, inspectionContext, ObjectDisplayOptions.UseQuotes); } return null; }
internal string GetValueString(DkmClrValue value, DkmInspectionContext inspectionContext, ObjectDisplayOptions options, GetValueFlags flags) { if (value.IsError()) { return (string)value.HostObjectValue; } if (UsesHexadecimalNumbers(inspectionContext)) { options |= ObjectDisplayOptions.UseHexadecimalNumbers; } var lmrType = value.Type.GetLmrType(); if (IsPredefinedType(lmrType) && !lmrType.IsObject()) { if (lmrType.IsString()) { var stringValue = (string)value.HostObjectValue; if (stringValue == null) { return _nullString; } return IncludeObjectId( value, FormatString(stringValue, options), flags); } else if (lmrType.IsCharacter()) { return IncludeObjectId( value, FormatLiteral((char)value.HostObjectValue, options | ObjectDisplayOptions.IncludeCodePoints), flags); } else { return IncludeObjectId( value, FormatPrimitive(value, options & ~ObjectDisplayOptions.UseQuotes, inspectionContext), flags); } } else if (value.IsNull && !lmrType.IsPointer) { return _nullString; } else if (lmrType.IsEnum) { return IncludeObjectId( value, GetEnumDisplayString(lmrType, value, options, (flags & GetValueFlags.IncludeTypeName) != 0, inspectionContext), flags); } else if (lmrType.IsArray) { return IncludeObjectId( value, GetArrayDisplayString(lmrType, value.ArrayDimensions, value.ArrayLowerBounds, options), flags); } else if (lmrType.IsPointer) { // NOTE: the HostObjectValue will have a size corresponding to the process bitness // and FormatPrimitive will adjust accordingly. var tmp = FormatPrimitive(value, ObjectDisplayOptions.UseHexadecimalNumbers, inspectionContext); // Always in hex. Debug.Assert(tmp != null); return tmp; } else if (lmrType.IsNullable()) { var nullableValue = value.GetNullableValue(inspectionContext); // It should be impossible to nest nullables, so this recursion should introduce only a single extra stack frame. return nullableValue == null ? _nullString : GetValueString(nullableValue, inspectionContext, ObjectDisplayOptions.None, GetValueFlags.IncludeTypeName); } // "value.EvaluateToString()" will check "Call string-conversion function on objects in variables windows" // (Tools > Options setting) and call "value.ToString()" if appropriate. return IncludeObjectId( value, string.Format(_defaultFormat, value.EvaluateToString(inspectionContext) ?? inspectionContext.GetTypeName(value.Type, CustomTypeInfo: null, FormatSpecifiers: NoFormatSpecifiers)), flags); }
private string GetUnderlyingStringImpl(DkmClrValue value, DkmInspectionContext inspectionContext) { Debug.Assert(!value.IsError()); if (value.IsNull) { return null; } var lmrType = value.Type.GetLmrType(); if (lmrType.IsEnum || lmrType.IsArray || lmrType.IsPointer) { return null; } if (lmrType.IsNullable()) { var nullableValue = value.GetNullableValue(inspectionContext); return nullableValue != null ? GetUnderlyingStringImpl(nullableValue, inspectionContext) : null; } if (lmrType.IsString()) { return (string)value.HostObjectValue; } else if (!IsPredefinedType(lmrType)) { // Check for special cased non-primitives that have underlying strings if (lmrType.IsType("System.Data.SqlTypes", "SqlString")) { var fieldValue = value.GetFieldValue(InternalWellKnownMemberNames.SqlStringValue, inspectionContext); return fieldValue.HostObjectValue as string; } else if (lmrType.IsOrInheritsFrom("System.Xml.Linq", "XNode")) { return value.EvaluateToString(inspectionContext); } } return null; }
internal EvalResultDataItem CreateDataItem( DkmInspectionContext inspectionContext, string name, TypeAndCustomInfo typeDeclaringMemberAndInfo, TypeAndCustomInfo declaredTypeAndInfo, DkmClrValue value, EvalResultDataItem parent, ExpansionFlags expansionFlags, bool childShouldParenthesize, string fullName, ReadOnlyCollection<string> formatSpecifiers, DkmEvaluationResultCategory category, DkmEvaluationResultFlags flags, DkmEvaluationFlags evalFlags) { if ((evalFlags & DkmEvaluationFlags.ShowValueRaw) != 0) { formatSpecifiers = Formatter.AddFormatSpecifier(formatSpecifiers, "raw"); } Expansion expansion; // If the declared type is Nullable<T>, the value should // have no expansion if null, or be expanded as a T. var declaredType = declaredTypeAndInfo.Type; var lmrNullableTypeArg = declaredType.GetNullableTypeArgument(); if (lmrNullableTypeArg != null && !value.HasExceptionThrown()) { Debug.Assert(value.Type.GetProxyType() == null); DkmClrValue nullableValue; if (value.IsError()) { expansion = null; } else if ((nullableValue = value.GetNullableValue(inspectionContext)) == null) { Debug.Assert(declaredType.Equals(value.Type.GetLmrType())); // No expansion of "null". expansion = null; } else { value = nullableValue; Debug.Assert(lmrNullableTypeArg.Equals(value.Type.GetLmrType())); // If this is not the case, add a test for includeRuntimeTypeIfNecessary. // CONSIDER: The DynamicAttribute for the type argument should just be Skip(1) of the original flag array. expansion = this.GetTypeExpansion(inspectionContext, new TypeAndCustomInfo(lmrNullableTypeArg), value, ExpansionFlags.IncludeResultsView); } } else if (value.IsError() || (inspectionContext.EvaluationFlags & DkmEvaluationFlags.NoExpansion) != 0) { expansion = null; } else { expansion = DebuggerTypeProxyExpansion.CreateExpansion( this, inspectionContext, name, typeDeclaringMemberAndInfo, declaredTypeAndInfo, value, childShouldParenthesize, fullName, flags.Includes(DkmEvaluationResultFlags.ExceptionThrown) ? null : fullName, formatSpecifiers, flags, this.Formatter.GetEditableValue(value, inspectionContext)); if (expansion == null) { expansion = value.HasExceptionThrown() ? this.GetTypeExpansion(inspectionContext, new TypeAndCustomInfo(value.Type), value, expansionFlags) : this.GetTypeExpansion(inspectionContext, declaredTypeAndInfo, value, expansionFlags); } } return new EvalResultDataItem( ExpansionKind.Default, name, typeDeclaringMemberAndInfo, declaredTypeAndInfo, parent: parent, value: value, displayValue: null, expansion: expansion, childShouldParenthesize: childShouldParenthesize, fullName: fullName, childFullNamePrefixOpt: flags.Includes(DkmEvaluationResultFlags.ExceptionThrown) ? null : fullName, formatSpecifiers: formatSpecifiers, category: category, flags: flags, editableValue: this.Formatter.GetEditableValue(value, inspectionContext), inspectionContext: inspectionContext); }
private static DkmEvaluationResult CreateEvaluationResult( DkmInspectionContext inspectionContext, DkmClrValue value, string name, string typeName, string display, EvalResultDataItem dataItem) { if (value.IsError()) { // Evaluation failed return DkmFailedEvaluationResult.Create( InspectionContext: inspectionContext, StackFrame: value.StackFrame, Name: name, FullName: dataItem.FullName, ErrorMessage: display, Flags: dataItem.Flags, Type: typeName, DataItem: dataItem); } else { ReadOnlyCollection<DkmCustomUIVisualizerInfo> customUIVisualizers = null; if (!value.IsNull) { DkmCustomUIVisualizerInfo[] customUIVisualizerInfo = value.Type.GetDebuggerCustomUIVisualizerInfo(); if (customUIVisualizerInfo != null) { customUIVisualizers = new ReadOnlyCollection<DkmCustomUIVisualizerInfo>(customUIVisualizerInfo); } } // If the EvalResultDataItem doesn't specify a particular category, we'll just propagate DkmClrValue.Category, // which typically appears to be set to the default value ("Other"). var category = (dataItem.Category != DkmEvaluationResultCategory.Other) ? dataItem.Category : value.Category; // Valid value return DkmSuccessEvaluationResult.Create( InspectionContext: inspectionContext, StackFrame: value.StackFrame, Name: name, FullName: dataItem.FullName, Flags: dataItem.Flags, Value: display, EditableValue: dataItem.EditableValue, Type: typeName, Category: category, Access: value.Access, StorageType: value.StorageType, TypeModifierFlags: value.TypeModifierFlags, Address: value.Address, CustomUIVisualizers: customUIVisualizers, ExternalModules: null, DataItem: dataItem); } }
private static DkmEvaluationResultFlags GetFlags(DkmClrValue value, DkmInspectionContext inspectionContext) { if (value == null) { return DkmEvaluationResultFlags.None; } var resultFlags = value.EvalFlags; var type = value.Type.GetLmrType(); if (type.IsBoolean()) { resultFlags |= DkmEvaluationResultFlags.Boolean; if (true.Equals(value.HostObjectValue)) { resultFlags |= DkmEvaluationResultFlags.BooleanTrue; } } if (!value.IsError() && value.HasUnderlyingString(inspectionContext)) { resultFlags |= DkmEvaluationResultFlags.RawString; } return resultFlags; }
internal EvalResultDataItem CreateDataItem( DkmInspectionContext inspectionContext, string name, Type typeDeclaringMember, Type declaredType, DkmClrValue value, EvalResultDataItem parent, ExpansionFlags expansionFlags, bool childShouldParenthesize, string fullName, ReadOnlyCollection<string> formatSpecifiers, DkmEvaluationResultCategory category, DkmEvaluationResultFlags flags, DkmEvaluationFlags evalFlags) { if ((evalFlags & DkmEvaluationFlags.ShowValueRaw) != 0) { formatSpecifiers = Formatter.AddFormatSpecifier(formatSpecifiers, "raw"); } Expansion expansion; // If the declared type is Nullable<T>, the value should // have no expansion if null, or be expanded as a T. var lmrNullableTypeArg = declaredType.GetNullableTypeArgument(); if (lmrNullableTypeArg != null && !value.HasExceptionThrown(parent)) { Debug.Assert(value.Type.GetProxyType() == null); var nullableValue = value.GetNullableValue(); if (nullableValue == null) { Debug.Assert(declaredType.Equals(value.Type.GetLmrType())); // No expansion of "null". expansion = null; } else { value = nullableValue; Debug.Assert(lmrNullableTypeArg.Equals(value.Type.GetLmrType())); // If this is not the case, add a test for includeRuntimeTypeIfNecessary. expansion = this.GetTypeExpansion(inspectionContext, lmrNullableTypeArg, value, ExpansionFlags.IncludeResultsView); } } else if (value.IsError() || (inspectionContext.EvaluationFlags & DkmEvaluationFlags.NoExpansion) != 0) { expansion = null; } else { expansion = DebuggerTypeProxyExpansion.CreateExpansion( this, inspectionContext, name, typeDeclaringMember, declaredType, value, childShouldParenthesize, fullName, flags.Includes(DkmEvaluationResultFlags.ExceptionThrown) ? null : fullName, formatSpecifiers, flags, this.Formatter.GetEditableValue(value)); if (expansion == null) { var expansionType = value.HasExceptionThrown(parent) ? value.Type.GetLmrType() : declaredType; expansion = this.GetTypeExpansion(inspectionContext, expansionType, value, expansionFlags); } } return new EvalResultDataItem( name, typeDeclaringMember, declaredType, value, expansion, childShouldParenthesize, fullName, flags.Includes(DkmEvaluationResultFlags.ExceptionThrown) ? null : fullName, formatSpecifiers, category, flags, this.Formatter.GetEditableValue(value)); }
private static bool IsEnumerableCandidate(DkmClrValue value) { Debug.Assert(!value.IsError()); if (value.IsNull || value.HasExceptionThrown()) { return false; } // Do not support Results View for strings // or arrays. (Matches legacy EE.) var type = value.Type.GetLmrType(); return !type.IsString() && !type.IsArray; }
private static DkmEvaluationResultFlags GetFlags(DkmClrValue value, DkmInspectionContext inspectionContext) { if (value == null) { return DkmEvaluationResultFlags.None; } var resultFlags = value.EvalFlags; var type = value.Type.GetLmrType(); if (type.IsBoolean()) { resultFlags |= DkmEvaluationResultFlags.Boolean; if (true.Equals(value.HostObjectValue)) { resultFlags |= DkmEvaluationResultFlags.BooleanTrue; } } if (!value.IsError() && value.HasUnderlyingString(inspectionContext)) { resultFlags |= DkmEvaluationResultFlags.RawString; } // As in the old EE, we won't allow editing members of a DynamicView expansion. if (type.IsDynamicProperty()) { resultFlags |= DkmEvaluationResultFlags.ReadOnly; } return resultFlags; }