File: System\Reflection\TypeLoading\Assemblies\Ecma\EcmaAssembly.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.CodeAnalysis;
using System.IO;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
 
namespace System.Reflection.TypeLoading.Ecma
{
    /// <summary>
    /// Base class for all Assembly objects created by a MetadataLoadContext and get its metadata from a PEReader.
    /// </summary>
    internal sealed partial class EcmaAssembly
    {
        public sealed override event ModuleResolveEventHandler? ModuleResolve;
 
        protected sealed override RoModule LoadModule(string moduleName, bool containsMetadata)
        {
            FileStream? peStream = FindModuleNextToAssembly(moduleName);
            if (peStream != null)
                return CreateModule(peStream, containsMetadata);
 
            Module? moduleFromEvent = ModuleResolve?.Invoke(this, new ResolveEventArgs(moduleName));
            if (moduleFromEvent != null)
            {
                if (!(moduleFromEvent is RoModule roModuleFromEvent && roModuleFromEvent.Loader == Loader))
                    throw new FileLoadException(SR.ModuleResolveEventReturnedExternalModule);
                return roModuleFromEvent;
            }
 
            throw new FileNotFoundException(SR.Format(SR.FileNotFoundModule, moduleName));
        }
 
        [UnconditionalSuppressMessage("SingleFile", "IL3000: Avoid accessing Assembly file path when publishing as a single file",
            Justification = "The code has a fallback using a ModuleResolveEventHandler")]
        private FileStream? FindModuleNextToAssembly(string moduleName)
        {
            Assembly containingAssembly = this;
            string location = containingAssembly.Location;
            if (string.IsNullOrEmpty(location))
                return null;
            string? directoryPath = Path.GetDirectoryName(location);
            string modulePath = Path.Combine(directoryPath!, moduleName);
            if (File.Exists(modulePath))
                return File.OpenRead(modulePath);
 
            return null;
        }
 
        protected sealed override RoModule CreateModule(Stream peStream, bool containsMetadata)
        {
            string location = RoModule.FullyQualifiedNameForModulesLoadedFromByteArrays;
            if (peStream is FileStream fs)
            {
                location = fs.Name;
            }
 
            if (!containsMetadata)
            {
                peStream.Close();
                return new RoResourceModule(this, location);
            }
 
            PEReader peReader = new PEReader(peStream);
            Loader.RegisterForDisposal(peReader);
            return new EcmaModule(this, location, peReader, peReader.GetMetadataReader());
        }
 
        protected sealed override IEnumerable<AssemblyFileInfo> GetAssemblyFileInfosFromManifest(bool includeManifestModule, bool includeResourceModules)
        {
            MetadataReader reader = Reader;
            if (includeManifestModule)
            {
                yield return new AssemblyFileInfo(reader.GetModuleDefinition().Name.GetString(reader), true, 0);
            }
 
            foreach (AssemblyFileHandle h in reader.AssemblyFiles)
            {
                AssemblyFile af = h.GetAssemblyFile(reader);
                if (includeResourceModules || af.ContainsMetadata)
                {
                    yield return new AssemblyFileInfo(af.Name.GetString(reader), af.ContainsMetadata, h.GetToken().GetTokenRowNumber());
                }
            }
        }
    }
}