diff --git a/src/Common/src/CoreLib/System/Activator.RuntimeType.cs b/src/Common/src/CoreLib/System/Activator.RuntimeType.cs index f328b9c25114..270aa6ad65cf 100644 --- a/src/Common/src/CoreLib/System/Activator.RuntimeType.cs +++ b/src/Common/src/CoreLib/System/Activator.RuntimeType.cs @@ -4,6 +4,7 @@ using System.Reflection; using System.Globalization; +using System.Runtime.Loader; using System.Runtime.Remoting; using System.Threading; @@ -126,7 +127,7 @@ private static ObjectHandle CreateInstanceInternal(string assemblyString, { // Classic managed type assembly = RuntimeAssembly.InternalLoadAssemblyName( - assemblyName, ref stackMark); + assemblyName, ref stackMark, AssemblyLoadContext.CurrentContextualReflectionContext); } } diff --git a/src/Common/src/CoreLib/System/Runtime/Loader/AssemblyLoadContext.cs b/src/Common/src/CoreLib/System/Runtime/Loader/AssemblyLoadContext.cs index 8316e1093e7b..4efd5dec1d58 100644 --- a/src/Common/src/CoreLib/System/Runtime/Loader/AssemblyLoadContext.cs +++ b/src/Common/src/CoreLib/System/Runtime/Loader/AssemblyLoadContext.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Reflection; @@ -31,18 +32,33 @@ private enum InternalState private static readonly Dictionary> s_allContexts = new Dictionary>(); private static long s_nextId; - // Indicates the state of this ALC (Alive or in Unloading state) - private InternalState _state; - - // Id used by s_allContexts - private readonly long _id; +#region private data members + // If you modify any of these fields, you must also update the + // AssemblyLoadContextBaseObject structure in object.h // synchronization primitive to protect against usage of this instance while unloading private readonly object _unloadLock; + private event Func _resolvingUnmanagedDll; + + private event Func _resolving; + + private event Action _unloading; + + private readonly string _name; + // Contains the reference to VM's representation of the AssemblyLoadContext private readonly IntPtr _nativeAssemblyLoadContext; + // Id used by s_allContexts + private readonly long _id; + + // Indicates the state of this ALC (Alive or in Unloading state) + private InternalState _state; + + private readonly bool _isCollectible; +#endregion + protected AssemblyLoadContext() : this(false, false, null) { } @@ -58,9 +74,9 @@ public AssemblyLoadContext(string name, bool isCollectible = false) : this(false private protected AssemblyLoadContext(bool representsTPALoadContext, bool isCollectible, string name) { // Initialize the VM side of AssemblyLoadContext if not already done. - IsCollectible = isCollectible; + _isCollectible = isCollectible; - Name = name; + _name = name; // The _unloadLock needs to be assigned after the IsCollectible to ensure proper behavior of the finalizer // even in case the following allocation fails or the thread is aborted between these two lines. @@ -103,7 +119,7 @@ private protected AssemblyLoadContext(bool representsTPALoadContext, bool isColl private void RaiseUnloadEvent() { // Ensure that we raise the Unload event only once - Interlocked.Exchange(ref Unloading, null)?.Invoke(this); + Interlocked.Exchange(ref _unloading, null)?.Invoke(this); } private void InitiateUnload() @@ -153,7 +169,17 @@ public IEnumerable Assemblies // // Inputs: Invoking assembly, and library name to resolve // Returns: A handle to the loaded native library - public event Func ResolvingUnmanagedDll; + public event Func ResolvingUnmanagedDll + { + add + { + _resolvingUnmanagedDll += value; + } + remove + { + _resolvingUnmanagedDll -= value; + } + } // Event handler for resolving managed assemblies. // This event is raised if the managed assembly could not be resolved via @@ -161,9 +187,29 @@ public IEnumerable Assemblies // // Inputs: The AssemblyLoadContext and AssemblyName to be loaded // Returns: The Loaded assembly object. - public event Func Resolving; + public event Func Resolving + { + add + { + _resolving += value; + } + remove + { + _resolving -= value; + } + } - public event Action Unloading; + public event Action Unloading + { + add + { + _unloading += value; + } + remove + { + _unloading -= value; + } + } // Occurs when an Assembly is loaded public static event AssemblyLoadEventHandler AssemblyLoad; @@ -180,9 +226,9 @@ public IEnumerable Assemblies public static AssemblyLoadContext Default => DefaultAssemblyLoadContext.s_loadContext; - public bool IsCollectible { get; } + public bool IsCollectible { get { return _isCollectible;} } - public string Name { get; } + public string Name { get { return _name;} } public override string ToString() => "\"" + Name + "\" " + GetType().ToString() + " #" + _id; @@ -240,7 +286,7 @@ public Assembly LoadFromAssemblyName(AssemblyName assemblyName) // Attempt to load the assembly, using the same ordering as static load, in the current load context. StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return Assembly.Load(assemblyName, ref stackMark, _nativeAssemblyLoadContext); + return Assembly.Load(assemblyName, ref stackMark, this); } // These methods load assemblies into the current AssemblyLoadContext @@ -396,6 +442,110 @@ private void VerifyIsAlive() throw new InvalidOperationException(SR.AssemblyLoadContext_Verify_NotUnloading); } } + + private static AsyncLocal s_asyncLocalCurrent; + + /// Nullable current AssemblyLoadContext used for context sensitive reflection APIs + /// + /// This is an advanced setting used in reflection assembly loading scenarios. + /// + /// There are a set of contextual reflection APIs which load managed assemblies through an inferred AssemblyLoadContext. + /// * + /// * + /// * + /// * + /// + /// When CurrentContextualReflectionContext is null, the AssemblyLoadContext is inferred. + /// The inference logic is simple. + /// * For static methods, it is the AssemblyLoadContext which loaded the method caller's assembly. + /// * For instance methods, it is the AssemblyLoadContext which loaded the instance's assembly. + /// + /// When this property is set, the CurrentContextualReflectionContext value is used by these contextual reflection APIs for loading. + /// + /// This property is typically set in a using block by + /// . + /// + /// The property is stored in an AsyncLocal<AssemblyLoadContext>. This means the setting can be unique for every async or thread in the process. + /// + /// For more details see https://github.com/dotnet/coreclr/blob/master/Documentation/design-docs/AssemblyLoadContext.ContextualReflection.md + /// + public static AssemblyLoadContext CurrentContextualReflectionContext + { + get { return s_asyncLocalCurrent?.Value; } + } + + private static void SetCurrentContextualReflectionContext(AssemblyLoadContext value) + { + if (s_asyncLocalCurrent == null) + { + Interlocked.CompareExchange(ref s_asyncLocalCurrent, new AsyncLocal(), null); + } + s_asyncLocalCurrent.Value = value; + } + + /// Enter scope using this AssemblyLoadContext for ContextualReflection + /// A disposable ContextualReflectionScope for use in a using block + /// + /// Sets CurrentContextualReflectionContext to this instance. + /// + /// + /// Returns a disposable ContextualReflectionScope for use in a using block. When the using calls the + /// Dispose() method, it restores the ContextualReflectionScope to its previous value. + /// + public ContextualReflectionScope EnterContextualReflection() + { + return new ContextualReflectionScope(this); + } + + /// Enter scope using this AssemblyLoadContext for ContextualReflection + /// Set CurrentContextualReflectionContext to the AssemblyLoadContext which loaded activating. + /// A disposable ContextualReflectionScope for use in a using block + /// + /// Sets CurrentContextualReflectionContext to to the AssemblyLoadContext which loaded activating. + /// + /// + /// Returns a disposable ContextualReflectionScope for use in a using block. When the using calls the + /// Dispose() method, it restores the ContextualReflectionScope to its previous value. + /// + public static ContextualReflectionScope EnterContextualReflection(Assembly activating) + { + return activating != null ? + GetLoadContext(activating).EnterContextualReflection() : + new ContextualReflectionScope(null); + } + + /// Opaque disposable struct used to restore CurrentContextualReflectionContext + /// + /// This is an implmentation detail of the AssemblyLoadContext.EnterContextualReflection APIs. + /// It is a struct, to avoid heap allocation. + /// It is required to be public to avoid boxing. + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public struct ContextualReflectionScope : IDisposable + { + private readonly AssemblyLoadContext _activated; + private readonly AssemblyLoadContext _predecessor; + private readonly bool _initialized; + + internal ContextualReflectionScope(AssemblyLoadContext activating) + { + _predecessor = AssemblyLoadContext.CurrentContextualReflectionContext; + AssemblyLoadContext.SetCurrentContextualReflectionContext(activating); + _activated = activating; + _initialized = true; + } + + public void Dispose() + { + if (_initialized) + { + // Do not clear initialized. Always restore the _predecessor in Dispose() + // _initialized = false; + AssemblyLoadContext.SetCurrentContextualReflectionContext(_predecessor); + } + } + } } internal sealed class DefaultAssemblyLoadContext : AssemblyLoadContext