Add support for AssemblyResolution when an assembly is not found in a given Load...
authorGaurav Khanna (CLR) <gaurav.khanna@microsoft.com>
Fri, 15 Jan 2016 23:06:01 +0000 (15:06 -0800)
committerGaurav Khanna <gkhanna@microsoft.com>
Thu, 21 Jan 2016 15:26:51 +0000 (07:26 -0800)
1) Expose AssemblyResolve event off AssemblyLoadContext
2) Invoke the event if the abstract Load implementation does not return an assembly.

src/binder/assemblybinder.cpp
src/binder/clrprivbinderassemblyloadcontext.cpp
src/binder/clrprivbindercoreclr.cpp
src/binder/inc/assemblybinder.hpp
src/binder/inc/clrprivbindercoreclr.h
src/mscorlib/model.xml
src/mscorlib/src/System/Runtime/Loader/AssemblyLoadContext.cs
src/vm/appdomain.cpp
src/vm/assemblynative.cpp

index 46f393b..139a66c 100644 (file)
@@ -56,7 +56,7 @@ extern BOOL RuntimeIsLegacyNetCF(DWORD adid);
 #include "clrprivbindercoreclr.h"
 #include "clrprivbinderassemblyloadcontext.h"
 // Helper function in the VM, invoked by the Binder, to invoke the host assembly resolver
-extern HRESULT RuntimeInvokeHostAssemblyResolver(CLRPrivBinderAssemblyLoadContext *pLoadContextToBindWithin, IAssemblyName *pIAssemblyName, ICLRPrivAssembly **ppLoadedAssembly);
+extern HRESULT RuntimeInvokeHostAssemblyResolver(INT_PTR pManagedAssemblyLoadContextToBindWithin, IAssemblyName *pIAssemblyName, ICLRPrivAssembly **ppLoadedAssembly);
 
 // Helper to check if we have a host assembly resolver set
 extern BOOL RuntimeCanUseAppPathAssemblyResolver(DWORD adid);
@@ -1856,7 +1856,7 @@ namespace BINDER_SPACE
 #endif //CROSSGEN_COMPILE
 
 #if defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN)
-HRESULT AssemblyBinder::BindUsingHostAssemblyResolver (/* in */ CLRPrivBinderAssemblyLoadContext *pLoadContextToBindWithin,
+HRESULT AssemblyBinder::BindUsingHostAssemblyResolver (/* in */ INT_PTR pManagedAssemblyLoadContextToBindWithin,
                                                        /* in */ AssemblyName       *pAssemblyName,
                                                       /* in */ IAssemblyName      *pIAssemblyName,
                                                       /* out */ Assembly           **ppAssembly)
@@ -1864,15 +1864,11 @@ HRESULT AssemblyBinder::BindUsingHostAssemblyResolver (/* in */ CLRPrivBinderAss
     HRESULT hr = E_FAIL;
     BINDER_LOG_ENTER(W("AssemblyBinder::BindUsingHostAssemblyResolver"));
     
-    _ASSERTE(pLoadContextToBindWithin != NULL);
-    
-    // Get the application context within which the assembly will be bound and loaded
-    ApplicationContext *pApplicationContext = pLoadContextToBindWithin->GetAppContext();
-    _ASSERTE(pApplicationContext != NULL);
+    _ASSERTE(pManagedAssemblyLoadContextToBindWithin != NULL);
     
     // Call into the VM to use the HostAssemblyResolver and load the assembly
     ICLRPrivAssembly *pLoadedAssembly = NULL;
-    hr = RuntimeInvokeHostAssemblyResolver(pLoadContextToBindWithin, pIAssemblyName, &pLoadedAssembly);
+    hr = RuntimeInvokeHostAssemblyResolver(pManagedAssemblyLoadContextToBindWithin, pIAssemblyName, &pLoadedAssembly);
     if (SUCCEEDED(hr))
     {
         _ASSERTE(pLoadedAssembly != NULL);
index f2ed7e1..344801a 100644 (file)
@@ -83,7 +83,7 @@ HRESULT CLRPrivBinderAssemblyLoadContext::BindAssemblyByName(IAssemblyName     *
                 // Thus, if default binder has been overridden, then invoke it in an attempt to perform the binding for it make the call
                 // of what to do next. The host-overridden binder can either fail the bind or return reference to an existing assembly
                 // that has been loaded.
-                hr = AssemblyBinder::BindUsingHostAssemblyResolver(this, pAssemblyName, pIAssemblyName, &pCoreCLRFoundAssembly);
+                hr = AssemblyBinder::BindUsingHostAssemblyResolver(GetManagedAssemblyLoadContext(), pAssemblyName, pIAssemblyName, &pCoreCLRFoundAssembly);
                 if (SUCCEEDED(hr))
                 {
                     // We maybe returned an assembly that was bound to a different AssemblyLoadContext instance.
index 238c522..4958875 100644 (file)
@@ -62,8 +62,42 @@ HRESULT CLRPrivBinderCoreCLR::BindAssemblyByName(IAssemblyName     *pIAssemblyNa
         IF_FAIL_GO(pAssemblyName->Init(pIAssemblyName));
         
         hr = BindAssemblyByNameWorker(pAssemblyName, &pCoreCLRFoundAssembly, false /* excludeAppPaths */);
+
+#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN)        
+        if ((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) ||
+            (hr == FUSION_E_APP_DOMAIN_LOCKED) || (hr == FUSION_E_REF_DEF_MISMATCH))
+        {
+            // If we are here, one of the following is possible:
+            //
+            // 1) The assembly has not been found in the current binder's application context (i.e. it has not already been loaded), OR
+            // 2) An assembly with the same simple name was already loaded in the context of the current binder but we ran into a Ref/Def
+            //    mismatch (either due to version difference or strong-name difference).
+            //
+            // Thus, if default binder has been overridden, then invoke it in an attempt to perform the binding for it make the call
+            // of what to do next. The host-overridden binder can either fail the bind or return reference to an existing assembly
+            // that has been loaded.
+
+            // Attempt to resolve the assembly via managed TPA ALC instance if one exists
+            INT_PTR pManagedAssemblyLoadContext = GetManagedAssemblyLoadContext();
+            if (pManagedAssemblyLoadContext != NULL)
+            {
+              hr = AssemblyBinder::BindUsingHostAssemblyResolver(pManagedAssemblyLoadContext, pAssemblyName, pIAssemblyName, &pCoreCLRFoundAssembly);
+              if (SUCCEEDED(hr))
+              {
+                  // We maybe returned an assembly that was bound to a different AssemblyLoadContext instance.
+                  // In such a case, we will not overwrite the binding context (which would be wrong since it would not
+                  // be present in the cache of the current binding context).
+                  if (pCoreCLRFoundAssembly->GetBinder() == NULL)
+                  {
+                      pCoreCLRFoundAssembly->SetBinder(this);
+                  }
+              }
+            }
+        }
+#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN)
+        
         IF_FAIL_GO(hr);
-            
+
         *ppAssembly = pCoreCLRFoundAssembly.Extract();
 
 Exit:;        
index 4ca0c4f..bfe851d 100644 (file)
@@ -76,7 +76,7 @@ namespace BINDER_SPACE
                                    /* in */  LPCTSTR      szMDAssemblyPath = NULL);
 
 #if defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN)
-        static HRESULT BindUsingHostAssemblyResolver (/* in */ CLRPrivBinderAssemblyLoadContext *pLoadContextToBindWithin,
+        static HRESULT BindUsingHostAssemblyResolver (/* in */ INT_PTR pManagedAssemblyLoadContextToBindWithin,
                                                       /* in */ AssemblyName       *pAssemblyName,
                                                       /* in */ IAssemblyName      *pIAssemblyName,
                                                       /* out */ Assembly           **ppAssembly);
index 4d98c9b..ce54556 100644 (file)
@@ -79,12 +79,12 @@ public:
             BINDER_SPACE::Assembly **ppCoreCLRFoundAssembly,
             bool excludeAppPaths);
 
-    INT_PTR GetManagedTPABinderInstance()
+    INT_PTR GetManagedAssemblyLoadContext()
     {
         return m_ptrManagedAssemblyLoadContext;
     }
 
-    void SetManagedTPABinderInstance(INT_PTR ptrManagedTPABinderInstance)
+    void SetManagedAssemblyLoadContext(INT_PTR ptrManagedTPABinderInstance)
     {
         _ASSERTE(m_ptrManagedAssemblyLoadContext == NULL);
 
index 1628cc2..8397d35 100644 (file)
     <Member Name="get_Default" />
     <Member Name="SetProfileOptimizationRoot(System.String)" />
     <Member Name="StartProfileOptimization(System.String)" />
+    <Member MemberType="Event" Name="Resolving" />
     </Type>
     <Type Name="System.Reflection.Metadata.AssemblyExtensions">
       <Member Name="TryGetRawMetadata(System.Reflection.Assembly,System.Byte*@,System.Int32@)"/>
index c3cdd81..c44365e 100644 (file)
@@ -64,6 +64,9 @@ namespace System.Runtime.Loader
             GCHandle gchALC = GCHandle.Alloc(this);
             IntPtr ptrALC = GCHandle.ToIntPtr(gchALC);
             m_pNativeAssemblyLoadContext = InitializeAssemblyLoadContext(ptrALC, fRepresentsTPALoadContext);
+
+            // Initialize the resolve event handler to be null by default
+            Resolving = null;
         }
 
         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
@@ -177,6 +180,29 @@ namespace System.Runtime.Loader
             return context.LoadFromAssemblyName(assemblyName);
         }
         
+        private Assembly GetFirstResolvedAssembly(AssemblyName assemblyName)
+        {
+            Assembly resolvedAssembly = null;
+
+            Func<AssemblyLoadContext, AssemblyName, Assembly> assemblyResolveHandler = Resolving;
+
+            if (assemblyResolveHandler != null)
+            {
+                // Loop through the event subscribers and return the first non-null Assembly instance
+                Delegate [] arrSubscribers = assemblyResolveHandler.GetInvocationList();
+                for(int i = 0; i < arrSubscribers.Length; i++)
+                {
+                    resolvedAssembly = ((Func<AssemblyLoadContext, AssemblyName, Assembly>)arrSubscribers[i])(this, assemblyName);
+                    if (resolvedAssembly != null)
+                    {
+                        break;
+                    }
+                }
+            }
+            
+            return resolvedAssembly;
+        }
+
         public Assembly LoadFromAssemblyName(AssemblyName assemblyName)
         {
             // AssemblyName is mutable. Cache the expected name before anybody gets a chance to modify it.
@@ -185,6 +211,12 @@ namespace System.Runtime.Loader
             Assembly assembly = Load(assemblyName);
             if (assembly == null)
             {
+                // Invoke the AssemblyResolve event callbacks if wired up
+                assembly = GetFirstResolvedAssembly(assemblyName);
+            }
+
+            if (assembly == null)
+            {
                 throw new FileNotFoundException(Environment.GetResourceString("IO.FileLoad"), requestedSimpleName);
             }
             
@@ -355,6 +387,8 @@ namespace System.Runtime.Loader
 #endif // FEATURE_MULTICOREJI
         }
         
+        public event Func<AssemblyLoadContext, AssemblyName, Assembly> Resolving;
+
         // Contains the reference to VM's representation of the AssemblyLoadContext
         private IntPtr m_pNativeAssemblyLoadContext;
         
@@ -374,7 +408,9 @@ namespace System.Runtime.Loader
         [System.Security.SecuritySafeCritical]  
         protected override Assembly Load(AssemblyName assemblyName)
         {
-            return Assembly.Load(assemblyName);
+            // We were loading an assembly into TPA ALC that was not found on TPA list. As a result we are here.
+            // Returning null will result in the AssemblyResolve event subscribers to be invoked to help resolve the assembly.
+            return null;
         }
     }
 }
index 997363e..dd7406c 100644 (file)
@@ -14227,7 +14227,7 @@ BOOL RuntimeCanUseAppPathAssemblyResolver(DWORD adid)
 }
 
 // Returns S_OK if the assembly was successfully loaded
-HRESULT RuntimeInvokeHostAssemblyResolver(CLRPrivBinderAssemblyLoadContext *pLoadContextToBindWithin, IAssemblyName *pIAssemblyName, ICLRPrivAssembly **ppLoadedAssembly)
+HRESULT RuntimeInvokeHostAssemblyResolver(INT_PTR pManagedAssemblyLoadContextToBindWithin, IAssemblyName *pIAssemblyName, ICLRPrivAssembly **ppLoadedAssembly)
 {
     CONTRACTL
     {
@@ -14256,9 +14256,6 @@ HRESULT RuntimeInvokeHostAssemblyResolver(CLRPrivBinderAssemblyLoadContext *pLoa
         
         GCPROTECT_BEGIN(_gcRefs);
         
-        // Get the pointer to the managed assembly load context
-        INT_PTR ptrManagedAssemblyLoadContext = pLoadContextToBindWithin->GetManagedAssemblyLoadContext();
-        
         // Prepare to invoke System.Runtime.Loader.AssemblyLoadContext.Resolve method.
         //
         // First, initialize an assembly spec for the requested assembly
@@ -14280,7 +14277,7 @@ HRESULT RuntimeInvokeHostAssemblyResolver(CLRPrivBinderAssemblyLoadContext *pLoa
             // Setup the arguments for the call
             ARG_SLOT args[2] =
             {
-                PtrToArgSlot(ptrManagedAssemblyLoadContext), // IntPtr for managed assembly load context instance
+                PtrToArgSlot(pManagedAssemblyLoadContextToBindWithin), // IntPtr for managed assembly load context instance
                 ObjToArgSlot(_gcRefs.oRefAssemblyName), // AssemblyName instance
             };
 
index 70bcccc..c96923d 100644 (file)
@@ -2533,14 +2533,14 @@ INT_PTR QCALLTYPE AssemblyNative::InitializeAssemblyLoadContext(INT_PTR ptrManag
     {
         // We are initializing the managed instance of Assembly Load Context that would represent the TPA binder.
         // First, confirm we do not have an existing managed ALC attached to the TPA binder.
-        INT_PTR ptrTPAAssemblyLoadContext = pTPABinderContext->GetManagedTPABinderInstance();
+        INT_PTR ptrTPAAssemblyLoadContext = pTPABinderContext->GetManagedAssemblyLoadContext();
         if ((ptrTPAAssemblyLoadContext != NULL) && (ptrTPAAssemblyLoadContext != ptrManagedAssemblyLoadContext))
         {
             COMPlusThrow(kInvalidOperationException, IDS_HOST_ASSEMBLY_RESOLVER_INCOMPATIBLE_TPA_BINDING_CONTEXT);
         }
 
         // Attach the managed TPA binding context with the native one.
-        pTPABinderContext->SetManagedTPABinderInstance(ptrManagedAssemblyLoadContext);
+        pTPABinderContext->SetManagedAssemblyLoadContext(ptrManagedAssemblyLoadContext);
         ptrNativeAssemblyLoadContext = reinterpret_cast<INT_PTR>(pTPABinderContext);
     }