diff --git a/src/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext/CoreCLR/CorePsAssemblyLoadContext.cs b/src/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext/CoreCLR/PowerShellAssemblyLoader.cs similarity index 92% rename from src/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext/CoreCLR/CorePsAssemblyLoadContext.cs rename to src/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext/CoreCLR/PowerShellAssemblyLoader.cs index cb2cc44d8e..418ec2b8a3 100644 --- a/src/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext/CoreCLR/CorePsAssemblyLoadContext.cs +++ b/src/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext/CoreCLR/PowerShellAssemblyLoader.cs @@ -15,14 +15,14 @@ using System.Runtime.Loader; namespace System.Management.Automation { /// - /// The powershell custom AssemblyLoadContext implementation + /// The powershell custom assembly loader implementation /// - internal partial class PowerShellAssemblyLoadContext : AssemblyLoadContext + internal partial class PowerShellAssemblyLoader { #region Resource_Strings // We cannot use a satellite resources.dll to store resource strings for Microsoft.PowerShell.CoreCLR.AssemblyLoadContext.dll. This is because when retrieving resource strings, ResourceManager - // tries to load the satellite resources.dll using a probing approach, which will cause an infinite loop to PowerShellAssemblyLoadContext.Load(AssemblyName). + // tries to load the satellite resources.dll using a probing approach, which will cause an infinite loop to PowerShellAssemblyLoader.Load(AssemblyName). // Take the 'en-US' culture as an example. When retrieving resource string to construct an exception, ResourceManager calls Assembly.Load(..) in the following order to load the resource dll: // 1. Load assembly with culture 'en-US' (Microsoft.PowerShell.CoreCLR.AssemblyLoadContext.resources, Version=3.0.0.0, Culture=en-US, PublicKeyToken=31bf3856ad364e35) // 2. Load assembly with culture 'en' (Microsoft.PowerShell.CoreCLR.AssemblyLoadContext.resources, Version=3.0.0.0, Culture=en, PublicKeyToken=31bf3856ad364e35) @@ -37,13 +37,6 @@ namespace System.Management.Automation #region Constructor - /// - /// This constructor is for testability purpose only - /// - protected PowerShellAssemblyLoadContext() - { - } - /// /// Constructor /// @@ -51,7 +44,7 @@ namespace System.Management.Automation /// Base directory paths that are separated by semicolon ';'. /// They will be the default paths to probe assemblies. /// - internal PowerShellAssemblyLoadContext(string basePaths) + internal PowerShellAssemblyLoader(string basePaths) { #region Validation this.basePaths = basePaths.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); @@ -77,12 +70,18 @@ namespace System.Management.Automation // - Key: namespace qualified type name (FullName) // - Value: strong name of the TPA that contains the type represented by Key. coreClrTypeCatalog = InitializeTypeCatalog(); + + this.loadContext = AssemblyLoadContext.Default; + loadContext.Resolving += Resolve; } #endregion Constructor #region Fields + // AssemblyLoadContext used by this loader + private readonly AssemblyLoadContext loadContext; + // Serialized type catalog file private readonly object syncObj = new object(); private readonly string[] basePaths; @@ -118,10 +117,15 @@ namespace System.Management.Automation #region Protected_Internal_Methods /// - /// Implement the AssemblyLoadContext.Load(AssemblyName). Search the requested assembly in probing paths. + /// The global instance of PowerShellAssemblyLoader + /// + internal static PowerShellAssemblyLoader Instance { get; set; } + + /// + /// Implement the AssemblyLoadContext.Resolving event handler. Search the requested assembly in probing paths. /// Search the file "[assemblyName.Name][.ni].dll" in probing paths. If the file is found and it matches the requested AssemblyName, load it with LoadFromAssemblyPath. /// - protected override Assembly Load(AssemblyName assemblyName) + internal Assembly Resolve(AssemblyLoadContext sender, AssemblyName assemblyName) { // Probe the assembly cache Assembly asmLoaded; @@ -153,7 +157,7 @@ namespace System.Management.Automation if (File.Exists(asmFilePath)) { isAssemblyFileFound = true; - AssemblyName asmNameFound = GetAssemblyName(asmFilePath); + AssemblyName asmNameFound = AssemblyLoadContext.GetAssemblyName(asmFilePath); if (IsAssemblyMatching(assemblyName, asmNameFound)) { isAssemblyFileMatching = true; @@ -187,8 +191,8 @@ namespace System.Management.Automation try { asmLoaded = asmFilePath.EndsWith(".ni.dll", StringComparison.OrdinalIgnoreCase) - ? LoadFromNativeImagePath(asmFilePath, null) - : LoadFromAssemblyPath(asmFilePath); + ? loadContext.LoadFromNativeImagePath(asmFilePath, null) + : loadContext.LoadFromAssemblyPath(asmFilePath); } // Since .NET CLI built versions of PowerShell have all the // built-in assemblies in the TPA list, the above will throw, @@ -211,6 +215,14 @@ namespace System.Management.Automation return asmLoaded; } + /// + /// Load an assembly from its name. + /// + internal Assembly LoadFromAssemblyName(AssemblyName assemblyName) + { + return loadContext.LoadFromAssemblyName(assemblyName); + } + /// /// Load an assembly from its file path. /// @@ -239,7 +251,7 @@ namespace System.Management.Automation #endregion Validation Assembly asmLoaded; - AssemblyName assemblyName = GetAssemblyName(assemblyPath); + AssemblyName assemblyName = AssemblyLoadContext.GetAssemblyName(assemblyPath); // Probe the assembly cache if (TryGetAssemblyFromCache(assemblyName, out asmLoaded)) @@ -256,8 +268,8 @@ namespace System.Management.Automation { // Load the assembly through 'LoadFromNativeImagePath' or 'LoadFromAssemblyPath' asmLoaded = assemblyPath.EndsWith(".ni.dll", StringComparison.OrdinalIgnoreCase) - ? LoadFromNativeImagePath(assemblyPath, null) - : LoadFromAssemblyPath(assemblyPath); + ? loadContext.LoadFromNativeImagePath(assemblyPath, null) + : loadContext.LoadFromAssemblyPath(assemblyPath); } // Since .NET CLI built versions of PowerShell have all the // built-in assemblies in the TPA list, the above will throw, @@ -290,7 +302,7 @@ namespace System.Management.Automation /// internal Assembly LoadFrom(Stream assembly) { - var asm = LoadFromStream(assembly); + var asm = loadContext.LoadFromStream(assembly); TryAddAssemblyToCache(asm); return asm; } @@ -498,13 +510,11 @@ namespace System.Management.Automation } /// - /// Set an instance of PowerShellAssemblyLoadContext to be the default Assembly Load Context. + /// Set an instance of PowerShellAssemblyLoader to be the default Assembly Load Context. /// This is the managed entry point for Microsoft.PowerShell.CoreCLR.AssemblyLoadContext.dll. /// public class PowerShellAssemblyLoadContextInitializer { - private static bool IsInitialized = false; - // Porting note: it's much easier to send an LPStr on Linux private const UnmanagedType stringType = #if LINUX @@ -519,19 +529,9 @@ namespace System.Management.Automation /// public static void SetPowerShellAssemblyLoadContext([MarshalAs(stringType)]string basePaths) { - if (!IsInitialized) + if (PowerShellAssemblyLoader.Instance == null) { - var psAsmLoadContext = new PowerShellAssemblyLoadContext(basePaths); - try - { - AssemblyLoadContext.InitializeDefaultContext(psAsmLoadContext); - } - catch (System.InvalidOperationException) - { - // We may not be able to set the default context. If we're under the - // xUnit test harness, it has already been set. - } - IsInitialized = true; + PowerShellAssemblyLoader.Instance = new PowerShellAssemblyLoader(basePaths); } } } diff --git a/src/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext/CorePsTypeCatalog.cs b/src/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext/CorePsTypeCatalog.cs index d00fa92ccf..7a5acf16f1 100644 --- a/src/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext/CorePsTypeCatalog.cs +++ b/src/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext/CorePsTypeCatalog.cs @@ -10,7 +10,7 @@ using System.Collections.Generic; namespace System.Management.Automation { - internal partial class PowerShellAssemblyLoadContext + internal partial class PowerShellAssemblyLoader { private Dictionary InitializeTypeCatalog() { diff --git a/src/System.Management.Automation/resources/ParserStrings.resx b/src/System.Management.Automation/resources/ParserStrings.resx index ba99c3d0da..807450a9d7 100644 --- a/src/System.Management.Automation/resources/ParserStrings.resx +++ b/src/System.Management.Automation/resources/ParserStrings.resx @@ -1352,7 +1352,7 @@ ModuleVersion : Version of module to import. If used, ModuleName must represent Cannot run a document in PowerShell Core: {0}. - The default AssemblyLoadContext in use is invalid. The default AssemblyLoadContext for PowerShell Core should be of type 'PowerShellAssemblyLoadContext'. + The default AssemblyLoadContext in use is invalid. The default AssemblyLoadContext for PowerShell Core should be of type 'PowerShellAssemblyLoader'. Multiple type constraints are not allowed on a method parameter. diff --git a/src/System.Management.Automation/utils/ClrFacade.cs b/src/System.Management.Automation/utils/ClrFacade.cs index edb4de9d64..744e2bb5e2 100644 --- a/src/System.Management.Automation/utils/ClrFacade.cs +++ b/src/System.Management.Automation/utils/ClrFacade.cs @@ -276,7 +276,7 @@ namespace System.Management.Automation internal static IEnumerable GetAssemblies(string namespaceQualifiedTypeName = null) { #if CORECLR - var psAssemblyLoader = GetAssemblyLoadContext(); + var psAssemblyLoader = GetAssemblyLoader(); return psAssemblyLoader.GetAssemblies(namespaceQualifiedTypeName); #else return AppDomain.CurrentDomain.GetAssemblies().Where(a => !(a.FullName.Length > 0 && a.FullName[0] == FIRST_CHAR_PSASSEMBLY_MARK)); @@ -290,7 +290,7 @@ namespace System.Management.Automation internal static Assembly LoadFrom(string assemblyPath) { #if CORECLR - var psAssemblyLoader = GetAssemblyLoadContext(); + var psAssemblyLoader = GetAssemblyLoader(); return psAssemblyLoader.LoadFrom(assemblyPath); #else return Assembly.LoadFrom(assemblyPath); @@ -303,7 +303,7 @@ namespace System.Management.Automation /// internal static Assembly LoadFrom(Stream assembly) { - var psAssemblyLoader = GetAssemblyLoadContext(); + var psAssemblyLoader = GetAssemblyLoader(); return psAssemblyLoader.LoadFrom(assembly); } #endif @@ -315,7 +315,7 @@ namespace System.Management.Automation internal static Assembly Load(AssemblyName assembly) { #if CORECLR - var psAssemblyLoader = GetAssemblyLoadContext(); + var psAssemblyLoader = GetAssemblyLoader(); return psAssemblyLoader.LoadFromAssemblyName(assembly); #else return Assembly.Load(assembly); @@ -328,7 +328,7 @@ namespace System.Management.Automation internal static Assembly Load(string assembly) { #if CORECLR - var psAssemblyLoader = GetAssemblyLoadContext(); + var psAssemblyLoader = GetAssemblyLoader(); return psAssemblyLoader.LoadFromAssemblyName(new AssemblyName(assembly)); #else return Assembly.Load(assembly); @@ -347,7 +347,7 @@ namespace System.Management.Automation #if CORECLR // Create the enum type and add the dynamic assembly to assembly cache. TypeInfo enumTypeinfo = enumBuilder.CreateTypeInfo(); - var psAssemblyLoader = GetAssemblyLoadContext(); + var psAssemblyLoader = GetAssemblyLoader(); psAssemblyLoader.TryAddAssemblyToCache(enumTypeinfo.Assembly); #else enumBuilder.CreateTypeInfo(); @@ -370,7 +370,7 @@ namespace System.Management.Automation throw new ArgumentNullException("assemblyShortName"); } - var psAssemblyLoader = GetAssemblyLoadContext(); + var psAssemblyLoader = GetAssemblyLoader(); return psAssemblyLoader.ProbeAssemblyFileForMetadataAnalysis(assemblyShortName, additionalSearchPath); } @@ -380,28 +380,22 @@ namespace System.Management.Automation /// internal static IEnumerable GetAvailableCoreClrDotNetTypes() { - var psAssemblyLoader = GetAssemblyLoadContext(); + var psAssemblyLoader = GetAssemblyLoader(); return psAssemblyLoader.GetAvailableDotNetTypes(); } /// /// Get the powershell custom AssemblyLoadContext. /// - internal static PowerShellAssemblyLoadContext GetAssemblyLoadContext() + internal static PowerShellAssemblyLoader GetAssemblyLoader() { if (_psLoadContext == null) { - _psLoadContext = AssemblyLoadContext.Default as PowerShellAssemblyLoadContext; - if (_psLoadContext == null) - { - // The default load context may not be ours. This can happen during, - // for instance, xUnit testing. - _psLoadContext = new PowerShellAssemblyLoadContext(String.Empty); - } + _psLoadContext = PowerShellAssemblyLoader.Instance; } return _psLoadContext; } - private static volatile PowerShellAssemblyLoadContext _psLoadContext; + private static volatile PowerShellAssemblyLoader _psLoadContext; #endif /// diff --git a/src/TypeCatalogGen/TypeCatalogGen.cs b/src/TypeCatalogGen/TypeCatalogGen.cs index ecaff01c02..5aa7a44f4a 100644 --- a/src/TypeCatalogGen/TypeCatalogGen.cs +++ b/src/TypeCatalogGen/TypeCatalogGen.cs @@ -41,7 +41,7 @@ Usage: TypeCatalogGen.exe <{0}> <{1}> /* * Go through all reference assemblies of .NET Core and generate the type catalog -> Dictionary - * Then auto-generate the partial class 'PowerShellAssemblyLoadContext' that has the code to initialize the type catalog cache. + * Then auto-generate the partial class 'PowerShellAssemblyLoader' that has the code to initialize the type catalog cache. * * In CoreCLR, there is no way to get all loaded TPA assemblies (.NET Framework Assemblies). In order to get type based on type name, powershell needs to know what .NET * types are available and in which TPA assemblies. So we have to generate the type catalog based on the reference assemblies of .NET Core. @@ -105,7 +105,7 @@ Usage: TypeCatalogGen.exe <{0}> <{1}> } } - WritePowerShellAssemblyLoadContextPartialClass(targetFilePath, typeNameToAssemblyMap); + WritePowerShellAssemblyLoaderPartialClass(targetFilePath, typeNameToAssemblyMap); } /// @@ -272,7 +272,7 @@ Usage: TypeCatalogGen.exe <{0}> <{1}> /// /// Generate the CSharp source code that initialize the type catalog. /// - private static void WritePowerShellAssemblyLoadContextPartialClass(string targetFilePath, Dictionary typeNameToAssemblyMap) + private static void WritePowerShellAssemblyLoaderPartialClass(string targetFilePath, Dictionary typeNameToAssemblyMap) { const string SourceFormat = " typeCatalog[\"{0}\"] = \"{1}\";"; const string SourceHead = @"// @@ -287,7 +287,7 @@ using System.Collections.Generic; namespace System.Management.Automation {{ - internal partial class PowerShellAssemblyLoadContext + internal partial class PowerShellAssemblyLoader {{ private Dictionary InitializeTypeCatalog() {{