File: System\Reflection\Runtime\Assemblies\RuntimeAssemblyInfo.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.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection.Runtime.General;
using System.Reflection.Runtime.TypeInfos;
using System.Runtime.Serialization;
using System.Security;

using Internal.Reflection.Core;
using Internal.Reflection.Core.Execution;

namespace System.Reflection.Runtime.Assemblies
{
    //
    // The runtime's implementation of an Assembly.
    //
    internal abstract partial class RuntimeAssemblyInfo : RuntimeAssembly, IEquatable<RuntimeAssemblyInfo>
    {
        public bool Equals(RuntimeAssemblyInfo? other)
        {
            if (other == null)
                return false;

            return this.Equals((object)other);
        }

        public sealed override string FullName
        {
            get
            {
                return GetName().FullName;
            }
        }

        [Obsolete(Obsoletions.LegacyFormatterImplMessage, DiagnosticId = Obsoletions.LegacyFormatterImplDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public sealed override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            throw new PlatformNotSupportedException();
        }

        public abstract override Module ManifestModule { get; }

        public sealed override IEnumerable<Module> Modules
        {
            get
            {
                yield return ManifestModule;
            }
        }

        public sealed override Module GetModule(string name)
        {
            if (name == ManifestModule.ScopeName)
                return ManifestModule;

            return null;
        }

        [RequiresUnreferencedCode("Types might be removed")]
        public sealed override Type GetType(string name, bool throwOnError, bool ignoreCase)
        {
            ArgumentException.ThrowIfNullOrEmpty(name);

            return TypeNameResolver.GetType(name,
                throwOnError: throwOnError,
                ignoreCase: ignoreCase,
                topLevelAssembly: this);
        }

#pragma warning disable 0067  // Silence warning about ModuleResolve not being used.
        public sealed override event ModuleResolveEventHandler? ModuleResolve;
#pragma warning restore 0067

        public sealed override bool ReflectionOnly => false; // ReflectionOnly loading not supported.

        public sealed override bool IsCollectible => false; // Unloading not supported.

        internal abstract RuntimeAssemblyName RuntimeAssemblyName { get; }

        public sealed override AssemblyName GetName()
        {
            return RuntimeAssemblyName.ToAssemblyName();
        }

        [RequiresUnreferencedCode("Types might be removed")]
        public sealed override Type[] GetForwardedTypes()
        {
            List<Type> types = new List<Type>();
            List<Exception>? exceptions = null;

            foreach (TypeForwardInfo typeForwardInfo in TypeForwardInfos)
            {
                string fullTypeName = typeForwardInfo.NamespaceName.Length == 0 ? typeForwardInfo.TypeName : typeForwardInfo.NamespaceName + "." + typeForwardInfo.TypeName;
                RuntimeAssemblyName redirectedAssemblyName = typeForwardInfo.RedirectedAssemblyName;

                Type? type = null;
                RuntimeAssemblyInfo redirectedAssembly;
                Exception exception = TryGetRuntimeAssembly(redirectedAssemblyName, out redirectedAssembly);
                if (exception == null)
                {
                    type = redirectedAssembly.GetTypeCore(fullTypeName, throwOnError: true, ignoreCase: false); // GetTypeCore() will follow any further type-forwards if needed.
                }

                Debug.Assert((type != null) != (exception != null)); // Exactly one of these must be non-null.

                if (type != null)
                {
                    types.Add(type);
                    AddPublicNestedTypes(type, types);
                }
                else
                {
                    exceptions ??= new List<Exception>();
                    exceptions.Add(exception);
                }
            }

            if (exceptions != null)
            {
                int numTypes = types.Count;
                int numExceptions = exceptions.Count;
                types.AddRange(new Type[numExceptions]); // add one null Type for each exception.
                exceptions.InsertRange(0, new Exception[numTypes]); // align the Exceptions with the null Types.
                throw new ReflectionTypeLoadException(types.ToArray(), exceptions.ToArray());
            }

            return types.ToArray();
        }

        /// <summary>
        /// Intentionally excludes forwards to nested types.
        /// </summary>
        protected abstract IEnumerable<TypeForwardInfo> TypeForwardInfos { get; }

        [RequiresUnreferencedCode("Types might be removed")]
        private static void AddPublicNestedTypes(Type type, List<Type> types)
        {
            foreach (Type nestedType in type.GetNestedTypes(BindingFlags.Public))
            {
                types.Add(nestedType);
                AddPublicNestedTypes(nestedType, types);
            }
        }

        /// <summary>
        /// Helper routine for the more general Type.GetType() family of apis.
        ///
        /// Resolves top-level named types only. No nested types. No constructed types.
        ///
        /// Returns null if the type does not exist. Throws for all other error cases.
        /// </summary>
        internal Type GetTypeCore(string fullName, bool throwOnError, bool ignoreCase)
        {
            RuntimeTypeInfo? type = ignoreCase ? GetTypeCoreCaseInsensitive(fullName) : GetTypeCoreCaseSensitive(fullName);
            if (type == null)
            {
                if (throwOnError)
                    throw Helpers.CreateTypeLoadException(fullName, this.FullName);
                return null;
            }
            return type.ToType();
        }

        // Types that derive from RuntimeAssembly must implement the following public surface area members
        public abstract override IEnumerable<CustomAttributeData> CustomAttributes { get; }
        public abstract override IEnumerable<TypeInfo> DefinedTypes
        {
            [RequiresUnreferencedCode("Types might be removed")]
            get;
        }
        public abstract override MethodInfo EntryPoint { get; }
        public abstract override IEnumerable<Type> ExportedTypes
        {
            [RequiresUnreferencedCode("Types might be removed")]
            get;
        }
        public abstract override ManifestResourceInfo GetManifestResourceInfo(string resourceName);
        public abstract override string[] GetManifestResourceNames();
        public abstract override Stream GetManifestResourceStream(string name);
        public abstract override string ImageRuntimeVersion { get; }
        public abstract override bool Equals(object obj);
        public abstract override int GetHashCode();

        /// <summary>
        /// Perform a lookup for a type based on a name. Overriders are expected to
        /// have a non-cached implementation, as the result is expected to be cached by
        /// callers of this method. Should be implemented by every format specific
        /// RuntimeAssembly implementor
        /// </summary>
        internal abstract RuntimeTypeInfo UncachedGetTypeCoreCaseSensitive(string fullName);


        /// <summary>
        /// Perform a lookup for a type based on a name. Overriders may or may not
        /// have a cached implementation, as the result is not expected to be cached by
        /// callers of this method, but it is also a rarely used api. Should be
        /// implemented by every format specific RuntimeAssembly implementor
        /// </summary>
        internal abstract RuntimeTypeInfo GetTypeCoreCaseInsensitive(string fullName);

        internal RuntimeTypeInfo GetTypeCoreCaseSensitive(string fullName)
        {
            return this.CaseSensitiveTypeTable.GetOrAdd(fullName);
        }

        private CaseSensitiveTypeCache CaseSensitiveTypeTable
        {
            get
            {
                return _lazyCaseSensitiveTypeTable ??= new CaseSensitiveTypeCache(this);
            }
        }

#pragma warning disable 0672  // GlobalAssemblyCache is Obsolete.
        public sealed override bool GlobalAssemblyCache
        {
            get
            {
                return false;
            }
        }
#pragma warning restore 0672

        public sealed override long HostContext
        {
            get
            {
                return 0;
            }
        }

        [RequiresUnreferencedCode("Types and members the loaded module depends on might be removed")]
        public sealed override Module LoadModule(string moduleName, byte[] rawModule, byte[] rawSymbolStore)
        {
            throw new PlatformNotSupportedException();
        }

        [RequiresAssemblyFiles(ThrowingMessageInRAF)]
        public sealed override FileStream GetFile(string name)
        {
            throw new FileNotFoundException();
        }

        [RequiresAssemblyFiles(ThrowingMessageInRAF)]
        public sealed override FileStream[] GetFiles(bool getResourceModules)
        {
            throw new FileNotFoundException();
        }

        public sealed override SecurityRuleSet SecurityRuleSet
        {
            get
            {
                return SecurityRuleSet.None;
            }
        }

        /// <summary>
        /// Returns a *freshly allocated* array of loaded Assemblies.
        /// </summary>
        internal static Assembly[] GetLoadedAssemblies()
        {
            // Important: The result of this method is the return value of the AppDomain.GetAssemblies() api so
            // so it must return a freshly allocated array on each call.

            AssemblyBinder binder = ReflectionCoreExecution.ExecutionEnvironment.AssemblyBinder;
            IList<AssemblyBindResult> bindResults = binder.GetLoadedAssemblies();
            Assembly[] results = new Assembly[bindResults.Count];
            for (int i = 0; i < bindResults.Count; i++)
            {
                Assembly assembly = GetRuntimeAssembly(bindResults[i]);
                results[i] = assembly;
            }
            return results;
        }

        private volatile CaseSensitiveTypeCache _lazyCaseSensitiveTypeTable;

        private sealed class CaseSensitiveTypeCache : ConcurrentUnifier<string, RuntimeTypeInfo>
        {
            public CaseSensitiveTypeCache(RuntimeAssemblyInfo runtimeAssembly)
            {
                _runtimeAssembly = runtimeAssembly;
            }

            protected sealed override RuntimeTypeInfo Factory(string key)
            {
                return _runtimeAssembly.UncachedGetTypeCoreCaseSensitive(key);
            }

            private readonly RuntimeAssemblyInfo _runtimeAssembly;
        }
    }
}