File: System\Reflection\TypeLoading\Assemblies\RoAssembly.Modules.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;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Threading;
 
namespace System.Reflection.TypeLoading
{
    /// <summary>
    /// Base class for all Assembly objects created by a MetadataLoadContext.
    /// </summary>
    internal abstract partial class RoAssembly
    {
        public sealed override Module? GetModule(string name) => GetRoModule(name);
        public sealed override Module[] GetModules(bool getResourceModules) => ComputeRoModules(getResourceModules).CloneArray<Module>();
 
#if NET
        [RequiresAssemblyFiles(ThrowingMessageInRAF)]
#endif
        public sealed override FileStream? GetFile(string name)
        {
            Module? m = GetModule(name);
            if (m == null)
                return null;
            return new FileStream(m.FullyQualifiedName, FileMode.Open, FileAccess.Read, FileShare.Read);
        }
 
#if NET
        [RequiresAssemblyFiles(ThrowingMessageInRAF)]
#endif
        public sealed override FileStream[] GetFiles(bool getResourceModules)
        {
            Module[] m = GetModules(getResourceModules);
            FileStream[] fs = new FileStream[m.Length];
            for (int i = 0; i < fs.Length; i++)
            {
                fs[i] = new FileStream(m[i].FullyQualifiedName, FileMode.Open, FileAccess.Read, FileShare.Read);
            }
            return fs;
        }
 
        public sealed override Module[] GetLoadedModules(bool getResourceModules)
        {
            List<Module> modules = new List<Module>(_loadedModules.Length + 1)
            {
                GetRoManifestModule()
            };
            for (int i = 0; i < _loadedModules.Length; i++)
            {
                RoModule? module = Volatile.Read(ref _loadedModules[i]);
                if (module != null && (getResourceModules || !module.IsResource()))
                    modules.Add(module);
            }
            return modules.ToArray();
        }
 
        public abstract override event ModuleResolveEventHandler? ModuleResolve;
 
        internal RoModule? GetRoModule(string name)
        {
            if (name is null)
                throw new ArgumentNullException(nameof(name));
 
            if (!TryGetAssemblyFileInfo(name, includeManifestModule: true, out AssemblyFileInfo afi))
                return null;
 
            return GetRoModule(afi);
        }
 
        private RoModule GetRoModule(in AssemblyFileInfo afi)
        {
            if (afi.RowIndex == 0)
                return GetRoManifestModule();
 
            int loadedModulesIndex = afi.RowIndex - 1;
            string moduleName = afi.Name;
            RoModule? prior = Volatile.Read(ref _loadedModules[loadedModulesIndex]);
            if (prior != null)
                return prior;
 
            RoModule newModule = LoadModule(moduleName, afi.ContainsMetadata);
            return Interlocked.CompareExchange(ref _loadedModules[loadedModulesIndex], newModule, null) ?? newModule;
        }
 
        internal RoModule[] ComputeRoModules(bool getResourceModules)
        {
            List<RoModule> modules = new List<RoModule>(_loadedModules.Length + 1);
            foreach (AssemblyFileInfo afi in GetAssemblyFileInfosFromManifest(includeManifestModule: true, includeResourceModules: getResourceModules))
            {
                RoModule module = GetRoModule(afi);
                modules.Add(module);
            }
            return modules.ToArray();
        }
 
#pragma warning disable CS8995 // Nullable type is null-checked and will throw if null.
        public sealed override Module LoadModule(string moduleName, byte[]? rawModule, byte[]? rawSymbolStore)
#pragma warning restore CS8995
        {
            if (moduleName is null)
                throw new ArgumentNullException(nameof(moduleName));
            if (rawModule is null)
                throw new ArgumentNullException(nameof(rawModule));
            if (!TryGetAssemblyFileInfo(moduleName, includeManifestModule: false, out AssemblyFileInfo afi))
                throw new ArgumentException(SR.Format(SR.SpecifiedFileNameInvalid, moduleName)); // Name not in manifest.
 
            Debug.Assert(afi.RowIndex != 0); // Since we excluded the manifest module from the search.
 
            int loadedModuleIndex = afi.RowIndex - 1;
            RoModule newModule = CreateModule(new MemoryStream(rawModule), afi.ContainsMetadata);
            Interlocked.CompareExchange(ref _loadedModules[loadedModuleIndex], newModule, null);
 
            // Somewhat shockingly, the compatible behavior is to return the newly created module always rather than the Module that
            // actually won the race to be resolved!
            return newModule;
        }
 
        private bool TryGetAssemblyFileInfo(string name, bool includeManifestModule, out AssemblyFileInfo afi)
        {
            foreach (AssemblyFileInfo candidate in GetAssemblyFileInfosFromManifest(includeManifestModule: includeManifestModule, includeResourceModules: true))
            {
                if (name.Equals(candidate.Name, StringComparison.OrdinalIgnoreCase))
                {
                    afi = candidate;
                    return true;
                }
            }
 
            afi = default;
            return false;
        }
 
        protected abstract RoModule LoadModule(string moduleName, bool containsMetadata);
        protected abstract RoModule CreateModule(Stream peStream, bool containsMetadata);
        protected abstract IEnumerable<AssemblyFileInfo> GetAssemblyFileInfosFromManifest(bool includeManifestModule, bool includeResourceModules);
    }
}