From 206f9680303134731862a35338618f2cbd225651 Mon Sep 17 00:00:00 2001 From: Steve MacLean Date: Thu, 11 Apr 2019 00:20:39 -0400 Subject: [PATCH] Contextual reflection (#23740) * 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 --- .../shared/System/Activator.RuntimeType.cs | 3 +- .../System/Runtime/Loader/AssemblyLoadContext.cs | 178 ++++- .../src/System/Reflection/Assembly.CoreCLR.cs | 10 +- .../src/System/Reflection/RuntimeAssembly.cs | 34 +- .../Runtime/Loader/AssemblyLoadContext.CoreCLR.cs | 4 +- .../src/System/RuntimeHandles.cs | 10 +- .../src/System/TypeNameParser.cs | 9 +- src/strongname/api/common.h | 2 +- src/vm/assemblynative.cpp | 44 +- src/vm/assemblynative.hpp | 4 +- src/vm/common.h | 2 +- src/vm/mscorlib.h | 10 + src/vm/object.h | 52 +- src/vm/qcall.cpp | 22 - src/vm/qcall.h | 29 - src/vm/runtimehandles.cpp | 15 +- src/vm/runtimehandles.h | 2 +- src/vm/typeparse.cpp | 19 +- src/vm/vars.hpp | 3 +- .../ContextualReflection/ContextualReflection.cs | 748 +++++++++++++++++++++ .../ContextualReflection.csproj | 18 + .../ContextualReflectionDependency.cs | 123 ++++ .../ContextualReflectionDependency.csproj | 17 + 23 files changed, 1236 insertions(+), 122 deletions(-) create mode 100644 tests/src/Loader/ContextualReflection/ContextualReflection.cs create mode 100644 tests/src/Loader/ContextualReflection/ContextualReflection.csproj create mode 100644 tests/src/Loader/ContextualReflection/ContextualReflectionDependency.cs create mode 100644 tests/src/Loader/ContextualReflection/ContextualReflectionDependency.csproj diff --git a/src/System.Private.CoreLib/shared/System/Activator.RuntimeType.cs b/src/System.Private.CoreLib/shared/System/Activator.RuntimeType.cs index f328b9c..270aa6a 100644 --- a/src/System.Private.CoreLib/shared/System/Activator.RuntimeType.cs +++ b/src/System.Private.CoreLib/shared/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 @@ namespace System { // Classic managed type assembly = RuntimeAssembly.InternalLoadAssemblyName( - assemblyName, ref stackMark); + assemblyName, ref stackMark, AssemblyLoadContext.CurrentContextualReflectionContext); } } diff --git a/src/System.Private.CoreLib/shared/System/Runtime/Loader/AssemblyLoadContext.cs b/src/System.Private.CoreLib/shared/System/Runtime/Loader/AssemblyLoadContext.cs index 8316e10..4efd5de 100644 --- a/src/System.Private.CoreLib/shared/System/Runtime/Loader/AssemblyLoadContext.cs +++ b/src/System.Private.CoreLib/shared/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 @@ namespace System.Runtime.Loader 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 @@ 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 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 @@ namespace System.Runtime.Loader // // 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 @@ 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 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 diff --git a/src/System.Private.CoreLib/src/System/Reflection/Assembly.CoreCLR.cs b/src/System.Private.CoreLib/src/System/Reflection/Assembly.CoreCLR.cs index 4683b88..42736a9 100644 --- a/src/System.Private.CoreLib/src/System/Reflection/Assembly.CoreCLR.cs +++ b/src/System.Private.CoreLib/src/System/Reflection/Assembly.CoreCLR.cs @@ -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)] diff --git a/src/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs b/src/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs index bb88978..64622f5 100644 --- a/src/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs +++ b/src/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs @@ -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) { diff --git a/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.CoreCLR.cs b/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.CoreCLR.cs index 5cabe51..c95043b 100644 --- a/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.CoreCLR.cs +++ b/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.CoreCLR.cs @@ -104,7 +104,7 @@ namespace System.Runtime.Loader { Assembly resolvedAssembly = null; - Func assemblyResolveHandler = Resolving; + Func assemblyResolveHandler = _resolving; if (assemblyResolveHandler != null) { @@ -200,7 +200,7 @@ namespace System.Runtime.Loader { IntPtr resolvedDll = IntPtr.Zero; - Func dllResolveHandler = ResolvingUnmanagedDll; + Func dllResolveHandler = _resolvingUnmanagedDll; if (dllResolveHandler != null) { diff --git a/src/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/System.Private.CoreLib/src/System/RuntimeHandles.cs index 56694c6..a2da2be 100644 --- a/src/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -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); diff --git a/src/System.Private.CoreLib/src/System/TypeNameParser.cs b/src/System.Private.CoreLib/src/System/TypeNameParser.cs index 3cd904e..4c96d78 100644 --- a/src/System.Private.CoreLib/src/System/TypeNameParser.cs +++ b/src/System.Private.CoreLib/src/System/TypeNameParser.cs @@ -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) { diff --git a/src/strongname/api/common.h b/src/strongname/api/common.h index 626d9bb..4347eba 100644 --- a/src/strongname/api/common.h +++ b/src/strongname/api/common.h @@ -94,11 +94,11 @@ 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; diff --git a/src/vm/assemblynative.cpp b/src/vm/assemblynative.cpp index 9c1e467..66214a1 100644 --- a/src/vm/assemblynative.cpp +++ b/src/vm/assemblynative.cpp @@ -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(pAssemblyLoadContext.m_ppObject); + + INT_PTR nativeAssemblyLoadContext = (*pAssemblyLoadContextRef)->GetNativeAssemblyLoadContext(); + + pPrivHostBinder = reinterpret_cast(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()) { diff --git a/src/vm/assemblynative.hpp b/src/vm/assemblynative.hpp index 0ce2fb2..12d4ff8 100644 --- a/src/vm/assemblynative.hpp +++ b/src/vm/assemblynative.hpp @@ -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); diff --git a/src/vm/common.h b/src/vm/common.h index 61ba2a7..6fd5a37 100644 --- a/src/vm/common.h +++ b/src/vm/common.h @@ -114,11 +114,11 @@ 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; diff --git a/src/vm/mscorlib.h b/src/vm/mscorlib.h index 32d19cb..ccdec0f 100644 --- a/src/vm/mscorlib.h +++ b/src/vm/mscorlib.h @@ -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) diff --git a/src/vm/object.h b/src/vm/object.h index 590f1e0..cc0810f 100644 --- a/src/vm/object.h +++ b/src/vm/object.h @@ -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 REFLECTFIELDREF; typedef REF THREADBASEREF; -typedef REF APPDOMAINREF; - typedef REF MARSHALBYREFOBJECTBASEREF; typedef REF ASSEMBLYREF; +typedef REF ASSEMBLYLOADCONTEXTREF; + typedef REF ASSEMBLYNAMEREF; typedef REF 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 diff --git a/src/vm/qcall.cpp b/src/vm/qcall.cpp index 3b35f90..1b7bbda 100644 --- a/src/vm/qcall.cpp +++ b/src/vm/qcall.cpp @@ -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 diff --git a/src/vm/qcall.h b/src/vm/qcall.h index 3ad328a..dd60bbb 100644 --- a/src/vm/qcall.h +++ b/src/vm/qcall.h @@ -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; diff --git a/src/vm/runtimehandles.cpp b/src/vm/runtimehandles.cpp index c25bcbf..d0a2b04 100644 --- a/src/vm/runtimehandles.cpp +++ b/src/vm/runtimehandles.cpp @@ -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(pAssemblyLoadContext.m_ppObject); + + INT_PTR nativeAssemblyLoadContext = (*pAssemblyLoadContextRef)->GetNativeAssemblyLoadContext(); + + pPrivHostBinder = reinterpret_cast(nativeAssemblyLoadContext); + } + + typeHandle = TypeName::GetTypeManaged(pwzClassName, NULL, bThrowOnError, bIgnoreCase, /*bProhibitAsmQualifiedName =*/ FALSE, SystemDomain::GetCallersAssembly(pStackMark), bLoadTypeFromPartialNameHack, (OBJECTREF*)keepAlive.m_ppObject, diff --git a/src/vm/runtimehandles.h b/src/vm/runtimehandles.h index 4c2ebdf..c83461a 100644 --- a/src/vm/runtimehandles.h +++ b/src/vm/runtimehandles.h @@ -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); diff --git a/src/vm/typeparse.cpp b/src/vm/typeparse.cpp index 858d9a3..0794454 100644 --- a/src/vm/typeparse.cpp +++ b/src/vm/typeparse.cpp @@ -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()); } diff --git a/src/vm/vars.hpp b/src/vm/vars.hpp index 2c3d978..68473b6 100644 --- a/src/vm/vars.hpp +++ b/src/vm/vars.hpp @@ -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 index 0000000..3356e3d --- /dev/null +++ b/tests/src/Loader/ContextualReflection/ContextualReflection.cs @@ -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 + { + } + + 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 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 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 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).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).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).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).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).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).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 index 0000000..6f88de3 --- /dev/null +++ b/tests/src/Loader/ContextualReflection/ContextualReflection.csproj @@ -0,0 +1,18 @@ + + + + + Debug + AnyCPU + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Exe + BuildAndRun + {78030DC5-F1A6-4B98-A130-A66F5047FF29} + + + + + + + + diff --git a/tests/src/Loader/ContextualReflection/ContextualReflectionDependency.cs b/tests/src/Loader/ContextualReflection/ContextualReflectionDependency.cs new file mode 100644 index 0000000..d8fc8ef --- /dev/null +++ b/tests/src/Loader/ContextualReflection/ContextualReflectionDependency.cs @@ -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 index 0000000..1d8fb64 --- /dev/null +++ b/tests/src/Loader/ContextualReflection/ContextualReflectionDependency.csproj @@ -0,0 +1,17 @@ + + + + + Debug + AnyCPU + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Library + BuildOnly + {95DBE3B0-AA86-4366-BB8A-E04B534365F3} + + + + + + + -- 2.7.4