File: Internal\Reflection\Execution\AssemblyBinderImplementation.cs
Web Access
Project: src\src\runtime\src\coreclr\nativeaot\System.Private.TypeLoader\src\System.Private.TypeLoader.csproj (System.Private.TypeLoader)
// 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.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Reflection.Runtime.General;

using Internal.Metadata.NativeFormat;
using Internal.Reflection.Core;
using Internal.Runtime.TypeLoader;

namespace Internal.Reflection.Execution
{
    //=============================================================================================================================
    // The assembly resolution policy for emulation of "classic reflection."
    //
    // The policy is very simple: the only assemblies that can be "loaded" are those that are statically linked into the running
    // native process. There is no support for probing for assemblies in directories, user-supplied files, GACs, NICs or any
    // other repository.
    //=============================================================================================================================
    public sealed partial class AssemblyBinderImplementation : AssemblyBinder
    {
        private AssemblyBinderImplementation()
        {
            ArrayBuilder<KeyValuePair<RuntimeAssemblyName, QScopeDefinition>> scopes = default;
            foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules())
            {
                MetadataReader reader = module.MetadataReader;
                foreach (ScopeDefinitionHandle scopeDefinitionHandle in reader.ScopeDefinitions)
                {
                    scopes.Add(new KeyValuePair<RuntimeAssemblyName, QScopeDefinition>(
                        scopeDefinitionHandle.ToRuntimeAssemblyName(reader),
                        new QScopeDefinition(reader, scopeDefinitionHandle)
                        ));
                }
            }

            ScopeGroups = scopes.ToArray();
        }

        public static AssemblyBinderImplementation Instance { get; } = new AssemblyBinderImplementation();

        public sealed override bool Bind(RuntimeAssemblyName refName, bool cacheMissedLookups, out AssemblyBindResult result, out Exception exception)
        {
            bool foundMatch = false;
            result = default(AssemblyBindResult);
            exception = null;

            Exception preferredException = null;

            foreach (KeyValuePair<RuntimeAssemblyName, QScopeDefinition> group in ScopeGroups)
            {
                if (AssemblyNameMatches(refName, group.Key, ref preferredException))
                {
                    if (foundMatch)
                    {
                        exception = new AmbiguousMatchException(SR.Format(SR.AmbiguousMatchException_Assembly, refName.FullName));
                        return false;
                    }

                    foundMatch = true;
                    QScopeDefinition scopeDefinitionGroup = group.Value;

                    result.Reader = scopeDefinitionGroup.Reader;
                    result.ScopeDefinitionHandle = scopeDefinitionGroup.Handle;
                }
            }

            if (!foundMatch)
            {
                exception = preferredException ?? new FileNotFoundException(SR.Format(SR.FileNotFound_AssemblyNotFound, refName.FullName));
                return false;
            }

            return true;
        }

        public sealed override IList<AssemblyBindResult> GetLoadedAssemblies()
        {
            List<AssemblyBindResult> loadedAssemblies = new List<AssemblyBindResult>(ScopeGroups.Length);
            foreach (KeyValuePair<RuntimeAssemblyName, QScopeDefinition> group in ScopeGroups)
            {
                QScopeDefinition scopeDefinitionGroup = group.Value;

                AssemblyBindResult result = default(AssemblyBindResult);
                result.Reader = scopeDefinitionGroup.Reader;
                result.ScopeDefinitionHandle = scopeDefinitionGroup.Handle;
                loadedAssemblies.Add(result);
            }

            return loadedAssemblies;
        }

        public sealed override int GetLoadedAssembliesCount()
        {
            return ScopeGroups.Length;
        }

        //
        // Encapsulates the assembly ref->def matching policy.
        //
        private static bool AssemblyNameMatches(RuntimeAssemblyName refName, RuntimeAssemblyName defName, ref Exception preferredException)
        {
            //
            // The defName came from trusted metadata so it should be fully specified.
            //
            Debug.Assert(defName.Version != null);
            Debug.Assert(defName.CultureName != null);

            if (!(refName.Name.Equals(defName.Name, StringComparison.OrdinalIgnoreCase)))
                return false;

            if (refName.Version != null)
            {
                if (!AssemblyVersionMatches(refVersion: refName.Version, defVersion: defName.Version))
                {
                    preferredException = new FileLoadException(SR.Format(SR.FileLoadException_RefDefMismatch, refName.FullName, defName.Version, refName.Version));
                    return false;
                }
            }

            if (refName.CultureName != null)
            {
                if (!(refName.CultureName.Equals(defName.CultureName)))
                    return false;
            }

            // Strong names are ignored in .NET Core

            return true;
        }

        private static bool AssemblyVersionMatches(Version refVersion, Version defVersion)
        {
            if (defVersion.Major < refVersion.Major)
                return false;
            if (defVersion.Major > refVersion.Major)
                return true;

            if (defVersion.Minor < refVersion.Minor)
                return false;
            if (defVersion.Minor > refVersion.Minor)
                return true;

            if (refVersion.Build == -1)
                return true;
            if (defVersion.Build < refVersion.Build)
                return false;
            if (defVersion.Build > refVersion.Build)
                return true;

            if (refVersion.Revision == -1)
                return true;
            if (defVersion.Revision < refVersion.Revision)
                return false;

            return true;
        }

        private KeyValuePair<RuntimeAssemblyName, QScopeDefinition>[] ScopeGroups { get; }
    }
}