File: SigFormat.cs
Web Access
Project: src\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.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Text;
using Microsoft.Diagnostics.DataContractReader.Contracts;

namespace Microsoft.Diagnostics.DataContractReader.Legacy
{
    internal static class SigFormat
    {
        public static unsafe void AppendSigFormat(Target target,
            StringBuilder stringBuilder,
            ReadOnlySpan<byte> signature,
            MetadataReader? metadata,
            string? memberName,
            string? className,
            string? namespaceName,
            ReadOnlySpan<TypeHandle> typeInstantiation,
            ReadOnlySpan<TypeHandle> methodInstantiation,
            bool CStringParmsOnly)
        {
            fixed (byte* pSignature = signature)
            {
                BlobReader blobReader = new BlobReader(pSignature, signature.Length);
                AppendSigFormat(target, stringBuilder, blobReader, metadata, memberName, className, namespaceName, typeInstantiation, methodInstantiation, CStringParmsOnly);
            }
        }

        public static void AppendSigFormat(Target target,
            StringBuilder stringBuilder,
            BlobReader signature,
            MetadataReader? metadata,
            string? memberName,
            string? className,
            string? namespaceName,
            ReadOnlySpan<TypeHandle> typeInstantiation,
            ReadOnlySpan<TypeHandle> methodInstantiation,
            bool CStringParmsOnly)
        {
            SignatureHeader header = signature.ReadSignatureHeader();

            if (header.IsGeneric)
            {
                signature.ReadCompressedInteger(); // Ignore generic parameter count
            }
            int cArgs = (int)signature.ReadCompressedInteger();
            bool isVarArg = header.CallingConvention == SignatureCallingConvention.VarArgs;

            if (!CStringParmsOnly)
            {
                AddTypeString(target, stringBuilder, ref signature, typeInstantiation, methodInstantiation, metadata);
                stringBuilder.Append(' ');
                if (!string.IsNullOrEmpty(namespaceName))
                {
                    stringBuilder.Append(namespaceName);
                    stringBuilder.Append('.');
                }
                if (!string.IsNullOrEmpty(className))
                {
                    stringBuilder.Append(className);
                    stringBuilder.Append('.');
                }
                if (!string.IsNullOrEmpty(memberName))
                {
                    stringBuilder.Append(memberName);
                    stringBuilder.Append('.');
                }
            }
            else
            {
                StringBuilder sbDummy = new StringBuilder();
                AddTypeString(target, sbDummy, ref signature, typeInstantiation, methodInstantiation, metadata);
            }

            stringBuilder.Append('(');
            for (int i = 0; i < cArgs; i++)
            {
                AddTypeString(target, stringBuilder, ref signature, typeInstantiation, methodInstantiation, metadata);
                if (i != cArgs - 1)
                    stringBuilder.Append(", ");
            }

            if (isVarArg)
            {
                if (cArgs > 0)
                    stringBuilder.Append(", ");
                stringBuilder.Append("...");
            }
            stringBuilder.Append(')');
        }

        private static unsafe void AddTypeString(Target target,
            StringBuilder stringBuilder,
            ref BlobReader signature,
            ReadOnlySpan<TypeHandle> typeInstantiation,
            ReadOnlySpan<TypeHandle> methodInstantiation,
            MetadataReader? metadata)
        {
            string _namespace;
            string name;

            while (true)
            {
                switch ((CorElementType)signature.ReadByte())
                {
                    case CorElementType.Void: stringBuilder.Append("Void"); return;
                    case CorElementType.Boolean: stringBuilder.Append("Boolean"); return;
                    case CorElementType.I: stringBuilder.Append("IntPtr"); return;
                    case CorElementType.U: stringBuilder.Append("UIntPtr"); return;
                    case CorElementType.I1: stringBuilder.Append("SByte"); return;
                    case CorElementType.U1: stringBuilder.Append("Byte"); return;
                    case CorElementType.I2: stringBuilder.Append("Int16"); return;
                    case CorElementType.U2: stringBuilder.Append("UInt16"); return;
                    case CorElementType.I4: stringBuilder.Append("Int32"); return;
                    case CorElementType.U4: stringBuilder.Append("UInt32"); return;
                    case CorElementType.I8: stringBuilder.Append("Int64"); return;
                    case CorElementType.U8: stringBuilder.Append("UInt64"); return;
                    case CorElementType.R4: stringBuilder.Append("Single"); return;
                    case CorElementType.R8: stringBuilder.Append("Double"); return;
                    case CorElementType.Char: stringBuilder.Append("Char"); return;

                    case CorElementType.Object: stringBuilder.Append("System.Object"); return;
                    case CorElementType.String: stringBuilder.Append("System.String"); return;

                    case CorElementType.ValueType:
                    case CorElementType.Class:
                        if (metadata == null)
                            throw new InvalidOperationException("Invalid signature without metadata reader");
                        EntityHandle handle = signature.ReadTypeHandle();
                        switch (handle.Kind)
                        {
                            case HandleKind.TypeDefinition:
                                TypeDefinition typeDef = metadata.GetTypeDefinition((TypeDefinitionHandle)handle);
                                _namespace = metadata.GetString(typeDef.Namespace);
                                name = metadata.GetString(typeDef.Name);
                                break;
                            case HandleKind.TypeReference:
                                TypeReference typeRef = metadata.GetTypeReference((TypeReferenceHandle)handle);
                                _namespace = metadata.GetString(typeRef.Namespace);
                                name = metadata.GetString(typeRef.Name);
                                break;
                            default:
                                return;
                        }

                        if (!string.IsNullOrEmpty(_namespace))
                        {
                            stringBuilder.Append(_namespace);
                            stringBuilder.Append('.');
                        }
                        stringBuilder.Append(name);
                        return;

                    case CorElementType.Internal:
                        TargetPointer typeHandlePointer = target.ReadPointerFromSpan(signature.ReadBytes(target.PointerSize));
                        IRuntimeTypeSystem runtimeTypeSystem = target.Contracts.RuntimeTypeSystem;
                        TypeHandle th = runtimeTypeSystem.GetTypeHandle(typeHandlePointer);
                        switch (runtimeTypeSystem.GetSignatureCorElementType(th))
                        {
                            case CorElementType.FnPtr:
                            case CorElementType.Ptr:
                                stringBuilder.Append("System.UIntPtr");
                                return;
                            case CorElementType.ValueType:
                                if (runtimeTypeSystem.HasTypeParam(th))
                                {
                                    th = runtimeTypeSystem.GetTypeParam(th);
                                }
                                break;

                            case CorElementType.Byref:
                            case CorElementType.Array:
                                AddType(target, stringBuilder, th);
                                return;
                        }

                        uint typeDefToken = runtimeTypeSystem.GetTypeDefToken(th);
                        TargetPointer modulePointer = target.Contracts.RuntimeTypeSystem.GetModule(th);
                        Contracts.ModuleHandle module = target.Contracts.Loader.GetModuleHandleFromModulePtr(modulePointer);
                        MetadataReader internalTypeMetadata = target.Contracts.EcmaMetadata.GetMetadata(module)!;

                        TypeDefinition internalTypeDef = internalTypeMetadata.GetTypeDefinition((TypeDefinitionHandle)MetadataTokens.Handle((int)typeDefToken));
                        _namespace = internalTypeMetadata.GetString(internalTypeDef.Namespace);
                        name = internalTypeMetadata.GetString(internalTypeDef.Name);

                        if (!string.IsNullOrEmpty(_namespace))
                        {
                            stringBuilder.Append(_namespace);
                            stringBuilder.Append('.');
                        }
                        stringBuilder.Append(name);
                        return;

                    case CorElementType.TypedByRef:
                        stringBuilder.Append("TypedReference");
                        return;

                    case CorElementType.Byref:
                        AddTypeString(target, stringBuilder, ref signature, typeInstantiation, methodInstantiation, metadata);
                        stringBuilder.Append(" ByRef");
                        return;

                    case CorElementType.Ptr:
                        AddTypeString(target, stringBuilder, ref signature, typeInstantiation, methodInstantiation, metadata);
                        stringBuilder.Append('*');
                        return;

                    case CorElementType.MVar:
                        int mvarIndex = signature.ReadCompressedInteger();
                        if (methodInstantiation.Length > mvarIndex)
                        {
                            AddType(target, stringBuilder, methodInstantiation[mvarIndex]);
                        }
                        else
                        {
                            stringBuilder.Append($"!!{mvarIndex}");
                        }
                        return;

                    case CorElementType.Var:
                        int varIndex = signature.ReadCompressedInteger();
                        if (typeInstantiation.Length > varIndex)
                        {
                            AddType(target, stringBuilder, typeInstantiation[varIndex]);
                        }
                        else
                        {
                            stringBuilder.Append($"!!{varIndex}");
                        }
                        return;

                    case CorElementType.GenericInst:
                        AddTypeString(target, stringBuilder, ref signature, typeInstantiation, methodInstantiation, metadata);
                        int genericArgCount = signature.ReadCompressedInteger();
                        stringBuilder.Append('<');
                        for (int i = 0; i < genericArgCount; i++)
                        {
                            if (i != 0)
                                stringBuilder.Append(',');
                            AddTypeString(target, stringBuilder, ref signature, typeInstantiation, methodInstantiation, metadata);
                        }
                        stringBuilder.Append('>');
                        return;

                    case CorElementType.SzArray:
                        AddTypeString(target, stringBuilder, ref signature, typeInstantiation, methodInstantiation, metadata);
                        stringBuilder.Append("[]");
                        return;

                    case CorElementType.Array:
                        AddTypeString(target, stringBuilder, ref signature, typeInstantiation, methodInstantiation, metadata);
                        stringBuilder.Append('[');
                        int rank = signature.ReadCompressedInteger();
                        for (uint i = 1; i < rank; i++)
                        {
                            stringBuilder.Append(',');
                        }
                        stringBuilder.Append(']');
                        int numSizes = signature.ReadCompressedInteger();
                        for (int i = 0; i < numSizes; i++)
                        {
                            _ = signature.ReadCompressedInteger();
                        }
                        int numLoBounds = signature.ReadCompressedInteger();
                        for (int i = 0; i < numLoBounds; i++)
                        {
                            _ = signature.ReadCompressedSignedInteger();
                        }
                        return;

                    case CorElementType.FnPtr:
                        SignatureHeader fnPtrHeader = signature.ReadSignatureHeader();
                        int cArgs = signature.ReadCompressedInteger();
                        AddTypeString(target, stringBuilder, ref signature, typeInstantiation, methodInstantiation, metadata);
                        stringBuilder.Append(" (");
                        for (uint i = 0; i < cArgs; i++)
                        {
                            AddTypeString(target, stringBuilder, ref signature, typeInstantiation, methodInstantiation, metadata);
                            if (i != cArgs - 1)
                                stringBuilder.Append(", ");
                        }
                        if (fnPtrHeader.CallingConvention == SignatureCallingConvention.VarArgs)
                        {
                            if (cArgs > 0)
                                stringBuilder.Append(", ");
                            stringBuilder.Append("...");
                        }
                        stringBuilder.Append(')');
                        return;

                    case CorElementType.CModOpt:
                    case CorElementType.CModReqd:
                        _ = signature.ReadTypeHandle();
                        break;

                    case CorElementType.CModInternal:
                        _ = signature.ReadByte();
                        _ = signature.ReadBytes(target.PointerSize);
                        break;

                    default:
                        stringBuilder.Append("**UNKNOWN TYPE**");
                        return;
                }
            }
        }

        private static void AddType(Target target, StringBuilder stringBuilder, TypeHandle typeHandle)
        {
            IRuntimeTypeSystem runtimeTypeSystem = target.Contracts.RuntimeTypeSystem;

            if (typeHandle.IsNull)
                stringBuilder.Append("**UNKNOWN TYPE**");
            CorElementType corElementType = runtimeTypeSystem.GetSignatureCorElementType(typeHandle);
            if (corElementType == CorElementType.ValueType && runtimeTypeSystem.HasTypeParam(typeHandle))
            {
                typeHandle = runtimeTypeSystem.GetTypeParam(typeHandle);
            }

            switch (corElementType)
            {
                case CorElementType.Void: stringBuilder.Append("Void"); return;
                case CorElementType.Boolean: stringBuilder.Append("Boolean"); return;
                case CorElementType.I: stringBuilder.Append("IntPtr"); return;
                case CorElementType.U: stringBuilder.Append("UIntPtr"); return;
                case CorElementType.I1: stringBuilder.Append("SByte"); return;
                case CorElementType.U1: stringBuilder.Append("Byte"); return;
                case CorElementType.I2: stringBuilder.Append("Int16"); return;
                case CorElementType.U2: stringBuilder.Append("UInt16"); return;
                case CorElementType.I4: stringBuilder.Append("Int32"); return;
                case CorElementType.U4: stringBuilder.Append("UInt32"); return;
                case CorElementType.I8: stringBuilder.Append("Int64"); return;
                case CorElementType.U8: stringBuilder.Append("UInt64"); return;
                case CorElementType.R4: stringBuilder.Append("Single"); return;
                case CorElementType.R8: stringBuilder.Append("Double"); return;
                case CorElementType.Char: stringBuilder.Append("Char"); return;

                case CorElementType.Object: stringBuilder.Append("System.Object"); return;
                case CorElementType.String: stringBuilder.Append("System.String"); return;

                case CorElementType.ValueType:
                case CorElementType.Class:
                    uint typeDefToken = runtimeTypeSystem.GetTypeDefToken(typeHandle);
                    TargetPointer modulePointer = target.Contracts.RuntimeTypeSystem.GetModule(typeHandle);
                    Contracts.ModuleHandle module = target.Contracts.Loader.GetModuleHandleFromModulePtr(modulePointer);
                    MetadataReader metadata = target.Contracts.EcmaMetadata.GetMetadata(module)!;
                    TypeDefinition typeDef = metadata.GetTypeDefinition((TypeDefinitionHandle)MetadataTokens.Handle((int)typeDefToken));
                    string _namespace = metadata.GetString(typeDef.Namespace);
                    string name = metadata.GetString(typeDef.Name);

                    if (!string.IsNullOrEmpty(_namespace))
                    {
                        stringBuilder.Append(_namespace);
                        stringBuilder.Append('.');
                    }
                    stringBuilder.Append(name);

                    ReadOnlySpan<TypeHandle> instantiation = runtimeTypeSystem.GetInstantiation(typeHandle);
                    if (instantiation.Length > 0)
                    {
                        stringBuilder.Append('<');
                        for (int i = 0; i < instantiation.Length; i++)
                        {
                            if (i != 0)
                                stringBuilder.Append(',');
                            AddType(target, stringBuilder, instantiation[i]);
                        }
                        stringBuilder.Append('>');
                    }

                    return;

                case CorElementType.TypedByRef:
                    stringBuilder.Append("TypedReference");
                    return;

                case CorElementType.Byref:
                    AddType(target, stringBuilder, runtimeTypeSystem.GetTypeParam(typeHandle));
                    stringBuilder.Append(" ByRef");
                    return;

                case CorElementType.Ptr:
                    AddType(target, stringBuilder, runtimeTypeSystem.GetTypeParam(typeHandle));
                    stringBuilder.Append('*');
                    return;

                case CorElementType.MVar:
                case CorElementType.Var:
                    runtimeTypeSystem.IsGenericVariable(typeHandle, out TargetPointer genericVariableModulePointer, out uint typeVarToken);
                    Contracts.ModuleHandle genericVariableModule = target.Contracts.Loader.GetModuleHandleFromModulePtr(genericVariableModulePointer);
                    MetadataReader generatedVariableMetadata = target.Contracts.EcmaMetadata.GetMetadata(genericVariableModule)!;
                    GenericParameter genericVariable = generatedVariableMetadata.GetGenericParameter((GenericParameterHandle)MetadataTokens.Handle((int)typeVarToken));
                    stringBuilder.Append(generatedVariableMetadata.GetString(genericVariable.Name));
                    return;

                case CorElementType.SzArray:
                    AddType(target, stringBuilder, runtimeTypeSystem.GetTypeParam(typeHandle));
                    stringBuilder.Append("[]");
                    return;

                case CorElementType.Array:
                    AddType(target, stringBuilder, runtimeTypeSystem.GetTypeParam(typeHandle));
                    stringBuilder.Append('[');

                    runtimeTypeSystem.IsArray(typeHandle, out uint rank);
                    for (uint i = 1; i < rank; i++)
                    {
                        stringBuilder.Append(',');
                    }
                    stringBuilder.Append(']');
                    return;

                case CorElementType.FnPtr:
                    runtimeTypeSystem.IsFunctionPointer(typeHandle, out ReadOnlySpan<TypeHandle> retAndArgTypes, out byte callConv);
                    SignatureHeader header = new SignatureHeader(callConv);
                    AddType(target, stringBuilder, retAndArgTypes[0]);
                    stringBuilder.Append(" (");
                    for (int i = 1; i < retAndArgTypes.Length; i++)
                    {
                        AddType(target, stringBuilder, retAndArgTypes[i]);
                        if (i != retAndArgTypes.Length - 1)
                            stringBuilder.Append(", ");
                    }
                    if (header.CallingConvention == SignatureCallingConvention.VarArgs)
                    {
                        if (retAndArgTypes.Length > 1)
                            stringBuilder.Append(", ");
                        stringBuilder.Append("...");
                    }
                    stringBuilder.Append(')');
                    return;

                default:
                    stringBuilder.Append("**UNKNOWN TYPE**");
                    return;
            }
        }
    }
}