From 57c05a8f5baf0f21bd191410a61217052bb6931f Mon Sep 17 00:00:00 2001 From: Tomas Matousek Date: Thu, 1 Dec 2016 17:55:12 -0800 Subject: [PATCH] Use custom resolver only when default resolver fails to load an assembly --- .../AssemblyLoader/CoreAssemblyLoaderImpl.cs | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/src/Scripting/Core/Hosting/AssemblyLoader/CoreAssemblyLoaderImpl.cs b/src/Scripting/Core/Hosting/AssemblyLoader/CoreAssemblyLoaderImpl.cs index 2c70dc18c12..c99fe899f05 100644 --- a/src/Scripting/Core/Hosting/AssemblyLoader/CoreAssemblyLoaderImpl.cs +++ b/src/Scripting/Core/Hosting/AssemblyLoader/CoreAssemblyLoaderImpl.cs @@ -15,7 +15,7 @@ internal sealed class CoreAssemblyLoaderImpl : AssemblyLoaderImpl internal CoreAssemblyLoaderImpl(InteractiveAssemblyLoader loader) : base(loader) { - _inMemoryAssemblyContext = new LoadContext(this, null); + _inMemoryAssemblyContext = new LoadContext(Loader, null); } public override Assembly LoadFromStream(Stream peStream, Stream pdbStream) @@ -25,7 +25,11 @@ public override Assembly LoadFromStream(Stream peStream, Stream pdbStream) public override AssemblyAndLocation LoadFromPath(string path) { - var assembly = new LoadContext(this, Path.GetDirectoryName(path)).LoadFromAssemblyPath(path); + // Create a new context that knows the directory where the assembly was loaded from + // and uses it to resolve dependencies of the assembly. We could create one context per directory, + // but there is no need to reuse contexts. + var assembly = new LoadContext(Loader, Path.GetDirectoryName(path)).LoadFromAssemblyPath(path); + return new AssemblyAndLocation(assembly, path, fromGac: false); } @@ -37,30 +41,33 @@ public override void Dispose() private sealed class LoadContext : AssemblyLoadContext { private readonly string _loadDirectoryOpt; - private readonly CoreAssemblyLoaderImpl _loader; + private readonly InteractiveAssemblyLoader _loader; - internal LoadContext(CoreAssemblyLoaderImpl loader, string loadDirectoryOpt) + internal LoadContext(InteractiveAssemblyLoader loader, string loadDirectoryOpt) { Debug.Assert(loader != null); + _loader = loader; _loadDirectoryOpt = loadDirectoryOpt; - } - protected override Assembly Load(AssemblyName assemblyName) - { - return _loader.Loader.ResolveAssembly(AssemblyIdentity.FromAssemblyReference(assemblyName), _loadDirectoryOpt) ?? - Default.LoadFromAssemblyName(assemblyName); - } + // CoreCLR resolves assemblies in steps: + // + // 1) Call AssemblyLoadContext.Load -- our context returns null + // 2) TPA list + // 3) Default.Resolving event + // 4) AssemblyLoadContext.Resolving event -- hooked below + // + // What we want is to let the default context load assemblies it knows about (this includes already loaded assemblies, + // assemblies in AppPath, platform assemblies, assemblies explciitly resolved by the App by hooking Default.Resolving, etc.). + // Only if the assembly can't be resolved that way, the interactive resolver steps in. + // + // This order is necessary to avoid loading assemblies twice (by the host App and by interactive loader). - public new Assembly LoadFromStream(Stream assembly, Stream assemblySymbols) - { - return base.LoadFromStream(assembly, assemblySymbols); + Resolving += (_, assemblyName) => + _loader.ResolveAssembly(AssemblyIdentity.FromAssemblyReference(assemblyName), _loadDirectoryOpt); } - public new Assembly LoadFromAssemblyPath(string assemblyPath) - { - return base.LoadFromAssemblyPath(assemblyPath); - } + protected override Assembly Load(AssemblyName assemblyName) => null; } } } -- GitLab