File: System\Reflection\Runtime\General\MetadataReaderExtensions.NativeFormat.cs
Web Access
Project: System.Private.CoreLib.csproj (System.Private.CoreLib)
// 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;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Runtime.Assemblies;
using System.Runtime.CompilerServices;

using Internal.LowLevelLinq;
using Internal.Metadata.NativeFormat;
using Internal.Reflection.Core;

using NativeFormatAssemblyFlags = global::Internal.Metadata.NativeFormat.AssemblyFlags;
using NativeFormatModifiedType = global::Internal.Metadata.NativeFormat.ModifiedType;

namespace System.Reflection.Runtime.General
{
    //
    // Collect various metadata reading tasks for better chunking...
    //
    [CLSCompliant(false)]
    public static class NativeFormatMetadataReaderExtensions
    {
        public static bool StringOrNullEquals(this ConstantStringValueHandle handle, string valueOrNull, MetadataReader reader)
        {
            if (valueOrNull == null)
                return handle.IsNil;
            if (handle.IsNil)
                return false;
            return handle.StringEquals(valueOrNull, reader);
        }

        public static int AsInt(this MethodHandle methodHandle)
        {
            unsafe
            {
                return *(int*)&methodHandle;
            }
        }

        // Return any custom modifiers modifying the passed-in type and whose required/optional bit matches the passed in boolean.
        // Because this is intended to service the GetCustomModifiers() apis, this helper will always return a freshly allocated array
        // safe for returning to api callers.
        internal static Type[] GetCustomModifiers(this Handle handle, MetadataReader reader, TypeContext typeContext, bool optional)
        {
            HandleType handleType = handle.HandleType;
            Debug.Assert(handleType == HandleType.TypeDefinition || handleType == HandleType.TypeReference || handleType == HandleType.TypeSpecification || handleType == HandleType.ModifiedType);
            if (handleType != HandleType.ModifiedType)
                return Array.Empty<Type>();

            ArrayBuilder<Type> customModifiers = default;
            do
            {
                NativeFormatModifiedType modifiedType = handle.ToModifiedTypeHandle(reader).GetModifiedType(reader);
                if (optional == modifiedType.IsOptional)
                {
                    Type customModifier = modifiedType.ModifierType.Resolve(reader, typeContext).ToType();
                    customModifiers.Add(customModifier);
                }

                handle = modifiedType.Type;
                handleType = handle.HandleType;
            }
            while (handleType == HandleType.ModifiedType);
            customModifiers.AsSpan().Reverse();
            return customModifiers.ToArray();
        }

        public static Handle SkipCustomModifiers(this Handle handle, MetadataReader reader)
        {
            HandleType handleType = handle.HandleType;
            Debug.Assert(handleType == HandleType.TypeDefinition || handleType == HandleType.TypeReference || handleType == HandleType.TypeSpecification || handleType == HandleType.ModifiedType);
            if (handleType != HandleType.ModifiedType)
                return handle;

            do
            {
                NativeFormatModifiedType modifiedType = handle.ToModifiedTypeHandle(reader).GetModifiedType(reader);
                handle = modifiedType.Type;
                handleType = handle.HandleType;
            }
            while (handleType == HandleType.ModifiedType);

            return handle;
        }

        public static MethodSignature ParseMethodSignature(this Handle handle, MetadataReader reader)
        {
            return handle.ToMethodSignatureHandle(reader).GetMethodSignature(reader);
        }

        //
        // Used to split methods between DeclaredMethods and DeclaredConstructors.
        //
        public static bool IsConstructor(this MethodHandle methodHandle, MetadataReader reader)
        {
            Method method = methodHandle.GetMethod(reader);
            return IsConstructor(ref method, reader);
        }

        // This is specially designed for a hot path so we make some compromises in the signature:
        //
        //     - "method" is passed by reference even though no side-effects are intended.
        //
        public static bool IsConstructor(ref Method method, MetadataReader reader)
        {
            if ((method.Flags & (MethodAttributes.RTSpecialName | MethodAttributes.SpecialName)) != (MethodAttributes.RTSpecialName | MethodAttributes.SpecialName))
                return false;

            ConstantStringValueHandle nameHandle = method.Name;
            return nameHandle.StringEquals(ConstructorInfo.ConstructorName, reader) || nameHandle.StringEquals(ConstructorInfo.TypeConstructorName, reader);
        }

        public static object? ParseConstantValue(this ConstantHandle handle, MetadataReader reader)
        {
            Constant constant = handle.GetConstant(reader);
            BlobReader blob = new BlobReader(constant.Blob.AsSpan());
            return constant.TypeCode switch
            {
                ConstantTypeCode.Boolean => blob.ReadBoolean(),
                ConstantTypeCode.Char => blob.ReadChar(),
                ConstantTypeCode.Byte => blob.ReadByte(),
                ConstantTypeCode.SByte => blob.ReadSByte(),
                ConstantTypeCode.Int16 => blob.ReadInt16(),
                ConstantTypeCode.UInt16 => blob.ReadUInt16(),
                ConstantTypeCode.Int32 => blob.ReadInt32(),
                ConstantTypeCode.UInt32 => blob.ReadUInt32(),
                ConstantTypeCode.Int64 => blob.ReadInt64(),
                ConstantTypeCode.UInt64 => blob.ReadUInt64(),
                ConstantTypeCode.Single => blob.ReadSingle(),
                ConstantTypeCode.Double => blob.ReadDouble(),
                ConstantTypeCode.String => blob.ReadStringHandle().GetString(reader),
                ConstantTypeCode.NullReference => null,
                _ => throw new BadImageFormatException()
            };
        }

        public static Handle GetAttributeTypeHandle(this CustomAttribute customAttribute,
                                                    MetadataReader reader)
        {
            HandleType constructorHandleType = customAttribute.Constructor.HandleType;

            if (constructorHandleType == HandleType.QualifiedMethod)
                return customAttribute.Constructor.ToQualifiedMethodHandle(reader).GetQualifiedMethod(reader).EnclosingType;
            else if (constructorHandleType == HandleType.MemberReference)
                return customAttribute.Constructor.ToMemberReferenceHandle(reader).GetMemberReference(reader).Parent;
            else
                throw new BadImageFormatException();
        }

        //
        // Lightweight check to see if a custom attribute's is of a well-known type.
        //
        // This check performs without instantiating the Type object and bloating memory usage. On the flip side,
        // it doesn't check on whether the type is defined in a paricular assembly. The desktop CLR typically doesn't
        // check this either so this is useful from a compat perspective as well.
        //
        public static bool IsCustomAttributeOfType(this CustomAttributeHandle customAttributeHandle,
                                                   MetadataReader reader,
                                                   string namespaceName,
                                                   string name)
        {
            Handle typeHandle = customAttributeHandle.GetCustomAttribute(reader).GetAttributeTypeHandle(reader);
            HandleType handleType = typeHandle.HandleType;
            if (handleType == HandleType.TypeDefinition)
            {
                TypeDefinition typeDefinition = typeHandle.ToTypeDefinitionHandle(reader).GetTypeDefinition(reader);
                if (!typeDefinition.Name.StringEquals(name, reader))
                    return false;
                Handle namespaceOrEnclosingType = typeDefinition.NamespaceOrEnclosingType;
                if (namespaceOrEnclosingType.HandleType != HandleType.NamespaceDefinition)
                    return false;
                NamespaceDefinition namespaceDefinition = namespaceOrEnclosingType.ToNamespaceDefinitionHandle(reader).GetNamespaceDefinition(reader);
                return namespaceDefinition.Name.StringOrNullEquals(namespaceName, reader);
            }
            else if (handleType == HandleType.TypeReference)
            {
                TypeReference typeReference = typeHandle.ToTypeReferenceHandle(reader).GetTypeReference(reader);
                if (!typeReference.TypeName.StringEquals(name, reader))
                    return false;
                Handle namespaceOrEnclosingType = typeReference.NamespaceOrEnclosingType;
                if (namespaceOrEnclosingType.HandleType != HandleType.NamespaceReference)
                    return false;
                NamespaceReference namespaceReference = namespaceOrEnclosingType.ToNamespaceReferenceHandle(reader).GetNamespaceReference(reader);
                return namespaceReference.Name.StringOrNullEquals(namespaceName, reader);
            }
            else if (handleType == HandleType.TypeSpecification)
                return false;
            else
                throw new NotSupportedException();
        }


        public static string ToNamespaceName(this NamespaceDefinitionHandle namespaceDefinitionHandle, MetadataReader reader)
        {
            return namespaceDefinitionHandle.GetNamespaceDefinition(reader).Name.GetStringOrNull(reader) ?? string.Empty;
        }

        public static IEnumerable<TypeDefinitionHandle> GetTransitiveTypes(this MetadataReader reader, IEnumerable<TypeDefinitionHandle> typeDefinitionHandles, bool publicOnly)
        {
            foreach (TypeDefinitionHandle typeDefinitionHandle in typeDefinitionHandles)
            {
                TypeDefinition typeDefinition = typeDefinitionHandle.GetTypeDefinition(reader);

                if (publicOnly)
                {
                    TypeAttributes visibility = typeDefinition.Flags & TypeAttributes.VisibilityMask;
                    if (visibility != TypeAttributes.Public && visibility != TypeAttributes.NestedPublic)
                        continue;
                }

                yield return typeDefinitionHandle;

                foreach (TypeDefinitionHandle nestedTypeDefinitionHandle in GetTransitiveTypes(reader, typeDefinition.NestedTypes.AsEnumerable(), publicOnly))
                    yield return nestedTypeDefinitionHandle;
            }
        }

        public static string ToFullyQualifiedTypeName(this NamespaceReferenceHandle namespaceReferenceHandle, string typeName, MetadataReader reader)
        {
            string namespaceName = namespaceReferenceHandle.GetNamespaceReference(reader).Name.GetStringOrNull(reader);
            return namespaceName is null ? typeName : namespaceName + "." + typeName;
        }

        public static IEnumerable<TypeDefinitionHandle> AsEnumerable(this TypeDefinitionHandleCollection collection)
        {
            foreach (TypeDefinitionHandle handle in collection)
                yield return handle;
        }

        public static IEnumerable<TypeDefinitionHandle> AsEnumerable(this TypeDefinitionHandleHashtable hashtable)
        {
            foreach (TypeDefinitionHandle handle in hashtable)
                yield return handle;
        }

        public static byte[] ToArray(this ByteCollection collection)
        {
            int count = collection.Count;
            byte[] result = new byte[count];
            int i = 0;
            foreach (byte element in collection)
            {
                result[i++] = element;
            }
            Debug.Assert(i == count);
            return result;
        }
    }
}