File: System\Reflection\TypeLoading\Types\RoType.cs
Web Access
Project: src\src\libraries\System.Reflection.MetadataLoadContext\src\System.Reflection.MetadataLoadContext.csproj (System.Reflection.MetadataLoadContext)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using StructLayoutAttribute = System.Runtime.InteropServices.StructLayoutAttribute;
 
namespace System.Reflection.TypeLoading
{
    /// <summary>
    /// Base class for all Type and TypeInfo objects created by a MetadataLoadContext.
    /// </summary>
    internal abstract partial class RoType : LeveledTypeInfo
    {
        private const TypeAttributes TypeAttributesSentinel = (TypeAttributes)(-1);
 
        private protected RoType() : base() { }
 
        public sealed override Type AsType() => this;
        public override Type UnderlyingSystemType => this;
 
        public override bool Equals([NotNullWhen(true)] object? obj)
        {
            if (obj is RoType objType)
            {
                if (obj is not RoModifiedType)
                {
                    return base.Equals(objType);
                }
            }
 
            return false;
        }
 
        public override int GetHashCode() => base.GetHashCode();
        public abstract override bool IsTypeDefinition { get; }
        public abstract override bool IsGenericTypeDefinition { get; }
        protected abstract override bool HasElementTypeImpl();
        internal bool Call_HasElementTypeImpl() => HasElementTypeImpl();
        protected abstract override bool IsArrayImpl();
        internal bool Call_IsArrayImpl() => IsArrayImpl();
        public abstract override bool IsSZArray { get; }
        public abstract override bool IsVariableBoundArray { get; }
        protected abstract override bool IsByRefImpl();
        internal bool Call_IsByRefImpl() => IsByRefImpl();
        protected abstract override bool IsPointerImpl();
        internal bool Call_IsPointerImpl() => IsPointerImpl();
        public abstract override bool IsConstructedGenericType { get; }
        public abstract override bool IsGenericParameter { get; }
        public abstract override bool IsGenericTypeParameter { get; }
        public abstract override bool IsGenericMethodParameter { get; }
        public sealed override bool IsByRefLike => (GetClassification() & TypeClassification.IsByRefLike) != 0;
 
        public abstract override bool IsFunctionPointer { get; }
        public abstract override bool IsUnmanagedFunctionPointer { get; }
 
        public override Type[] GetFunctionPointerCallingConventions()
        {
            if (!IsFunctionPointer)
            {
                throw new InvalidOperationException(SR.InvalidOperation_NotFunctionPointer);
            }
 
            // Requires a modified type to return the modifiers.
            return EmptyTypes;
        }
 
        public abstract override Type GetFunctionPointerReturnType();
        public abstract override Type[] GetFunctionPointerParameterTypes();
 
        // RoModifiedType overrides these.
        public override Type[] GetOptionalCustomModifiers() => EmptyTypes;
        public override Type[] GetRequiredCustomModifiers() => EmptyTypes;
 
        public abstract override bool ContainsGenericParameters { get; }
 
        // Applies if IsGenericTypeDefinition == true
        public sealed override Type[] GenericTypeParameters => GetGenericTypeParametersNoCopy().CloneArray<Type>();
        internal abstract RoType[] GetGenericTypeParametersNoCopy();
 
        // Applies if HasElementType == true
        public sealed override Type? GetElementType() => GetRoElementType();
        internal abstract RoType? GetRoElementType();
 
        // Applies if IsArray == true
        public abstract override int GetArrayRank();
 
        // Applies if IsConstructedGenericType == true
        public abstract override Type GetGenericTypeDefinition();
        public sealed override Type[] GenericTypeArguments => GetGenericTypeArgumentsNoCopy().CloneArray<Type>();
        internal abstract RoType[] GetGenericTypeArgumentsNoCopy();
 
        // Applies if IsGenericParameter == true
        public abstract override GenericParameterAttributes GenericParameterAttributes { get; }
        public abstract override int GenericParameterPosition { get; }
        public abstract override Type[] GetGenericParameterConstraints();
 
        // .NET 2.0 apis for detecting/deconstructing generic type definition/constructed generic types.
        public sealed override bool IsGenericType => IsConstructedGenericType || IsGenericTypeDefinition;
 
        //  Don't seal since we may need to convert any modified types to unmodified.
        public override Type[] GetGenericArguments() => GetGenericArgumentsNoCopy().CloneArray();
 
        protected internal abstract RoType[] GetGenericArgumentsNoCopy();
 
        // Naming
        public sealed override string Name => _lazyName ??= ComputeName();
        protected abstract string ComputeName();
        internal string Call_ComputeName() => ComputeName();
        private volatile string? _lazyName;
 
        public sealed override string? Namespace => _lazyNamespace ??= ComputeNamespace();
        protected abstract string? ComputeNamespace();
        internal string? Call_ComputeNamespace() => ComputeNamespace();
        private volatile string? _lazyNamespace;
 
        public sealed override string? FullName => _lazyFullName ??= ComputeFullName();
        protected abstract string? ComputeFullName();
        internal string? Call_ComputeFullName() => ComputeFullName();
        private volatile string? _lazyFullName;
        public override string? AssemblyQualifiedName => _lazyAssemblyQualifiedFullName ??= ComputeAssemblyQualifiedName();
        private string? ComputeAssemblyQualifiedName()
        {
            string? fullName = FullName;
            if (fullName == null)   // Open types return null for FullName by design.
                return null;
            string? assemblyName = Assembly.FullName;
            return fullName + ", " + assemblyName;
        }
        private volatile string? _lazyAssemblyQualifiedFullName;
 
        // Assembly and module
        public sealed override Assembly Assembly => Module.Assembly;
        public sealed override Module Module => GetRoModule();
        internal abstract RoModule GetRoModule();
 
        // Nesting
        public sealed override Type? DeclaringType => GetRoDeclaringType();
        protected abstract RoType? ComputeDeclaringType();
        internal RoType? GetRoDeclaringType() => _lazyDeclaringType ??= ComputeDeclaringType();
        internal RoType? Call_ComputeDeclaringType() => ComputeDeclaringType();
        private volatile RoType? _lazyDeclaringType;
 
        public abstract override MethodBase? DeclaringMethod { get; }
        // .NET Framework compat: For types, ReflectedType == DeclaringType. Nested types are always looked up as if BindingFlags.DeclaredOnly was passed.
        // For non-nested types, the concept of a ReflectedType doesn't even make sense.
        public sealed override Type? ReflectedType => DeclaringType;
 
        // CustomAttributeData
        public sealed override IList<CustomAttributeData> GetCustomAttributesData() => CustomAttributes.ToReadOnlyCollection();
        public abstract override IEnumerable<CustomAttributeData> CustomAttributes { get; }
 
        // Optimized routines that find a custom attribute by type name only.
        internal abstract bool IsCustomAttributeDefined(ReadOnlySpan<byte> ns, ReadOnlySpan<byte> name);
        internal abstract CustomAttributeData? TryFindCustomAttribute(ReadOnlySpan<byte> ns, ReadOnlySpan<byte> name);
 
        // Inheritance
        public sealed override Type? BaseType => GetRoBaseType();
        internal RoType? GetRoBaseType() => object.ReferenceEquals(_lazyBaseType, Sentinels.RoType) ? (_lazyBaseType = ComputeBaseType()) : _lazyBaseType;
        private RoType? ComputeBaseType()
        {
            RoType? baseType = ComputeBaseTypeWithoutDesktopQuirk();
            if (baseType != null && baseType.IsGenericParameter)
            {
                // .NET Framework quirk: a generic parameter whose constraint is another generic parameter reports its BaseType as System.Object
                // unless that other generic parameter has a "class" constraint.
                GenericParameterAttributes genericParameterAttributes = baseType.GenericParameterAttributes;
                if (0 == (genericParameterAttributes & GenericParameterAttributes.ReferenceTypeConstraint))
                    baseType = Loader.GetCoreType(CoreType.Object);
            }
            return baseType;
        }
        private volatile RoType? _lazyBaseType = Sentinels.RoType;
 
        //
        // This internal method implements BaseType without the following .NET Framework quirk:
        //
        //     class Foo<X,Y>
        //       where X:Y
        //       where Y:MyReferenceClass
        //
        // .NET Framework reports "X"'s base type as "System.Object" rather than "Y", even though it does
        // report any interfaces that are in MyReferenceClass's interface list.
        //
        // This seriously messes up the implementation of Type.GetInterfaces() which assumes
        // that it can recover the transitive interface closure by combining the directly mentioned interfaces and
        // the BaseType's own interface closure.
        //
        // To implement this with the least amount of code smell, we'll implement the idealized version of BaseType here
        // and make the special-case adjustment in the public version of BaseType.
        //
        internal abstract RoType? ComputeBaseTypeWithoutDesktopQuirk();
 
        public sealed override Type[] GetInterfaces() => GetInterfacesNoCopy().CloneArray<Type>();
 
        public sealed override IEnumerable<Type> ImplementedInterfaces
        {
            get
            {
                foreach (Type ifc in GetInterfacesNoCopy())
                {
                    yield return ifc;
                }
            }
        }
 
        internal abstract IEnumerable<RoType> ComputeDirectlyImplementedInterfaces();
 
        internal RoType[] GetInterfacesNoCopy() => _lazyInterfaces ??= ComputeInterfaceClosure();
        private RoType[] ComputeInterfaceClosure()
        {
            HashSet<RoType> ifcs = new HashSet<RoType>();
 
            RoType? baseType = ComputeBaseTypeWithoutDesktopQuirk();
            if (baseType != null)
            {
                foreach (RoType ifc in baseType.GetInterfacesNoCopy())
                {
                    ifcs.Add(ifc);
                }
            }
 
            foreach (RoType ifc in ComputeDirectlyImplementedInterfaces())
            {
                bool notSeenBefore = ifcs.Add(ifc);
                if (!notSeenBefore)
                {
                    foreach (RoType indirectIfc in ifc.GetInterfacesNoCopy())
                    {
                        ifcs.Add(indirectIfc);
                    }
                }
            }
 
            if (ifcs.Count == 0)
            {
                return Array.Empty<RoType>();
            }
 
            var arr = new RoType[ifcs.Count];
            ifcs.CopyTo(arr);
            return arr;
        }
 
        private volatile RoType[]? _lazyInterfaces;
 
        public sealed override InterfaceMapping GetInterfaceMap(Type interfaceType) => throw new NotSupportedException(SR.NotSupported_InterfaceMapping);
 
        // Assignability
        public sealed override bool IsAssignableFrom(TypeInfo? typeInfo) => IsAssignableFrom((Type?)typeInfo);
        public sealed override bool IsAssignableFrom(Type? c)
        {
            if (c == null)
                return false;
 
            if (object.ReferenceEquals(c, this))
                return true;
 
            c = c.UnderlyingSystemType;
            if (!(c is RoType roType && roType.Loader == Loader))
                return false;
 
            return Assignability.IsAssignableFrom(this, c, Loader.GetAllFoundCoreTypes());
        }
 
        // Identify interesting subgroups of Types
        protected sealed override bool IsCOMObjectImpl() => false;   // RCW's are irrelevant in a MetadataLoadContext without object creation.
        public override bool IsEnum => (GetBaseTypeClassification() & BaseTypeClassification.IsEnum) != 0;
        protected override bool IsValueTypeImpl() => (GetBaseTypeClassification() & BaseTypeClassification.IsValueType) != 0;
 
        // Metadata
        public abstract override int MetadataToken { get; }
        public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) => this.HasSameMetadataDefinitionAsCore(other);
 
        // TypeAttributes
        protected sealed override TypeAttributes GetAttributeFlagsImpl() => (_lazyTypeAttributes == TypeAttributesSentinel) ? (_lazyTypeAttributes = ComputeAttributeFlags()) : _lazyTypeAttributes;
        protected abstract TypeAttributes ComputeAttributeFlags();
        internal TypeAttributes Call_ComputeAttributeFlags() => ComputeAttributeFlags();
        private volatile TypeAttributes _lazyTypeAttributes = TypeAttributesSentinel;
 
        // Miscellaneous properties
        public sealed override MemberTypes MemberType => IsPublic || IsNotPublic ? MemberTypes.TypeInfo : MemberTypes.NestedType;
        protected abstract override TypeCode GetTypeCodeImpl();
        internal TypeCode Call_GetTypeCodeImpl() => GetTypeCodeImpl();
        public abstract override string ToString();
 
        // Random interop stuff
        public abstract override Guid GUID { get; }
        public abstract override StructLayoutAttribute? StructLayoutAttribute { get; }
 
        public sealed override MemberInfo[] GetDefaultMembers()
        {
            string? defaultMemberName = GetDefaultMemberName();
            return defaultMemberName != null ? GetMember(defaultMemberName) : Array.Empty<MemberInfo>();
        }
 
        private string? GetDefaultMemberName()
        {
            for (RoType? type = this; type != null; type = type.GetRoBaseType())
            {
                CustomAttributeData? attribute = type.TryFindCustomAttribute(Utf8Constants.SystemReflection, Utf8Constants.DefaultMemberAttribute);
                if (attribute != null)
                {
                    IList<CustomAttributeTypedArgument> fixedArguments = attribute.ConstructorArguments;
                    if (fixedArguments.Count == 1 && fixedArguments[0].Value is string memberName)
                        return memberName;
                }
            }
            return null;
        }
 
        // Type Factories
        public sealed override Type MakeArrayType() => this.GetUniqueArrayType();
        public sealed override Type MakeArrayType(int rank)
        {
            if (rank <= 0)
                throw new IndexOutOfRangeException(); // This is an impressively uninformative exception, unfortunately, this is the compatible behavior.
 
            return this.GetUniqueArrayType(rank);
        }
 
        public sealed override Type MakeByRefType() => this.GetUniqueByRefType();
        public sealed override Type MakePointerType() => this.GetUniquePointerType();
        [RequiresUnreferencedCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")]
        public abstract override Type MakeGenericType(params Type[] typeArguments);
 
        // Enum methods
        public sealed override Type GetEnumUnderlyingType() => _lazyUnderlyingEnumType ??= ComputeEnumUnderlyingType();
        protected internal abstract RoType ComputeEnumUnderlyingType();
        private volatile RoType? _lazyUnderlyingEnumType;
        public sealed override Array GetEnumValues() => throw new InvalidOperationException(SR.Arg_InvalidOperation_Reflection);
 
#if NET
        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2085:UnrecognizedReflectionPattern",
            Justification = "Enum Types are not trimmed.")]
        public override Array GetEnumValuesAsUnderlyingType()
        {
            if (!IsEnum)
                throw new ArgumentException(SR.Arg_MustBeEnum, "enumType");
 
            FieldInfo[] enumFields = GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
            int numValues = enumFields.Length;
            Array ret = Type.GetTypeCode(GetEnumUnderlyingType()) switch
            {
                TypeCode.Byte => new byte[numValues],
                TypeCode.SByte => new sbyte[numValues],
                TypeCode.UInt16 => new ushort[numValues],
                TypeCode.Int16 => new short[numValues],
                TypeCode.UInt32 => new uint[numValues],
                TypeCode.Int32 => new int[numValues],
                TypeCode.UInt64 => new ulong[numValues],
                TypeCode.Int64 => new long[numValues],
                _ => throw new NotSupportedException(),
            };
 
            for (int i = 0; i < numValues; i++)
            {
                ret.SetValue(enumFields[i].GetRawConstantValue(), i);
            }
 
            return ret;
        }
#endif
 
        // No trust environment to apply these to.
        public sealed override bool IsSecurityCritical => throw new InvalidOperationException(SR.InvalidOperation_IsSecurity);
        public sealed override bool IsSecuritySafeCritical => throw new InvalidOperationException(SR.InvalidOperation_IsSecurity);
        public sealed override bool IsSecurityTransparent => throw new InvalidOperationException(SR.InvalidOperation_IsSecurity);
 
        // Prohibited for ReflectionOnly types
        public sealed override RuntimeTypeHandle TypeHandle => throw new InvalidOperationException(SR.Arg_InvalidOperation_Reflection);
        public sealed override object[] GetCustomAttributes(bool inherit) => throw new InvalidOperationException(SR.Arg_ReflectionOnlyCA);
        public sealed override object[] GetCustomAttributes(Type attributeType, bool inherit) => throw new InvalidOperationException(SR.Arg_ReflectionOnlyCA);
        public sealed override bool IsDefined(Type attributeType, bool inherit) => throw new InvalidOperationException(SR.Arg_ReflectionOnlyCA);
        public sealed override object? InvokeMember(string name, BindingFlags invokeAttr, Binder? binder, object? target, object?[]? args, ParameterModifier[]? modifiers, CultureInfo? culture, string[]? namedParameters) => throw new InvalidOperationException(SR.Arg_ReflectionOnlyInvoke);
 
        // Low level support for the BindingFlag-driven enumerator apis. These return members declared (not inherited) on the current
        // type, possibly doing case-sensitive/case-insensitive filtering on a supplied name.
        internal abstract IEnumerable<ConstructorInfo> GetConstructorsCore(NameFilter? filter);
        internal abstract IEnumerable<MethodInfo> GetMethodsCore(NameFilter? filter, Type reflectedType);
        internal abstract IEnumerable<EventInfo> GetEventsCore(NameFilter? filter, Type reflectedType);
        internal abstract IEnumerable<FieldInfo> GetFieldsCore(NameFilter? filter, Type reflectedType);
        internal abstract IEnumerable<PropertyInfo> GetPropertiesCore(NameFilter? filter, Type reflectedType);
        internal abstract IEnumerable<RoType> GetNestedTypesCore(NameFilter? filter);
 
        // Backdoor for RoModule to invoke GetMethodImpl();
        internal MethodInfo? InternalGetMethodImpl(string name, BindingFlags bindingAttr, Binder? binder, CallingConventions callConvention, Type[]? types, ParameterModifier[]? modifiers)
        {
            return GetMethodImpl(name, bindingAttr, binder, callConvention, types, modifiers);
        }
 
        // Returns the MetadataLoadContext used to load this type.
        internal MetadataLoadContext Loader => GetRoModule().Loader;
    }
}