File: InstructionDecoder.cs
Web Access
Project: src\src\ExpressionEvaluator\Core\Source\ExpressionCompiler\Microsoft.CodeAnalysis.ExpressionCompiler.csproj (Microsoft.CodeAnalysis.ExpressionEvaluator.ExpressionCompiler)
// 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.
 
using System.Collections.Immutable;
using System.Text;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Symbols;
using Microsoft.VisualStudio.Debugger.Clr;
 
namespace Microsoft.CodeAnalysis.ExpressionEvaluator
{
    internal abstract class InstructionDecoder<TCompilation, TMethodSymbol, TModuleSymbol, TTypeSymbol, TTypeParameterSymbol>
        where TCompilation : Compilation
        where TMethodSymbol : class, IMethodSymbolInternal
        where TModuleSymbol : class, IModuleSymbolInternal
        where TTypeSymbol : class, ITypeSymbolInternal
        where TTypeParameterSymbol : class, ITypeParameterSymbolInternal
    {
        internal static readonly SymbolDisplayFormat DisplayFormat = new SymbolDisplayFormat(
            typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
            genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
            memberOptions: SymbolDisplayMemberOptions.IncludeContainingType | SymbolDisplayMemberOptions.IncludeExplicitInterface,
            miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes);
 
        private readonly bool _useReferencedAssembliesOnly;
 
        internal InstructionDecoder()
        {
            // Should be passed by the ExpressionCompiler as an argument to this constructor.
            _useReferencedAssembliesOnly = ExpressionCompiler.GetUseReferencedAssembliesOnlySetting();
        }
 
        internal MakeAssemblyReferencesKind GetMakeAssemblyReferencesKind()
        {
            return _useReferencedAssembliesOnly ? MakeAssemblyReferencesKind.AllReferences : MakeAssemblyReferencesKind.AllAssemblies;
        }
 
        internal abstract void AppendFullName(StringBuilder builder, TMethodSymbol method);
 
        internal virtual void AppendParameterTypeName(StringBuilder builder, IParameterSymbol parameter)
        {
            builder.Append(parameter.Type.ToDisplayString(DisplayFormat));
        }
 
        /// <summary>
        /// Constructs a method and any of its generic containing types using the specified <paramref name="typeArguments"/>.
        /// </summary>
        internal abstract TMethodSymbol ConstructMethod(TMethodSymbol method, ImmutableArray<TTypeParameterSymbol> typeParameters, ImmutableArray<TTypeSymbol> typeArguments);
 
        internal abstract ImmutableArray<TTypeParameterSymbol> GetAllTypeParameters(TMethodSymbol method);
 
        internal abstract TCompilation GetCompilation(DkmClrModuleInstance moduleInstance);
 
        internal abstract TMethodSymbol GetMethod(TCompilation compilation, DkmClrInstructionAddress instructionAddress);
 
        internal string GetName(TMethodSymbol method, bool includeParameterTypes, bool includeParameterNames, ArrayBuilder<string?>? argumentValues = null)
        {
            var pooled = PooledStringBuilder.GetInstance();
            var builder = pooled.Builder;
 
            // "full name" of method...
            AppendFullName(builder, method);
 
            // parameter list...
            var parameters = ((IMethodSymbol)method.GetISymbol()).Parameters;
            var includeArgumentValues = argumentValues != null && parameters.Length == argumentValues.Count;
            if (includeParameterTypes || includeParameterNames || includeArgumentValues)
            {
                builder.Append('(');
                for (int i = 0; i < parameters.Length; i++)
                {
                    if (i > 0)
                    {
                        builder.Append(", ");
                    }
 
                    var parameter = parameters[i];
 
                    if (includeParameterTypes)
                    {
                        AppendParameterTypeName(builder, parameter);
                    }
 
                    if (includeParameterNames)
                    {
                        if (includeParameterTypes)
                        {
                            builder.Append(' ');
                        }
 
                        builder.Append(parameter.Name);
                    }
 
                    if (includeArgumentValues)
                    {
                        var argumentValue = argumentValues![i];
                        if (argumentValue != null)
                        {
                            if (includeParameterTypes || includeParameterNames)
                            {
                                builder.Append(" = ");
                            }
 
                            builder.Append(argumentValue);
                        }
                    }
                }
                builder.Append(')');
            }
 
            return pooled.ToStringAndFree();
        }
 
        internal string GetReturnTypeName(TMethodSymbol method)
        {
            return ((IMethodSymbol)method.GetISymbol()).ReturnType.ToDisplayString(DisplayFormat);
        }
 
        internal abstract TypeNameDecoder<TModuleSymbol, TTypeSymbol> GetTypeNameDecoder(TCompilation compilation, TMethodSymbol method);
 
        internal ImmutableArray<TTypeSymbol> GetTypeSymbols(TCompilation compilation, TMethodSymbol method, string?[]? serializedTypeNames)
        {
            if (serializedTypeNames == null)
            {
                return ImmutableArray<TTypeSymbol>.Empty;
            }
 
            var builder = ArrayBuilder<TTypeSymbol>.GetInstance();
            foreach (var name in serializedTypeNames)
            {
                // The list of type names will include null values if type arguments are not available.
                // It seems unlikely that only some type arguments will be missing (and it also seems
                // like very little incremental value to include only some of the arguments), so we'll
                // keep things simple and omit all type arguments if any are missing.
                if (name == null)
                {
                    builder.Free();
                    return ImmutableArray<TTypeSymbol>.Empty;
                }
 
                var typeNameDecoder = GetTypeNameDecoder(compilation, method);
                builder.Add(typeNameDecoder.GetTypeSymbolForSerializedType(name));
            }
            return builder.ToImmutableAndFree();
        }
    }
}