namespace System.Runtime.Loader
{
- public abstract partial class AssemblyLoadContext
+ public partial class AssemblyLoadContext
{
private enum InternalState
{
// Contains the reference to VM's representation of the AssemblyLoadContext
private readonly IntPtr _nativeAssemblyLoadContext;
- protected AssemblyLoadContext() : this(false, false)
+ protected AssemblyLoadContext() : this(false, false, null)
{
}
- protected AssemblyLoadContext(bool isCollectible) : this(false, isCollectible)
+ protected AssemblyLoadContext(bool isCollectible) : this(false, isCollectible, null)
{
}
- private protected AssemblyLoadContext(bool representsTPALoadContext, bool isCollectible)
+ public AssemblyLoadContext(string name, bool isCollectible = true) : this(false, isCollectible, name)
+ {
+ }
+
+ private protected AssemblyLoadContext(bool representsTPALoadContext, bool isCollectible, string name)
{
// Initialize the VM side of AssemblyLoadContext if not already done.
IsCollectible = isCollectible;
+
+ 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.
_unloadLock = new object();
}
}
+ public IEnumerable<Assembly> Assemblies
+ {
+ get
+ {
+ foreach (Assembly a in GetLoadedAssemblies())
+ {
+ AssemblyLoadContext alc = GetLoadContext(a);
+
+ if (alc == this)
+ {
+ yield return a;
+ }
+ }
+ }
+ }
+
// Event handler for resolving native libraries.
// This event is raised if the native library could not be resolved via
// the default resolution logic [including AssemblyLoadContext.LoadUnmanagedDll()]
public bool IsCollectible { get; }
+ public string Name { get; }
+
+ public override string ToString() => "\"" + Name + "\" " + GetType().ToString() + " #" + _id;
+
+ public static IEnumerable<AssemblyLoadContext> All
+ {
+ get
+ {
+ AssemblyLoadContext d = AssemblyLoadContext.Default; // Ensure default is initialized
+
+ List<WeakReference<AssemblyLoadContext>> alcList = null;
+ lock (s_contextsToUnload)
+ {
+ // To make this thread safe we need a quick snapshot while locked
+ alcList = new List<WeakReference<AssemblyLoadContext>>(s_contextsToUnload.Values);
+ }
+
+ foreach (WeakReference<AssemblyLoadContext> weakAlc in alcList)
+ {
+ AssemblyLoadContext alc = null;
+
+ weakAlc.TryGetTarget(out alc);
+
+ if (alc != null)
+ {
+ yield return alc;
+ }
+ }
+ }
+ }
+
// Helper to return AssemblyName corresponding to the path of an IL assembly
public static AssemblyName GetAssemblyName(string assemblyPath)
{
// Custom AssemblyLoadContext implementations can override this
// method to perform custom processing and use one of the protected
// helpers above to load the assembly.
- protected abstract Assembly Load(AssemblyName assemblyName);
+ protected virtual Assembly Load(AssemblyName assemblyName)
+ {
+ return null;
+ }
[System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
public Assembly LoadFromAssemblyName(AssemblyName assemblyName)
{
internal static readonly AssemblyLoadContext s_loadContext = new DefaultAssemblyLoadContext();
- internal DefaultAssemblyLoadContext() : base(true, false)
+ internal DefaultAssemblyLoadContext() : base(true, false, "Default")
{
}
-
- protected override Assembly Load(AssemblyName 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;
- }
}
internal sealed class IndividualAssemblyLoadContext : AssemblyLoadContext
{
- internal IndividualAssemblyLoadContext() : base(false, false)
+ internal IndividualAssemblyLoadContext(String name) : base(false, false, name)
{
}
-
- protected override Assembly Load(AssemblyName assemblyName)
- {
- return null;
- }
}
-}
\ No newline at end of file
+}
--- /dev/null
+// 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.Reflection;
+using System.Runtime.Loader;
+using System.IO;
+
+using Console = Internal.Console;
+
+namespace My
+{
+ public class CustomAssemblyLoadContext : AssemblyLoadContext
+ {
+ public CustomAssemblyLoadContext() : base()
+ {
+ }
+ }
+}
+
+public class Program
+{
+ private static bool passed = true;
+
+ public static void Assert(bool value, string message = "none")
+ {
+ if (!value)
+ {
+ Console.WriteLine("FAIL! " + message);
+ passed = false;
+ }
+ }
+
+ public static bool AssembliesContainAssembly(AssemblyLoadContext alc, Assembly a)
+ {
+ foreach (Assembly b in alc.Assemblies)
+ {
+ if (a == b)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static bool PropertyAllContainsContext(AssemblyLoadContext alc)
+ {
+ foreach (AssemblyLoadContext c in AssemblyLoadContext.All)
+ {
+ if (alc == c)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static void DefaultName()
+ {
+ try
+ {
+ Console.WriteLine("DefaultName()");
+
+ AssemblyLoadContext alc = AssemblyLoadContext.Default;
+ Assert(alc == AssemblyLoadContext.GetLoadContext(typeof(Program).Assembly));
+ Assert(PropertyAllContainsContext(alc));
+ Assert(AssembliesContainAssembly(alc, typeof(Program).Assembly));
+
+ Console.WriteLine(alc.Name);
+ Assert(alc.Name == "Default");
+
+ Console.WriteLine(alc.GetType().ToString());
+ Assert(alc.GetType().ToString() == "System.Runtime.Loader.DefaultAssemblyLoadContext");
+
+ Console.WriteLine(alc.ToString());
+ Assert(alc.ToString().Contains("\"Default"));
+ Assert(alc.ToString().Contains("\" System.Runtime.Loader.DefaultAssemblyLoadContext"));
+ Assert(alc.ToString().Contains(" #"));
+ }
+ catch (Exception e)
+ {
+ Assert(false, e.ToString());
+ }
+ }
+
+ public static void AssemblyLoadFileName()
+ {
+ try
+ {
+ Console.WriteLine("AssemblyLoadFileName()");
+
+ String path = typeof(Program).Assembly.Location;
+ Assembly a = Assembly.LoadFile(path);
+
+ Assert(a != typeof(Program).Assembly);
+
+ AssemblyLoadContext alc = AssemblyLoadContext.GetLoadContext(a);
+ Assert(PropertyAllContainsContext(alc));
+ Assert(AssembliesContainAssembly(alc, a));
+ Assert(alc != AssemblyLoadContext.Default);
+
+
+ Console.WriteLine(alc.Name);
+ Assert(alc.Name == String.Format("Assembly.LoadFile({0})", path));
+
+ Console.WriteLine(alc.GetType().ToString());
+ Assert(alc.GetType().ToString() == "System.Runtime.Loader.IndividualAssemblyLoadContext");
+
+ Console.WriteLine(alc.ToString());
+ Assert(alc.ToString().Contains("\"" + String.Format("Assembly.LoadFile({0})", path)));
+ Assert(alc.ToString().Contains("\" System.Runtime.Loader.IndividualAssemblyLoadContext"));
+ Assert(alc.ToString().Contains(" #"));
+ }
+ catch (Exception e)
+ {
+ Assert(false, e.ToString());
+ }
+ }
+
+ public static void AssemblyLoadByteArrayName()
+ {
+#if ReadAllBytes // System.IO.File.ReadAllBytes is not found when ReferenceSystemPrivateCoreLib is true
+ try
+ {
+ Console.WriteLine("AssemblyLoadByteArrayName()");
+
+ String path = typeof(Program).Assembly.Location;
+ Byte [] byteArray = System.IO.File.ReadAllBytes(path);
+ Assembly a = Assembly.Load(byteArray);
+ AssemblyLoadContext alc = AssemblyLoadContext.GetLoadContext(a);
+ Assert(PropertyAllContainsContext(alc));
+ Assert(AssembliesContainAssembly(alc, a));
+
+ Console.WriteLine(alc.Name);
+ Assert(alc.Name == "Assembly.Load(byte[], ...)");
+
+ Console.WriteLine(alc.GetType().ToString());
+ Assert(alc.GetType().ToString() == "System.Runtime.Loader.IndividualAssemblyLoadContext");
+
+ Console.WriteLine(alc.ToString());
+ Assert(alc.ToString().Contains("\"Default"));
+ Assert(alc.ToString().Contains("\" System.Runtime.Loader.IndividualAssemblyLoadContext"));
+ Assert(alc.ToString().Contains(" #"));
+ }
+ catch (Exception e)
+ {
+ Assert(false, e.ToString());
+ }
+#endif
+ }
+
+ public static void CustomWOName()
+ {
+ try
+ {
+ Console.WriteLine("CustomWOName()");
+
+ // ALC should be a concrete class
+ AssemblyLoadContext alc = new My.CustomAssemblyLoadContext();
+ Assert(PropertyAllContainsContext(alc));
+
+ Console.WriteLine(alc.Name);
+ Assert(alc.Name == null);
+
+ Console.WriteLine(alc.GetType().ToString());
+ Assert(alc.GetType().ToString() == "My.CustomAssemblyLoadContext");
+
+ Console.WriteLine(alc.ToString());
+ Assert(alc.ToString().Contains("\"\" "));
+ Assert(alc.ToString().Contains("\" My.CustomAssemblyLoadContext"));
+ Assert(alc.ToString().Contains(" #"));
+ }
+ catch (Exception e)
+ {
+ Assert(false, e.ToString());
+ }
+ }
+
+ public static void CustomName()
+ {
+ try
+ {
+ Console.WriteLine("CustomName()");
+
+ // ALC should be a concrete class
+ AssemblyLoadContext alc = new AssemblyLoadContext("CustomName");
+ Assert(PropertyAllContainsContext(alc));
+
+ Console.WriteLine(alc.Name);
+ Assert(alc.Name == "CustomName");
+
+ Console.WriteLine(alc.GetType().ToString());
+ Assert(alc.GetType().ToString() == "System.Runtime.Loader.AssemblyLoadContext");
+
+ Console.WriteLine(alc.ToString());
+ Assert(alc.ToString().Contains("\"CustomName"));
+ Assert(alc.ToString().Contains("\" System.Runtime.Loader.AssemblyLoadContext"));
+ Assert(alc.ToString().Contains(" #"));
+ }
+ catch (Exception e)
+ {
+ Assert(false, e.ToString());
+ }
+ }
+
+ public static int Main()
+ {
+ foreach (AssemblyLoadContext alc in AssemblyLoadContext.All)
+ {
+ Console.WriteLine(alc.ToString());
+ foreach (Assembly a in alc.Assemblies)
+ {
+ Console.WriteLine(a.ToString());
+ }
+ }
+
+ DefaultName();
+ AssemblyLoadFileName();
+ AssemblyLoadByteArrayName();
+ CustomWOName();
+ CustomName();
+
+ foreach (AssemblyLoadContext alc in AssemblyLoadContext.All)
+ {
+ Console.WriteLine(alc.ToString());
+ foreach (Assembly a in alc.Assemblies)
+ {
+ Console.WriteLine(a.ToString());
+ }
+ }
+
+ if (passed)
+ {
+ Console.WriteLine("Test PASSED!");
+ return 100;
+ }
+ else
+ {
+ Console.WriteLine("Test FAILED!");
+ return -1;
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <AssemblyName>AssemblyLoadContext30Extensions</AssemblyName>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+ <ReferenceSystemPrivateCoreLib>true</ReferenceSystemPrivateCoreLib>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+ </PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="AssemblyLoadContext30Extensions.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>