File: TypeHandleExtensions.cs
Web Access
Project: src\runtime\src\native\managed\cdac\Microsoft.Diagnostics.DataContractReader.Legacy\Microsoft.Diagnostics.DataContractReader.Legacy.csproj (Microsoft.Diagnostics.DataContractReader.Legacy)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Text;
using Microsoft.Diagnostics.DataContractReader.Contracts;

namespace Microsoft.Diagnostics.DataContractReader.Legacy;

internal static class TypeHandleExtensions
{
    public static string GetName(this ITypeHandle typeHandle, Target target)
    {
        StringBuilder result = new();
        AppendName(typeHandle, target, result);
        return result.ToString();
    }

    private static void AppendName(ITypeHandle typeHandle, Target target, StringBuilder result)
    {
        IRuntimeTypeSystem runtimeTypeSystem = target.Contracts.RuntimeTypeSystem;
        if (runtimeTypeSystem.IsTypeDesc(typeHandle))
        {
            AppendTypeDescName(typeHandle, target, result);
            return;
        }

        if (runtimeTypeSystem.IsArray(typeHandle, out uint rank))
        {
            AppendName(runtimeTypeSystem.GetTypeParam(typeHandle), target, result);
            AddNameSuffix(result, runtimeTypeSystem.GetInternalCorElementType(typeHandle), rank);
        }
        else
        {
            uint typeDefToken = runtimeTypeSystem.GetTypeDefToken(typeHandle);
            EntityHandle typeDefHandle = MetadataTokens.EntityHandle((int)typeDefToken);
            if (!typeDefHandle.IsNil)
            {
                Contracts.ModuleHandle module = target.Contracts.Loader.GetModuleHandleFromModulePtr(runtimeTypeSystem.GetModule(typeHandle));
                MetadataReader reader = target.Contracts.EcmaMetadata.GetMetadata(module)!;
                TypeDefinition typeDef = reader.GetTypeDefinition((TypeDefinitionHandle)typeDefHandle);
                string typeNamespace = reader.GetString(typeDef.Namespace);
                if (typeNamespace.Length > 0)
                {
                    result.Append(typeNamespace);
                    result.Append('.');
                }
                result.Append(reader.GetString(typeDef.Name));
            }
        }

        ReadOnlySpan<ITypeHandle> instantiation = runtimeTypeSystem.GetInstantiation(typeHandle);
        if (!instantiation.IsEmpty)
        {
            TypeNameBuilder.AppendInst(target, result, instantiation, TypeNameFormat.FormatNamespace);
        }
    }

    private static void AppendTypeDescName(ITypeHandle typeHandle, Target target, StringBuilder result)
    {
        IRuntimeTypeSystem runtimeTypeSystem = target.Contracts.RuntimeTypeSystem;
        CorElementType kind = runtimeTypeSystem.GetInternalCorElementType(typeHandle);

        if (kind is CorElementType.Byref or CorElementType.Ptr or CorElementType.SzArray or CorElementType.Array)
        {
            AppendName(runtimeTypeSystem.GetTypeParam(typeHandle), target, result);
        }

        uint rank = 0;
        if (kind is CorElementType.Var or CorElementType.MVar)
        {
            bool isGenericVariable = runtimeTypeSystem.IsGenericVariable(typeHandle, out _, out _, out uint index);
            Debug.Assert(isGenericVariable);
            rank = index;
        }

        AddNameSuffix(result, kind, rank);
    }

    private static void AddNameSuffix(StringBuilder result, CorElementType kind, uint rank)
    {
        switch (kind)
        {
            case CorElementType.Byref:
                result.Append('&');
                break;
            case CorElementType.Ptr:
                result.Append('*');
                break;
            case CorElementType.SzArray:
                result.Append("[]");
                break;
            case CorElementType.Array:
                result.Append('[');
                if (rank == 1)
                {
                    result.Append('*');
                }
                else
                {
                    for (uint dimension = 1; dimension < rank; dimension++)
                    {
                        result.Append(',');
                    }
                }
                result.Append(']');
                break;
            case CorElementType.Var:
                result.Append('!');
                result.Append(rank);
                break;
            case CorElementType.MVar:
                result.Append("!!");
                result.Append(rank);
                break;
            case CorElementType.FnPtr:
                result.Append("FNPTR");
                break;
            default:
                string? name = kind switch
                {
                    CorElementType.Void => "Void",
                    CorElementType.Boolean => "Boolean",
                    CorElementType.Char => "Char",
                    CorElementType.I1 => "SByte",
                    CorElementType.U1 => "Byte",
                    CorElementType.I2 => "Int16",
                    CorElementType.U2 => "UInt16",
                    CorElementType.I4 => "Int32",
                    CorElementType.U4 => "UInt32",
                    CorElementType.I8 => "Int64",
                    CorElementType.U8 => "UInt64",
                    CorElementType.R4 => "Single",
                    CorElementType.R8 => "Double",
                    CorElementType.String => "String",
                    CorElementType.TypedByRef => "TypedReference",
                    CorElementType.I => "IntPtr",
                    CorElementType.U => "UIntPtr",
                    CorElementType.Object => "Object",
                    _ => null,
                };

                if (name is not null)
                {
                    result.Append("System.");
                    result.Append(name);
                }
                break;
        }
    }
}