Contextual reflection (#23740)
authorSteve MacLean <stmaclea@microsoft.com>
Thu, 11 Apr 2019 04:20:39 +0000 (00:20 -0400)
committerGitHub <noreply@github.com>
Thu, 11 Apr 2019 04:20:39 +0000 (00:20 -0400)
* Add ContextualReflection APIs

Add ContextualReflection APIs approved in dotnet/corefx#36236
Fix issue #22213

* SetParentAssembly even when IsCollectible()
* ContextualReflection tests

* PR Feedback
* Add more usage tests

Add using statement tests
Add bad usage tests including Assert.Throws<>

* Only initialize on set
* Add XML API comments
* Unify VerifyIsolation
* Fix unused expectedAssembly
* Remove ContextualReflectionScope throw
* Clean up TestResolveMissingAssembly et. al
* Remove unused QCall::AppDomainHandle
* Remove AppDomainBaseObject
* Pass AssemblyLoadContext as managed object to native
* Fix AssemblyLoadContextBaseObject packing

* AssemblyLoadContext backing stores

Use explicit backing stores for events and properties

* Remove StaticAsyncLocalCurrentContextualReflectionContext
* Remove PermissionSetObject

23 files changed:
src/System.Private.CoreLib/shared/System/Activator.RuntimeType.cs
src/System.Private.CoreLib/shared/System/Runtime/Loader/AssemblyLoadContext.cs
src/System.Private.CoreLib/src/System/Reflection/Assembly.CoreCLR.cs
src/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs
src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.CoreCLR.cs
src/System.Private.CoreLib/src/System/RuntimeHandles.cs
src/System.Private.CoreLib/src/System/TypeNameParser.cs
src/strongname/api/common.h
src/vm/assemblynative.cpp
src/vm/assemblynative.hpp
src/vm/common.h
src/vm/mscorlib.h
src/vm/object.h
src/vm/qcall.cpp
src/vm/qcall.h
src/vm/runtimehandles.cpp
src/vm/runtimehandles.h
src/vm/typeparse.cpp
src/vm/vars.hpp
tests/src/Loader/ContextualReflection/ContextualReflection.cs [new file with mode: 0644]
tests/src/Loader/ContextualReflection/ContextualReflection.csproj [new file with mode: 0644]
tests/src/Loader/ContextualReflection/ContextualReflectionDependency.cs [new file with mode: 0644]
tests/src/Loader/ContextualReflection/ContextualReflectionDependency.csproj [new file with mode: 0644]

index f328b9c..270aa6a 100644 (file)
@@ -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 @@ namespace System
                 {
                     // Classic managed type
                     assembly = RuntimeAssembly.InternalLoadAssemblyName(
-                        assemblyName, ref stackMark);
+                        assemblyName, ref stackMark, AssemblyLoadContext.CurrentContextualReflectionContext);
                 }
             }
 
index 8316e10..4efd5de 100644 (file)
@@ -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 @@ namespace System.Runtime.Loader
         private static readonly Dictionary<long, WeakReference<AssemblyLoadContext>> s_allContexts = new Dictionary<long, WeakReference<AssemblyLoadContext>>();
         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<Assembly, string, IntPtr> _resolvingUnmanagedDll;
+
+        private event Func<AssemblyLoadContext, AssemblyName, Assembly> _resolving;
+
+        private event Action<AssemblyLoadContext> _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 @@ namespace System.Runtime.Loader
         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 @@ namespace System.Runtime.Loader
         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 @@ namespace System.Runtime.Loader
         //
         // Inputs: Invoking assembly, and library name to resolve
         // Returns: A handle to the loaded native library
-        public event Func<Assembly, string, IntPtr> ResolvingUnmanagedDll;
+        public event Func<Assembly, string, IntPtr> 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 @@ namespace System.Runtime.Loader
         //
         // Inputs: The AssemblyLoadContext and AssemblyName to be loaded
         // Returns: The Loaded assembly object.
-        public event Func<AssemblyLoadContext, AssemblyName, Assembly> Resolving;
+        public event Func<AssemblyLoadContext, AssemblyName, Assembly> Resolving
+        {
+            add
+            {
+                _resolving += value;
+            }
+            remove
+            {
+                _resolving -= value;
+            }
+        }
 
-        public event Action<AssemblyLoadContext> Unloading;
+        public event Action<AssemblyLoadContext> Unloading
+        {
+            add
+            {
+                _unloading += value;
+            }
+            remove
+            {
+                _unloading -= value;
+            }
+        }
 
         // Occurs when an Assembly is loaded
         public static event AssemblyLoadEventHandler AssemblyLoad;
@@ -180,9 +226,9 @@ namespace System.Runtime.Loader
 
         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 @@ namespace System.Runtime.Loader
 
             // 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 @@ namespace System.Runtime.Loader
                 throw new InvalidOperationException(SR.AssemblyLoadContext_Verify_NotUnloading);
             }
         }
+
+        private static AsyncLocal<AssemblyLoadContext> s_asyncLocalCurrent;
+
+        /// <summary>Nullable current AssemblyLoadContext used for context sensitive reflection APIs</summary>
+        /// <remarks>
+        /// 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.
+        /// * <see cref="System.Activator.CreateInstance" />
+        /// * <see cref="System.Reflection.Assembly.Load" />
+        /// * <see cref="System.Reflection.Assembly.GetType" />
+        /// * <see cref="System.Type.GetType" />
+        ///
+        /// 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
+        /// <see cref="System.Runtime.Loader.AssemblyLoadContext.EnterContextualReflection"/>.
+        ///
+        /// The property is stored in an AsyncLocal&lt;AssemblyLoadContext&gt;. 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
+        /// </remarks>
+        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<AssemblyLoadContext>(), null);
+            }
+            s_asyncLocalCurrent.Value = value;
+        }
+
+        /// <summary>Enter scope using this AssemblyLoadContext for ContextualReflection</summary>
+        /// <returns>A disposable ContextualReflectionScope for use in a using block</returns>
+        /// <remarks>
+        /// Sets CurrentContextualReflectionContext to this instance.
+        /// <see cref="System.Runtime.Loader.AssemblyLoadContext.CurrentContextualReflectionContext"/>
+        ///
+        /// 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.
+        /// </remarks>
+        public ContextualReflectionScope EnterContextualReflection()
+        {
+            return new ContextualReflectionScope(this);
+        }
+
+        /// <summary>Enter scope using this AssemblyLoadContext for ContextualReflection</summary>
+        /// <param name="activating">Set CurrentContextualReflectionContext to the AssemblyLoadContext which loaded activating.</param>
+        /// <returns>A disposable ContextualReflectionScope for use in a using block</returns>
+        /// <remarks>
+        /// Sets CurrentContextualReflectionContext to to the AssemblyLoadContext which loaded activating.
+        /// <see cref="System.Runtime.Loader.AssemblyLoadContext.CurrentContextualReflectionContext"/>
+        ///
+        /// 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.
+        /// </remarks>
+        public static ContextualReflectionScope EnterContextualReflection(Assembly activating)
+        {
+            return activating != null ?
+                GetLoadContext(activating).EnterContextualReflection() :
+                new ContextualReflectionScope(null);
+        }
+
+        /// <summary>Opaque disposable struct used to restore CurrentContextualReflectionContext</summary>
+        /// <remarks>
+        /// 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.
+        /// <see cref="System.Runtime.Loader.AssemblyLoadContext.EnterContextualReflection"/>
+        /// </remarks>
+        [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
index 4683b88..42736a9 100644 (file)
@@ -15,13 +15,13 @@ namespace System.Reflection
 {
     public abstract partial class Assembly : ICustomAttributeProvider, ISerializable
     {
-        // Locate an assembly by the long form of the assembly name. 
+        // Locate an assembly by the long form of the assembly name.
         // eg. "Toolbox.dll, version=1.1.10.1220, locale=en, publickey=1234567890123456789012345678901234567890"
         [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
         public static Assembly Load(string assemblyString)
         {
             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
-            return RuntimeAssembly.InternalLoad(assemblyString, ref stackMark);
+            return RuntimeAssembly.InternalLoad(assemblyString, ref stackMark, AssemblyLoadContext.CurrentContextualReflectionContext);
         }
 
         // Locate an assembly by its name. The name can be strong or
@@ -33,12 +33,12 @@ namespace System.Reflection
                 throw new ArgumentNullException(nameof(assemblyRef));
 
             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
-            return Load(assemblyRef, ref stackMark, IntPtr.Zero);
+            return Load(assemblyRef, ref stackMark, AssemblyLoadContext.CurrentContextualReflectionContext);
         }
 
         // Locate an assembly by its name. The name can be strong or
         // weak. The assembly is loaded into the domain of the caller.
-        internal static Assembly Load(AssemblyName assemblyRef, ref StackCrawlMark stackMark, IntPtr ptrLoadContextBinder)
+        internal static Assembly Load(AssemblyName assemblyRef, ref StackCrawlMark stackMark, AssemblyLoadContext assemblyLoadContext)
         {
             AssemblyName modifiedAssemblyRef = null;
             if (assemblyRef.CodeBase != null)
@@ -51,7 +51,7 @@ namespace System.Reflection
                 modifiedAssemblyRef = assemblyRef;
             }
 
-            return RuntimeAssembly.InternalLoadAssemblyName(modifiedAssemblyRef, ref stackMark, ptrLoadContextBinder);
+            return RuntimeAssembly.InternalLoadAssemblyName(modifiedAssemblyRef, ref stackMark, assemblyLoadContext);
         }
 
         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
index bb88978..64622f5 100644 (file)
@@ -10,6 +10,7 @@ using System.IO;
 using StringBuilder = System.Text.StringBuilder;
 using System.Configuration.Assemblies;
 using StackCrawlMark = System.Threading.StackCrawlMark;
+using System.Runtime.Loader;
 using System.Runtime.InteropServices;
 using System.Runtime.CompilerServices;
 using System.Runtime.Serialization;
@@ -149,11 +150,12 @@ namespace System.Reflection
 
         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
         private static extern void GetType(RuntimeAssembly assembly,
-                                                        string name,
-                                                        bool throwOnError,
-                                                        bool ignoreCase,
-                                                        ObjectHandleOnStack type,
-                                                        ObjectHandleOnStack keepAlive);
+                                            string name,
+                                            bool throwOnError,
+                                            bool ignoreCase,
+                                            ObjectHandleOnStack type,
+                                            ObjectHandleOnStack keepAlive,
+                                            ObjectHandleOnStack assemblyLoadContext);
 
         public override Type GetType(string name, bool throwOnError, bool ignoreCase)
         {
@@ -163,7 +165,15 @@ namespace System.Reflection
 
             RuntimeType type = null;
             object keepAlive = null;
-            GetType(GetNativeHandle(), name, throwOnError, ignoreCase, JitHelpers.GetObjectHandleOnStack(ref type), JitHelpers.GetObjectHandleOnStack(ref keepAlive));
+            AssemblyLoadContext assemblyLoadContextStack = AssemblyLoadContext.CurrentContextualReflectionContext;
+
+            GetType(GetNativeHandle(),
+                    name,
+                    throwOnError,
+                    ignoreCase,
+                    JitHelpers.GetObjectHandleOnStack(ref type),
+                    JitHelpers.GetObjectHandleOnStack(ref keepAlive),
+                    JitHelpers.GetObjectHandleOnStack(ref assemblyLoadContextStack));
             GC.KeepAlive(keepAlive);
 
             return type;
@@ -293,7 +303,7 @@ namespace System.Reflection
             return CustomAttributeData.GetCustomAttributesInternal(this);
         }
 
-        internal static RuntimeAssembly InternalLoad(string assemblyString, ref StackCrawlMark stackMark)
+        internal static RuntimeAssembly InternalLoad(string assemblyString, ref StackCrawlMark stackMark, AssemblyLoadContext assemblyLoadContext = null)
         {
             RuntimeAssembly assembly;
             AssemblyName an = CreateAssemblyName(assemblyString, out assembly);
@@ -304,7 +314,7 @@ namespace System.Reflection
                 return assembly;
             }
 
-            return InternalLoadAssemblyName(an, ref stackMark);
+            return InternalLoadAssemblyName(an, ref stackMark, assemblyLoadContext);
         }
 
         // Creates AssemblyName. Fills assembly if AssemblyResolve event has been raised.
@@ -327,7 +337,7 @@ namespace System.Reflection
             return an;
         }
 
-        internal static RuntimeAssembly InternalLoadAssemblyName(AssemblyName assemblyRef, ref StackCrawlMark stackMark, IntPtr ptrLoadContextBinder = default)
+        internal static RuntimeAssembly InternalLoadAssemblyName(AssemblyName assemblyRef, ref StackCrawlMark stackMark, AssemblyLoadContext assemblyLoadContext = null)
         {
 #if FEATURE_APPX
             if (ApplicationModel.IsUap)
@@ -348,7 +358,7 @@ namespace System.Reflection
 
             string codeBase = VerifyCodeBase(assemblyRef.CodeBase);
 
-            return nLoad(assemblyRef, codeBase, null, ref stackMark, true, ptrLoadContextBinder);
+            return nLoad(assemblyRef, codeBase, null, ref stackMark, true, assemblyLoadContext);
         }
 
         [MethodImplAttribute(MethodImplOptions.InternalCall)]
@@ -357,7 +367,7 @@ namespace System.Reflection
                                                     RuntimeAssembly assemblyContext,
                                                     ref StackCrawlMark stackMark,
                                                     bool throwOnFileNotFound,
-                                                    IntPtr ptrLoadContextBinder);
+                                                    AssemblyLoadContext assemblyLoadContext = null);
 
         public override bool ReflectionOnly
         {
@@ -647,7 +657,7 @@ namespace System.Reflection
             // This stack crawl mark is never used because the requesting assembly is explicitly specified,
             // so the value could be anything.
             StackCrawlMark unused = default;
-            RuntimeAssembly retAssembly = nLoad(an, null, this, ref unused, throwOnFileNotFound, IntPtr.Zero);
+            RuntimeAssembly retAssembly = nLoad(an, null, this, ref unused, throwOnFileNotFound);
 
             if (retAssembly == this)
             {
index 5cabe51..c95043b 100644 (file)
@@ -104,7 +104,7 @@ namespace System.Runtime.Loader
         {
             Assembly resolvedAssembly = null;
 
-            Func<AssemblyLoadContext, AssemblyName, Assembly> assemblyResolveHandler = Resolving;
+            Func<AssemblyLoadContext, AssemblyName, Assembly> assemblyResolveHandler = _resolving;
 
             if (assemblyResolveHandler != null)
             {
@@ -200,7 +200,7 @@ namespace System.Runtime.Loader
         {
             IntPtr resolvedDll = IntPtr.Zero;
 
-            Func<Assembly, string, IntPtr> dllResolveHandler = ResolvingUnmanagedDll;
+            Func<Assembly, string, IntPtr> dllResolveHandler = _resolvingUnmanagedDll;
 
             if (dllResolveHandler != null)
             {
index 56694c6..a2da2be 100644 (file)
@@ -6,6 +6,7 @@ using System.Diagnostics;
 using System.Reflection;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
+using System.Runtime.Loader;
 using System.Runtime.Serialization;
 using System.Threading;
 
@@ -414,17 +415,17 @@ namespace System
 
         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
         private static extern void GetTypeByName(string name, bool throwOnError, bool ignoreCase, StackCrawlMarkHandle stackMark,
-            IntPtr pPrivHostBinder,
+            ObjectHandleOnStack assemblyLoadContext,
             bool loadTypeFromPartialName, ObjectHandleOnStack type, ObjectHandleOnStack keepalive);
 
         // Wrapper function to reduce the need for ifdefs.
         internal static RuntimeType GetTypeByName(string name, bool throwOnError, bool ignoreCase, ref StackCrawlMark stackMark, bool loadTypeFromPartialName)
         {
-            return GetTypeByName(name, throwOnError, ignoreCase, ref stackMark, IntPtr.Zero, loadTypeFromPartialName);
+            return GetTypeByName(name, throwOnError, ignoreCase, ref stackMark, AssemblyLoadContext.CurrentContextualReflectionContext, loadTypeFromPartialName);
         }
 
         internal static RuntimeType GetTypeByName(string name, bool throwOnError, bool ignoreCase, ref StackCrawlMark stackMark,
-                                                  IntPtr pPrivHostBinder,
+                                                  AssemblyLoadContext assemblyLoadContext,
                                                   bool loadTypeFromPartialName)
         {
             if (name == null || name.Length == 0)
@@ -438,9 +439,10 @@ namespace System
             RuntimeType type = null;
 
             object keepAlive = null;
+            AssemblyLoadContext assemblyLoadContextStack = assemblyLoadContext;
             GetTypeByName(name, throwOnError, ignoreCase,
                 JitHelpers.GetStackCrawlMarkHandle(ref stackMark),
-                pPrivHostBinder,
+                JitHelpers.GetObjectHandleOnStack(ref assemblyLoadContextStack),
                 loadTypeFromPartialName, JitHelpers.GetObjectHandleOnStack(ref type), JitHelpers.GetObjectHandleOnStack(ref keepAlive));
             GC.KeepAlive(keepAlive);
 
index 3cd904e..4c96d78 100644 (file)
@@ -6,12 +6,13 @@ using System;
 using System.Diagnostics;
 using System.IO;
 using System.Reflection;
-using System.Security;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
+using System.Runtime.Loader;
+using System.Runtime.Versioning;
+using System.Security;
 using System.Text;
 using System.Threading;
-using System.Runtime.Versioning;
 using Microsoft.Win32.SafeHandles;
 
 namespace System
@@ -192,7 +193,7 @@ namespace System
             {
                 if (throwOnError)
                 {
-                    assembly = RuntimeAssembly.InternalLoad(asmName, ref stackMark);
+                    assembly = RuntimeAssembly.InternalLoad(asmName, ref stackMark, AssemblyLoadContext.CurrentContextualReflectionContext);
                 }
                 else
                 {
@@ -200,7 +201,7 @@ namespace System
                     // Other exceptions like BadImangeFormatException should still fly.
                     try
                     {
-                        assembly = RuntimeAssembly.InternalLoad(asmName, ref stackMark);
+                        assembly = RuntimeAssembly.InternalLoad(asmName, ref stackMark, AssemblyLoadContext.CurrentContextualReflectionContext);
                     }
                     catch (FileNotFoundException)
                     {
index 626d9bb..4347eba 100644 (file)
 
 typedef VPTR(class LoaderAllocator)     PTR_LoaderAllocator;
 typedef VPTR(class AppDomain)           PTR_AppDomain;
-typedef VPTR(class AppDomainBaseObject) PTR_AppDomainBaseObject;
 typedef DPTR(class ArrayBase)           PTR_ArrayBase;
 typedef DPTR(class ArrayTypeDesc)       PTR_ArrayTypeDesc;
 typedef DPTR(class Assembly)            PTR_Assembly;
 typedef DPTR(class AssemblyBaseObject)  PTR_AssemblyBaseObject;
+typedef DPTR(class AssemblyLoadContextBaseObject) PTR_AssemblyLoadContextBaseObject;
 typedef DPTR(class AssemblyNameBaseObject) PTR_AssemblyNameBaseObject;
 typedef VPTR(class BaseDomain)          PTR_BaseDomain;
 typedef DPTR(class MscorlibBinder)      PTR_MscorlibBinder;
index 9c1e467..66214a1 100644 (file)
@@ -38,22 +38,24 @@ FCIMPL6(Object*, AssemblyNative::Load, AssemblyNameBaseObject* assemblyNameUNSAF
         AssemblyBaseObject* requestingAssemblyUNSAFE,
         StackCrawlMark* stackMark,
         CLR_BOOL fThrowOnFileNotFound,
-        INT_PTR ptrLoadContextBinder)
+        AssemblyLoadContextBaseObject *assemblyLoadContextUNSAFE)
 {
     FCALL_CONTRACT;
 
     struct _gc
     {
-        ASSEMBLYNAMEREF assemblyName;
-        STRINGREF       codeBase;
-        ASSEMBLYREF     requestingAssembly; 
-        ASSEMBLYREF     rv;
+        ASSEMBLYNAMEREF        assemblyName;
+        STRINGREF              codeBase;
+        ASSEMBLYREF            requestingAssembly;
+        ASSEMBLYREF            rv;
+        ASSEMBLYLOADCONTEXTREF assemblyLoadContext;
     } gc;
 
-    gc.assemblyName    = (ASSEMBLYNAMEREF) assemblyNameUNSAFE;
-    gc.codeBase        = (STRINGREF)       codeBaseUNSAFE;
-    gc.requestingAssembly    = (ASSEMBLYREF)     requestingAssemblyUNSAFE;
-    gc.rv              = NULL;
+    gc.assemblyName        = (ASSEMBLYNAMEREF)        assemblyNameUNSAFE;
+    gc.codeBase            = (STRINGREF)              codeBaseUNSAFE;
+    gc.requestingAssembly  = (ASSEMBLYREF)            requestingAssemblyUNSAFE;
+    gc.rv                  = NULL;
+    gc.assemblyLoadContext = (ASSEMBLYLOADCONTEXTREF) assemblyLoadContextUNSAFE;
 
     HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
 
@@ -66,6 +68,8 @@ FCIMPL6(Object*, AssemblyNative::Load, AssemblyNameBaseObject* assemblyNameUNSAF
     DomainAssembly * pParentAssembly = NULL;
     Assembly * pRefAssembly = NULL;
 
+    INT_PTR ptrLoadContextBinder = (gc.assemblyLoadContext != NULL) ? gc.assemblyLoadContext->GetNativeAssemblyLoadContext() : NULL;
+
     if(gc.assemblyName->GetSimpleName() == NULL)
     {
         if (gc.codeBase == NULL)
@@ -446,7 +450,13 @@ void QCALLTYPE AssemblyNative::LoadTypeForWinRTTypeNameInContext(INT_PTR ptrAsse
 }
 #endif
 
-void QCALLTYPE AssemblyNative::GetType(QCall::AssemblyHandle pAssembly, LPCWSTR wszName, BOOL bThrowOnError, BOOL bIgnoreCase, QCall::ObjectHandleOnStack retType, QCall::ObjectHandleOnStack keepAlive)
+void QCALLTYPE AssemblyNative::GetType(QCall::AssemblyHandle pAssembly,
+                                       LPCWSTR wszName,
+                                       BOOL bThrowOnError,
+                                       BOOL bIgnoreCase,
+                                       QCall::ObjectHandleOnStack retType,
+                                       QCall::ObjectHandleOnStack keepAlive,
+                                       QCall::ObjectHandleOnStack pAssemblyLoadContext)
 {
     CONTRACTL
     {
@@ -464,8 +474,20 @@ void QCALLTYPE AssemblyNative::GetType(QCall::AssemblyHandle pAssembly, LPCWSTR
 
     BOOL prohibitAsmQualifiedName = TRUE;
 
+    ICLRPrivBinder * pPrivHostBinder = NULL;
+
+    if (*pAssemblyLoadContext.m_ppObject != NULL)
+    {
+        GCX_COOP();
+        ASSEMBLYLOADCONTEXTREF * pAssemblyLoadContextRef = reinterpret_cast<ASSEMBLYLOADCONTEXTREF *>(pAssemblyLoadContext.m_ppObject);
+
+        INT_PTR nativeAssemblyLoadContext = (*pAssemblyLoadContextRef)->GetNativeAssemblyLoadContext();
+
+        pPrivHostBinder = reinterpret_cast<ICLRPrivBinder *>(nativeAssemblyLoadContext);
+    }
+
     // Load the class from this assembly (fail if it is in a different one).
-    retTypeHandle = TypeName::GetTypeManaged(wszName, pAssembly, bThrowOnError, bIgnoreCase, prohibitAsmQualifiedName, NULL, FALSE, (OBJECTREF*)keepAlive.m_ppObject);
+    retTypeHandle = TypeName::GetTypeManaged(wszName, pAssembly, bThrowOnError, bIgnoreCase, prohibitAsmQualifiedName, pAssembly->GetAssembly(), FALSE, (OBJECTREF*)keepAlive.m_ppObject, pPrivHostBinder);
 
     if (!retTypeHandle.IsNull())
     {
index 0ce2fb2..12d4ff8 100644 (file)
@@ -37,7 +37,7 @@ public:
                                                                 AssemblyBaseObject* requestingAssemblyUNSAFE,
                                                                 StackCrawlMark* stackMark,
                                                                 CLR_BOOL fThrowOnFileNotFound,
-                                                                INT_PTR ptrLoadContextBinder);
+                                                                AssemblyLoadContextBaseObject *assemblyLoadContextUNSAFE);
 
     //
     // instance FCALLs
@@ -78,7 +78,7 @@ public:
     void QCALLTYPE GetVersion(QCall::AssemblyHandle pAssembly, INT32* pMajorVersion, INT32* pMinorVersion, INT32*pBuildNumber, INT32* pRevisionNumber);
 
     static 
-    void QCALLTYPE GetType(QCall::AssemblyHandle pAssembly, LPCWSTR wszName, BOOL bThrowOnError, BOOL bIgnoreCase, QCall::ObjectHandleOnStack retType, QCall::ObjectHandleOnStack keepAlive);
+    void QCALLTYPE GetType(QCall::AssemblyHandle pAssembly, LPCWSTR wszName, BOOL bThrowOnError, BOOL bIgnoreCase, QCall::ObjectHandleOnStack retType, QCall::ObjectHandleOnStack keepAlive, QCall::ObjectHandleOnStack pAssemblyLoadContext);
 
     static
     void QCALLTYPE GetForwardedType(QCall::AssemblyHandle pAssembly, mdToken mdtExternalType, QCall::ObjectHandleOnStack retType);
index 61ba2a7..6fd5a37 100644 (file)
 
 typedef VPTR(class LoaderAllocator)     PTR_LoaderAllocator;
 typedef VPTR(class AppDomain)           PTR_AppDomain;
-typedef VPTR(class AppDomainBaseObject) PTR_AppDomainBaseObject;
 typedef DPTR(class ArrayBase)           PTR_ArrayBase;
 typedef DPTR(class ArrayTypeDesc)       PTR_ArrayTypeDesc;
 typedef DPTR(class Assembly)            PTR_Assembly;
 typedef DPTR(class AssemblyBaseObject)  PTR_AssemblyBaseObject;
+typedef DPTR(class AssemblyLoadContextBaseObject) PTR_AssemblyLoadContextBaseObject;
 typedef DPTR(class AssemblyNameBaseObject) PTR_AssemblyNameBaseObject;
 typedef VPTR(class BaseDomain)          PTR_BaseDomain;
 typedef DPTR(class ClassLoader)         PTR_ClassLoader;
index 32d19cb..ccdec0f 100644 (file)
@@ -889,6 +889,16 @@ DEFINE_METHOD(UNHANDLED_EVENTARGS,  CTOR,                   .ctor,
 DEFINE_CLASS(FIRSTCHANCE_EVENTARGS,   ExceptionServices,      FirstChanceExceptionEventArgs)
 DEFINE_METHOD(FIRSTCHANCE_EVENTARGS,  CTOR,                   .ctor,                      IM_Exception_RetVoid)
 
+DEFINE_CLASS_U(Loader,             AssemblyLoadContext,           AssemblyLoadContextBaseObject)
+DEFINE_FIELD_U(_unloadLock,                 AssemblyLoadContextBaseObject, _unloadLock)
+DEFINE_FIELD_U(_resolvingUnmanagedDll,      AssemblyLoadContextBaseObject, _resovlingUnmanagedDll)
+DEFINE_FIELD_U(_resolving,                  AssemblyLoadContextBaseObject, _resolving)
+DEFINE_FIELD_U(_unloading,                  AssemblyLoadContextBaseObject, _unloading)
+DEFINE_FIELD_U(_name,                       AssemblyLoadContextBaseObject, _name)
+DEFINE_FIELD_U(_nativeAssemblyLoadContext,  AssemblyLoadContextBaseObject, _nativeAssemblyLoadContext)
+DEFINE_FIELD_U(_id,                         AssemblyLoadContextBaseObject, _id)
+DEFINE_FIELD_U(_state,                      AssemblyLoadContextBaseObject, _state)
+DEFINE_FIELD_U(_isCollectible,              AssemblyLoadContextBaseObject, _isCollectible)
 DEFINE_CLASS(ASSEMBLYLOADCONTEXT,  Loader,                AssemblyLoadContext)
 DEFINE_METHOD(ASSEMBLYLOADCONTEXT,  RESOLVE,          Resolve,                      SM_IntPtr_AssemblyName_RetAssemblyBase)
 DEFINE_METHOD(ASSEMBLYLOADCONTEXT,  RESOLVEUNMANAGEDDLL,          ResolveUnmanagedDll,                      SM_Str_IntPtr_RetIntPtr)
index 590f1e0..cc0810f 100644 (file)
@@ -1610,6 +1610,53 @@ class AssemblyBaseObject : public Object
 NOINLINE AssemblyBaseObject* GetRuntimeAssemblyHelper(LPVOID __me, DomainAssembly *pAssembly, OBJECTREF keepAlive);
 #define FC_RETURN_ASSEMBLY_OBJECT(pAssembly, refKeepAlive) FC_INNER_RETURN(AssemblyBaseObject*, GetRuntimeAssemblyHelper(__me, pAssembly, refKeepAlive))
 
+// AssemblyLoadContextBaseObject
+// This class is the base class for AssemblyLoadContext
+//
+#if (defined(_TARGET_X86_) || defined(_TARGET_ARM_)) && !defined(FEATURE_PAL)
+#include "pshpack4.h"
+#endif // (defined(_TARGET_X86_) || defined(_TARGET_ARM_)) && !defined(FEATURE_PAL)
+class AssemblyLoadContextBaseObject : public Object
+{
+    friend class MscorlibBinder;
+
+  protected:
+    // READ ME:
+    // Modifying the order or fields of this object may require other changes to the
+    //  classlib class definition of this object.
+#ifdef _TARGET_64BIT_
+    OBJECTREF     _unloadLock;
+    OBJECTREF     _resovlingUnmanagedDll;
+    OBJECTREF     _resolving;
+    OBJECTREF     _unloading;
+    OBJECTREF     _name;
+    INT_PTR       _nativeAssemblyLoadContext;
+    int64_t       _id; // On 64-bit platforms this is a value type so it is placed after references and pointers
+    DWORD         _state;
+    CLR_BOOL      _isCollectible;
+#else // _TARGET_64BIT_
+    int64_t       _id; // On 32-bit platforms this 64-bit value type is larger than a pointer so JIT places it first
+    OBJECTREF     _unloadLock;
+    OBJECTREF     _resovlingUnmanagedDll;
+    OBJECTREF     _resolving;
+    OBJECTREF     _unloading;
+    OBJECTREF     _name;
+    INT_PTR       _nativeAssemblyLoadContext;
+    DWORD         _state;
+    CLR_BOOL      _isCollectible;
+#endif // _TARGET_64BIT_
+
+  protected:
+    AssemblyLoadContextBaseObject() { LIMITED_METHOD_CONTRACT; }
+   ~AssemblyLoadContextBaseObject() { LIMITED_METHOD_CONTRACT; }
+
+  public:
+    INT_PTR GetNativeAssemblyLoadContext() { LIMITED_METHOD_CONTRACT; return _nativeAssemblyLoadContext; }
+};
+#if (defined(_TARGET_X86_) || defined(_TARGET_ARM_)) && !defined(FEATURE_PAL)
+#include "poppack.h"
+#endif // (defined(_TARGET_X86_) || defined(_TARGET_ARM_)) && !defined(FEATURE_PAL)
+
 // AssemblyNameBaseObject 
 // This class is the base class for assembly names
 //  
@@ -1700,12 +1747,12 @@ typedef REF<ReflectFieldObject> REFLECTFIELDREF;
 
 typedef REF<ThreadBaseObject> THREADBASEREF;
 
-typedef REF<AppDomainBaseObject> APPDOMAINREF;
-
 typedef REF<MarshalByRefObjectBaseObject> MARSHALBYREFOBJECTBASEREF;
 
 typedef REF<AssemblyBaseObject> ASSEMBLYREF;
 
+typedef REF<AssemblyLoadContextBaseObject> ASSEMBLYLOADCONTEXTREF;
+
 typedef REF<AssemblyNameBaseObject> ASSEMBLYNAMEREF;
 
 typedef REF<VersionBaseObject> VERSIONREF;
@@ -1753,6 +1800,7 @@ typedef PTR_ReflectMethodObject REFLECTMETHODREF;
 typedef PTR_ReflectFieldObject REFLECTFIELDREF;
 typedef PTR_ThreadBaseObject THREADBASEREF;
 typedef PTR_AssemblyBaseObject ASSEMBLYREF;
+typedef PTR_AssemblyLoadContextBaseObject ASSEMBLYLOADCONTEXTREF;
 typedef PTR_AssemblyNameBaseObject ASSEMBLYNAMEREF;
 
 #ifndef DACCESS_COMPILE
index 3b35f90..1b7bbda 100644 (file)
@@ -76,25 +76,3 @@ void QCall::ObjectHandleOnStack::SetGuidArray(const GUID * p, COUNT_T length)
     memcpyNoGCRefs(arr->GetDataPtr(), p, length * sizeof(GUID));
     Set(arr);
 }
-
-//
-// Helpers for passing an AppDomain to a QCall
-//
-
-#ifdef _DEBUG
-
-//---------------------------------------------------------------------------------------
-//
-// Verify that the AppDomain being passed from the BCL is valid for use in a QCall. Note: some additional
-// checks are in System.AppDomain.GetNativeHandle()
-//
-
-void QCall::AppDomainHandle::VerifyDomainHandle() const
-{
-    LIMITED_METHOD_CONTRACT;
-
-    // System.AppDomain.GetNativeHandle() should ensure that we're not calling through with a null AppDomain pointer.
-    _ASSERTE(m_pAppDomain);
-}
-
-#endif // _DEBUG
index 3ad328a..dd60bbb 100644 (file)
@@ -237,35 +237,6 @@ public:
         }
     };
 
-    // AppDomainHandle is used for passing AppDomains into QCalls via System.AppDomainHandle
-    struct AppDomainHandle
-    {
-        AppDomain *m_pAppDomain;
-
-        operator AppDomain *()
-        {
-            LIMITED_METHOD_CONTRACT;
-#ifdef _DEBUG
-            VerifyDomainHandle();
-#endif // _DEBUG
-            return m_pAppDomain;
-        }
-
-        AppDomain *operator->() const
-        {
-            LIMITED_METHOD_CONTRACT;
-#ifdef _DEBUG
-            VerifyDomainHandle();
-#endif // _DEBUG
-            return m_pAppDomain;
-        }
-
-    private:
-#ifdef _DEBUG
-        void VerifyDomainHandle() const;
-#endif // _DEBUG
-    };
-
     struct AssemblyHandle
     {
         DomainAssembly * m_pAssembly;
index c25bcbf..d0a2b04 100644 (file)
@@ -1415,7 +1415,7 @@ void QCALLTYPE RuntimeTypeHandle::GetTypeByNameUsingCARules(LPCWSTR pwzClassName
 
 void QCALLTYPE RuntimeTypeHandle::GetTypeByName(LPCWSTR pwzClassName, BOOL bThrowOnError, BOOL bIgnoreCase,
                                                 QCall::StackCrawlMarkHandle pStackMark, 
-                                                ICLRPrivBinder * pPrivHostBinder,
+                                                QCall::ObjectHandleOnStack pAssemblyLoadContext,
                                                 BOOL bLoadTypeFromPartialNameHack, QCall::ObjectHandleOnStack retType,
                                                 QCall::ObjectHandleOnStack keepAlive)
 {
@@ -1429,6 +1429,19 @@ void QCALLTYPE RuntimeTypeHandle::GetTypeByName(LPCWSTR pwzClassName, BOOL bThro
             COMPlusThrowArgumentNull(W("className"),W("ArgumentNull_String"));
 
     {
+        ICLRPrivBinder * pPrivHostBinder = NULL;
+
+        if (*pAssemblyLoadContext.m_ppObject != NULL)
+        {
+            GCX_COOP();
+            ASSEMBLYLOADCONTEXTREF * pAssemblyLoadContextRef = reinterpret_cast<ASSEMBLYLOADCONTEXTREF *>(pAssemblyLoadContext.m_ppObject);
+
+            INT_PTR nativeAssemblyLoadContext = (*pAssemblyLoadContextRef)->GetNativeAssemblyLoadContext();
+
+            pPrivHostBinder = reinterpret_cast<ICLRPrivBinder *>(nativeAssemblyLoadContext);
+        }
+
+
         typeHandle = TypeName::GetTypeManaged(pwzClassName, NULL, bThrowOnError, bIgnoreCase, /*bProhibitAsmQualifiedName =*/ FALSE,
                                               SystemDomain::GetCallersAssembly(pStackMark),
                                               bLoadTypeFromPartialNameHack, (OBJECTREF*)keepAlive.m_ppObject,
index 4c2ebdf..c83461a 100644 (file)
@@ -177,7 +177,7 @@ public:
     static
     void QCALLTYPE GetTypeByName(LPCWSTR pwzClassName, BOOL bThrowOnError, BOOL bIgnoreCase,
                                  QCall::StackCrawlMarkHandle pStackMark, 
-                                 ICLRPrivBinder * pPrivHostBinder,
+                                 QCall::ObjectHandleOnStack pAssemblyLoadContext,
                                  BOOL bLoadTypeFromPartialNameHack, QCall::ObjectHandleOnStack retType,
                                  QCall::ObjectHandleOnStack keepAlive);
 
index 858d9a3..0794454 100644 (file)
@@ -1507,20 +1507,23 @@ DomainAssembly * LoadDomainAssembly(
         spec.SetWindowsRuntimeType(*pssOuterTypeName);
     }
 
-    if (pPrivHostBinder)
-    {
-        spec.SetHostBinder(pPrivHostBinder);
-    }
-    else if (pRequestingAssembly && (!pRequestingAssembly->IsCollectible()))
+    if (pRequestingAssembly)
     {
         GCX_PREEMP();
         spec.SetParentAssembly(pRequestingAssembly->GetDomainAssembly());
     }
 
-    // If the requesting assembly has Fallback LoadContext binder available,
-    // then set it up in the AssemblySpec.
-    if (pRequestingAssembly != NULL)
+    // Have we been passed the reference to the binder against which this load should be triggered?
+    // If so, then use it to set the fallback load context binder.
+    if (pPrivHostBinder != NULL)
+    {
+        spec.SetFallbackLoadContextBinderForRequestingAssembly(pPrivHostBinder);
+        spec.SetPreferFallbackLoadContextBinder();
+    }
+    else if (pRequestingAssembly != NULL)
     {
+        // If the requesting assembly has Fallback LoadContext binder available,
+        // then set it up in the AssemblySpec.
         PEFile *pRequestingAssemblyManifestFile = pRequestingAssembly->GetManifestFile();
         spec.SetFallbackLoadContextBinderForRequestingAssembly(pRequestingAssemblyManifestFile->GetFallbackLoadContextBinder());
     }
index 2c3d978..68473b6 100644 (file)
@@ -162,8 +162,7 @@ class OBJECTREF {
 
         class ReflectClassBaseObject* m_asReflectClass;
         class ExecutionContextObject* m_asExecutionContext;
-        class AppDomainBaseObject* m_asAppDomainBase;
-        class PermissionSetObject* m_asPermissionSetObject;
+        class AssemblyLoadContextBaseObject* m_asAssemblyLoadContextBase;
     };
 
     public:
diff --git a/tests/src/Loader/ContextualReflection/ContextualReflection.cs b/tests/src/Loader/ContextualReflection/ContextualReflection.cs
new file mode 100644 (file)
index 0000000..3356e3d
--- /dev/null
@@ -0,0 +1,748 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.Loader;
+using System.Runtime.Remoting;
+using System.Threading.Tasks;
+using TestLibrary;
+
+namespace ContextualReflectionTest
+{
+    class AGenericClass<T>
+    {
+    }
+
+    class Program : IProgram
+    {
+        public AssemblyLoadContext alc { get; set; }
+        public Assembly alcAssembly { get; set; }
+        public Type alcProgramType { get; set; }
+        public IProgram alcProgramInstance { get; set; }
+        public Assembly defaultAssembly { get; set; }
+
+        public static int Main()
+        {
+            Program program = new Program(isolated:false);
+
+            program.RunTests();
+
+            Console.WriteLine("Success");
+
+            return 100;
+        }
+
+        public Program()
+        {
+            InitializeIsolation(true);
+        }
+
+        public Program(bool isolated)
+        {
+            InitializeIsolation(isolated);
+        }
+
+        public void InitializeIsolation(bool isolated)
+        {
+            if (isolated == false)
+            {
+                alc = new AssemblyLoadContext("Isolated", isCollectible: true);
+                defaultAssembly = Assembly.GetExecutingAssembly();
+                alcAssembly = alc.LoadFromAssemblyPath(defaultAssembly.Location);
+
+                Assert.AreEqual(alcAssembly, alc.LoadFromAssemblyName(alcAssembly.GetName()));
+
+                alcProgramType = alcAssembly.GetType("ContextualReflectionTest.Program");
+
+                AssemblyLoadContext.Default.Resolving += TestResolve.ResolvingTestDefault;
+                alc.Resolving += TestResolve.ResolvingTestIsolated;
+
+                alcProgramInstance = (IProgram) Activator.CreateInstance(alcProgramType);
+            }
+            else
+            {
+                alcAssembly = Assembly.GetExecutingAssembly();
+                alc = AssemblyLoadContext.GetLoadContext(alcAssembly);
+                alcProgramType = typeof(Program);
+                alcProgramInstance = this;
+                defaultAssembly = AssemblyLoadContext.Default.LoadFromAssemblyName(alcAssembly.GetName());
+            }
+        }
+
+        void VerifyIsolationDefault()
+        {
+            VerifyIsolation();
+            Assert.AreEqual(defaultAssembly, Assembly.GetExecutingAssembly());
+            Assert.AreEqual(AssemblyLoadContext.Default, AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()));
+            Assert.AreNotEqual(alcProgramType, typeof(Program));
+            Assert.AreNotEqual((object)alcProgramInstance, (object)this);
+        }
+
+        void VerifyIsolationAlc()
+        {
+            VerifyIsolation();
+            Assert.AreEqual(alcAssembly, Assembly.GetExecutingAssembly());
+            Assert.AreEqual(alc, AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()));
+            Assert.AreEqual(alcProgramType, typeof(Program));
+            Assert.AreEqual((object)alcProgramInstance, (object)this);
+        }
+
+        void VerifyIsolation()
+        {
+            Assert.AreEqual("Default", AssemblyLoadContext.Default.Name);
+
+            Assert.IsNotNull(alc);
+            Assert.IsNotNull(alcAssembly);
+            Assert.IsNotNull(alcProgramType);
+            Assert.IsNotNull(alcProgramInstance);
+
+            Assert.AreEqual("Isolated", alc.Name);
+
+            Assert.AreNotEqual(defaultAssembly, alcAssembly);
+            Assert.AreNotEqual(alc, AssemblyLoadContext.Default);
+
+            Assert.AreEqual(alc, AssemblyLoadContext.GetLoadContext(alcProgramInstance.alcAssembly));
+            Assert.AreEqual(alcAssembly, alcProgramInstance.alcAssembly);
+            Assert.AreEqual(alcProgramType, alcProgramInstance.alcProgramType);
+            Assert.AreEqual(alcProgramInstance, alcProgramInstance.alcProgramInstance);
+        }
+
+        void VerifyTestResolve()
+        {
+            TestResolve.Assert(ResolveEvents.ExpectedEvent, () => AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName("TestDefaultLoad")));
+            TestResolve.Assert(ResolveEvents.NoEvent, () => AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName("TestIsolatedLoad")));
+            TestResolve.Assert(ResolveEvents.ExpectedEvent, () => alc.LoadFromAssemblyName(new AssemblyName("TestIsolatedLoad")));
+            TestResolve.Assert(ResolveEvents.ExpectedEvent, () => alc.LoadFromAssemblyName(new AssemblyName("TestDefaultLoad")));
+
+            // Make sure failure is not cached
+            TestResolve.Assert(ResolveEvents.ExpectedEvent, () => AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName("TestDefaultLoad")));
+            TestResolve.Assert(ResolveEvents.NoEvent, () => AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName("TestIsolatedLoad")));
+            TestResolve.Assert(ResolveEvents.ExpectedEvent, () => alc.LoadFromAssemblyName(new AssemblyName("TestIsolatedLoad")));
+            TestResolve.Assert(ResolveEvents.ExpectedEvent, () => alc.LoadFromAssemblyName(new AssemblyName("TestDefaultLoad")));
+        }
+
+        void VerifyContextualReflectionProxy()
+        {
+            Assert.IsNull(ConntextualReflectionProxy.CurrentContextualReflectionContext);
+
+            using (ConntextualReflectionProxy.EnterContextualReflection(alc))
+            {
+                Assert.AreEqual(alc, ConntextualReflectionProxy.CurrentContextualReflectionContext);
+                using (ConntextualReflectionProxy.EnterContextualReflection(AssemblyLoadContext.Default))
+                {
+                    Assert.AreEqual(AssemblyLoadContext.Default, ConntextualReflectionProxy.CurrentContextualReflectionContext);
+                    using (ConntextualReflectionProxy.EnterContextualReflection((Assembly)null))
+                    {
+                        Assert.IsNull(ConntextualReflectionProxy.CurrentContextualReflectionContext);
+                        using (ConntextualReflectionProxy.EnterContextualReflection(alcAssembly))
+                        {
+                            Assert.AreEqual(alc, ConntextualReflectionProxy.CurrentContextualReflectionContext);
+                        }
+                        Assert.IsNull(ConntextualReflectionProxy.CurrentContextualReflectionContext);
+                    }
+                    Assert.AreEqual(AssemblyLoadContext.Default, ConntextualReflectionProxy.CurrentContextualReflectionContext);
+                }
+                Assert.AreEqual(alc, ConntextualReflectionProxy.CurrentContextualReflectionContext);
+            }
+            Assert.IsNull(ConntextualReflectionProxy.CurrentContextualReflectionContext);
+        }
+
+        void VerifyUsingStatementContextualReflectionUsage()
+        {
+            Assert.IsNull(ConntextualReflectionProxy.CurrentContextualReflectionContext);
+
+            {
+                using IDisposable alcScope = ConntextualReflectionProxy.EnterContextualReflection(alc);
+                Assert.AreEqual(alc, ConntextualReflectionProxy.CurrentContextualReflectionContext);
+            }
+
+            Assert.IsNull(ConntextualReflectionProxy.CurrentContextualReflectionContext);
+
+            {
+                using IDisposable alcScope = ConntextualReflectionProxy.EnterContextualReflection(alc);
+                Assert.AreEqual(alc, ConntextualReflectionProxy.CurrentContextualReflectionContext);
+                alcScope.Dispose();
+                Assert.IsNull(ConntextualReflectionProxy.CurrentContextualReflectionContext);
+            }
+
+            Assert.IsNull(ConntextualReflectionProxy.CurrentContextualReflectionContext);
+
+            {
+                using IDisposable alcScope = ConntextualReflectionProxy.EnterContextualReflection(alc);
+                Assert.AreEqual(alc, ConntextualReflectionProxy.CurrentContextualReflectionContext);
+                alcScope.Dispose();
+                Assert.IsNull(ConntextualReflectionProxy.CurrentContextualReflectionContext);
+                alcScope.Dispose();
+            }
+
+            Assert.IsNull(ConntextualReflectionProxy.CurrentContextualReflectionContext);
+
+            {
+                using IDisposable alcScope = ConntextualReflectionProxy.EnterContextualReflection(alc);
+                Assert.AreEqual(alc, ConntextualReflectionProxy.CurrentContextualReflectionContext);
+                {
+                    using IDisposable defaultScope = ConntextualReflectionProxy.EnterContextualReflection(AssemblyLoadContext.Default);
+                    Assert.AreEqual(AssemblyLoadContext.Default, ConntextualReflectionProxy.CurrentContextualReflectionContext);
+
+                }
+                Assert.AreEqual(alc, ConntextualReflectionProxy.CurrentContextualReflectionContext);
+            }
+
+            Assert.IsNull(ConntextualReflectionProxy.CurrentContextualReflectionContext);
+
+            {
+                using IDisposable alcScope = ConntextualReflectionProxy.EnterContextualReflection(alc);
+                Assert.AreEqual(alc, ConntextualReflectionProxy.CurrentContextualReflectionContext);
+                try
+                {
+                    using IDisposable defaultScope = ConntextualReflectionProxy.EnterContextualReflection(AssemblyLoadContext.Default);
+                    Assert.AreEqual(AssemblyLoadContext.Default, ConntextualReflectionProxy.CurrentContextualReflectionContext);
+
+                    throw new InvalidOperationException();
+                }
+                catch
+                {
+                }
+                Assert.AreEqual(alc, ConntextualReflectionProxy.CurrentContextualReflectionContext);
+            }
+
+            Assert.IsNull(ConntextualReflectionProxy.CurrentContextualReflectionContext);
+
+            {
+                using IDisposable alcScope = ConntextualReflectionProxy.EnterContextualReflection(alc);
+                Assert.AreEqual(alc, ConntextualReflectionProxy.CurrentContextualReflectionContext);
+                using IDisposable defaultScope = ConntextualReflectionProxy.EnterContextualReflection(AssemblyLoadContext.Default);
+                Assert.AreEqual(AssemblyLoadContext.Default, ConntextualReflectionProxy.CurrentContextualReflectionContext);
+                defaultScope.Dispose();
+                Assert.AreEqual(alc, ConntextualReflectionProxy.CurrentContextualReflectionContext);
+                alcScope.Dispose();
+            }
+
+            Assert.IsNull(ConntextualReflectionProxy.CurrentContextualReflectionContext);
+        }
+
+        void VerifyBadContextualReflectionUsage()
+        {
+            Assert.IsNull(ConntextualReflectionProxy.CurrentContextualReflectionContext);
+
+            {
+                IDisposable alcScope = ConntextualReflectionProxy.EnterContextualReflection(alc);
+                Assert.AreEqual(alc, ConntextualReflectionProxy.CurrentContextualReflectionContext);
+                alcScope.Dispose();
+            }
+
+            Assert.IsNull(ConntextualReflectionProxy.CurrentContextualReflectionContext);
+
+            {
+                IDisposable alcScope = ConntextualReflectionProxy.EnterContextualReflection(alc);
+                Assert.AreEqual(alc, ConntextualReflectionProxy.CurrentContextualReflectionContext);
+                alcScope.Dispose();
+                alcScope.Dispose();
+            }
+
+            Assert.IsNull(ConntextualReflectionProxy.CurrentContextualReflectionContext);
+
+            {
+                IDisposable alcScope = ConntextualReflectionProxy.EnterContextualReflection(alc);
+                Assert.AreEqual(alc, ConntextualReflectionProxy.CurrentContextualReflectionContext);
+                IDisposable defaultScope = ConntextualReflectionProxy.EnterContextualReflection(AssemblyLoadContext.Default);
+                Assert.AreEqual(AssemblyLoadContext.Default, ConntextualReflectionProxy.CurrentContextualReflectionContext);
+                defaultScope.Dispose();
+                Assert.AreEqual(alc, ConntextualReflectionProxy.CurrentContextualReflectionContext);
+                alcScope.Dispose();
+            }
+
+            Assert.IsNull(ConntextualReflectionProxy.CurrentContextualReflectionContext);
+
+            {
+                IDisposable alcScope = ConntextualReflectionProxy.EnterContextualReflection(alc);
+                Assert.AreEqual(alc, ConntextualReflectionProxy.CurrentContextualReflectionContext);
+                IDisposable defaultScope = ConntextualReflectionProxy.EnterContextualReflection(AssemblyLoadContext.Default);
+                Assert.AreEqual(AssemblyLoadContext.Default, ConntextualReflectionProxy.CurrentContextualReflectionContext);
+
+                alcScope.Dispose();
+                Assert.IsNull(ConntextualReflectionProxy.CurrentContextualReflectionContext);
+
+                defaultScope.Dispose();
+                Assert.AreEqual(alc, ConntextualReflectionProxy.CurrentContextualReflectionContext);
+                alcScope.Dispose();
+            }
+
+            Assert.IsNull(ConntextualReflectionProxy.CurrentContextualReflectionContext);
+
+            {
+                IDisposable alcScope = ConntextualReflectionProxy.EnterContextualReflection(alc);
+                Assert.AreEqual(alc, ConntextualReflectionProxy.CurrentContextualReflectionContext);
+                try
+                {
+                    IDisposable defaultScope = ConntextualReflectionProxy.EnterContextualReflection((Assembly)null);
+                    Assert.AreEqual(null, ConntextualReflectionProxy.CurrentContextualReflectionContext);
+
+                    throw new InvalidOperationException();
+                }
+                catch
+                {
+                }
+            }
+
+            Assert.IsNull(ConntextualReflectionProxy.CurrentContextualReflectionContext);
+        }
+
+        void TestResolveMissingAssembly(bool isolated, Action<string> action, bool skipNullIsolated = false)
+        {
+            using (ConntextualReflectionProxy.EnterContextualReflection((Assembly)null))
+            {
+                TestResolve.Assert(ResolveEvents.ExpectedEvent, () => action("TestDefaultLoad"));
+                if (!skipNullIsolated)
+                    TestResolve.Assert(isolated ? ResolveEvents.ExpectedEvent : ResolveEvents.NoEvent, () => action("TestIsolatedLoad"));
+            }
+            using (ConntextualReflectionProxy.EnterContextualReflection(AssemblyLoadContext.Default))
+            {
+                TestResolve.Assert(ResolveEvents.ExpectedEvent, () => action("TestDefaultLoad"));
+                TestResolve.Assert(ResolveEvents.NoEvent, () => action("TestIsolatedLoad"));
+            }
+            using (ConntextualReflectionProxy.EnterContextualReflection(alc))
+            {
+                TestResolve.Assert(ResolveEvents.ExpectedEvent, () => action("TestDefaultLoad"));
+                TestResolve.Assert(ResolveEvents.ExpectedEvent, () => action("TestIsolatedLoad"));
+            }
+        }
+
+        void TestAssemblyLoad(bool isolated)
+        {
+            TestAssemblyLoad(isolated, (string assemblyName) => Assembly.Load(assemblyName));
+            TestAssemblyLoad(isolated, (string assemblyName) => Assembly.Load(new AssemblyName(assemblyName)));
+        }
+
+        void TestAssemblyLoad(bool isolated, Func<string, Assembly> assemblyLoad)
+        {
+            TestResolveMissingAssembly(isolated, (string assemblyName) => assemblyLoad(assemblyName));
+
+            using (ConntextualReflectionProxy.EnterContextualReflection((Assembly)null))
+            {
+                Assembly assembly = assemblyLoad("ContextualReflection");
+
+                Assert.AreEqual(isolated ? alc : AssemblyLoadContext.Default, AssemblyLoadContext.GetLoadContext(assembly));
+
+                Assembly depends = assemblyLoad("ContextualReflectionDependency");
+
+                Assert.AreEqual(AssemblyLoadContext.Default, AssemblyLoadContext.GetLoadContext(depends));
+            }
+            using (ConntextualReflectionProxy.EnterContextualReflection(AssemblyLoadContext.Default))
+            {
+                Assembly assembly = assemblyLoad("ContextualReflection");
+
+                Assert.AreEqual(AssemblyLoadContext.Default, AssemblyLoadContext.GetLoadContext(assembly));
+
+                Assembly depends = assemblyLoad("ContextualReflectionDependency");
+
+                Assert.AreEqual(AssemblyLoadContext.Default, AssemblyLoadContext.GetLoadContext(depends));
+            }
+            using (ConntextualReflectionProxy.EnterContextualReflection(alc))
+            {
+                Assembly assembly = assemblyLoad("ContextualReflection");
+
+                Assert.AreEqual(alc, AssemblyLoadContext.GetLoadContext(assembly));
+
+                Assembly depends = assemblyLoad("ContextualReflectionDependency");
+
+                Assert.AreEqual(AssemblyLoadContext.Default, AssemblyLoadContext.GetLoadContext(depends));
+            }
+        }
+
+        void TestTypeGetType(bool isolated)
+        {
+            TestTypeGetType(isolated, (string typeName) => Type.GetType(typeName));
+            TestTypeGetType(isolated, (string typeName) => Type.GetType(typeName, throwOnError : false));
+            TestTypeGetType(isolated, (string typeName) => Type.GetType(typeName, throwOnError : false, ignoreCase : false));
+            TestTypeGetType(isolated, (string typeName) => Type.GetType(typeName, assemblyResolver : null, typeResolver : null));
+            TestTypeGetType(isolated, (string typeName) => Type.GetType(typeName, assemblyResolver : null, typeResolver : null, throwOnError : false));
+            TestTypeGetType(isolated, (string typeName) => Type.GetType(typeName, assemblyResolver : null, typeResolver : null, throwOnError : false, ignoreCase : false));
+        }
+
+        void TestTypeGetType(bool isolated, Func<string, System.Type> typeGetType)
+        {
+            TestResolveMissingAssembly(isolated, (string assemblyName) => typeGetType(string.Format("MyType, {0}", assemblyName)));
+
+            using (ConntextualReflectionProxy.EnterContextualReflection((Assembly)null))
+            {
+                {
+                    Type p = typeGetType("ContextualReflectionTest.Program");
+
+                    Assembly expectedAssembly = Assembly.GetExecutingAssembly();
+
+                    Assert.IsNotNull(p);
+                    Assert.AreEqual(expectedAssembly, p.Assembly);
+                    Assert.AreEqual(typeof (Program), p);
+                }
+                {
+                    Type p = typeGetType("ContextualReflectionTest.Program, ContextualReflection");
+
+                    Assembly expectedAssembly = Assembly.GetExecutingAssembly();
+
+                    Assert.IsNotNull(p);
+                    Assert.AreEqual(expectedAssembly, p.Assembly);
+                    Assert.AreEqual(typeof (Program), p);
+                }
+                {
+                    Type g = typeGetType("ContextualReflectionTest.AGenericClass`1[[ContextualReflectionTest.Program, ContextualReflection]], ContextualReflection");
+
+                    Assembly expectedAssembly = Assembly.GetExecutingAssembly();
+
+                    Assert.IsNotNull(g);
+                    Assert.AreEqual(expectedAssembly, g.Assembly);
+                    Assert.AreEqual(expectedAssembly, g.GenericTypeArguments[0].Assembly);
+                    Assert.AreEqual(typeof (Program), g.GenericTypeArguments[0]);
+                    Assert.AreEqual(isolated ? alc : AssemblyLoadContext.Default, AssemblyLoadContext.GetLoadContext(g.GenericTypeArguments[0].Assembly));
+                }
+            }
+            using (ConntextualReflectionProxy.EnterContextualReflection(AssemblyLoadContext.Default))
+            {
+                {
+                    Type p = typeGetType("ContextualReflectionTest.Program");
+
+                    Assembly expectedAssembly = Assembly.GetExecutingAssembly();
+
+                    Assert.IsNotNull(p);
+                    Assert.AreEqual(expectedAssembly, p.Assembly);
+                    Assert.AreEqual(typeof (Program), p);
+                }
+                {
+                    Type p = typeGetType("ContextualReflectionTest.Program, ContextualReflection");
+
+                    Assembly expectedAssembly = defaultAssembly;
+
+                    Assert.IsNotNull(p);
+                    Assert.AreEqual(expectedAssembly, p.Assembly);
+                    Assert.AreEqual(AssemblyLoadContext.Default, AssemblyLoadContext.GetLoadContext(p.Assembly));
+                }
+                {
+                    Type g = typeGetType("ContextualReflectionTest.AGenericClass`1[[ContextualReflectionTest.Program, ContextualReflection]], ContextualReflection");
+
+                    Assembly expectedAssembly = defaultAssembly;
+
+                    Assert.IsNotNull(g);
+                    Assert.AreEqual(expectedAssembly, g.Assembly);
+                    Assert.AreEqual(expectedAssembly, g.GenericTypeArguments[0].Assembly);
+                    Assert.AreEqual(AssemblyLoadContext.Default, AssemblyLoadContext.GetLoadContext(g.Assembly));
+                    Assert.AreEqual(AssemblyLoadContext.Default, AssemblyLoadContext.GetLoadContext(g.GenericTypeArguments[0].Assembly));
+                }
+            }
+            using (ConntextualReflectionProxy.EnterContextualReflection(alc))
+            {
+                {
+                    Type p = typeGetType("ContextualReflectionTest.Program");
+
+                    Assembly expectedAssembly = Assembly.GetExecutingAssembly();
+
+                    Assert.IsNotNull(p);
+                    Assert.AreEqual(expectedAssembly, p.Assembly);
+                    Assert.AreEqual(typeof (Program), p);
+                }
+                {
+                    Type p = typeGetType("ContextualReflectionTest.Program, ContextualReflection");
+
+                    Assembly expectedAssembly = alcAssembly;
+
+                    Assert.IsNotNull(p);
+                    Assert.AreEqual(expectedAssembly, p.Assembly);
+                    Assert.AreEqual(alc, AssemblyLoadContext.GetLoadContext(p.Assembly));
+                }
+                {
+                    Type g = typeGetType("ContextualReflectionTest.AGenericClass`1[[ContextualReflectionTest.Program, ContextualReflection]], ContextualReflection");
+
+                    Assembly expectedAssembly = alcAssembly;
+
+                    Assert.IsNotNull(g);
+                    Assert.AreEqual(expectedAssembly, g.Assembly);
+                    Assert.AreEqual(expectedAssembly, g.GenericTypeArguments[0].Assembly);
+                    Assert.AreEqual(alc, AssemblyLoadContext.GetLoadContext(g.Assembly));
+                    Assert.AreEqual(alc, AssemblyLoadContext.GetLoadContext(g.GenericTypeArguments[0].Assembly));
+                }
+            }
+        }
+
+        void TestAssemblyGetType(bool isolated)
+        {
+            Assembly assembly = Assembly.GetExecutingAssembly();
+            TestResolveMissingAssembly(isolated,
+                (string assemblyName) => assembly.GetType(string.Format("ContextualReflectionTest.AGenericClass`1[[MyType, {0}]]", assemblyName)));
+
+            using (ConntextualReflectionProxy.EnterContextualReflection((Assembly)null))
+            {
+                {
+                    Type g = assembly.GetType("ContextualReflectionTest.AGenericClass`1[[ContextualReflectionTest.Program]]", throwOnError : false);
+
+                    Assert.IsNotNull(g);
+                    Assert.AreEqual(assembly, g.Assembly);
+                    Assert.AreEqual(assembly, g.GenericTypeArguments[0].Assembly);
+                    Assert.AreEqual(Assembly.GetExecutingAssembly(), g.GenericTypeArguments[0].Assembly);
+                    Assert.AreEqual(typeof (Program), g.GenericTypeArguments[0]);
+                }
+                {
+                    Type g = assembly.GetType("ContextualReflectionTest.AGenericClass`1[[ContextualReflectionTest.Program, ContextualReflection]]", throwOnError : false);
+
+                    Assert.IsNotNull(g);
+                    Assert.AreEqual(assembly, g.Assembly);
+                    Assert.AreEqual(assembly, g.GenericTypeArguments[0].Assembly);
+                    Assert.AreEqual(Assembly.GetExecutingAssembly(), g.GenericTypeArguments[0].Assembly);
+                    Assert.AreEqual(typeof (Program), g.GenericTypeArguments[0]);
+                }
+                {
+                    Assembly mscorlib = typeof (System.Collections.Generic.List<string>).Assembly;
+
+                    Type m = mscorlib.GetType("System.Collections.Generic.List`1[[ContextualReflectionTest.Program, ContextualReflection]]", throwOnError : false);
+
+                    Assembly expectedAssembly = mscorlib;
+
+                    Assert.IsNotNull(m);
+                    Assert.AreEqual(expectedAssembly, m.Assembly);
+                    Assert.AreEqual(AssemblyLoadContext.Default, AssemblyLoadContext.GetLoadContext(m.GenericTypeArguments[0].Assembly));
+                }
+            }
+            using (ConntextualReflectionProxy.EnterContextualReflection(AssemblyLoadContext.Default))
+            {
+                {
+                    Type g = assembly.GetType("ContextualReflectionTest.AGenericClass`1[[ContextualReflectionTest.Program]]", throwOnError : false);
+
+                    Assert.IsNotNull(g);
+                    Assert.AreEqual(assembly, g.Assembly);
+                    Assert.AreEqual(assembly, g.GenericTypeArguments[0].Assembly);
+                    Assert.AreEqual(Assembly.GetExecutingAssembly(), g.GenericTypeArguments[0].Assembly);
+                    Assert.AreEqual(typeof (Program), g.GenericTypeArguments[0]);
+                }
+                {
+                    Type g = assembly.GetType("ContextualReflectionTest.AGenericClass`1[[ContextualReflectionTest.Program, ContextualReflection]]", throwOnError : false);
+
+                    Assembly expectedAssembly = defaultAssembly;
+
+                    Assert.IsNotNull(g);
+                    Assert.AreEqual(assembly, g.Assembly);
+                    Assert.AreEqual(expectedAssembly, g.GenericTypeArguments[0].Assembly);
+                }
+                {
+                    Assembly mscorlib = typeof (System.Collections.Generic.List<string>).Assembly;
+
+                    Type m = mscorlib.GetType("System.Collections.Generic.List`1[[ContextualReflectionTest.Program, ContextualReflection]]", throwOnError : false);
+
+                    Assembly expectedAssembly = mscorlib;
+
+                    Assert.IsNotNull(m);
+                    Assert.AreEqual(expectedAssembly, m.Assembly);
+                    Assert.AreEqual(AssemblyLoadContext.Default, AssemblyLoadContext.GetLoadContext(m.GenericTypeArguments[0].Assembly));
+                }
+            }
+            using (ConntextualReflectionProxy.EnterContextualReflection(alc))
+            {
+                {
+                    Type g = assembly.GetType("ContextualReflectionTest.AGenericClass`1[[ContextualReflectionTest.Program]]", throwOnError : false);
+
+                    Assert.IsNotNull(g);
+                    Assert.AreEqual(assembly, g.Assembly);
+                    Assert.AreEqual(assembly, g.GenericTypeArguments[0].Assembly);
+                    Assert.AreEqual(Assembly.GetExecutingAssembly(), g.GenericTypeArguments[0].Assembly);
+                    Assert.AreEqual(typeof (Program), g.GenericTypeArguments[0]);
+                }
+                {
+                    Type g = assembly.GetType("ContextualReflectionTest.AGenericClass`1[[ContextualReflectionTest.Program, ContextualReflection]]", throwOnError : false);
+
+                    Assembly expectedAssembly = alcAssembly;
+
+                    Assert.IsNotNull(g);
+                    Assert.AreEqual(assembly, g.Assembly);
+                    Assert.AreEqual(expectedAssembly, g.GenericTypeArguments[0].Assembly);
+                }
+                {
+                    Assembly mscorlib = typeof (System.Collections.Generic.List<string>).Assembly;
+
+                    Type m = mscorlib.GetType("System.Collections.Generic.List`1[[ContextualReflectionTest.Program, ContextualReflection]]", throwOnError : false);
+
+                    Assembly expectedAssembly = mscorlib;
+
+                    Assert.IsNotNull(m);
+                    Assert.AreEqual(expectedAssembly, m.Assembly);
+                    Assert.AreEqual(alc, AssemblyLoadContext.GetLoadContext(m.GenericTypeArguments[0].Assembly));
+                }
+            }
+        }
+
+        void TestActivatorCreateInstance(bool isolated)
+        {
+            TestResolveMissingAssembly(isolated, (string assemblyName) => Activator.CreateInstance(assemblyName, "MyType"));
+            TestResolveMissingAssembly(isolated,
+                (string assemblyName) => Activator.CreateInstance("System.Private.CoreLib", string.Format("System.Collections.Generic.List`1[[MyType, {0}]]", assemblyName)),
+                skipNullIsolated : true);
+
+            TestResolveMissingAssembly(isolated,
+                (string assemblyName) => Activator.CreateInstance("ContextualReflection", string.Format("ContextualReflectionTest.AGenericClass`1[[MyType, {0}]]", assemblyName)));
+
+            Assembly assembly = Assembly.GetExecutingAssembly();
+
+            using (ConntextualReflectionProxy.EnterContextualReflection((Assembly)null))
+            {
+                {
+                    ObjectHandle objectHandle = Activator.CreateInstance(null, "ContextualReflectionTest.AGenericClass`1[[ContextualReflectionTest.Program]]");
+                    Type g = objectHandle.Unwrap().GetType();
+
+                    Assert.IsNotNull(g);
+                    Assert.AreEqual(assembly, g.Assembly);
+                    Assert.AreEqual(assembly, g.GenericTypeArguments[0].Assembly);
+                    Assert.AreEqual(Assembly.GetExecutingAssembly(), g.GenericTypeArguments[0].Assembly);
+                    Assert.AreEqual(typeof (Program), g.GenericTypeArguments[0]);
+                }
+                {
+                    ObjectHandle objectHandle = Activator.CreateInstance(null, "ContextualReflectionTest.AGenericClass`1[[ContextualReflectionTest.Program, ContextualReflection]]");
+                    Type g = objectHandle.Unwrap().GetType();
+
+                    Assert.IsNotNull(g);
+                    Assert.AreEqual(assembly, g.Assembly);
+                    Assert.AreEqual(assembly, g.GenericTypeArguments[0].Assembly);
+                    Assert.AreEqual(Assembly.GetExecutingAssembly(), g.GenericTypeArguments[0].Assembly);
+                    Assert.AreEqual(typeof (Program), g.GenericTypeArguments[0]);
+                }
+                {
+                    ObjectHandle objectHandle = Activator.CreateInstance("ContextualReflection" , "ContextualReflectionTest.AGenericClass`1[[ContextualReflectionTest.Program, ContextualReflection]]");
+                    Type g = objectHandle.Unwrap().GetType();
+
+                    Assembly expectedAssembly = assembly;
+
+                    Assert.IsNotNull(g);
+                    Assert.AreEqual(expectedAssembly, g.Assembly);
+                    Assert.AreEqual(expectedAssembly, g.GenericTypeArguments[0].Assembly);
+                }
+                {
+                    Assembly expectedAssembly = alcAssembly;
+
+                    Assembly mscorlib = typeof (System.Collections.Generic.List<string>).Assembly;
+
+                    ObjectHandle objectHandle = Activator.CreateInstance(mscorlib.GetName().Name, "System.Collections.Generic.List`1[[ContextualReflectionTest.Program, ContextualReflection]]");
+                    Type m = objectHandle.Unwrap().GetType();
+
+                    Assert.IsNotNull(m);
+                    Assert.AreEqual(mscorlib, m.Assembly);
+                    Assert.AreEqual(AssemblyLoadContext.Default, AssemblyLoadContext.GetLoadContext(m.GenericTypeArguments[0].Assembly));
+                }
+            }
+            using (ConntextualReflectionProxy.EnterContextualReflection(AssemblyLoadContext.Default))
+            {
+                {
+                    ObjectHandle objectHandle = Activator.CreateInstance(null, "ContextualReflectionTest.AGenericClass`1[[ContextualReflectionTest.Program]]");
+                    Type g = objectHandle.Unwrap().GetType();
+
+                    Assert.IsNotNull(g);
+                    Assert.AreEqual(assembly, g.Assembly);
+                    Assert.AreEqual(assembly, g.GenericTypeArguments[0].Assembly);
+                    Assert.AreEqual(Assembly.GetExecutingAssembly(), g.GenericTypeArguments[0].Assembly);
+                    Assert.AreEqual(typeof (Program), g.GenericTypeArguments[0]);
+                }
+                {
+                    ObjectHandle objectHandle = Activator.CreateInstance(null, "ContextualReflectionTest.AGenericClass`1[[ContextualReflectionTest.Program, ContextualReflection]]");
+                    Type g = objectHandle.Unwrap().GetType();
+
+                    Assembly expectedAssembly = defaultAssembly;
+
+                    Assert.IsNotNull(g);
+                    Assert.AreEqual(assembly, g.Assembly);
+                    Assert.AreEqual(expectedAssembly, g.GenericTypeArguments[0].Assembly);
+                }
+                {
+                    ObjectHandle objectHandle = Activator.CreateInstance("ContextualReflection" , "ContextualReflectionTest.AGenericClass`1[[ContextualReflectionTest.Program, ContextualReflection]]");
+                    Type g = objectHandle.Unwrap().GetType();
+
+                    Assembly expectedAssembly = defaultAssembly;
+
+                    Assert.IsNotNull(g);
+                    Assert.AreEqual(expectedAssembly, g.Assembly);
+                    Assert.AreEqual(expectedAssembly, g.GenericTypeArguments[0].Assembly);
+                }
+                {
+                    Assembly mscorlib = typeof (System.Collections.Generic.List<string>).Assembly;
+
+                    ObjectHandle objectHandle = Activator.CreateInstance(mscorlib.GetName().Name, "System.Collections.Generic.List`1[[ContextualReflectionTest.Program, ContextualReflection]]");
+                    Type m = objectHandle.Unwrap().GetType();
+
+                    Assembly expectedAssembly = mscorlib;
+
+                    Assert.IsNotNull(m);
+                    Assert.AreEqual(expectedAssembly, m.Assembly);
+                    Assert.AreEqual(AssemblyLoadContext.Default, AssemblyLoadContext.GetLoadContext(m.GenericTypeArguments[0].Assembly));
+                }
+            }
+            using (ConntextualReflectionProxy.EnterContextualReflection(alc))
+            {
+                {
+                    ObjectHandle objectHandle = Activator.CreateInstance(null, "ContextualReflectionTest.AGenericClass`1[[ContextualReflectionTest.Program]]");
+                    Type g = objectHandle.Unwrap().GetType();
+
+                    Assert.IsNotNull(g);
+                    Assert.AreEqual(assembly, g.Assembly);
+                    Assert.AreEqual(assembly, g.GenericTypeArguments[0].Assembly);
+                    Assert.AreEqual(Assembly.GetExecutingAssembly(), g.GenericTypeArguments[0].Assembly);
+                    Assert.AreEqual(typeof (Program), g.GenericTypeArguments[0]);
+                }
+                {
+                    ObjectHandle objectHandle = Activator.CreateInstance(null, "ContextualReflectionTest.AGenericClass`1[[ContextualReflectionTest.Program, ContextualReflection]]");
+                    Type g = objectHandle.Unwrap().GetType();
+
+                    Assembly expectedAssembly = alcAssembly;
+
+                    Assert.IsNotNull(g);
+                    Assert.AreEqual(assembly, g.Assembly);
+                    Assert.AreEqual(expectedAssembly, g.GenericTypeArguments[0].Assembly);
+                }
+                {
+                    ObjectHandle objectHandle = Activator.CreateInstance("ContextualReflection" , "ContextualReflectionTest.AGenericClass`1[[ContextualReflectionTest.Program, ContextualReflection]]");
+                    Type g = objectHandle.Unwrap().GetType();
+
+                    Assembly expectedAssembly = alcAssembly;
+
+                    Assert.IsNotNull(g);
+                    Assert.AreEqual(expectedAssembly, g.Assembly);
+                    Assert.AreEqual(expectedAssembly, g.GenericTypeArguments[0].Assembly);
+                }
+                {
+                    Assembly mscorlib = typeof (System.Collections.Generic.List<string>).Assembly;
+
+                    ObjectHandle objectHandle = Activator.CreateInstance(mscorlib.GetName().Name, "System.Collections.Generic.List`1[[ContextualReflectionTest.Program, ContextualReflection]]");
+                    Type m = objectHandle.Unwrap().GetType();
+
+                    Assert.IsNotNull(m);
+                    Assert.AreEqual(mscorlib, m.Assembly);
+                    Assert.AreEqual(alc, AssemblyLoadContext.GetLoadContext(m.GenericTypeArguments[0].Assembly));
+                }
+            }
+        }
+
+        public void RunTests()
+        {
+            VerifyIsolationDefault();
+            VerifyTestResolve();
+            VerifyContextualReflectionProxy();
+            VerifyUsingStatementContextualReflectionUsage();
+            VerifyBadContextualReflectionUsage();
+
+            RunTests(isolated : false);
+            alcProgramInstance.RunTestsIsolated();
+        }
+
+        public void RunTests(bool isolated)
+        {
+            TestAssemblyLoad(isolated);
+            TestTypeGetType(isolated);
+            TestAssemblyGetType(isolated);
+            TestActivatorCreateInstance(isolated);
+        }
+
+        [MethodImplAttribute(MethodImplOptions.NoInlining)]
+        public void RunTestsIsolated()
+        {
+            VerifyIsolationAlc();
+            RunTests(isolated : true);
+        }
+    }
+}
+
diff --git a/tests/src/Loader/ContextualReflection/ContextualReflection.csproj b/tests/src/Loader/ContextualReflection/ContextualReflection.csproj
new file mode 100644 (file)
index 0000000..6f88de3
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <OutputType>Exe</OutputType>
+    <CLRTestKind>BuildAndRun</CLRTestKind>
+    <ProjectGuid>{78030DC5-F1A6-4B98-A130-A66F5047FF29}</ProjectGuid>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="ContextualReflection.cs" />
+    <ProjectReference Include="ContextualReflectionDependency.csproj" />
+    <ProjectReference Include="../../Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
diff --git a/tests/src/Loader/ContextualReflection/ContextualReflectionDependency.cs b/tests/src/Loader/ContextualReflection/ContextualReflectionDependency.cs
new file mode 100644 (file)
index 0000000..d8fc8ef
--- /dev/null
@@ -0,0 +1,123 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+using System;
+using System.IO;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.Loader;
+
+namespace ContextualReflectionTest
+{
+    public interface IProgram
+    {
+        AssemblyLoadContext alc { get; }
+        Assembly alcAssembly { get; }
+        Type alcProgramType { get; }
+        IProgram alcProgramInstance { get; }
+        [MethodImplAttribute(MethodImplOptions.NoInlining)]
+        void RunTestsIsolated();
+    }
+
+    public class ConntextualReflectionProxy
+    {
+        public static AssemblyLoadContext CurrentContextualReflectionContext
+        {
+            get
+            {
+#if AssemblyLoadContextContextualReflectionFacade
+                return AssemblyLoadContext.CurrentContextualReflectionContext;
+#else
+                Type t = typeof (AssemblyLoadContext);
+
+                object result = t.InvokeMember("CurrentContextualReflectionContext",
+                    BindingFlags.Public | BindingFlags.Static | BindingFlags.GetProperty,
+                    null,
+                    null,
+                    new object [] {});
+
+                return (AssemblyLoadContext) result;
+#endif
+            }
+        }
+
+        static public IDisposable EnterContextualReflection(AssemblyLoadContext alc)
+        {
+#if AssemblyLoadContextContextualReflectionFacade
+            return alc.EnterContextualReflection();
+#else
+            Type t = typeof (AssemblyLoadContext);
+
+            object result = t.InvokeMember("EnterContextualReflection",
+                BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Instance,
+                null,
+                alc,
+                new object [] {});
+
+            return (IDisposable) result;
+#endif
+        }
+
+        static public IDisposable EnterContextualReflection(Assembly activating)
+        {
+#if AssemblyLoadContextContextualReflectionFacade
+            return AssemblyLoadContext.EnterContextualReflection(activating);
+#else
+            Type t = typeof (AssemblyLoadContext);
+
+            object result = t.InvokeMember("EnterContextualReflection",
+                BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Static,
+                null,
+                null,
+                new object [] {activating});
+
+            return (IDisposable) result;
+#endif
+        }
+    }
+
+    public enum ResolveEvents
+    {
+        NoEvent,
+        ExpectedEvent,
+    };
+
+    public class TestResolve
+    {
+        static public ResolveEvents ResolveEvent { get; set;}
+
+        static public Assembly ResolvingTestDefault(AssemblyLoadContext alc, AssemblyName assemblyName)
+        {
+            if (assemblyName.Name.Contains("TestDefaultLoad") && (ResolveEvent == ResolveEvents.NoEvent))
+            {
+                ResolveEvent = ResolveEvents.ExpectedEvent;
+            }
+            return null;
+        }
+
+        static public Assembly ResolvingTestIsolated(AssemblyLoadContext alc, AssemblyName assemblyName)
+        {
+            if (assemblyName.Name.Contains("TestIsolatedLoad") && (ResolveEvent == ResolveEvents.NoEvent))
+            {
+                ResolveEvent = ResolveEvents.ExpectedEvent;
+            }
+            return null;
+        }
+
+        static public void Assert(ResolveEvents expected, Action action)
+        {
+            ResolveEvent = ResolveEvents.NoEvent;
+            try
+            {
+                action();
+            }
+            catch
+            {
+            }
+            finally
+            {
+                TestLibrary.Assert.AreEqual(expected, ResolveEvent);
+            }
+        }
+    }
+}
diff --git a/tests/src/Loader/ContextualReflection/ContextualReflectionDependency.csproj b/tests/src/Loader/ContextualReflection/ContextualReflectionDependency.csproj
new file mode 100644 (file)
index 0000000..1d8fb64
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <OutputType>Library</OutputType>
+    <CLRTestKind>BuildOnly</CLRTestKind>
+    <ProjectGuid>{95DBE3B0-AA86-4366-BB8A-E04B534365F3}</ProjectGuid>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="ContextualReflectionDependency.cs" />
+    <ProjectReference Include="../../Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>