internal DynamicFlagsCustomTypeInfo SubstituteDynamicFlags(Type type, DynamicFlagsCustomTypeInfo originalDynamicFlags) { if (_typeDefinition == null) { return originalDynamicFlags; } var substitutedFlags = ArrayBuilder<bool>.GetInstance(); int f = 0; foreach (Type curr in new TypeWalker(type)) { if (curr.IsGenericParameter && curr.DeclaringType.Equals(_typeDefinition)) { AppendFlagsFor(curr, substitutedFlags); } else { substitutedFlags.Add(originalDynamicFlags[f]); } f++; } var result = DynamicFlagsCustomTypeInfo.Create(substitutedFlags); substitutedFlags.Free(); return result; }
protected override void AppendTupleElement( StringBuilder builder, Type type, string nameOpt, ReadOnlyCollection<byte> dynamicFlags, ref int dynamicFlagIndex, ReadOnlyCollection<string> tupleElementNames, ref int tupleElementIndex, bool escapeKeywordIdentifiers, out bool sawInvalidIdentifier) { sawInvalidIdentifier = false; bool sawSingleInvalidIdentifier; AppendQualifiedTypeName( builder, type, dynamicFlags, ref dynamicFlagIndex, tupleElementNames, ref tupleElementIndex, escapeKeywordIdentifiers, sawInvalidIdentifier: out sawSingleInvalidIdentifier); Debug.Assert(!sawSingleInvalidIdentifier); if (!string.IsNullOrEmpty(nameOpt)) { builder.Append(' '); AppendIdentifier(builder, escapeKeywordIdentifiers, nameOpt, out sawSingleInvalidIdentifier); sawInvalidIdentifier |= sawSingleInvalidIdentifier; } }
public EvalResultDataItem( string name, Type typeDeclaringMember, Type declaredType, DkmClrValue value, Expansion expansion, bool childShouldParenthesize, string fullName, string childFullNamePrefixOpt, ReadOnlyCollection<string> formatSpecifiers, DkmEvaluationResultCategory category, DkmEvaluationResultFlags flags, string editableValue) { Debug.Assert(formatSpecifiers != null); Debug.Assert((flags & DkmEvaluationResultFlags.Expandable) == 0); this.NameOpt = name; this.TypeDeclaringMember = typeDeclaringMember; this.DeclaredType = declaredType; this.Value = value; this.ChildShouldParenthesize = childShouldParenthesize; this.FullNameWithoutFormatSpecifiers = fullName; this.ChildFullNamePrefix = childFullNamePrefixOpt; this.FormatSpecifiers = formatSpecifiers; this.Category = category; this.EditableValue = editableValue; this.Flags = flags | GetFlags(value) | ((expansion == null) ? DkmEvaluationResultFlags.None : DkmEvaluationResultFlags.Expandable); this.Expansion = expansion; }
protected override void AppendGenericTypeArgumentList( StringBuilder builder, Type[] typeArguments, int typeArgumentOffset, DynamicFlagsCustomTypeInfo dynamicFlags, ref int index, int arity, bool escapeKeywordIdentifiers, out bool sawInvalidIdentifier) { sawInvalidIdentifier = false; builder.Append('<'); for (int i = 0; i < arity; i++) { if (i > 0) { builder.Append(", "); } Type typeArgument = typeArguments[typeArgumentOffset + i]; bool sawSingleInvalidIdentifier; AppendQualifiedTypeName(builder, typeArgument, dynamicFlags, ref index, escapeKeywordIdentifiers, out sawSingleInvalidIdentifier); sawInvalidIdentifier |= sawSingleInvalidIdentifier; } builder.Append('>'); }
private void AppendEnumTypeAndName(StringBuilder builder, Type typeToDisplayOpt, string name) { if (typeToDisplayOpt != null) { // We're showing the type of a value, so "dynamic" does not apply. bool unused; int index1 = 0; int index2 = 0; AppendQualifiedTypeName( builder, typeToDisplayOpt, null, ref index1, null, ref index2, escapeKeywordIdentifiers: true, sawInvalidIdentifier: out unused); builder.Append('.'); AppendIdentifierEscapingPotentialKeywords(builder, name, sawInvalidIdentifier: out unused); } else { builder.Append(name); } }
/// <summary> /// Append the qualified name (i.e. including containing types and namespaces) of a named, /// pointer, or array type to <paramref name="builder"/>. /// </summary> /// <remarks> /// Keyword strings are appended for primitive types (e.g. "int" for "System.Int32"). /// Question mark syntax is used for <see cref="Nullable{T}"/>. /// No special handling is required for anonymous types - they are expected to be /// emitted with <see cref="DebuggerDisplayAttribute.Type"/> set to "<Anonymous Type>. /// This is fortunate, since we don't have a good way to recognize them in metadata. /// Does not call itself (directly). /// </remarks> protected void AppendQualifiedTypeName( StringBuilder builder, Type type, DynamicFlagsCustomTypeInfo dynamicFlags, ref int index, bool escapeKeywordIdentifiers, out bool sawInvalidIdentifier) { Type originalType = type; // Can have an array of pointers, but not a pointer to an array, so consume these first. // We'll reconstruct this information later from originalType. while (type.IsArray) { index++; type = type.GetElementType(); } int pointerCount = 0; while (type.IsPointer) { var elementType = type.GetElementType(); if (elementType == null) { // Null for function pointers. break; } index++; pointerCount++; type = elementType; } int nullableCount = 0; Type typeArg; while ((typeArg = type.GetNullableTypeArgument()) != null) { index++; nullableCount++; type = typeArg; } Debug.Assert(nullableCount < 2, "Benign: someone is nesting nullables."); Debug.Assert(pointerCount == 0 || nullableCount == 0, "Benign: pointer to nullable?"); int oldLength = builder.Length; AppendQualifiedTypeNameInternal(builder, type, dynamicFlags, ref index, escapeKeywordIdentifiers, out sawInvalidIdentifier); string name = builder.ToString(oldLength, builder.Length - oldLength); builder.Append('?', nullableCount); builder.Append('*', pointerCount); type = originalType; while (type.IsArray) { AppendRankSpecifier(builder, type.GetArrayRank()); type = type.GetElementType(); } }
internal override string GetArrayDisplayString(Type lmrType, ReadOnlyCollection<int> sizes, ReadOnlyCollection<int> lowerBounds, ObjectDisplayOptions options) { Debug.Assert(lmrType.IsArray); Type originalLmrType = lmrType; // Strip off all array types. We'll process them at the end. while (lmrType.IsArray) { lmrType = lmrType.GetElementType(); } var pooled = PooledStringBuilder.GetInstance(); var builder = pooled.Builder; builder.Append('{'); // We're showing the type of a value, so "dynamic" does not apply. bool unused; builder.Append(GetTypeName(new TypeAndCustomInfo(lmrType), escapeKeywordIdentifiers: false, sawInvalidIdentifier: out unused)); // NOTE: call our impl directly, since we're coupled anyway. var numSizes = sizes.Count; builder.Append('['); for (int i = 0; i < numSizes; i++) { if (i > 0) { builder.Append(", "); } var lowerBound = lowerBounds[i]; var size = sizes[i]; if (lowerBound == 0) { builder.Append(FormatLiteral(size, options)); } else { builder.Append(FormatLiteral(lowerBound, options)); builder.Append(".."); builder.Append(FormatLiteral(size + lowerBound - 1, options)); } } builder.Append(']'); lmrType = originalLmrType.GetElementType(); // Strip off one layer (already handled). while (lmrType.IsArray) { builder.Append('['); builder.Append(',', lmrType.GetArrayRank() - 1); builder.Append(']'); lmrType = lmrType.GetElementType(); } builder.Append('}'); return pooled.ToStringAndFree(); }
/// <returns>The qualified name (i.e. including containing types and namespaces) of a named, /// pointer, or array type.</returns> internal string GetTypeName(Type type, bool escapeKeywordIdentifiers = false) { if (type == null) { throw new ArgumentNullException("type"); } var pooled = PooledStringBuilder.GetInstance(); AppendQualifiedTypeName(pooled.Builder, type, escapeKeywordIdentifiers); return pooled.ToStringAndFree(); }
private void AppendEnumTypeAndName(StringBuilder builder, Type typeToDisplayOpt, string name) { if (typeToDisplayOpt != null) { AppendQualifiedTypeName(builder, typeToDisplayOpt, escapeKeywordIdentifiers: true); builder.Append('.'); AppendIdentifierEscapingPotentialKeywords(builder, name); } else { builder.Append(name); } }
internal TypeVariablesExpansion(Type declaredType) { Debug.Assert(declaredType.IsGenericType); Debug.Assert(!declaredType.IsGenericTypeDefinition); var typeDef = declaredType.GetGenericTypeDefinition(); _typeParameters = typeDef.GetGenericArguments(); _typeArguments = declaredType.GetGenericArguments(); Debug.Assert(_typeParameters.Length == _typeArguments.Length); Debug.Assert(Array.TrueForAll(_typeParameters, t => t.IsGenericParameter)); Debug.Assert(Array.TrueForAll(_typeArguments, t => !t.IsGenericParameter)); }
private DynamicFlagsMap( Type typeDefinition, DynamicFlagsCustomTypeInfo dynamicFlagsArray, int[] startIndices) { Debug.Assert(typeDefinition != null); Debug.Assert(startIndices != null); Debug.Assert(typeDefinition.IsGenericTypeDefinition); Debug.Assert(startIndices.Length == typeDefinition.GetGenericArguments().Length + 1); _typeDefinition = typeDefinition; _dynamicFlags = dynamicFlagsArray; _startIndices = startIndices; }
protected override void AppendGenericTypeArgumentList(StringBuilder builder, Type[] typeArguments, int typeArgumentOffset, int arity, bool escapeKeywordIdentifiers) { builder.Append('<'); for (int i = 0; i < arity; i++) { if (i > 0) { builder.Append(", "); } Type typeArgument = typeArguments[typeArgumentOffset + i]; AppendQualifiedTypeName(builder, typeArgument, escapeKeywordIdentifiers); } builder.Append('>'); }
private void AppendEnumTypeAndName(StringBuilder builder, Type typeToDisplayOpt, string name) { if (typeToDisplayOpt != null) { // We're showing the type of a value, so "dynamic" does not apply. int index = 0; AppendQualifiedTypeName(builder, typeToDisplayOpt, default(DynamicFlagsCustomTypeInfo), ref index, escapeKeywordIdentifiers: true); builder.Append('.'); AppendIdentifierEscapingPotentialKeywords(builder, name); } else { builder.Append(name); } }
/// <summary> /// Convert a type from the debugger's type system into Iris's type system /// </summary> /// <param name="lmrType">LMR Type</param> /// <returns>Iris type</returns> public static IrisType GetIrisTypeForLmrType(Type lmrType) { if (lmrType.IsPrimitive) { switch (lmrType.FullName) { case "System.Int32": return IrisType.Integer; case "System.Boolean": return IrisType.Boolean; } } else if (lmrType.IsArray) { if (lmrType.GetArrayRank() != 1) return IrisType.Invalid; IrisType elementType = GetIrisTypeForLmrType(lmrType.GetElementType()); if (elementType == IrisType.Invalid) return IrisType.Invalid; return elementType.MakeArrayType(); } else if (lmrType.IsByRef) { IrisType elementType = GetIrisTypeForLmrType(lmrType.GetElementType()); if (elementType == IrisType.Invalid) return IrisType.Invalid; return elementType.MakeByRefType(); } else if (lmrType.FullName.Equals("System.String")) { return IrisType.String; } // Unknown return IrisType.Invalid; }
protected override bool AppendSpecialTypeName(StringBuilder builder, Type type, bool escapeKeywordIdentifiers) { if (type.IsPredefinedType()) { builder.Append(type.GetPredefinedTypeName()); // Not an identifier, does not require escaping. return true; } if (type.IsGenericParameter) { AppendIdentifier(builder, escapeKeywordIdentifiers, type.Name); return true; } if (type.IsVoid()) { builder.Append("void"); // Not an identifier, does not require escaping. return true; } return false; }
protected override bool AppendSpecialTypeName(StringBuilder builder, Type type, bool isDynamic) { if (isDynamic) { Debug.Assert(type.IsObject()); builder.Append("dynamic"); // Not a keyword, does not require escaping. return true; } if (type.IsPredefinedType()) { builder.Append(type.GetPredefinedTypeName()); // Not an identifier, does not require escaping. return true; } if (type.IsVoid()) { builder.Append("void"); // Not an identifier, does not require escaping. return true; } return false; }
protected override void AppendGenericTypeArguments( StringBuilder builder, Type[] typeArguments, int typeArgumentOffset, ReadOnlyCollection<byte> dynamicFlags, ref int dynamicFlagIndex, ReadOnlyCollection<string> tupleElementNames, ref int tupleElementIndex, int arity, bool escapeKeywordIdentifiers, out bool sawInvalidIdentifier) { sawInvalidIdentifier = false; builder.Append('<'); for (int i = 0; i < arity; i++) { if (i > 0) { builder.Append(", "); } Type typeArgument = typeArguments[typeArgumentOffset + i]; bool sawSingleInvalidIdentifier; AppendQualifiedTypeName( builder, typeArgument, dynamicFlags, ref dynamicFlagIndex, tupleElementNames, ref tupleElementIndex, escapeKeywordIdentifiers, out sawSingleInvalidIdentifier); sawInvalidIdentifier |= sawSingleInvalidIdentifier; } builder.Append('>'); }
#pragma warning disable RS0010 /// <remarks> /// The corresponding native code is in EEUserStringBuilder::ErrTryAppendConstantEnum. /// The corresponding roslyn code is in /// <see cref="M:Microsoft.CodeAnalysis.SymbolDisplay.AbstractSymbolDisplayVisitor`1.AddEnumConstantValue(Microsoft.CodeAnalysis.INamedTypeSymbol, System.Object, System.Boolean)"/>. /// NOTE: no curlies for enum values. /// </remarks> #pragma warning restore RS0010 private string GetEnumDisplayString(Type lmrType, DkmClrValue value, ObjectDisplayOptions options, bool includeTypeName, DkmInspectionContext inspectionContext) { Debug.Assert(lmrType.IsEnum); Debug.Assert(value != null); object underlyingValue = value.HostObjectValue; Debug.Assert(underlyingValue != null); string displayString; ArrayBuilder <EnumField> fields = ArrayBuilder <EnumField> .GetInstance(); FillEnumFields(fields, lmrType); // We will normalize/extend all enum values to ulong to ensure that we are always comparing the full underlying value. ulong valueForComparison = ConvertEnumUnderlyingTypeToUInt64(underlyingValue, Type.GetTypeCode(lmrType)); var typeToDisplayOpt = includeTypeName ? lmrType : null; if (valueForComparison != 0 && IsFlagsEnum(lmrType)) { displayString = GetNamesForFlagsEnumValue(fields, underlyingValue, valueForComparison, options, typeToDisplayOpt); } else { displayString = GetNameForEnumValue(fields, underlyingValue, valueForComparison, options, typeToDisplayOpt); } fields.Free(); return(displayString ?? FormatPrimitive(value, options, inspectionContext)); }
internal override bool IsPredefinedType(Type type) { return(type.IsPredefinedType()); }
internal override string GetArrayDisplayString( DkmClrAppDomain appDomain, Type lmrType, ReadOnlyCollection <int> sizes, ReadOnlyCollection <int> lowerBounds, ObjectDisplayOptions options ) { Debug.Assert(lmrType.IsArray); Type originalLmrType = lmrType; // Strip off all array types. We'll process them at the end. while (lmrType.IsArray) { lmrType = lmrType.GetElementType(); } var pooled = PooledStringBuilder.GetInstance(); var builder = pooled.Builder; builder.Append('{'); // We're showing the type of a value, so "dynamic" does not apply. bool unused; builder.Append( GetTypeName( new TypeAndCustomInfo(DkmClrType.Create(appDomain, lmrType)), escapeKeywordIdentifiers: false, sawInvalidIdentifier: out unused ) ); // NOTE: call our impl directly, since we're coupled anyway. var numSizes = sizes.Count; builder.Append('['); for (int i = 0; i < numSizes; i++) { if (i > 0) { builder.Append(", "); } var lowerBound = lowerBounds[i]; var size = sizes[i]; if (lowerBound == 0) { builder.Append(FormatLiteral(size, options)); } else { builder.Append(FormatLiteral(lowerBound, options)); builder.Append(".."); builder.Append(FormatLiteral(size + lowerBound - 1, options)); } } builder.Append(']'); lmrType = originalLmrType.GetElementType(); // Strip off one layer (already handled). while (lmrType.IsArray) { builder.Append('['); builder.Append(',', lmrType.GetArrayRank() - 1); builder.Append(']'); lmrType = lmrType.GetElementType(); } builder.Append('}'); return(pooled.ToStringAndFree()); }
internal override bool IsPredefinedType(Type type) { return type.IsPredefinedType(); }
internal override string GetNameForEnumValue(ArrayBuilder<EnumField> fields, object value, ulong underlyingValue, ObjectDisplayOptions options, Type typeToDisplayOpt) { foreach (var field in fields) { // First match wins (deterministic since sorted). if (underlyingValue == field.Value) { var pooled = PooledStringBuilder.GetInstance(); var builder = pooled.Builder; AppendEnumTypeAndName(builder, typeToDisplayOpt, field.Name); return pooled.ToStringAndFree(); } } return null; }
internal abstract string GetNameForEnumValue(ArrayBuilder <EnumField> fields, object value, ulong underlyingValue, ObjectDisplayOptions options, Type typeToDisplayOpt);
internal abstract string GetArrayDisplayString(Type lmrType, ReadOnlyCollection<int> sizes, ReadOnlyCollection<int> lowerBounds, ObjectDisplayOptions options);
/// <summary> /// Append the name of the type and its type arguments. Do not append the type's containing type or namespace. /// </summary> /// <param name="builder">Builder to which the name will be appended.</param> /// <param name="type">Type, the name of which will be appended.</param> /// <param name="dynamicFlags">Flags indicating which occurrences of "object" need to be replaced by "dynamic".</param> /// <param name="index">Current index into <paramref name="dynamicFlags"/>.</param> /// <param name="escapeKeywordIdentifiers">True if identifiers that are also keywords should be prefixed with '@'.</param> /// <param name="typeArguments"> /// The type arguments of the type passed to <see cref="AppendQualifiedTypeNameInternal"/>, which might be nested /// within <paramref name="type"/>. In the Reflection/LMR object model, all type arguments are passed to the /// most nested type. To get back to the C# model, we have to propagate them out to containing types. /// </param> /// <param name="typeArgumentOffset"> /// The first position in <paramref name="typeArguments"/> that is a type argument to <paramref name="type"/>, /// from a C# perspective. /// </param> /// <param name="arity"> /// The number of type parameters of <paramref name="type"/>, from a C# perspective. /// </param> /// <param name="sawInvalidIdentifier">True if the name includes an invalid identifier (see <see cref="IsValidIdentifier"/>); false otherwise.</param> /// <remarks> /// We're passing the full array plus bounds, rather than a tailored array, to avoid creating a lot of short-lived /// temporary arrays. /// </remarks> private void AppendUnqualifiedTypeName( StringBuilder builder, Type type, DynamicFlagsCustomTypeInfo dynamicFlags, ref int index, bool escapeKeywordIdentifiers, Type[] typeArguments, int typeArgumentOffset, int arity, out bool sawInvalidIdentifier) { if (typeArguments == null || arity == 0) { AppendIdentifier(builder, escapeKeywordIdentifiers, type.Name, out sawInvalidIdentifier); return; } var mangledName = type.Name; var separatorIndex = mangledName.IndexOf('`'); var unmangledName = separatorIndex < 0 ? mangledName : mangledName.Substring(0, separatorIndex); AppendIdentifier(builder, escapeKeywordIdentifiers, unmangledName, out sawInvalidIdentifier); bool argumentsSawInvalidIdentifier; AppendGenericTypeArgumentList(builder, typeArguments, typeArgumentOffset, dynamicFlags, ref index, arity, escapeKeywordIdentifiers, out argumentsSawInvalidIdentifier); sawInvalidIdentifier |= argumentsSawInvalidIdentifier; }
protected abstract bool AppendSpecialTypeName(StringBuilder builder, Type type, bool isDynamic);
internal abstract bool IsPredefinedType(Type type);
internal static string GetPredefinedTypeName(this Type type) { if (type.IsEnum) { return(null); } switch (Type.GetTypeCode(type)) { case TypeCode.Object: if (type.IsObject()) { return("object"); } return(null); case TypeCode.Boolean: return("bool"); case TypeCode.Char: return("char"); case TypeCode.SByte: return("sbyte"); case TypeCode.Byte: return("byte"); case TypeCode.Int16: return("short"); case TypeCode.UInt16: return("ushort"); case TypeCode.Int32: return("int"); case TypeCode.UInt32: return("uint"); case TypeCode.Int64: return("long"); case TypeCode.UInt64: return("ulong"); case TypeCode.Single: return("float"); case TypeCode.Double: return("double"); case TypeCode.Decimal: return("decimal"); case TypeCode.String: return("string"); default: return(null); } }
public TypeWalker(Type type) { _type = type; }
internal override string GetNameForEnumValue(ArrayBuilder <EnumField> fields, object value, ulong underlyingValue, ObjectDisplayOptions options, Type typeToDisplayOpt) { foreach (var field in fields) { // First match wins (deterministic since sorted). if (underlyingValue == field.Value) { var pooled = PooledStringBuilder.GetInstance(); var builder = pooled.Builder; AppendEnumTypeAndName(builder, typeToDisplayOpt, field.Name); return(pooled.ToStringAndFree()); } } return(null); }
internal override string GetNamesForFlagsEnumValue(ArrayBuilder <EnumField> fields, object value, ulong underlyingValue, ObjectDisplayOptions options, Type typeToDisplayOpt) { var usedFields = ArrayBuilder <EnumField> .GetInstance(); FillUsedEnumFields(usedFields, fields, underlyingValue); if (usedFields.Count == 0) { return(null); } var pooled = PooledStringBuilder.GetInstance(); var builder = pooled.Builder; for (int i = usedFields.Count - 1; i >= 0; i--) // Backwards to list smallest first. { AppendEnumTypeAndName(builder, typeToDisplayOpt, usedFields[i].Name); if (i > 0) { builder.Append(" | "); } } usedFields.Free(); return(pooled.ToStringAndFree()); }
/// <summary> /// Append the qualified name (i.e. including containing types and namespaces) of a named type /// (i.e. not a pointer or array type) to <paramref name="builder"/>. /// </summary> /// <remarks> /// Keyword strings are appended for primitive types (e.g. "int" for "System.Int32"). /// </remarks> /// <remarks> /// Does not call itself or <see cref="AppendQualifiedTypeName"/> (directly). /// </remarks> private void AppendQualifiedTypeNameInternal( StringBuilder builder, Type type, DynamicFlagsCustomTypeInfo dynamicFlags, ref int index, bool escapeKeywordIdentifiers, out bool sawInvalidIdentifier) { var isDynamic = dynamicFlags[index++] && type.IsObject(); if (AppendSpecialTypeName(builder, type, isDynamic)) { sawInvalidIdentifier = false; return; } Debug.Assert(!isDynamic, $"Dynamic should have been handled by {nameof(AppendSpecialTypeName)}"); Debug.Assert(!IsPredefinedType(type)); if (type.IsGenericParameter) { AppendIdentifier(builder, escapeKeywordIdentifiers, type.Name, out sawInvalidIdentifier); return; } // Note: in the Reflection/LMR object model, all type arguments are on the most nested type. var hasTypeArguments = type.IsGenericType; var typeArguments = type.IsGenericType ? type.GetGenericArguments() : null; Debug.Assert(hasTypeArguments == (typeArguments != null)); var numTypeArguments = hasTypeArguments ? typeArguments.Length : 0; sawInvalidIdentifier = false; bool sawSingleInvalidIdentifier; if (type.IsNested) { // Push from inside, out. var stack = ArrayBuilder<Type>.GetInstance(); { var containingType = type.DeclaringType; while (containingType != null) { stack.Add(containingType); containingType = containingType.DeclaringType; } } var lastContainingTypeIndex = stack.Count - 1; AppendNamespacePrefix(builder, stack[lastContainingTypeIndex], escapeKeywordIdentifiers, out sawSingleInvalidIdentifier); sawInvalidIdentifier |= sawSingleInvalidIdentifier; var typeArgumentOffset = 0; // Pop from outside, in. for (int i = lastContainingTypeIndex; i >= 0; i--) { var containingType = stack[i]; // ACASEY: I explored the type in the debugger and couldn't find the arity stored/exposed separately. int arity = hasTypeArguments ? containingType.GetGenericArguments().Length - typeArgumentOffset : 0; AppendUnqualifiedTypeName(builder, containingType, dynamicFlags, ref index, escapeKeywordIdentifiers, typeArguments, typeArgumentOffset, arity, out sawSingleInvalidIdentifier); sawInvalidIdentifier |= sawSingleInvalidIdentifier; builder.Append('.'); typeArgumentOffset += arity; } stack.Free(); AppendUnqualifiedTypeName(builder, type, dynamicFlags, ref index, escapeKeywordIdentifiers, typeArguments, typeArgumentOffset, numTypeArguments - typeArgumentOffset, out sawSingleInvalidIdentifier); sawInvalidIdentifier |= sawSingleInvalidIdentifier; } else { AppendNamespacePrefix(builder, type, escapeKeywordIdentifiers, out sawSingleInvalidIdentifier); sawInvalidIdentifier |= sawSingleInvalidIdentifier; AppendUnqualifiedTypeName(builder, type, dynamicFlags, ref index, escapeKeywordIdentifiers, typeArguments, 0, numTypeArguments, out sawSingleInvalidIdentifier); sawInvalidIdentifier |= sawSingleInvalidIdentifier; } }
private static void FillEnumFields(ArrayBuilder<EnumField> fields, Type lmrType) { var fieldInfos = lmrType.GetFields(); var enumTypeCode = Type.GetTypeCode(lmrType); foreach (var info in fieldInfos) { if (!info.IsSpecialName) // Skip __value. { fields.Add(new EnumField(info.Name, ConvertEnumUnderlyingTypeToUInt64(info.GetRawConstantValue(), enumTypeCode))); } } fields.Sort(EnumField.Comparer); }
/// <summary> /// Helper for appending the qualified name of the containing namespace of a type. /// NOTE: Unless the qualified name is empty, there will always be a trailing dot. /// </summary> private void AppendNamespacePrefix(StringBuilder builder, Type type, bool escapeKeywordIdentifiers, out bool sawInvalidIdentifier) { sawInvalidIdentifier = false; var @namespace = type.Namespace; if (!string.IsNullOrEmpty(@namespace)) { if (@namespace.Contains(".")) { bool sawSingleInvalidIdentifier; var pooled = PooledStringBuilder.GetInstance(); var identifierBuilder = pooled.Builder; foreach (var ch in @namespace) { if (ch == '.') { AppendIdentifier(builder, escapeKeywordIdentifiers, identifierBuilder.ToString(), out sawSingleInvalidIdentifier); sawInvalidIdentifier |= sawSingleInvalidIdentifier; builder.Append(ch); identifierBuilder.Clear(); } else { identifierBuilder.Append(ch); } } AppendIdentifier(builder, escapeKeywordIdentifiers, identifierBuilder.ToString(), out sawSingleInvalidIdentifier); sawInvalidIdentifier |= sawSingleInvalidIdentifier; pooled.Free(); } else { AppendIdentifier(builder, escapeKeywordIdentifiers, @namespace, out sawInvalidIdentifier); } builder.Append('.'); } }
internal abstract string GetArrayDisplayString(Type lmrType, ReadOnlyCollection <int> sizes, ReadOnlyCollection <int> lowerBounds, ObjectDisplayOptions options);
protected abstract void AppendGenericTypeArgumentList( StringBuilder builder, Type[] typeArguments, int typeArgumentOffset, DynamicFlagsCustomTypeInfo dynamicFlags, ref int index, int arity, bool escapeKeywordIdentifiers, out bool sawInvalidIdentifier);
internal static bool IsPredefinedType(this Type type) { return(type.GetPredefinedTypeName() != null); }
#pragma warning disable RS0010 /// <remarks> /// The corresponding native code is in EEUserStringBuilder::ErrTryAppendConstantEnum. /// The corresponding roslyn code is in /// <see cref="M:Microsoft.CodeAnalysis.SymbolDisplay.AbstractSymbolDisplayVisitor`1.AddEnumConstantValue(Microsoft.CodeAnalysis.INamedTypeSymbol, System.Object, System.Boolean)"/>. /// NOTE: no curlies for enum values. /// </remarks> #pragma warning restore RS0010 private string GetEnumDisplayString(Type lmrType, DkmClrValue value, ObjectDisplayOptions options, bool includeTypeName, DkmInspectionContext inspectionContext) { Debug.Assert(lmrType.IsEnum); Debug.Assert(value != null); object underlyingValue = value.HostObjectValue; Debug.Assert(underlyingValue != null); string displayString; ArrayBuilder<EnumField> fields = ArrayBuilder<EnumField>.GetInstance(); FillEnumFields(fields, lmrType); // We will normalize/extend all enum values to ulong to ensure that we are always comparing the full underlying value. ulong valueForComparison = ConvertEnumUnderlyingTypeToUInt64(underlyingValue, Type.GetTypeCode(lmrType)); var typeToDisplayOpt = includeTypeName ? lmrType : null; if (valueForComparison != 0 && IsFlagsEnum(lmrType)) { displayString = GetNamesForFlagsEnumValue(fields, underlyingValue, valueForComparison, options, typeToDisplayOpt); } else { displayString = GetNameForEnumValue(fields, underlyingValue, valueForComparison, options, typeToDisplayOpt); } fields.Free(); return displayString ?? FormatPrimitive(value, options, inspectionContext); }
internal override bool IsPrimitiveType(Type type) { return type.IsPredefinedType(); }
private static bool IsFlagsEnum(Type lmrType) { Debug.Assert(lmrType.IsEnum); var attributes = lmrType.GetCustomAttributesData(); foreach (var attribute in attributes) { // NOTE: AttributeType is not available in 2.0 if (attribute.Constructor.DeclaringType.FullName == "System.FlagsAttribute") { return true; } } return false; }
internal abstract string GetNameForEnumValue(ArrayBuilder<EnumField> fields, object value, ulong underlyingValue, ObjectDisplayOptions options, Type typeToDisplayOpt);
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, inspectionContext)); if (expansion == null) { var expansionType = value.HasExceptionThrown(parent) ? value.Type.GetLmrType() : declaredType; expansion = this.GetTypeExpansion(inspectionContext, expansionType, value, expansionFlags); } } return(new EvalResultDataItem( ExpansionKind.Default, name, typeDeclaringMember, declaredType, 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)); }
internal override string GetNamesForFlagsEnumValue(ArrayBuilder<EnumField> fields, object value, ulong underlyingValue, ObjectDisplayOptions options, Type typeToDisplayOpt) { var usedFields = ArrayBuilder<EnumField>.GetInstance(); FillUsedEnumFields(usedFields, fields, underlyingValue); if (usedFields.Count == 0) { return null; } var pooled = PooledStringBuilder.GetInstance(); var builder = pooled.Builder; for (int i = usedFields.Count - 1; i >= 0; i--) // Backwards to list smallest first. { AppendEnumTypeAndName(builder, typeToDisplayOpt, usedFields[i].Name); if (i > 0) { builder.Append(" | "); } } usedFields.Free(); return pooled.ToStringAndFree(); }
protected void AppendQualifiedTypeName( StringBuilder builder, Type type, ReadOnlyCollection <byte> dynamicFlags, ref int dynamicFlagIndex, ReadOnlyCollection <string> tupleElementNames, ref int tupleElementIndex, bool escapeKeywordIdentifiers, out bool sawInvalidIdentifier) { Type originalType = type; // Can have an array of pointers, but not a pointer to an array, so consume these first. // We'll reconstruct this information later from originalType. while (type.IsArray) { dynamicFlagIndex++; type = type.GetElementType(); } int pointerCount = 0; while (type.IsPointer) { var elementType = type.GetElementType(); if (elementType == null) { // Null for function pointers. break; } dynamicFlagIndex++; pointerCount++; type = elementType; } int nullableCount = 0; Type typeArg; while ((typeArg = type.GetNullableTypeArgument()) != null) { dynamicFlagIndex++; nullableCount++; type = typeArg; } Debug.Assert(nullableCount < 2, "Benign: someone is nesting nullables."); Debug.Assert(pointerCount == 0 || nullableCount == 0, "Benign: pointer to nullable?"); AppendQualifiedTypeNameInternal( builder, type, dynamicFlags, ref dynamicFlagIndex, tupleElementNames, ref tupleElementIndex, escapeKeywordIdentifiers, out sawInvalidIdentifier); builder.Append('?', nullableCount); builder.Append('*', pointerCount); type = originalType; while (type.IsArray) { AppendRankSpecifier(builder, type.GetArrayRank()); type = type.GetElementType(); } }
private void AppendTupleElements( StringBuilder builder, Type type, int cardinality, ReadOnlyCollection <byte> dynamicFlags, ref int dynamicFlagIndex, ReadOnlyCollection <string> tupleElementNames, ref int tupleElementIndex, bool escapeKeywordIdentifiers, out bool sawInvalidIdentifier) { sawInvalidIdentifier = false; #if DEBUG int lastNameIndex = tupleElementIndex + cardinality; #endif int nameIndex = tupleElementIndex; builder.Append('('); bool any = false; while (true) { tupleElementIndex += cardinality; var typeArguments = type.GetGenericArguments(); int nTypeArgs = typeArguments.Length; Debug.Assert(nTypeArgs > 0); Debug.Assert(nTypeArgs <= TypeHelpers.TupleFieldRestPosition); int nFields = Math.Min(nTypeArgs, TypeHelpers.TupleFieldRestPosition - 1); for (int i = 0; i < nFields; i++) { if (any) { builder.Append(", "); } bool sawSingleInvalidIdentifier; var name = CustomTypeInfo.GetTupleElementNameIfAny(tupleElementNames, nameIndex); nameIndex++; AppendTupleElement( builder, typeArguments[i], name, dynamicFlags, ref dynamicFlagIndex, tupleElementNames, ref tupleElementIndex, escapeKeywordIdentifiers, sawInvalidIdentifier: out sawSingleInvalidIdentifier); sawInvalidIdentifier |= sawSingleInvalidIdentifier; any = true; } if (nTypeArgs < TypeHelpers.TupleFieldRestPosition) { break; } Debug.Assert(!DynamicFlagsCustomTypeInfo.GetFlag(dynamicFlags, dynamicFlagIndex)); dynamicFlagIndex++; type = typeArguments[nTypeArgs - 1]; cardinality = type.GetTupleCardinalityIfAny(); } #if DEBUG Debug.Assert(nameIndex == lastNameIndex); #endif builder.Append(')'); }
private void AppendQualifiedTypeNameInternal( StringBuilder builder, Type type, ReadOnlyCollection <byte> dynamicFlags, ref int dynamicFlagIndex, ReadOnlyCollection <string> tupleElementNames, ref int tupleElementIndex, bool escapeKeywordIdentifiers, out bool sawInvalidIdentifier) { var isDynamic = DynamicFlagsCustomTypeInfo.GetFlag(dynamicFlags, dynamicFlagIndex++) && type.IsObject(); if (AppendSpecialTypeName(builder, type, isDynamic)) { sawInvalidIdentifier = false; return; } Debug.Assert(!isDynamic, $"Dynamic should have been handled by {nameof(AppendSpecialTypeName)}"); Debug.Assert(!IsPredefinedType(type)); if (type.IsGenericParameter) { AppendIdentifier(builder, escapeKeywordIdentifiers, type.Name, out sawInvalidIdentifier); return; } int cardinality; if (type.IsTupleCompatible(out cardinality)) { if (cardinality == 1) { // Not displayed as a tuple but is included in tuple element names. tupleElementIndex++; } else { AppendTupleElements( builder, type, cardinality, dynamicFlags, ref dynamicFlagIndex, tupleElementNames, ref tupleElementIndex, escapeKeywordIdentifiers, out sawInvalidIdentifier); return; } } // Note: in the Reflection/LMR object model, all type arguments are on the most nested type. var hasTypeArguments = type.IsGenericType; var typeArguments = hasTypeArguments ? type.GetGenericArguments() : null; Debug.Assert(hasTypeArguments == (typeArguments != null)); var numTypeArguments = hasTypeArguments ? typeArguments.Length : 0; sawInvalidIdentifier = false; bool sawSingleInvalidIdentifier; var typeArgumentOffset = 0; if (type.IsNested) { // Push from inside, out. var stack = ArrayBuilder <Type> .GetInstance(); { var containingType = type.DeclaringType; while (containingType != null) { stack.Add(containingType); containingType = containingType.DeclaringType; } } var lastContainingTypeIndex = stack.Count - 1; AppendNamespacePrefix(builder, stack[lastContainingTypeIndex], escapeKeywordIdentifiers, out sawSingleInvalidIdentifier); sawInvalidIdentifier |= sawSingleInvalidIdentifier; // Pop from outside, in. for (int i = lastContainingTypeIndex; i >= 0; i--) { var containingType = stack[i]; // ACASEY: I explored the type in the debugger and couldn't find the arity stored/exposed separately. int arity = hasTypeArguments ? containingType.GetGenericArguments().Length - typeArgumentOffset : 0; AppendUnqualifiedTypeName( builder, containingType, dynamicFlags, ref dynamicFlagIndex, tupleElementNames, ref tupleElementIndex, escapeKeywordIdentifiers, typeArguments, typeArgumentOffset, arity, out sawSingleInvalidIdentifier); sawInvalidIdentifier |= sawSingleInvalidIdentifier; builder.Append('.'); typeArgumentOffset += arity; } stack.Free(); } else { AppendNamespacePrefix(builder, type, escapeKeywordIdentifiers, out sawSingleInvalidIdentifier); sawInvalidIdentifier |= sawSingleInvalidIdentifier; } AppendUnqualifiedTypeName( builder, type, dynamicFlags, ref dynamicFlagIndex, tupleElementNames, ref tupleElementIndex, escapeKeywordIdentifiers, typeArguments, typeArgumentOffset, numTypeArguments - typeArgumentOffset, out sawSingleInvalidIdentifier); sawInvalidIdentifier |= sawSingleInvalidIdentifier; }