File: System\Reflection\Runtime\CustomAttributes\NativeFormat\NativeFormatCustomAttributeData.cs
Web Access
Project: src\src\runtime\src\coreclr\nativeaot\System.Private.CoreLib\src\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.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
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.LowLevelLinq;
using Internal.Metadata.NativeFormat;
using Internal.Reflection.Augments;
using Internal.Reflection.Core;
using Internal.Reflection.Core.Execution;

namespace System.Reflection.Runtime.CustomAttributes.NativeFormat
{
    //
    // The Runtime's implementation of CustomAttributeData for normal metadata-based attributes
    //
    internal sealed class NativeFormatCustomAttributeData : RuntimeCustomAttributeData
    {
        internal NativeFormatCustomAttributeData(MetadataReader reader, CustomAttributeHandle customAttributeHandle)
        {
            _reader = reader;
            _customAttribute = customAttributeHandle.GetCustomAttribute(reader);
        }

        public sealed override Type AttributeType
        {
            get
            {
                Type lazyAttributeType = _lazyAttributeType;
                if (lazyAttributeType == null)
                {
                    lazyAttributeType = _lazyAttributeType = _customAttribute.GetAttributeTypeHandle(_reader).Resolve(_reader, new TypeContext(null, null)).ToType();
                }
                return lazyAttributeType;
            }
        }

        public sealed override ConstructorInfo Constructor
        {
            [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2072:UnrecognizedReflectionPattern",
                Justification = "Metadata generation ensures custom attribute constructors are resolvable.")]
            get
            {
                MetadataReader reader = _reader;
                HandleType constructorHandleType = _customAttribute.Constructor.HandleType;

                if (constructorHandleType == HandleType.QualifiedMethod)
                {
                    QualifiedMethod qualifiedMethod = _customAttribute.Constructor.ToQualifiedMethodHandle(reader).GetQualifiedMethod(reader);
                    TypeDefinitionHandle declaringType = qualifiedMethod.EnclosingType;
                    MethodHandle methodHandle = qualifiedMethod.Method;
                    NativeFormatRuntimeNamedTypeInfo attributeType = NativeFormatRuntimeNamedTypeInfo.GetRuntimeNamedTypeInfo(reader, declaringType, default(RuntimeTypeHandle));
                    return RuntimePlainConstructorInfo<NativeFormatMethodCommon>.GetRuntimePlainConstructorInfo(new NativeFormatMethodCommon(methodHandle, attributeType, attributeType));
                }
                else if (constructorHandleType == HandleType.MemberReference)
                {
                    MemberReference memberReference = _customAttribute.Constructor.ToMemberReferenceHandle(reader).GetMemberReference(reader);

                    // There is no chance a custom attribute type will be an open type specification so we can safely pass in the empty context here.
                    TypeContext typeContext = new TypeContext(Array.Empty<RuntimeTypeInfo>(), Array.Empty<RuntimeTypeInfo>());
                    RuntimeTypeInfo attributeRuntimeTypeInfo = memberReference.Parent.Resolve(reader, typeContext);
                    Type attributeType = attributeRuntimeTypeInfo.ToType();
                    MethodSignature sig = memberReference.Signature.ParseMethodSignature(reader);
                    HandleCollection parameters = sig.Parameters;
                    int numParameters = parameters.Count;
                    if (numParameters == 0)
                        return ResolveAttributeConstructor(attributeType, Array.Empty<Type>());

                    Type[] expectedParameterTypes = new Type[numParameters];
                    int index = 0;
                    foreach (Handle _parameterHandle in parameters)
                    {
                        Handle parameterHandle = _parameterHandle;
                        expectedParameterTypes[index++] = parameterHandle.Resolve(reader, attributeRuntimeTypeInfo.TypeContext).ToType();
                    }
                    return ResolveAttributeConstructor(attributeType, expectedParameterTypes);
                }
                else
                {
                    throw new BadImageFormatException();
                }
            }
        }

        //
        // If throwIfMissingMetadata is false, returns null rather than throwing a missing metadata exception.
        //
        internal sealed override IList<CustomAttributeTypedArgument> GetConstructorArguments(bool throwIfMissingMetadata)
        {
            int index = 0;

            HandleCollection parameterTypeSignatureHandles;
            HandleType handleType = _customAttribute.Constructor.HandleType;
            switch (handleType)
            {
                case HandleType.QualifiedMethod:
                    parameterTypeSignatureHandles = _customAttribute.Constructor.ToQualifiedMethodHandle(_reader).GetQualifiedMethod(_reader).Method.GetMethod(_reader).Signature.GetMethodSignature(_reader).Parameters;
                    break;

                case HandleType.MemberReference:
                    parameterTypeSignatureHandles = _customAttribute.Constructor.ToMemberReferenceHandle(_reader).GetMemberReference(_reader).Signature.ToMethodSignatureHandle(_reader).GetMethodSignature(_reader).Parameters;
                    break;
                default:
                    throw new BadImageFormatException();
            }
            Handle[] ctorTypeHandles = parameterTypeSignatureHandles.ToArray();

            ArrayBuilder<CustomAttributeTypedArgument> customAttributeTypedArguments = new ArrayBuilder<CustomAttributeTypedArgument>(_customAttribute.FixedArguments.Count);
            foreach (Handle fixedArgumentHandle in _customAttribute.FixedArguments)
            {
                Handle typeHandle = ctorTypeHandles[index];
                Exception? exception = null;
                RuntimeTypeInfo? argumentType = typeHandle.TryResolve(_reader, AttributeType.ToRuntimeTypeInfo().TypeContext, ref exception);
                if (argumentType == null)
                {
                    if (throwIfMissingMetadata)
                        throw exception!;
                    return null;
                }

                Exception e = fixedArgumentHandle.TryParseConstantValue(_reader, out object? value);
                CustomAttributeTypedArgument customAttributeTypedArgument;
                if (e != null)
                {
                    if (throwIfMissingMetadata)
                        throw e;
                    else
                        return null;
                }
                else
                {
                    customAttributeTypedArgument = WrapInCustomAttributeTypedArgument(value, argumentType.ToType());
                }

                customAttributeTypedArguments.Add(customAttributeTypedArgument);
                index++;
            }

            return customAttributeTypedArguments.ToArray();
        }

        //
        // If throwIfMissingMetadata is false, returns null rather than throwing a missing metadata exception.
        //
        internal sealed override IList<CustomAttributeNamedArgument> GetNamedArguments(bool throwIfMissingMetadata)
        {
            ArrayBuilder<CustomAttributeNamedArgument> customAttributeNamedArguments = new ArrayBuilder<CustomAttributeNamedArgument>(_customAttribute.NamedArguments.Count);
            foreach (NamedArgumentHandle namedArgumentHandle in _customAttribute.NamedArguments)
            {
                NamedArgument namedArgument = namedArgumentHandle.GetNamedArgument(_reader);
                string memberName = namedArgument.Name.GetString(_reader);
                bool isField = (namedArgument.Flags == NamedArgumentMemberKind.Field);

                Exception? exception = null;
                RuntimeTypeInfo? argumentType = namedArgument.Type.TryResolve(_reader, AttributeType.ToRuntimeTypeInfo().TypeContext, ref exception);
                if (argumentType == null)
                {
                    if (throwIfMissingMetadata)
                        throw exception!;
                    else
                        return null;
                }

                object? value;
                Exception e = namedArgument.Value.TryParseConstantValue(_reader, out value);
                if (e != null)
                {
                    if (throwIfMissingMetadata)
                        throw e;
                    else
                        return null;
                }
                CustomAttributeTypedArgument typedValue = WrapInCustomAttributeTypedArgument(value, argumentType.ToType());

                customAttributeNamedArguments.Add(CreateCustomAttributeNamedArgument(this.AttributeType, memberName, isField, typedValue));
            }
            return customAttributeNamedArguments.ToArray();
        }

        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
            Justification = "Metadata generation ensures fields/properties referenced from attributes are preserved.")]
        private static CustomAttributeNamedArgument CreateCustomAttributeNamedArgument(Type attributeType, string memberName, bool isField, CustomAttributeTypedArgument typedValue)
        {
            MemberInfo? memberInfo;

            if (isField)
                memberInfo = attributeType.GetField(memberName, BindingFlags.Public | BindingFlags.Instance);
            else
                memberInfo = attributeType.GetProperty(memberName, BindingFlags.Public | BindingFlags.Instance);

            if (memberInfo == null)
                throw ReflectionCoreExecution.ExecutionEnvironment.CreateMissingMetadataException(attributeType);

            return new CustomAttributeNamedArgument(memberInfo, typedValue);
        }

        // Equals/GetHashCode no need to override (they just implement reference equality but desktop never unified these things.)

        private readonly MetadataReader _reader;
        private readonly CustomAttribute _customAttribute;

        private volatile Type _lazyAttributeType;
    }
}