| File: Hosting\ObjectFormatter\CommonTypeNameFormatter.cs | Web Access |
| Project: src\roslyn\src\Scripting\Core\Microsoft.CodeAnalysis.Scripting.csproj (Microsoft.CodeAnalysis.Scripting) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable using System; using System.Globalization; using System.Reflection; using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.Scripting.Hosting { using static ObjectFormatterHelpers; using TypeInfo = System.Reflection.TypeInfo; internal abstract partial class CommonTypeNameFormatter { protected abstract string GetPrimitiveTypeName(SpecialType type); protected abstract string GenericParameterOpening { get; } protected abstract string GenericParameterClosing { get; } protected abstract string ArrayOpening { get; } protected abstract string ArrayClosing { get; } protected abstract CommonPrimitiveFormatter PrimitiveFormatter { get; } // TODO (tomat): Use DebuggerDisplay.Type if specified? public virtual string FormatTypeName(Type type, CommonTypeNameFormatterOptions options) { if (type == null) { throw new ArgumentNullException(nameof(type)); } string primitiveTypeName = GetPrimitiveTypeName(GetPrimitiveSpecialType(type)); if (primitiveTypeName != null) { return primitiveTypeName; } if (type.IsGenericParameter) { return type.Name; } if (type.IsArray) { return FormatArrayTypeName(type, arrayOpt: null, options: options); } var typeInfo = type.GetTypeInfo(); if (typeInfo.IsGenericType) { return FormatGenericTypeName(typeInfo, options); } return FormatNonGenericTypeName(typeInfo, options); } private static string FormatNonGenericTypeName(TypeInfo typeInfo, CommonTypeNameFormatterOptions options) { if (options.ShowNamespaces) { return typeInfo.FullName.Replace('+', '.'); } if (typeInfo.DeclaringType == null) { return typeInfo.Name; } var stack = ArrayBuilder<string>.GetInstance(); do { stack.Push(typeInfo.Name); typeInfo = typeInfo.DeclaringType?.GetTypeInfo(); } while (typeInfo != null); stack.ReverseContents(); var typeName = string.Join(".", stack); stack.Free(); return typeName; } public virtual string FormatTypeArguments(Type[] typeArguments, CommonTypeNameFormatterOptions options) { if (typeArguments == null) { throw new ArgumentNullException(nameof(typeArguments)); } if (typeArguments.Length == 0) { throw new ArgumentException(null, nameof(typeArguments)); } var pooled = PooledStringBuilder.GetInstance(); var builder = pooled.Builder; builder.Append(GenericParameterOpening); var first = true; foreach (var typeArgument in typeArguments) { if (first) { first = false; } else { builder.Append(", "); } builder.Append(FormatTypeName(typeArgument, options)); } builder.Append(GenericParameterClosing); return pooled.ToStringAndFree(); } /// <summary> /// Formats an array type name (vector or multidimensional). /// </summary> public virtual string FormatArrayTypeName(Type arrayType, Array arrayOpt, CommonTypeNameFormatterOptions options) { if (arrayType == null) { throw new ArgumentNullException(nameof(arrayType)); } StringBuilder sb = new StringBuilder(); // print the inner-most element type first: Type elementType = arrayType.GetElementType(); while (elementType.IsArray) { elementType = elementType.GetElementType(); } sb.Append(FormatTypeName(elementType, options)); // print all components of a jagged array: Type type = arrayType; do { if (arrayOpt != null) { sb.Append(ArrayOpening); int rank = type.GetArrayRank(); bool anyNonzeroLowerBound = false; for (int i = 0; i < rank; i++) { if (arrayOpt.GetLowerBound(i) > 0) { anyNonzeroLowerBound = true; break; } } for (int i = 0; i < rank; i++) { int lowerBound = arrayOpt.GetLowerBound(i); int length = arrayOpt.GetLength(i); if (i > 0) { sb.Append(", "); } if (anyNonzeroLowerBound) { AppendArrayBound(sb, lowerBound, options.ArrayBoundRadix); sb.Append(".."); AppendArrayBound(sb, length + lowerBound, options.ArrayBoundRadix); } else { AppendArrayBound(sb, length, options.ArrayBoundRadix); } } sb.Append(ArrayClosing); arrayOpt = null; } else { AppendArrayRank(sb, type); } type = type.GetElementType(); } while (type.IsArray); return sb.ToString(); } private void AppendArrayBound(StringBuilder sb, long bound, int numberRadix) { var options = new CommonPrimitiveFormatterOptions( numberRadix: numberRadix, includeCodePoints: false, quoteStringsAndCharacters: true, escapeNonPrintableCharacters: true, cultureInfo: CultureInfo.InvariantCulture); var formatted = bound is >= int.MinValue and <= int.MaxValue ? PrimitiveFormatter.FormatPrimitive((int)bound, options) : PrimitiveFormatter.FormatPrimitive(bound, options); sb.Append(formatted); } private void AppendArrayRank(StringBuilder sb, Type arrayType) { sb.Append(ArrayOpening); int rank = arrayType.GetArrayRank(); if (rank > 1) { sb.Append(',', rank - 1); } sb.Append(ArrayClosing); } private string FormatGenericTypeName(TypeInfo typeInfo, CommonTypeNameFormatterOptions options) { var pooledBuilder = PooledStringBuilder.GetInstance(); var builder = pooledBuilder.Builder; // consolidated generic arguments (includes arguments of all declaring types): // TODO (DevDiv #173210): shouldn't need parameters, but StackTrace gives us unconstructed symbols. Type[] genericArguments = typeInfo.IsGenericTypeDefinition ? typeInfo.GenericTypeParameters : typeInfo.GenericTypeArguments; if (typeInfo.DeclaringType != null) { var nestedTypes = ArrayBuilder<TypeInfo>.GetInstance(); do { nestedTypes.Add(typeInfo); typeInfo = typeInfo.DeclaringType?.GetTypeInfo(); } while (typeInfo != null); if (options.ShowNamespaces) { var @namespace = nestedTypes.Last().Namespace; if (@namespace != null) { builder.Append(@namespace + "."); } } int typeArgumentIndex = 0; for (int i = nestedTypes.Count - 1; i >= 0; i--) { AppendTypeInstantiation(builder, nestedTypes[i], genericArguments, ref typeArgumentIndex, options); if (i > 0) { builder.Append('.'); } } nestedTypes.Free(); } else { int typeArgumentIndex = 0; AppendTypeInstantiation(builder, typeInfo, genericArguments, ref typeArgumentIndex, options); } return pooledBuilder.ToStringAndFree(); } private void AppendTypeInstantiation( StringBuilder builder, TypeInfo typeInfo, Type[] genericArguments, ref int genericArgIndex, CommonTypeNameFormatterOptions options) { // generic arguments of all the outer types and the current type; int currentArgCount = (typeInfo.IsGenericTypeDefinition ? typeInfo.GenericTypeParameters.Length : typeInfo.GenericTypeArguments.Length) - genericArgIndex; if (currentArgCount > 0) { string name = typeInfo.Name; int backtick = name.IndexOf('`'); if (backtick > 0) { builder.Append(name[..backtick]); } else { builder.Append(name); } builder.Append(GenericParameterOpening); for (int i = 0; i < currentArgCount; i++) { if (i > 0) { builder.Append(", "); } builder.Append(FormatTypeName(genericArguments[genericArgIndex++], options)); } builder.Append(GenericParameterClosing); } else { builder.Append(typeInfo.Name); } } } }