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.CodeAnalysis;
using System.Reflection;
using System.Runtime.InteropServices;

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.TypeInfos;
using System.Reflection.Runtime.TypeInfos.NativeFormat;

using Internal.Metadata.NativeFormat;

namespace System.Reflection
{
    internal sealed partial class RuntimeCustomAttributeData : CustomAttributeData
    {
        internal static IList<CustomAttributeData> GetCustomAttributes(
            MetadataReader? reader, CustomAttributeHandleCollection customAttributeHandles)
        {
            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();
        }
    }

    public readonly partial struct CustomAttributeTypedArgument
    {
        [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode",
            Justification = "The compiler ensures we have array types referenced from custom attribute blobs.")]
        internal static object? ConvertToRuntimeValue(CustomAttributeEncodedArgument encodedArg)
        {
            CustomAttributeEncoding encodedType = encodedArg.CustomAttributeType.EncodedType;

            if (encodedType == CustomAttributeEncoding.Undefined)
                throw new ArgumentException(null, nameof(encodedArg));

            if (encodedType == CustomAttributeEncoding.Enum)
            {
                return Enum.ToObject(
                    encodedArg.CustomAttributeType.EnumType!,
                    EncodedValueToRawValue(encodedArg.PrimitiveValue, encodedArg.CustomAttributeType.EncodedEnumType));
            }

            if (encodedType == CustomAttributeEncoding.String)
                return encodedArg.StringValue;

            if (encodedType == CustomAttributeEncoding.Type)
                return encodedArg.TypeValue;

            if (encodedType == CustomAttributeEncoding.Array)
            {
                if (encodedArg.ArrayValue is null)
                    return null;

                CustomAttributeEncoding encodedElementType = encodedArg.CustomAttributeType.EncodedArrayType;
                Type elementType = encodedElementType == CustomAttributeEncoding.Enum ?
                    encodedArg.CustomAttributeType.EnumType! :
                    CustomAttributeEncodingToType(encodedElementType);
                Array array = Array.CreateInstanceFromArrayType(elementType.MakeArrayType(), encodedArg.ArrayValue.Length);

                for (int i = 0; i < encodedArg.ArrayValue.Length; i++)
                {
                    array.SetValue(ConvertToRuntimeValue(encodedArg.ArrayValue[i]), i);
                }

                return array;
            }

            return EncodedValueToRawValue(encodedArg.PrimitiveValue, encodedType);
        }
    }

    internal sealed partial class CustomAttributeEncodedArgument
    {
        internal static object? ParseValue(MetadataReader reader, Handle value, RuntimeType argumentType)
        {
            try
            {
                CustomAttributeDataParser parser = new CustomAttributeDataParser(default, reader);
                CustomAttributeEncodedArgument encodedArgument = parser.ParseValue(value, new CustomAttributeType(argumentType));
                return CustomAttributeTypedArgument.ConvertToRuntimeValue(encodedArgument);
            }
            catch (Exception ex) when (ex is not OutOfMemoryException)
            {
                throw new CustomAttributeFormatException(ex.Message, ex);
            }
        }

        /// <summary>
        /// Used to parse NativeFormat custom attribute data.
        /// </summary>
        private readonly struct CustomAttributeDataParser
        {
            private readonly CustomAttribute _attribute;
            private readonly MetadataReader _reader;

            public CustomAttributeDataParser(CustomAttribute attribute, MetadataReader reader)
            {
                _attribute = attribute;
                _reader = reader;
            }

            public CustomAttribute Attribute => _attribute;

            public bool ValidateProlog() => _reader is not null;

            public CustomAttributeEncodedArgument ParseValue(Handle value, CustomAttributeType type)
            {
                CustomAttributeType attributeType = type.EncodedType is CustomAttributeEncoding.Object
                    ? GetCustomAttributeType(value)
                    : type;
                if (value.HandleType is HandleType.ConstantEnumValue)
                {
                    value = value.ToConstantEnumValueHandle(_reader).GetConstantEnumValue(_reader).Value;
                }

                CustomAttributeEncodedArgument argument = new CustomAttributeEncodedArgument(attributeType);

                switch (value.HandleType)
                {
                    case HandleType.ConstantBooleanValue:
                        argument.PrimitiveValue = new PrimitiveValue() { Byte4 = value.ToConstantBooleanValueHandle(_reader).GetConstantBooleanValue(_reader).Value ? 1 : 0 };
                        break;
                    case HandleType.ConstantCharValue:
                        argument.PrimitiveValue = new PrimitiveValue() { Byte4 = value.ToConstantCharValueHandle(_reader).GetConstantCharValue(_reader).Value };
                        break;
                    case HandleType.ConstantByteValue:
                        argument.PrimitiveValue = new PrimitiveValue() { Byte4 = value.ToConstantByteValueHandle(_reader).GetConstantByteValue(_reader).Value };
                        break;
                    case HandleType.ConstantSByteValue:
                        argument.PrimitiveValue = new PrimitiveValue() { Byte4 = (byte)value.ToConstantSByteValueHandle(_reader).GetConstantSByteValue(_reader).Value };
                        break;
                    case HandleType.ConstantInt16Value:
                        argument.PrimitiveValue = new PrimitiveValue() { Byte4 = (ushort)value.ToConstantInt16ValueHandle(_reader).GetConstantInt16Value(_reader).Value };
                        break;
                    case HandleType.ConstantUInt16Value:
                        argument.PrimitiveValue = new PrimitiveValue() { Byte4 = value.ToConstantUInt16ValueHandle(_reader).GetConstantUInt16Value(_reader).Value };
                        break;
                    case HandleType.ConstantInt32Value:
                        argument.PrimitiveValue = new PrimitiveValue() { Byte4 = value.ToConstantInt32ValueHandle(_reader).GetConstantInt32Value(_reader).Value };
                        break;
                    case HandleType.ConstantUInt32Value:
                        argument.PrimitiveValue = new PrimitiveValue() { Byte4 = (int)value.ToConstantUInt32ValueHandle(_reader).GetConstantUInt32Value(_reader).Value };
                        break;
                    case HandleType.ConstantInt64Value:
                        argument.PrimitiveValue = new PrimitiveValue() { Byte8 = value.ToConstantInt64ValueHandle(_reader).GetConstantInt64Value(_reader).Value };
                        break;
                    case HandleType.ConstantUInt64Value:
                        argument.PrimitiveValue = new PrimitiveValue() { Byte8 = (long)value.ToConstantUInt64ValueHandle(_reader).GetConstantUInt64Value(_reader).Value };
                        break;
                    case HandleType.ConstantSingleValue:
                        argument.PrimitiveValue = new PrimitiveValue() { Byte4 = BitConverter.SingleToInt32Bits(value.ToConstantSingleValueHandle(_reader).GetConstantSingleValue(_reader).Value) };
                        break;
                    case HandleType.ConstantDoubleValue:
                        argument.PrimitiveValue = new PrimitiveValue() { Byte8 = BitConverter.DoubleToInt64Bits(value.ToConstantDoubleValueHandle(_reader).GetConstantDoubleValue(_reader).Value) };
                        break;
                    case HandleType.ConstantStringValue:
                        argument.StringValue = value.ToConstantStringValueHandle(_reader).GetConstantStringValue(_reader).Value;
                        break;
                    case HandleType.TypeDefinition:
                    case HandleType.TypeReference:
                    case HandleType.TypeSpecification:
                        argument.TypeValue = value.Resolve(_reader, default).ToType();
                        break;
                    case HandleType.ConstantReferenceValue:
                        break;
                    default:
                        ParseArrayValue(value, attributeType, argument);
                        break;
                }

                return argument;
            }

            private void ParseArrayValue(
                Handle value,
                CustomAttributeType arrayType,
                CustomAttributeEncodedArgument argument)
            {
                if (value.HandleType is HandleType.ConstantEnumArray)
                {
                    value = value.ToConstantEnumArrayHandle(_reader).GetConstantEnumArray(_reader).Value;
                }

                if (value.HandleType is HandleType.ConstantReferenceValue)
                    return;

                CustomAttributeType elementType = new CustomAttributeType(
                    arrayType.EncodedArrayType,
                    CustomAttributeEncoding.Undefined,
                    arrayType.EncodedEnumType,
                    arrayType.EnumType);
                argument.ArrayValue = value.HandleType switch
                {
                    HandleType.ConstantBooleanArray => ToEncodedArguments(value.ToConstantBooleanArrayHandle(_reader).GetConstantBooleanArray(_reader).Value, elementType),
                    HandleType.ConstantCharArray => ToEncodedArguments(value.ToConstantCharArrayHandle(_reader).GetConstantCharArray(_reader).Value, elementType),
                    HandleType.ConstantByteArray => ToEncodedArguments(value.ToConstantByteArrayHandle(_reader).GetConstantByteArray(_reader).Value, elementType),
                    HandleType.ConstantSByteArray => ToEncodedArguments(value.ToConstantSByteArrayHandle(_reader).GetConstantSByteArray(_reader).Value, elementType),
                    HandleType.ConstantInt16Array => ToEncodedArguments(value.ToConstantInt16ArrayHandle(_reader).GetConstantInt16Array(_reader).Value, elementType),
                    HandleType.ConstantUInt16Array => ToEncodedArguments(value.ToConstantUInt16ArrayHandle(_reader).GetConstantUInt16Array(_reader).Value, elementType),
                    HandleType.ConstantInt32Array => ToEncodedArguments(value.ToConstantInt32ArrayHandle(_reader).GetConstantInt32Array(_reader).Value, elementType),
                    HandleType.ConstantUInt32Array => ToEncodedArguments(value.ToConstantUInt32ArrayHandle(_reader).GetConstantUInt32Array(_reader).Value, elementType),
                    HandleType.ConstantInt64Array => ToEncodedArguments(value.ToConstantInt64ArrayHandle(_reader).GetConstantInt64Array(_reader).Value, elementType),
                    HandleType.ConstantUInt64Array => ToEncodedArguments(value.ToConstantUInt64ArrayHandle(_reader).GetConstantUInt64Array(_reader).Value, elementType),
                    HandleType.ConstantSingleArray => ToEncodedArguments(value.ToConstantSingleArrayHandle(_reader).GetConstantSingleArray(_reader).Value, elementType),
                    HandleType.ConstantDoubleArray => ToEncodedArguments(value.ToConstantDoubleArrayHandle(_reader).GetConstantDoubleArray(_reader).Value, elementType),
                    HandleType.ConstantStringArray => ToEncodedArguments(value.ToConstantStringArrayHandle(_reader).GetConstantStringArray(_reader).Value, elementType),
                    HandleType.ConstantHandleArray => ToEncodedArguments(value.ToConstantHandleArrayHandle(_reader).GetConstantHandleArray(_reader).Value, elementType),
                    _ => throw new BadImageFormatException()
                };
            }

            private CustomAttributeEncodedArgument[] ToEncodedArguments(HandleCollection values, CustomAttributeType elementType)
            {
                CustomAttributeEncodedArgument[] result = new CustomAttributeEncodedArgument[values.Count];
                int index = 0;
                foreach (Handle value in values)
                {
                    result[index++] = ParseValue(value, elementType);
                }
                return result;
            }

            private static CustomAttributeEncodedArgument[] ToEncodedArguments(BooleanCollection values, CustomAttributeType elementType)
            {
                CustomAttributeEncodedArgument[] result = new CustomAttributeEncodedArgument[values.Count];
                int index = 0;
                foreach (bool value in values)
                {
                    result[index++] = CreatePrimitiveArgument(elementType, new PrimitiveValue() { Byte4 = value ? 1 : 0 });
                }
                return result;
            }

            private static CustomAttributeEncodedArgument[] ToEncodedArguments(CharCollection values, CustomAttributeType elementType)
            {
                CustomAttributeEncodedArgument[] result = new CustomAttributeEncodedArgument[values.Count];
                int index = 0;
                foreach (char value in values)
                {
                    result[index++] = CreatePrimitiveArgument(elementType, new PrimitiveValue() { Byte4 = value });
                }
                return result;
            }

            private static CustomAttributeEncodedArgument[] ToEncodedArguments(ByteCollection values, CustomAttributeType elementType)
            {
                CustomAttributeEncodedArgument[] result = new CustomAttributeEncodedArgument[values.Count];
                int index = 0;
                foreach (byte value in values)
                {
                    result[index++] = CreatePrimitiveArgument(elementType, new PrimitiveValue() { Byte4 = value });
                }
                return result;
            }

            private static CustomAttributeEncodedArgument[] ToEncodedArguments(SByteCollection values, CustomAttributeType elementType)
            {
                CustomAttributeEncodedArgument[] result = new CustomAttributeEncodedArgument[values.Count];
                int index = 0;
                foreach (sbyte value in values)
                {
                    result[index++] = CreatePrimitiveArgument(elementType, new PrimitiveValue() { Byte4 = (byte)value });
                }
                return result;
            }

            private static CustomAttributeEncodedArgument[] ToEncodedArguments(Int16Collection values, CustomAttributeType elementType)
            {
                CustomAttributeEncodedArgument[] result = new CustomAttributeEncodedArgument[values.Count];
                int index = 0;
                foreach (short value in values)
                {
                    result[index++] = CreatePrimitiveArgument(elementType, new PrimitiveValue() { Byte4 = (ushort)value });
                }
                return result;
            }

            private static CustomAttributeEncodedArgument[] ToEncodedArguments(UInt16Collection values, CustomAttributeType elementType)
            {
                CustomAttributeEncodedArgument[] result = new CustomAttributeEncodedArgument[values.Count];
                int index = 0;
                foreach (ushort value in values)
                {
                    result[index++] = CreatePrimitiveArgument(elementType, new PrimitiveValue() { Byte4 = value });
                }
                return result;
            }

            private static CustomAttributeEncodedArgument[] ToEncodedArguments(Int32Collection values, CustomAttributeType elementType)
            {
                CustomAttributeEncodedArgument[] result = new CustomAttributeEncodedArgument[values.Count];
                int index = 0;
                foreach (int value in values)
                {
                    result[index++] = CreatePrimitiveArgument(elementType, new PrimitiveValue() { Byte4 = value });
                }
                return result;
            }

            private static CustomAttributeEncodedArgument[] ToEncodedArguments(UInt32Collection values, CustomAttributeType elementType)
            {
                CustomAttributeEncodedArgument[] result = new CustomAttributeEncodedArgument[values.Count];
                int index = 0;
                foreach (uint value in values)
                {
                    result[index++] = CreatePrimitiveArgument(elementType, new PrimitiveValue() { Byte4 = (int)value });
                }
                return result;
            }

            private static CustomAttributeEncodedArgument[] ToEncodedArguments(Int64Collection values, CustomAttributeType elementType)
            {
                CustomAttributeEncodedArgument[] result = new CustomAttributeEncodedArgument[values.Count];
                int index = 0;
                foreach (long value in values)
                {
                    result[index++] = CreatePrimitiveArgument(elementType, new PrimitiveValue() { Byte8 = value });
                }
                return result;
            }

            private static CustomAttributeEncodedArgument[] ToEncodedArguments(UInt64Collection values, CustomAttributeType elementType)
            {
                CustomAttributeEncodedArgument[] result = new CustomAttributeEncodedArgument[values.Count];
                int index = 0;
                foreach (ulong value in values)
                {
                    result[index++] = CreatePrimitiveArgument(elementType, new PrimitiveValue() { Byte8 = (long)value });
                }
                return result;
            }

            private static CustomAttributeEncodedArgument[] ToEncodedArguments(SingleCollection values, CustomAttributeType elementType)
            {
                CustomAttributeEncodedArgument[] result = new CustomAttributeEncodedArgument[values.Count];
                int index = 0;
                foreach (float value in values)
                {
                    result[index++] = CreatePrimitiveArgument(elementType, new PrimitiveValue() { Byte4 = BitConverter.SingleToInt32Bits(value) });
                }
                return result;
            }

            private static CustomAttributeEncodedArgument[] ToEncodedArguments(DoubleCollection values, CustomAttributeType elementType)
            {
                CustomAttributeEncodedArgument[] result = new CustomAttributeEncodedArgument[values.Count];
                int index = 0;
                foreach (double value in values)
                {
                    result[index++] = CreatePrimitiveArgument(elementType, new PrimitiveValue() { Byte8 = BitConverter.DoubleToInt64Bits(value) });
                }
                return result;
            }

            private static CustomAttributeEncodedArgument CreatePrimitiveArgument(CustomAttributeType type, PrimitiveValue value)
                => new CustomAttributeEncodedArgument(type) { PrimitiveValue = value };

            private CustomAttributeType GetCustomAttributeType(Handle value)
            {
                return value.HandleType switch
                {
                    HandleType.ConstantBooleanValue => CreateType(CustomAttributeEncoding.Boolean),
                    HandleType.ConstantCharValue => CreateType(CustomAttributeEncoding.Char),
                    HandleType.ConstantByteValue => CreateType(CustomAttributeEncoding.Byte),
                    HandleType.ConstantSByteValue => CreateType(CustomAttributeEncoding.SByte),
                    HandleType.ConstantInt16Value => CreateType(CustomAttributeEncoding.Int16),
                    HandleType.ConstantUInt16Value => CreateType(CustomAttributeEncoding.UInt16),
                    HandleType.ConstantInt32Value => CreateType(CustomAttributeEncoding.Int32),
                    HandleType.ConstantUInt32Value => CreateType(CustomAttributeEncoding.UInt32),
                    HandleType.ConstantInt64Value => CreateType(CustomAttributeEncoding.Int64),
                    HandleType.ConstantUInt64Value => CreateType(CustomAttributeEncoding.UInt64),
                    HandleType.ConstantSingleValue => CreateType(CustomAttributeEncoding.Float),
                    HandleType.ConstantDoubleValue => CreateType(CustomAttributeEncoding.Double),
                    // A null object argument is reported as a null string to match CoreCLR.
                    HandleType.ConstantStringValue or HandleType.ConstantReferenceValue => CreateType(CustomAttributeEncoding.String),
                    HandleType.TypeDefinition or HandleType.TypeReference or HandleType.TypeSpecification => CreateType(CustomAttributeEncoding.Type),
                    HandleType.ConstantEnumValue => CreateEnumType(
                        value.ToConstantEnumValueHandle(_reader).GetConstantEnumValue(_reader).Type,
                        isArray: false),
                    HandleType.ConstantBooleanArray => CreateArrayType(CustomAttributeEncoding.Boolean),
                    HandleType.ConstantCharArray => CreateArrayType(CustomAttributeEncoding.Char),
                    HandleType.ConstantByteArray => CreateArrayType(CustomAttributeEncoding.Byte),
                    HandleType.ConstantSByteArray => CreateArrayType(CustomAttributeEncoding.SByte),
                    HandleType.ConstantInt16Array => CreateArrayType(CustomAttributeEncoding.Int16),
                    HandleType.ConstantUInt16Array => CreateArrayType(CustomAttributeEncoding.UInt16),
                    HandleType.ConstantInt32Array => CreateArrayType(CustomAttributeEncoding.Int32),
                    HandleType.ConstantUInt32Array => CreateArrayType(CustomAttributeEncoding.UInt32),
                    HandleType.ConstantInt64Array => CreateArrayType(CustomAttributeEncoding.Int64),
                    HandleType.ConstantUInt64Array => CreateArrayType(CustomAttributeEncoding.UInt64),
                    HandleType.ConstantSingleArray => CreateArrayType(CustomAttributeEncoding.Float),
                    HandleType.ConstantDoubleArray => CreateArrayType(CustomAttributeEncoding.Double),
                    HandleType.ConstantStringArray => CreateArrayType(CustomAttributeEncoding.String),
                    HandleType.ConstantHandleArray => CreateArrayType(CustomAttributeEncoding.Object),
                    HandleType.ConstantEnumArray => CreateEnumType(
                        value.ToConstantEnumArrayHandle(_reader).GetConstantEnumArray(_reader).ElementType,
                        isArray: true),
                    _ => throw new BadImageFormatException()
                };
            }

            private CustomAttributeType CreateEnumType(Handle enumTypeHandle, bool isArray)
            {
                RuntimeType enumType = (RuntimeType)enumTypeHandle.Resolve(_reader, default).ToType();
                if (!enumType.IsEnum)
                {
                    throw new BadImageFormatException();
                }

                CustomAttributeEncoding underlyingType =
                    RuntimeCustomAttributeData.TypeToCustomAttributeEncoding((RuntimeType)enumType.GetEnumUnderlyingType());
                return isArray
                    ? new CustomAttributeType(CustomAttributeEncoding.Array, CustomAttributeEncoding.Enum, underlyingType, enumType)
                    : new CustomAttributeType(CustomAttributeEncoding.Enum, CustomAttributeEncoding.Undefined, underlyingType, enumType);
            }

            private static CustomAttributeType CreateType(CustomAttributeEncoding type)
                => new CustomAttributeType(
                    type,
                    CustomAttributeEncoding.Undefined,
                    CustomAttributeEncoding.Undefined,
                    enumType: null);

            private static CustomAttributeType CreateArrayType(CustomAttributeEncoding elementType)
                => new CustomAttributeType(
                    CustomAttributeEncoding.Array,
                    elementType,
                    CustomAttributeEncoding.Undefined,
                    enumType: null);
        }
    }

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

            ListBuilder<object> derivedAttributes = default;
            foreach (CustomAttributeHandle customAttributeHandle in customAttributeHandles)
            {
                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);
        }

        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:MethodParameterDoesntMeetThisParameterRequirements",
            Justification = "Linker guarantees presence of all the property setters and fields which are accessed by any " +
                            "attribute instantiation which is present in the code linker has analyzed.")]
        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2067:MethodParameterDoesntMeetThisParameterRequirements",
            Justification = "Linker guarantees presence of all the constructor parameters which are accessed by any " +
                            "attribute instantiation which is present in the code linker has analyzed.")]
        private static void AddCustomAttributes(
            ref ListBuilder<object> attributes,
            MetadataReader? reader,
            CustomAttributeHandleCollection customAttributeHandles,
            RuntimeType? attributeFilterType,
            bool mustBeInheritable,
            ListBuilder<object> derivedAttributes)
        {
            if (reader is null)
                return;

            foreach (CustomAttributeHandle customAttributeHandle in customAttributeHandles)
            {
                CustomAttribute customAttribute = customAttributeHandle.GetCustomAttribute(reader);

                if (!FilterCustomAttributeRecord(
                    customAttribute,
                    reader,
                    attributeFilterType!,
                    mustBeInheritable,
                    ref derivedAttributes,
                    out RuntimeType attributeType))
                {
                    continue;
                }

                // TODO-NativeAOT: Match CoreCLR's custom attribute constructor visibility checks.
                ConstructorInfo constructor = RuntimeCustomAttributeData.ResolveAttributeConstructor(reader, customAttribute, attributeType);
                ReadOnlySpan<ParameterInfo> constructorParameters = constructor.GetParametersAsSpan();
                int fixedArgumentCount = customAttribute.FixedArguments.Count;
                if (fixedArgumentCount != constructorParameters.Length)
                {
                    throw new CustomAttributeFormatException();
                }

                object?[]? invokeArguments = null;
                if (fixedArgumentCount != 0)
                {
                    invokeArguments = new object?[fixedArgumentCount];
                    int index = 0;
                    foreach (Handle fixedArgument in customAttribute.FixedArguments)
                    {
                        invokeArguments[index] = CustomAttributeEncodedArgument.ParseValue(
                            reader,
                            fixedArgument,
                            (RuntimeType)constructorParameters[index].ParameterType);
                        index++;
                    }
                }

                object attribute = constructor.Invoke(BindingFlags.Default, binder: null, invokeArguments, culture: null);

                foreach (NamedArgumentHandle namedArgumentHandle in customAttribute.NamedArguments)
                {
                    NamedArgument namedArgument = namedArgumentHandle.GetNamedArgument(reader);
                    string name = namedArgument.Name.GetString(reader);
                    bool isProperty = namedArgument.Flags == NamedArgumentMemberKind.Property;
                    RuntimeType type = (RuntimeType)namedArgument.Type.Resolve(reader, default).ToType();
                    object? value = CustomAttributeEncodedArgument.ParseValue(reader, namedArgument.Value, type);

                    try
                    {
                        if (isProperty)
                        {
                            PropertyInfo? property = attributeType.GetProperty(name, type, []) ??
                                throw new CustomAttributeFormatException(SR.Format(SR.RFLCT_InvalidPropFail, name));
                            MethodInfo setMethod = property.GetSetMethod(true)!;

                            // Public properties may have non-public setter methods
                            if (!setMethod.IsPublic)
                            {
                                continue;
                            }

                            property.SetValue(attribute, value, BindingFlags.Default, binder: null, index: null, culture: null);
                        }
                        else
                        {
                            FieldInfo field = attributeType.GetField(name)!;
                            field.SetValue(attribute, value, BindingFlags.Default, Type.DefaultBinder, null);
                        }
                    }
                    catch (Exception e)
                    {
                        throw new CustomAttributeFormatException(
                            SR.Format(isProperty ? SR.RFLCT_InvalidPropFail : SR.RFLCT_InvalidFieldFail, name), e);
                    }
                }

                attributes.Add(attribute);
            }
        }
    }

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