From 7b0b3d327de49ecdaef181a5dbf1c3e3bc064bb6 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Tue, 12 Apr 2022 19:28:36 +0200 Subject: [PATCH 1/5] Fix Assembly reference in unloadability When an assembly A1 from an unloadable AssemblyLoadContext 1 is referenced by another assembly A2 from an unloadable AssemblyLoadContext 2, e.g. when a class from A2 implements an interface from A1 and there are no instances of types from A1 and the managed assembly type and the related AssemblyLoadContext is gone too, the AssemblyLoadContext 1 can incorrectly get collected, resulting in later crashes, null references etc. The problem is caused by the fact that we were missing adding references between native LoaderAllocators corresponding to the AssemblyLoadContexts in these cases. The fix is to add such reference whenever an AssemblyLoadContext.Load or the Resolving event of an AssemblyLoadContext returns an assembly from a collectible AssemblyLoadContext. That way, that collectible AssemblyLoadContext is held alive for the lifetime of the other context. --- src/coreclr/vm/appdomain.cpp | 41 +++-- .../coreclr/GitHub_67144/Class1.cs | 15 ++ .../coreclr/GitHub_67144/Interface1.cs | 17 ++ .../coreclr/GitHub_67144/TestClass.csproj | 16 ++ .../coreclr/GitHub_67144/TestInterface.csproj | 12 ++ .../coreclr/GitHub_67144/test67144.cs | 162 ++++++++++++++++++ .../coreclr/GitHub_67144/test67144.csproj | 9 + 7 files changed, 260 insertions(+), 12 deletions(-) create mode 100644 src/tests/Regressions/coreclr/GitHub_67144/Class1.cs create mode 100644 src/tests/Regressions/coreclr/GitHub_67144/Interface1.cs create mode 100644 src/tests/Regressions/coreclr/GitHub_67144/TestClass.csproj create mode 100644 src/tests/Regressions/coreclr/GitHub_67144/TestInterface.csproj create mode 100644 src/tests/Regressions/coreclr/GitHub_67144/test67144.cs create mode 100644 src/tests/Regressions/coreclr/GitHub_67144/test67144.csproj diff --git a/src/coreclr/vm/appdomain.cpp b/src/coreclr/vm/appdomain.cpp index 84169a53e70586..87d285dc41c921 100644 --- a/src/coreclr/vm/appdomain.cpp +++ b/src/coreclr/vm/appdomain.cpp @@ -3352,20 +3352,37 @@ BOOL AppDomain::AddFileToCache(AssemblySpec* pSpec, PEAssembly * pPEAssembly, BO } CONTRACTL_END; - GCX_PREEMP(); - DomainCacheCrstHolderForGCCoop holder(this); - - // !!! suppress exceptions - if(!m_AssemblyCache.StorePEAssembly(pSpec, pPEAssembly) && !fAllowFailure) { - // TODO: Disabling the below assertion as currently we experience - // inconsistency on resolving the Microsoft.Office.Interop.MSProject.dll - // This causes below assertion to fire and crashes the VS. This issue - // is being tracked with Dev10 Bug 658555. Brought back it when this bug - // is fixed. - // _ASSERTE(FALSE); + GCX_PREEMP(); + DomainCacheCrstHolderForGCCoop holder(this); + + // !!! suppress exceptions + if(!m_AssemblyCache.StorePEAssembly(pSpec, pPEAssembly) && !fAllowFailure) + { + // TODO: Disabling the below assertion as currently we experience + // inconsistency on resolving the Microsoft.Office.Interop.MSProject.dll + // This causes below assertion to fire and crashes the VS. This issue + // is being tracked with Dev10 Bug 658555. Brought back it when this bug + // is fixed. + // _ASSERTE(FALSE); + + EEFileLoadException::Throw(pSpec, FUSION_E_CACHEFILE_FAILED, NULL); + } + } - EEFileLoadException::Throw(pSpec, FUSION_E_CACHEFILE_FAILED, NULL); + DomainAssembly *pParentAssembly = pSpec->GetParentAssembly(); + BINDER_SPACE::Assembly *pBinderSpaceAssembly = pPEAssembly->GetHostAssembly(); + DomainAssembly *pResultAssembly = pBinderSpaceAssembly->GetDomainAssembly(); + if ((pParentAssembly != NULL) && (pResultAssembly != NULL)) + { + LoaderAllocator *pParentAssemblyLoaderAllocator = pParentAssembly->GetLoaderAllocator(); + LoaderAllocator *pResultAssemblyLoaderAllocator = pResultAssembly->GetLoaderAllocator(); + _ASSERTE(pParentAssemblyLoaderAllocator); + _ASSERTE(pResultAssemblyLoaderAllocator); + if (pResultAssemblyLoaderAllocator->IsCollectible()) + { + pParentAssemblyLoaderAllocator->EnsureReference(pResultAssemblyLoaderAllocator); + } } return TRUE; diff --git a/src/tests/Regressions/coreclr/GitHub_67144/Class1.cs b/src/tests/Regressions/coreclr/GitHub_67144/Class1.cs new file mode 100644 index 00000000000000..c78dda0511d5dc --- /dev/null +++ b/src/tests/Regressions/coreclr/GitHub_67144/Class1.cs @@ -0,0 +1,15 @@ +using TestInterface; + +namespace TestClass +{ + public class Class : TestInterface.Interface + { + public void MainTest() + { + TestInterface.Class.Test(); + } + public void Test() + { + } + } +} \ No newline at end of file diff --git a/src/tests/Regressions/coreclr/GitHub_67144/Interface1.cs b/src/tests/Regressions/coreclr/GitHub_67144/Interface1.cs new file mode 100644 index 00000000000000..6e35a0ab044708 --- /dev/null +++ b/src/tests/Regressions/coreclr/GitHub_67144/Interface1.cs @@ -0,0 +1,17 @@ +using System; + +namespace TestInterface +{ + public interface Interface + { + void Test(); + } + + public class Class + { + public static void Test() + { + Console.WriteLine("TestInterface.Class.Test"); + } + } +} \ No newline at end of file diff --git a/src/tests/Regressions/coreclr/GitHub_67144/TestClass.csproj b/src/tests/Regressions/coreclr/GitHub_67144/TestClass.csproj new file mode 100644 index 00000000000000..e0dee565739cb9 --- /dev/null +++ b/src/tests/Regressions/coreclr/GitHub_67144/TestClass.csproj @@ -0,0 +1,16 @@ + + + + Library + BuildOnly + + + + + + + + + + + diff --git a/src/tests/Regressions/coreclr/GitHub_67144/TestInterface.csproj b/src/tests/Regressions/coreclr/GitHub_67144/TestInterface.csproj new file mode 100644 index 00000000000000..56dabcc5082504 --- /dev/null +++ b/src/tests/Regressions/coreclr/GitHub_67144/TestInterface.csproj @@ -0,0 +1,12 @@ + + + + Library + BuildOnly + + + + + + + diff --git a/src/tests/Regressions/coreclr/GitHub_67144/test67144.cs b/src/tests/Regressions/coreclr/GitHub_67144/test67144.cs new file mode 100644 index 00000000000000..7aeae89bbd2b96 --- /dev/null +++ b/src/tests/Regressions/coreclr/GitHub_67144/test67144.cs @@ -0,0 +1,162 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.Loader; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.IO; + +class TestAssemblyLoadContext : AssemblyLoadContext +{ + WeakReference interfaceAssemblyRef = null; + + public TestAssemblyLoadContext(string name, bool isCollectible) : base(name, isCollectible) + { + } + + public WeakReference InterfaceAssemblyRef { get { return interfaceAssemblyRef; } } + + protected override Assembly Load(AssemblyName assemblyName) + { + if (assemblyName.Name == "TestInterface") + { + AssemblyLoadContext alc1 = new AssemblyLoadContext("Dependencies", true); + Console.WriteLine($"Loading TestInterface by alc {alc1} for alc {this}"); + Assembly a = alc1.LoadFromAssemblyPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\TestInterface\TestInterface.dll")); + interfaceAssemblyRef = new WeakReference(a); + return a; + } + + return null; + } +} + +class Test +{ + static AssemblyLoadContext alc1 = null; + static WeakReference interfaceAssemblyRef = null; + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Assembly LoadUsingResolvingEvent() + { + alc1 = new AssemblyLoadContext("Dependencies", true); + AssemblyLoadContext alc2 = new AssemblyLoadContext("Test1", true); + alc2.Resolving += Alc2_Resolving; + Assembly assembly = alc2.LoadFromAssemblyPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\TestClass\TestClass.dll")); + + Type t = assembly.GetType("TestClass.Class"); + Console.WriteLine($"Type {t} obtained"); + + MethodInfo mi = t.GetMethod("MainTest"); + Console.WriteLine($"Method {mi} obtained"); + + alc1 = null; + Console.WriteLine("Load done"); + + return assembly; + } + + private static Assembly Alc2_Resolving(AssemblyLoadContext arg1, AssemblyName arg2) + { + Console.WriteLine($"Resolving event by alc {alc1} for alc {arg1}"); + if (alc1 != null && arg2.Name == "TestInterface") + { + Console.WriteLine($"Loading TestInterface by alc {alc1} for alc {arg1}"); + Assembly a = alc1.LoadFromAssemblyPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\TestInterface\TestInterface.dll")); + interfaceAssemblyRef = new WeakReference(a); + return a; + } + + return null; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Assembly LoadUsingLoadOverride() + { + TestAssemblyLoadContext alc2 = new TestAssemblyLoadContext("Test2", true); + Assembly assembly = alc2.LoadFromAssemblyPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\TestClass\TestClass.dll")); + + Type t = assembly.GetType("TestClass.Class"); + + Console.WriteLine($"Load done, type {t} obtained"); + + interfaceAssemblyRef = alc2.InterfaceAssemblyRef; + + return assembly; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static WeakReference TestDependencies(string testCase) + { + Assembly assembly; + + if (testCase == "ResolvingEvent") + { + assembly = LoadUsingResolvingEvent(); + } + else + { + assembly = LoadUsingLoadOverride(); + } + + for (int i = 0; interfaceAssemblyRef.IsAlive && (i < 10); i++) + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + } + + Type t = assembly.GetType("TestClass.Class"); + Type[] ifaces = t.GetInterfaces(); + ConstructorInfo ctor = t.GetConstructor(new Type[] { }); + object instance = ctor.Invoke(new object[0]); + MethodInfo mi = t.GetMethod("MainTest"); + mi.Invoke(instance, null); + + return new WeakReference(assembly); + } + + public static int TestFullUnload(string testCase) + { + Console.WriteLine($"Running test case {testCase}"); + + WeakReference assemblyRef = TestDependencies(testCase); + if (assemblyRef == null) + { + return 101; + } + + for (int i = 0; assemblyRef.IsAlive && (i < 10); i++) + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + } + + if (assemblyRef.IsAlive) + { + Console.WriteLine("Failed to unload alc2"); + return 102; + } + + if (interfaceAssemblyRef.IsAlive) + { + Console.WriteLine("Failed to unload alc1"); + return 103; + } + + Console.WriteLine(); + return 100; + + } + + public static int Main(string[] args) + { + int status = 100; + foreach (string testCase in new string[] {"LoadOverride", "ResolvingEvent"}) + { + status = TestFullUnload(testCase); + } + + return status; + } +} diff --git a/src/tests/Regressions/coreclr/GitHub_67144/test67144.csproj b/src/tests/Regressions/coreclr/GitHub_67144/test67144.csproj new file mode 100644 index 00000000000000..5e000f281ef16c --- /dev/null +++ b/src/tests/Regressions/coreclr/GitHub_67144/test67144.csproj @@ -0,0 +1,9 @@ + + + Exe + 1 + + + + + From bd6c6549575f81091e669aac89660240782b5ad2 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Wed, 13 Apr 2022 23:17:32 +0200 Subject: [PATCH 2/5] Reflect PR feedback --- src/coreclr/vm/appdomain.cpp | 1 + .../ResolvedFromDifferentContext/Class.cs} | 2 +- .../ResolvedFromDifferentContext/Interface.cs} | 2 +- .../ResolvedFromDifferentContext.cs} | 8 ++++++++ .../ResolvedFromDifferentContext.csproj} | 2 +- .../ResolvedFromDifferentContext}/TestClass.csproj | 2 +- .../ResolvedFromDifferentContext}/TestInterface.csproj | 2 +- 7 files changed, 14 insertions(+), 5 deletions(-) rename src/tests/{Regressions/coreclr/GitHub_67144/Class1.cs => Loader/CollectibleAssemblies/ResolvedFromDifferentContext/Class.cs} (90%) rename src/tests/{Regressions/coreclr/GitHub_67144/Interface1.cs => Loader/CollectibleAssemblies/ResolvedFromDifferentContext/Interface.cs} (93%) rename src/tests/{Regressions/coreclr/GitHub_67144/test67144.cs => Loader/CollectibleAssemblies/ResolvedFromDifferentContext/ResolvedFromDifferentContext.cs} (91%) rename src/tests/{Regressions/coreclr/GitHub_67144/test67144.csproj => Loader/CollectibleAssemblies/ResolvedFromDifferentContext/ResolvedFromDifferentContext.csproj} (76%) rename src/tests/{Regressions/coreclr/GitHub_67144 => Loader/CollectibleAssemblies/ResolvedFromDifferentContext}/TestClass.csproj (88%) rename src/tests/{Regressions/coreclr/GitHub_67144 => Loader/CollectibleAssemblies/ResolvedFromDifferentContext}/TestInterface.csproj (83%) diff --git a/src/coreclr/vm/appdomain.cpp b/src/coreclr/vm/appdomain.cpp index 87d285dc41c921..3e21e1973a40b4 100644 --- a/src/coreclr/vm/appdomain.cpp +++ b/src/coreclr/vm/appdomain.cpp @@ -3372,6 +3372,7 @@ BOOL AppDomain::AddFileToCache(AssemblySpec* pSpec, PEAssembly * pPEAssembly, BO DomainAssembly *pParentAssembly = pSpec->GetParentAssembly(); BINDER_SPACE::Assembly *pBinderSpaceAssembly = pPEAssembly->GetHostAssembly(); + _ASSERTE(pBinderSpaceAssembly != NULL); DomainAssembly *pResultAssembly = pBinderSpaceAssembly->GetDomainAssembly(); if ((pParentAssembly != NULL) && (pResultAssembly != NULL)) { diff --git a/src/tests/Regressions/coreclr/GitHub_67144/Class1.cs b/src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/Class.cs similarity index 90% rename from src/tests/Regressions/coreclr/GitHub_67144/Class1.cs rename to src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/Class.cs index c78dda0511d5dc..5b3ff4f99b4f45 100644 --- a/src/tests/Regressions/coreclr/GitHub_67144/Class1.cs +++ b/src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/Class.cs @@ -1,4 +1,4 @@ -using TestInterface; +using TestInterface; namespace TestClass { diff --git a/src/tests/Regressions/coreclr/GitHub_67144/Interface1.cs b/src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/Interface.cs similarity index 93% rename from src/tests/Regressions/coreclr/GitHub_67144/Interface1.cs rename to src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/Interface.cs index 6e35a0ab044708..24097240737548 100644 --- a/src/tests/Regressions/coreclr/GitHub_67144/Interface1.cs +++ b/src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/Interface.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace TestInterface { diff --git a/src/tests/Regressions/coreclr/GitHub_67144/test67144.cs b/src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/ResolvedFromDifferentContext.cs similarity index 91% rename from src/tests/Regressions/coreclr/GitHub_67144/test67144.cs rename to src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/ResolvedFromDifferentContext.cs index 7aeae89bbd2b96..6dbe8bc79c8310 100644 --- a/src/tests/Regressions/coreclr/GitHub_67144/test67144.cs +++ b/src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/ResolvedFromDifferentContext.cs @@ -1,6 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +// When an assembly A1 from an unloadable AssemblyLoadContext 1 is referenced +// by another assembly A2 from an unloadable AssemblyLoadContext 2, e.g. +// when a class from A2 implements an interface from A1 and there are no +// instances of types from A1 and the managed assembly type and the related +// AssemblyLoadContext is gone too, the AssemblyLoadContext 1 cannot +// get collected, it would result in later crashes, null references +// etc. + using System; using System.Runtime.Loader; using System.Reflection; diff --git a/src/tests/Regressions/coreclr/GitHub_67144/test67144.csproj b/src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/ResolvedFromDifferentContext.csproj similarity index 76% rename from src/tests/Regressions/coreclr/GitHub_67144/test67144.csproj rename to src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/ResolvedFromDifferentContext.csproj index 5e000f281ef16c..53b9f985789b9d 100644 --- a/src/tests/Regressions/coreclr/GitHub_67144/test67144.csproj +++ b/src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/ResolvedFromDifferentContext.csproj @@ -4,6 +4,6 @@ 1 - + diff --git a/src/tests/Regressions/coreclr/GitHub_67144/TestClass.csproj b/src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/TestClass.csproj similarity index 88% rename from src/tests/Regressions/coreclr/GitHub_67144/TestClass.csproj rename to src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/TestClass.csproj index e0dee565739cb9..fcca6914f7609a 100644 --- a/src/tests/Regressions/coreclr/GitHub_67144/TestClass.csproj +++ b/src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/TestClass.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/tests/Regressions/coreclr/GitHub_67144/TestInterface.csproj b/src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/TestInterface.csproj similarity index 83% rename from src/tests/Regressions/coreclr/GitHub_67144/TestInterface.csproj rename to src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/TestInterface.csproj index 56dabcc5082504..1a58590d462931 100644 --- a/src/tests/Regressions/coreclr/GitHub_67144/TestInterface.csproj +++ b/src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/TestInterface.csproj @@ -6,7 +6,7 @@ - + From 7535870639d8a3991832281920f1a0396c7768f0 Mon Sep 17 00:00:00 2001 From: Vladimir Sadov Date: Thu, 14 Apr 2022 16:32:17 -0700 Subject: [PATCH 3/5] Apply suggestions from code review Co-authored-by: Jeremy Koritzinsky --- .../ResolvedFromDifferentContext/TestClass.csproj | 2 +- .../ResolvedFromDifferentContext/TestInterface.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/TestClass.csproj b/src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/TestClass.csproj index fcca6914f7609a..f155b1db94d688 100644 --- a/src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/TestClass.csproj +++ b/src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/TestClass.csproj @@ -2,7 +2,7 @@ Library - BuildOnly + SharedLibrary diff --git a/src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/TestInterface.csproj b/src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/TestInterface.csproj index 1a58590d462931..c593219fb45473 100644 --- a/src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/TestInterface.csproj +++ b/src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/TestInterface.csproj @@ -2,7 +2,7 @@ Library - BuildOnly + SharedLibrary From 3663f89a58fb9f15762c445737d3f763ab68c2e1 Mon Sep 17 00:00:00 2001 From: Vladimir Sadov Date: Fri, 15 Apr 2022 11:06:28 -0700 Subject: [PATCH 4/5] Adding detailed comments per PR feedback. --- src/coreclr/vm/appdomain.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/coreclr/vm/appdomain.cpp b/src/coreclr/vm/appdomain.cpp index 3e21e1973a40b4..96230a41c929eb 100644 --- a/src/coreclr/vm/appdomain.cpp +++ b/src/coreclr/vm/appdomain.cpp @@ -3370,6 +3370,21 @@ BOOL AppDomain::AddFileToCache(AssemblySpec* pSpec, PEAssembly * pPEAssembly, BO } } + // If the ALC for the result (pPEAssembly) is collectible and not the same as the ALC for the request (pSpec), + // which can happen when extension points (AssemblyLoadContext.Load, AssemblyLoadContext.Resolving) resolve the + // assembly, we need to ensure the result ALC is not collected before the request ALC. We do this by adding an + // explicit reference between the LoaderAllocators corresponding to the ALCs. + // + // To get the LoaderAllocators, we rely on the DomainAssembly corresponding to: + // - Parent assembly for the request + // - For loads via explicit load or reflection, this will be NULL. In these cases, the request ALC should not + // implicitly (as in not detectable via managed references) depend on the result ALC, so it is fine to not + // add the reference. + // - For loads via assembly references, this will never be NULL. + // - Result assembly for the result + // - For dynamic assemblies, there will be no host assembly, so we have the result assembly / LoaderAllocator. + // We currently block resolving to dynamic assemblies, so we simply assert that we have a host assembly. + // - For non-dynamic assemblies, we should be able to get the host assembly. DomainAssembly *pParentAssembly = pSpec->GetParentAssembly(); BINDER_SPACE::Assembly *pBinderSpaceAssembly = pPEAssembly->GetHostAssembly(); _ASSERTE(pBinderSpaceAssembly != NULL); From da58ee5a7b71de1ef22f0f55e15b9221671d6d56 Mon Sep 17 00:00:00 2001 From: Vladimir Sadov Date: Fri, 15 Apr 2022 11:24:40 -0700 Subject: [PATCH 5/5] Update src/coreclr/vm/appdomain.cpp Co-authored-by: Elinor Fung --- src/coreclr/vm/appdomain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/appdomain.cpp b/src/coreclr/vm/appdomain.cpp index 96230a41c929eb..e53000ceddb5e3 100644 --- a/src/coreclr/vm/appdomain.cpp +++ b/src/coreclr/vm/appdomain.cpp @@ -3382,7 +3382,7 @@ BOOL AppDomain::AddFileToCache(AssemblySpec* pSpec, PEAssembly * pPEAssembly, BO // add the reference. // - For loads via assembly references, this will never be NULL. // - Result assembly for the result - // - For dynamic assemblies, there will be no host assembly, so we have the result assembly / LoaderAllocator. + // - For dynamic assemblies, there is no host assembly, so we don't have the result assembly / LoaderAllocator. // We currently block resolving to dynamic assemblies, so we simply assert that we have a host assembly. // - For non-dynamic assemblies, we should be able to get the host assembly. DomainAssembly *pParentAssembly = pSpec->GetParentAssembly();