File: src\ExpressionEvaluator\CSharp\Source\ResultProvider\CSharpFormatter.Values.cs
Web Access
Project: src\src\ExpressionEvaluator\CSharp\Source\ResultProvider\Portable\Microsoft.CodeAnalysis.CSharp.ResultProvider.csproj (Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator.ResultProvider)
// 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.Collections.ObjectModel;
using System.Diagnostics;
using System.Text;
using Microsoft.CodeAnalysis.ExpressionEvaluator;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.VisualStudio.Debugger.Clr;
using Roslyn.Utilities;
using Type = Microsoft.VisualStudio.Debugger.Metadata.Type;
 
namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator
{
    internal sealed partial class CSharpFormatter : Formatter
    {
        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);
            }
        }
 
        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 string GetArrayIndexExpression(string[] indices)
        {
            return indices.ToCommaSeparatedString('[', ']');
        }
 
        internal override string GetCastExpression(string argument, string type, DkmClrCastExpressionOptions options)
        {
            Debug.Assert(!string.IsNullOrEmpty(argument));
            Debug.Assert(!string.IsNullOrEmpty(type));
 
            var pooled = PooledStringBuilder.GetInstance();
            var builder = pooled.Builder;
 
            if ((options & DkmClrCastExpressionOptions.ParenthesizeEntireExpression) != 0)
            {
                builder.Append('(');
            }
            if ((options & DkmClrCastExpressionOptions.ParenthesizeArgument) != 0)
            {
                argument = $"({argument})";
            }
            if ((options & DkmClrCastExpressionOptions.ConditionalCast) != 0)
            {
                builder.Append(argument);
                builder.Append(" as ");
                builder.Append(type);
            }
            else
            {
                builder.Append('(');
                builder.Append(type);
                builder.Append(')');
                builder.Append(argument);
            }
            if ((options & DkmClrCastExpressionOptions.ParenthesizeEntireExpression) != 0)
            {
                builder.Append(')');
            }
 
            return pooled.ToStringAndFree();
        }
 
        internal override string GetTupleExpression(string[] values)
        {
            return values.ToCommaSeparatedString('(', ')');
        }
 
        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();
        }
 
        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 GetObjectCreationExpression(string type, string[] arguments)
        {
            Debug.Assert(!string.IsNullOrEmpty(type));
 
            var pooled = PooledStringBuilder.GetInstance();
            var builder = pooled.Builder;
 
            builder.Append("new ");
            builder.Append(type);
            builder.Append('(');
            builder.AppendCommaSeparatedList(arguments);
            builder.Append(')');
 
            return pooled.ToStringAndFree();
        }
 
        internal override string FormatLiteral(char c, ObjectDisplayOptions options)
        {
            return ObjectDisplay.FormatLiteral(c, options);
        }
 
        internal override string FormatLiteral(int value, ObjectDisplayOptions options)
        {
            return ObjectDisplay.FormatLiteral(value, options & ~(ObjectDisplayOptions.UseQuotes | ObjectDisplayOptions.EscapeNonPrintableCharacters));
        }
 
        internal override string FormatPrimitiveObject(object value, ObjectDisplayOptions options)
        {
            return ObjectDisplay.FormatPrimitive(value, options);
        }
 
        internal override string FormatString(string str, ObjectDisplayOptions options)
        {
            return ObjectDisplay.FormatLiteral(str, options);
        }
    }
}