File: Contracts\CallingConvention\SignatureTypeInfoProvider.cs
Web Access
Project: src\runtime\src\native\managed\cdac\Microsoft.Diagnostics.DataContractReader.Contracts\Microsoft.Diagnostics.DataContractReader.Contracts.csproj (Microsoft.Diagnostics.DataContractReader.Contracts)
// 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.Collections.Immutable;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using Microsoft.Diagnostics.DataContractReader.Contracts;
using Microsoft.Diagnostics.DataContractReader.SignatureHelpers;

namespace Microsoft.Diagnostics.DataContractReader.Contracts.CallingConventionHelpers;

internal readonly record struct SignatureTypeContext(
    MethodDescHandle? Method,
    SignatureTypeInfo OwningType);

internal sealed class SignatureTypeInfoProvider
    : IRuntimeSignatureTypeProvider<SignatureTypeInfo, SignatureTypeContext>
{
    private readonly Target _target;
    private readonly ModuleHandle _moduleHandle;
    private readonly ILoader _loader;
    private readonly IRuntimeTypeSystem _rts;

    internal SignatureTypeInfoProvider(Target target, ModuleHandle moduleHandle)
    {
        _target = target;
        _moduleHandle = moduleHandle;
        _loader = target.Contracts.Loader;
        _rts = target.Contracts.RuntimeTypeSystem;
    }

    public SignatureTypeInfo GetArrayType(SignatureTypeInfo elementType, ArrayShape shape)
        => CreateConstructedType(elementType, CorElementType.Array, shape.Rank);

    public SignatureTypeInfo GetByReferenceType(SignatureTypeInfo elementType)
        => CreateConstructedType(elementType, CorElementType.Byref, rank: 0);

    public SignatureTypeInfo GetFunctionPointerType(MethodSignature<SignatureTypeInfo> signature)
        => new(CorElementType.FnPtr, _rts.GetPrimitiveType(CorElementType.I));

    public SignatureTypeInfo GetGenericInstantiation(
        SignatureTypeInfo genericType,
        ImmutableArray<SignatureTypeInfo> typeArguments)
    {
        ITypeHandle? exactType = null;
        if (genericType.ExactTypeHandle is not null)
        {
            ImmutableArray<ITypeHandle?> exactArguments = typeArguments
                .Select(static typeArgument => typeArgument.ExactTypeHandle)
                .ToImmutableArray();
            exactType = _rts.GetConstructedType(
                genericType.ExactTypeHandle,
                CorElementType.GenericInst,
                rank: 0,
                exactArguments);
        }

        return new SignatureTypeInfo(
            genericType.ElementType,
            exactType,
            genericType.ExactTypeHandle ?? genericType.GenericTypeDefinition,
            typeArguments);
    }

    public SignatureTypeInfo GetGenericMethodParameter(SignatureTypeContext context, int index)
    {
        if (context.Method is not MethodDescHandle method)
        {
            return new SignatureTypeInfo(CorElementType.MVar, exactTypeHandle: null);
        }

        ITypeHandle exactType = _rts.GetGenericMethodInstantiation(method)[index];
        return FromExactType(exactType);
    }

    public SignatureTypeInfo GetGenericTypeParameter(SignatureTypeContext context, int index)
    {
        if ((uint)index < (uint)context.OwningType.TypeArguments.Length)
        {
            return context.OwningType.TypeArguments[index];
        }

        if (context.OwningType.ExactTypeHandle is ITypeHandle exactOwningType)
        {
            ITypeHandle exactType = _rts.GetInstantiation(exactOwningType)[index];
            return FromExactType(exactType);
        }

        return new SignatureTypeInfo(CorElementType.Var, exactTypeHandle: null);
    }

    public SignatureTypeInfo GetModifiedType(
        SignatureTypeInfo modifier,
        SignatureTypeInfo unmodifiedType,
        bool isRequired)
        => unmodifiedType;

    public SignatureTypeInfo GetPinnedType(SignatureTypeInfo elementType)
        => elementType;

    public SignatureTypeInfo GetPointerType(SignatureTypeInfo elementType)
        => CreateConstructedType(elementType, CorElementType.Ptr, rank: 0);

    public SignatureTypeInfo GetPrimitiveType(PrimitiveTypeCode typeCode)
    {
        CorElementType elementType = (CorElementType)typeCode;
        return new SignatureTypeInfo(elementType, _rts.GetPrimitiveType(elementType));
    }

    public SignatureTypeInfo GetSZArrayType(SignatureTypeInfo elementType)
        => CreateConstructedType(elementType, CorElementType.SzArray, rank: 1);

    public SignatureTypeInfo GetTypeFromDefinition(
        MetadataReader reader,
        TypeDefinitionHandle handle,
        byte rawTypeKind)
        => GetTypeFromToken(
            MetadataTokens.GetToken(handle),
            ModuleLookupMapKind.TypeDefToMethodTable,
            rawTypeKind);

    public SignatureTypeInfo GetTypeFromReference(
        MetadataReader reader,
        TypeReferenceHandle handle,
        byte rawTypeKind)
        => GetTypeFromToken(
            MetadataTokens.GetToken(handle),
            ModuleLookupMapKind.TypeRefToMethodTable,
            rawTypeKind);

    public SignatureTypeInfo GetTypeFromSpecification(
        MetadataReader reader,
        SignatureTypeContext context,
        TypeSpecificationHandle handle,
        byte rawTypeKind)
    {
        BlobReader blobReader = reader.GetBlobReader(reader.GetTypeSpecification(handle).Signature);
        RuntimeSignatureDecoder<SignatureTypeInfo, SignatureTypeContext> decoder =
            new(this, _target, reader, context);
        return decoder.DecodeType(ref blobReader, allowTypeSpecifications: true);
    }

    public SignatureTypeInfo GetInternalType(TargetPointer typeHandlePointer)
        => typeHandlePointer == TargetPointer.Null
            ? new SignatureTypeInfo(CorElementType.Internal, exactTypeHandle: null)
            : FromExactType(_rts.GetTypeHandle(typeHandlePointer));

    public SignatureTypeInfo GetInternalModifiedType(
        TargetPointer typeHandlePointer,
        SignatureTypeInfo unmodifiedType,
        bool isRequired)
        => unmodifiedType;

    internal SignatureTypeInfo FromExactType(ITypeHandle exactType)
    {
        ReadOnlySpan<ITypeHandle> exactArguments = _rts.GetInstantiation(exactType);
        ImmutableArray<SignatureTypeInfo>.Builder typeArguments =
            ImmutableArray.CreateBuilder<SignatureTypeInfo>(exactArguments.Length);
        for (int i = 0; i < exactArguments.Length; i++)
        {
            typeArguments.Add(FromExactType(exactArguments[i]));
        }

        return new SignatureTypeInfo(
            _rts.GetSignatureCorElementType(exactType),
            exactType,
            genericTypeDefinition: null,
            typeArguments.MoveToImmutable());
    }

    private SignatureTypeInfo CreateConstructedType(
        SignatureTypeInfo elementType,
        CorElementType constructedElementType,
        int rank)
    {
        ITypeHandle? exactType = elementType.ExactTypeHandle is null
            ? null
            : _rts.GetConstructedType(elementType.ExactTypeHandle, constructedElementType, rank, []);
        return new SignatureTypeInfo(constructedElementType, exactType);
    }

    private SignatureTypeInfo GetTypeFromToken(int token, ModuleLookupMapKind lookupMapKind, byte rawTypeKind)
    {
        TargetPointer typeHandlePointer = _loader.GetModuleLookupMapElement(
            _moduleHandle,
            lookupMapKind,
            (uint)token,
            out _);
        ITypeHandle? exactType = typeHandlePointer == TargetPointer.Null
            ? null
            : _rts.GetTypeHandle(typeHandlePointer);

        CorElementType elementType = rawTypeKind switch
        {
            (byte)SignatureTypeKind.Class => CorElementType.Class,
            (byte)SignatureTypeKind.ValueType => CorElementType.ValueType,
            _ when exactType is not null => _rts.GetSignatureCorElementType(exactType),
            _ => default,
        };

        return new SignatureTypeInfo(elementType, exactType);
    }
}