File: System\Reflection\RuntimeCustomAttributeData.NativeAot.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.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.InteropServices;

using System.Reflection.Runtime.EventInfos;
using System.Reflection.Runtime.FieldInfos;
using System.Reflection.Runtime.General;
using System.Reflection.Runtime.MethodInfos;
using System.Reflection.Runtime.MethodInfos.NativeFormat;
using System.Reflection.Runtime.Modules;
using System.Reflection.Runtime.PropertyInfos;
using System.Reflection.Runtime.TypeInfos;
using System.Reflection.Runtime.TypeInfos.NativeFormat;

using Internal.Metadata.NativeFormat;

namespace System.Reflection
{
    internal readonly struct QCustomAttributeList(MetadataReader? reader, CustomAttributeHandleCollection handles)
    {
        public MetadataReader? Reader { get; } = reader;
        public CustomAttributeHandleCollection Handles { get; } = handles;

        public QCustomAttributeList(RuntimeType target)
            : this(target.GetMetadataReader(), target.GetCustomAttributeHandles()) { }

        public QCustomAttributeList(RuntimeFieldInfo target)
            : this(target.GetMetadataReader(), target.GetCustomAttributeHandles()) { }

        public QCustomAttributeList(RuntimeMethodInfo target)
            : this(target.GetMetadataReader(), target.GetCustomAttributeHandles()) { }

        public QCustomAttributeList(RuntimeConstructorInfo target)
            : this(target.GetMetadataReader(), target.GetCustomAttributeHandles()) { }

        public QCustomAttributeList(RuntimeEventInfo target)
            : this(target.GetMetadataReader(), target.GetCustomAttributeHandles()) { }

        public QCustomAttributeList(RuntimePropertyInfo target)
            : this(target.GetMetadataReader(), target.GetCustomAttributeHandles()) { }

        public QCustomAttributeList(RuntimeModule target)
            : this(target.GetMetadataReader(), target.GetCustomAttributeHandles()) { }

        public QCustomAttributeList(RuntimeAssembly target)
            : this(target.GetMetadataReader(), target.GetCustomAttributeHandles()) { }

        public QCustomAttributeList(RuntimeParameterInfo target)
            : this(target.GetMetadataReader(), target.GetCustomAttributeHandles()) { }
    }

    internal sealed partial class RuntimeCustomAttributeData : CustomAttributeData
    {
        private static IList<CustomAttributeData> GetCustomAttributes(QCustomAttributeList attributeList)
        {
            MetadataReader? reader = attributeList.Reader;
            CustomAttributeHandleCollection customAttributeHandles = attributeList.Handles;
            if (reader is null || customAttributeHandles.Count == 0)
                return Array.Empty<CustomAttributeData>();

            CustomAttributeData[] customAttributes = new CustomAttributeData[customAttributeHandles.Count];
            int index = 0;
            foreach (CustomAttributeHandle customAttributeHandle in customAttributeHandles)
                customAttributes[index++] = new RuntimeCustomAttributeData(reader, customAttributeHandle);

            return Array.AsReadOnly(customAttributes);
        }

        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2072:UnrecognizedReflectionPattern",
            Justification = "Metadata generation ensures custom attribute constructors are resolvable.")]
        private static ConstructorInfo ResolveAttributeConstructor(MetadataReader reader, CustomAttribute customAttribute)
        {
            // There is no chance a custom attribute type will be an open type specification so we can safely pass in the empty context here.
            RuntimeType attributeType = (RuntimeType)customAttribute.GetAttributeTypeHandle(reader)
                .Resolve(reader, new TypeContext(null, null))
                .ToType();
            return ResolveAttributeConstructor(reader, customAttribute, attributeType);
        }

        internal static ConstructorInfo ResolveAttributeConstructor(
            MetadataReader reader,
            CustomAttribute customAttribute,
            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]
            RuntimeType attributeType)
        {
            RuntimeTypeInfo attributeTypeInfo = attributeType.GetRuntimeTypeInfo();

            if (customAttribute.Constructor.HandleType == HandleType.QualifiedMethod)
            {
                QualifiedMethod qualifiedMethod = customAttribute.Constructor.ToQualifiedMethodHandle(reader).GetQualifiedMethod(reader);
                MethodHandle methodHandle = qualifiedMethod.Method;
                NativeFormatRuntimeNamedTypeInfo namedAttributeType = (NativeFormatRuntimeNamedTypeInfo)attributeTypeInfo;
                NativeFormatMethodCommon methodCommon = new NativeFormatMethodCommon(methodHandle, namedAttributeType, attributeTypeInfo);
                return attributeTypeInfo.GetConstructorWithSameMetadataDefinitionAs(methodCommon);
            }

            MemberReference memberReference = customAttribute.Constructor.ToMemberReferenceHandle(reader).GetMemberReference(reader);
            MethodSignature signature = memberReference.Signature.ParseMethodSignature(reader);
            HandleCollection signatureParameters = signature.Parameters;
            Type[] expectedParameterTypes = new Type[signatureParameters.Count];
            int index = 0;
            foreach (Handle parameterHandle in signatureParameters)
            {
                expectedParameterTypes[index++] = parameterHandle.Resolve(reader, attributeTypeInfo.TypeContext).ToType();
            }

            foreach (ConstructorInfo candidate in attributeType.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly))
            {
                ReadOnlySpan<ParameterInfo> candidateParameters = candidate.GetParametersAsSpan();
                if (expectedParameterTypes.Length != candidateParameters.Length)
                    continue;

                bool matches = true;
                for (int i = 0; i < expectedParameterTypes.Length; i++)
                {
                    if (!expectedParameterTypes[i].Equals(candidateParameters[i].ParameterType))
                    {
                        matches = false;
                        break;
                    }
                }

                if (matches)
                    return candidate;
            }

            throw new MissingMethodException();
        }
    }

    internal static partial class RuntimeCustomAttribute
    {
        private static bool IsCustomAttributeDefined(
            QCustomAttributeList customAttributes,
            RuntimeType attributeFilterType,
            bool mustBeInheritable = false)
        {
            MetadataReader? reader = customAttributes.Reader;
            if (reader is null)
                return false;

            ListBuilder<object> derivedAttributes = default;
            foreach (CustomAttributeHandle customAttributeHandle in customAttributes.Handles)
            {
                CustomAttribute customAttribute = customAttributeHandle.GetCustomAttribute(reader);
                if (FilterCustomAttributeRecord(
                    customAttribute,
                    reader,
                    attributeFilterType,
                    mustBeInheritable,
                    ref derivedAttributes,
                    out _))
                {
                    return true;
                }
            }

            return false;
        }

        private static bool FilterCustomAttributeRecord(
            CustomAttribute customAttribute,
            MetadataReader reader,
            RuntimeType attributeFilterType,
            bool mustBeInheritable,
            ref ListBuilder<object> derivedAttributes,
            out RuntimeType attributeType)
        {
            Handle attributeTypeHandle = customAttribute.GetAttributeTypeHandle(reader);
            attributeType = (RuntimeType)attributeTypeHandle.Resolve(reader, new TypeContext(null, null)).ToType();

            if (!MatchesTypeFilter(attributeType, attributeFilterType))
                return false;

            return AttributeUsageCheck(attributeType, mustBeInheritable, ref derivedAttributes);
        }
    }

    internal static partial class PseudoCustomAttribute
    {
        private static FieldOffsetAttribute? GetFieldOffsetCustomAttribute(RuntimeFieldInfo field)
        {
            return field.DeclaringType!.IsExplicitLayout ?
                new FieldOffsetAttribute(field.ExplicitLayoutFieldOffsetData) :
                null;
        }
    }
}