From 8ae6c5bb3bcc44f93f8db3144e4f70ac79c880a2 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Mon, 5 Jun 2017 20:59:30 -0700 Subject: [PATCH] Refactor PowerShell Core to use the default CoreCLR loader instead (#3903) Remove the code that spins up our own assembly load context. Keep the code that registers our `Resolve` method to the default loader's `Resolving` event, so that we can continue to do special assembly resolution as needed (such as the GAC probing logic needed for consuming FullCLR PS modules). Essentially, the assembly `Microsoft.PowerShell.CoreCLR.AssemblyLoadContext.dll` is not needed anymore, the remaining code should be moved to S.M.A.dll. However, that will break DSC and other native hosts that are hosting powershell. So this assembly is kept for now. --- .../cmdletization/cim/cimConverter.cs | 2 +- .../commands/management/Process.cs | 23 +- .../commands/management/Service.cs | 2 +- .../commands/utility/AddType.cs | 18 +- .../commands/utility/Import-LocalizedData.cs | 2 +- .../host/msh/ConsoleHost.cs | 4 +- .../host/msh/ManagedEntrance.cs | 5 +- .../CoreCLR/CorePsAssemblyLoadContext.cs | 584 +++--------------- .../CoreCLR/CorePsExtensions.cs | 16 +- .../cimSupport/cmdletization/EnumWriter.cs | 2 +- .../CommandCompletion/CompletionCompleters.cs | 61 +- .../engine/CommandInfo.cs | 3 +- .../engine/ExecutionContext.cs | 2 +- .../engine/InitialSessionState.cs | 6 +- .../engine/Utils.cs | 2 +- .../engine/hostifaces/LocalPipeline.cs | 4 +- .../engine/parser/Compiler.cs | 2 +- .../fanin/InitialSessionStateProvider.cs | 2 +- .../engine/remoting/fanin/WSManNativeAPI.cs | 2 +- .../engine/remoting/fanin/WSManPlugin.cs | 6 +- .../remoting/fanin/WSManPluginShellSession.cs | 2 +- .../fanin/WSManPluginTransportManager.cs | 2 +- .../help/UpdateHelpCommand.cs | 2 +- .../minishell/api/RunspaceConfiguration.cs | 2 +- .../resources/ParserStrings.resx | 3 - .../security/SecureStringHelper.cs | 2 +- .../security/SecuritySupport.cs | 2 +- .../config/RunspaceConfigForSingleShell.cs | 4 +- .../utils/ClrFacade.cs | 260 +------- .../utils/CryptoUtils.cs | 2 +- src/TypeCatalogGen/TypeCatalogGen.cs | 2 +- src/powershell/Program.cs | 13 - 32 files changed, 186 insertions(+), 858 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimConverter.cs b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimConverter.cs index 35e968d4d3..6792eb6568 100644 --- a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimConverter.cs +++ b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimConverter.cs @@ -80,7 +80,7 @@ namespace Microsoft.PowerShell.Cim internal void Copy(SecureString source, int offset) { - IntPtr plainTextString = ClrFacade.SecureStringToCoTaskMemUnicode(source); + IntPtr plainTextString = Marshal.SecureStringToCoTaskMemUnicode(source); try { unsafe diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs index ed1b0881ab..a7a2458870 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs @@ -641,7 +641,7 @@ namespace Microsoft.PowerShell.Commands { //assigning to tempmodule to rethrow for exceptions on 64 bit machines tempmodule = pmodule; - WriteObject(ClrFacade.GetProcessModuleFileVersionInfo(pmodule), true); + WriteObject(pmodule.FileVersionInfo, true); } } catch (InvalidOperationException exception) @@ -658,7 +658,7 @@ namespace Microsoft.PowerShell.Commands { if (exception.HResult == 299) { - WriteObject(ClrFacade.GetProcessModuleFileVersionInfo(tempmodule), true); + WriteObject(tempmodule.FileVersionInfo, true); } else { @@ -710,7 +710,7 @@ namespace Microsoft.PowerShell.Commands //if fileversion of each process is to be displayed try { - WriteObject(ClrFacade.GetProcessModuleFileVersionInfo(PsUtils.GetMainModule(process)), true); + WriteObject(PsUtils.GetMainModule(process).FileVersionInfo, true); } catch (InvalidOperationException exception) { @@ -726,7 +726,7 @@ namespace Microsoft.PowerShell.Commands { if (exception.HResult == 299) { - WriteObject(ClrFacade.GetProcessModuleFileVersionInfo(PsUtils.GetMainModule(process)), true); + WriteObject(PsUtils.GetMainModule(process).FileVersionInfo, true); } else { @@ -1966,7 +1966,7 @@ namespace Microsoft.PowerShell.Commands //UseNewEnvironment if (_UseNewEnvironment) { - ClrFacade.GetProcessEnvironment(startInfo).Clear(); + startInfo.EnvironmentVariables.Clear(); LoadEnvironmentVariable(startInfo, Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Machine)); LoadEnvironmentVariable(startInfo, Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User)); } @@ -2173,7 +2173,7 @@ namespace Microsoft.PowerShell.Commands private void LoadEnvironmentVariable(ProcessStartInfo startinfo, IDictionary EnvironmentVariables) { - var processEnvironment = ClrFacade.GetProcessEnvironment(startinfo); + var processEnvironment = startinfo.EnvironmentVariables; foreach (DictionaryEntry entry in EnvironmentVariables) { if (processEnvironment.ContainsKey(entry.Key.ToString())) @@ -2373,12 +2373,7 @@ namespace Microsoft.PowerShell.Commands return builder; } - private static byte[] ConvertEnvVarsToByteArray( -#if CORECLR - IDictionary sd) -#else - StringDictionary sd) -#endif + private static byte[] ConvertEnvVarsToByteArray(StringDictionary sd) { string[] array = new string[sd.Count]; byte[] bytes = null; @@ -2497,7 +2492,7 @@ namespace Microsoft.PowerShell.Commands creationFlags |= 0x00000004; IntPtr AddressOfEnvironmentBlock = IntPtr.Zero; - var environmentVars = ClrFacade.GetProcessEnvironment(startinfo); + var environmentVars = startinfo.EnvironmentVariables; if (environmentVars != null) { if (this.UseNewEnvironment) @@ -2647,7 +2642,7 @@ namespace Microsoft.PowerShell.Commands internal bool AssignProcessToJobObject(Process process) { // Add the process to the job object - bool result = NativeMethods.AssignProcessToJobObject(_jobObjectHandle, ClrFacade.GetRawProcessHandle(process)); + bool result = NativeMethods.AssignProcessToJobObject(_jobObjectHandle, process.Handle); return result; } diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs index fb6bedc658..67a6da5df8 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs @@ -2054,7 +2054,7 @@ namespace Microsoft.PowerShell.Commands if (null != Credential) { username = Credential.UserName; - password = ClrFacade.SecureStringToCoTaskMemUnicode(Credential.Password); + password = Marshal.SecureStringToCoTaskMemUnicode(Credential.Password); } // Create the service diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/AddType.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/AddType.cs index ef824f8b65..8876ede015 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/AddType.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/AddType.cs @@ -964,7 +964,7 @@ namespace Microsoft.PowerShell.Commands Assembly assembly = LoadAssemblyHelper(assemblyName); if (assembly == null) { - assembly = LoadFrom(ResolveAssemblyName(assemblyName, false)); + assembly = Assembly.LoadFrom(ResolveAssemblyName(assemblyName, false)); } if (passThru) @@ -1276,7 +1276,7 @@ namespace Microsoft.PowerShell.Commands { ms.Flush(); ms.Seek(0, SeekOrigin.Begin); - Assembly assembly = LoadFrom(ms); + Assembly assembly = Assembly.Load(ms.ToArray()); CheckTypesForDuplicates(assembly); if (passThru) { @@ -1292,7 +1292,7 @@ namespace Microsoft.PowerShell.Commands { if (passThru) { - Assembly assembly = LoadFrom(outputAssembly); + Assembly assembly = Assembly.LoadFrom(outputAssembly); CheckTypesForDuplicates(assembly); WriteTypes(assembly); } @@ -1318,16 +1318,6 @@ namespace Microsoft.PowerShell.Commands ErrorNumber = d.Id }).ToArray(); } - - private Assembly LoadFrom(Stream assembly) - { - return ClrFacade.LoadFrom(assembly); - } - - private Assembly LoadFrom(string path) - { - return ClrFacade.LoadFrom(path); - } } @@ -1955,7 +1945,7 @@ namespace Microsoft.PowerShell.Commands { foreach (string path in paths) { - generatedTypes.AddRange(ClrFacade.LoadFrom(path).GetTypes()); + generatedTypes.AddRange(Assembly.LoadFrom(path).GetTypes()); } } // Load the assembly by name diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Import-LocalizedData.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Import-LocalizedData.cs index 9130ea3270..fdcbb57a16 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Import-LocalizedData.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Import-LocalizedData.cs @@ -285,7 +285,7 @@ namespace Microsoft.PowerShell.Commands { try { - culture = ClrFacade.GetCultureInfo(_uiculture); + culture = CultureInfo.GetCultureInfo(_uiculture); } catch (ArgumentException) { diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs index e1ab4f607d..5dc6229832 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs @@ -1079,8 +1079,8 @@ namespace Microsoft.PowerShell } #endif - ClrFacade.SetCurrentThreadUiCulture(this.CurrentUICulture); - ClrFacade.SetCurrentThreadCulture(this.CurrentCulture); + Thread.CurrentThread.CurrentUICulture = this.CurrentUICulture; + Thread.CurrentThread.CurrentCulture = this.CurrentCulture; // BUG: 610329. Tell PowerShell engine to apply console // related properties while launching Pipeline Execution // Thread. diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ManagedEntrance.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ManagedEntrance.cs index f7a0688199..e6365ae8c2 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ManagedEntrance.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ManagedEntrance.cs @@ -9,6 +9,7 @@ using System.Management.Automation.Internal; using System.Management.Automation.Runspaces; using System.Management.Automation.Tracing; using System.Globalization; +using System.Threading; #if CORECLR using System.Runtime.InteropServices; @@ -65,8 +66,8 @@ namespace Microsoft.PowerShell // The currentUICulture returned NativeCultureResolver supports this non // traditional fallback on Vista. So it is important to set currentUICulture // in the beginning before we do anything. - ClrFacade.SetCurrentThreadUiCulture(NativeCultureResolver.UICulture); - ClrFacade.SetCurrentThreadCulture(NativeCultureResolver.Culture); + Thread.CurrentThread.CurrentUICulture = NativeCultureResolver.UICulture; + Thread.CurrentThread.CurrentCulture = NativeCultureResolver.Culture; RunspaceConfigForSingleShell configuration = null; PSConsoleLoadException warning = null; diff --git a/src/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext/CoreCLR/CorePsAssemblyLoadContext.cs b/src/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext/CoreCLR/CorePsAssemblyLoadContext.cs index 703431d60a..2250b432dd 100644 --- a/src/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext/CoreCLR/CorePsAssemblyLoadContext.cs +++ b/src/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext/CoreCLR/CorePsAssemblyLoadContext.cs @@ -19,7 +19,7 @@ namespace System.Management.Automation /// /// The powershell custom AssemblyLoadContext implementation /// - internal partial class PowerShellAssemblyLoadContext : AssemblyLoadContext + internal partial class PowerShellAssemblyLoadContext { #region Resource_Strings @@ -31,11 +31,7 @@ namespace System.Management.Automation // When the first attempt fails, we again need to retrieve the resource string to construct another exception, which ends up with an infinite loop. private const string BaseFolderDoesNotExist = "The base directory '{0}' does not exist."; private const string ManifestDefinitionDoesNotMatch = "Could not load file or assembly '{0}' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference."; - private const string AssemblyPathDoesNotExist = "Could not load file or assembly '{0}' or one of its dependencies. The system cannot find the file specified."; - private const string InvalidAssemblyExtensionName = "Could not load file or assembly '{0}' or one of its dependencies. The file specified is not a DLL file."; - private const string AbsolutePathRequired = "Absolute path information is required."; private const string SingletonAlreadyInitialized = "The singleton of PowerShellAssemblyLoadContext has already been initialized."; - private const string UseResolvingEventHandlerOnly = "PowerShellAssemblyLoadContext was initialized to use its 'Resolving' event handler only."; #endregion Resource_Strings @@ -44,14 +40,14 @@ namespace System.Management.Automation /// /// Initialize a singleton of PowerShellAssemblyLoadContext /// - internal static PowerShellAssemblyLoadContext InitializeSingleton(string basePaths, bool useResolvingHandlerOnly) + internal static PowerShellAssemblyLoadContext InitializeSingleton(string basePaths) { lock (s_syncObj) { if (Instance != null) throw new InvalidOperationException(SingletonAlreadyInitialized); - Instance = new PowerShellAssemblyLoadContext(basePaths, useResolvingHandlerOnly); + Instance = new PowerShellAssemblyLoadContext(basePaths); return Instance; } } @@ -60,96 +56,52 @@ namespace System.Management.Automation /// Constructor /// /// - /// Base directory paths that are separated by semicolon ';'. - /// They will be the default paths to probe assemblies. + /// Base directory paths that are separated by semicolon ';'. They will be the default paths to probe assemblies. + /// The passed-in argument could be null or an empty string, in which case there is no default paths to probe assemblies. /// - /// - /// Indicate whether this instance is going to be used as a - /// full fledged ALC, or only its 'Resolve' handler is going - /// to be used. - /// - /// - /// When is true, we will register to the 'Resolving' event of the default - /// load context with our 'Resolve' method, and depend on the default load context to resolve/load assemblies for PS. - /// This mode is used when TPA list of the native host only contains .NET Core libraries. - /// In this case, TPA binder will be consulted before hitting our resolving logic. The binding order of Assembly.Load is: - /// TPA binder --> Resolving event - /// - /// When is false, we will use this instance as a full fledged load context - /// to resolve/load assemblies for PS. This mode is used when TPA list of the native host contains both .NET Core libraries - /// and PS assemblies. - /// In this case, our Load override will kick in before consulting the TPA binder. The binding order of Assembly.Load is: - /// Load override --> TPA binder --> Resolving event - /// - private PowerShellAssemblyLoadContext(string basePaths, bool useResolvingHandlerOnly) + private PowerShellAssemblyLoadContext(string basePaths) { - #region Validation + // FIRST: Validate and populate probing paths if (string.IsNullOrEmpty(basePaths)) { - throw new ArgumentNullException("basePaths"); - } - - _basePaths = basePaths.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); - for (int i = 0; i < _basePaths.Length; i++) - { - string basePath = _basePaths[i]; - if (!Directory.Exists(basePath)) - { - string message = string.Format( - CultureInfo.CurrentCulture, - BaseFolderDoesNotExist, - basePath); - throw new ArgumentException(message, "basePaths"); - } - _basePaths[i] = basePath.Trim(); - } - #endregion Validation - - // FIRST: Add basePaths to probing paths - _probingPaths = new List(_basePaths); - - // NEXT: Initialize the CoreCLR type catalog dictionary [OrdinalIgnoreCase] - _coreClrTypeCatalog = InitializeTypeCatalog(); - - // LAST: Handle useResolvingHandlerOnly flag - _useResolvingHandlerOnly = useResolvingHandlerOnly; - _activeLoadContext = useResolvingHandlerOnly ? Default : this; - if (useResolvingHandlerOnly) - { - Default.Resolving += Resolve; + _probingPaths = Array.Empty(); } else { - var tempSet = new HashSet(_coreClrTypeCatalog.Values, StringComparer.OrdinalIgnoreCase); - _tpaSet = new HashSet(StringComparer.OrdinalIgnoreCase) { "Microsoft.PowerShell.CoreCLR.AssemblyLoadContext" }; - foreach (string tpa in tempSet) + _probingPaths = basePaths.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); + for (int i = 0; i < _probingPaths.Length; i++) { - string shortName = tpa.Substring(0, tpa.IndexOf(',')); - _tpaSet.Add(shortName); + string basePath = _probingPaths[i]; + if (!Directory.Exists(basePath)) + { + string message = string.Format(CultureInfo.CurrentCulture, BaseFolderDoesNotExist, basePath); + throw new ArgumentException(message, "basePaths"); + } + _probingPaths[i] = basePath.Trim(); } } + + // NEXT: Initialize the CoreCLR type catalog dictionary [OrdinalIgnoreCase] + _coreClrTypeCatalog = InitializeTypeCatalog(); + _availableDotNetAssemblyNames = new Lazy>( + () => new HashSet(_coreClrTypeCatalog.Values, StringComparer.Ordinal)); + + // LAST: Register 'Resolving' handler on the default load context. + AssemblyLoadContext.Default.Resolving += Resolve; } #endregion Constructor #region Fields - private readonly bool _useResolvingHandlerOnly; - private readonly AssemblyLoadContext _activeLoadContext; private readonly static object s_syncObj = new object(); - private readonly string[] _basePaths; - // Initially, 'probingPaths' only contains psbase path. But every time we load an assembly through 'LoadFrom(string AssemblyPath)', we - // add its parent path to 'probingPaths', so that we are able to support implicit loading of an assembly from the same place where the - // requesting assembly is located. - // We don't need to worry about removing any paths from 'probingPaths', because once an assembly is loaded, it won't be unloaded until - // the current process exits, and thus the assembly itself and its parent folder cannot be deleted or renamed. - private readonly List _probingPaths; + private readonly string[] _probingPaths; + private readonly string[] _extensions = new string[] { ".ni.dll", ".dll" }; // CoreCLR type catalog dictionary // - Key: namespace qualified type name (FullName) // - Value: strong name of the TPA that contains the type represented by Key. private readonly Dictionary _coreClrTypeCatalog; - private readonly HashSet _tpaSet; - private readonly string[] _extensions = new string[] { ".ni.dll", ".dll" }; + private readonly Lazy> _availableDotNetAssemblyNames; /// /// Assembly cache across the AppDomain @@ -180,38 +132,71 @@ namespace System.Management.Automation get; private set; } + /// + /// Get the namespace-qualified type names of all available .NET Core types shipped with PowerShell Core. + /// This is used for type name auto-completion in PS engine. + /// + internal IEnumerable AvailableDotNetTypeNames + { + get { return _coreClrTypeCatalog.Keys; } + } + + /// + /// Get the assembly names of all available .NET Core assemblies shipped with PowerShell Core. + /// This is used for type name auto-completion in PS engine. + /// + internal HashSet AvailableDotNetAssemblyNames + { + get { return _availableDotNetAssemblyNames.Value; } + } + #endregion Properties - #region Events + #region Internal_Methods /// - /// Assembly load event + /// Get the current loaded assemblies /// - internal event Action AssemblyLoad; - - #endregion Events - - #region Protected_Internal_Methods - - /// - /// Implement the AssemblyLoadContext.Load(AssemblyName). 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 IEnumerable GetAssembly(string namespaceQualifiedTypeName) { - if (_useResolvingHandlerOnly) - throw new NotSupportedException(UseResolvingEventHandlerOnly); + // If 'namespaceQualifiedTypeName' is specified and it's a CoreCLR framework type, + // then we only return that specific TPA assembly. + if (!string.IsNullOrEmpty(namespaceQualifiedTypeName)) + { + if (_coreClrTypeCatalog.TryGetValue(namespaceQualifiedTypeName, out string tpaStrongName)) + { + try + { + return new Assembly[] { GetTrustedPlatformAssembly(tpaStrongName) }; + } + catch (FileNotFoundException) { } + } + } - // We let the default context load the assemblies included in the type catalog as there - // appears to be a bug in .NET with method resolution with system libraries loaded by our - // context and not the default. We use the short name because some packages have inconsistent - // versions between reference and runtime assemblies. - if (_tpaSet.Contains(assemblyName.Name)) - return null; - - return Resolve(this, assemblyName); + // Otherwise, we return null + return null; } + /// + /// Set the profile optimization root on the appropriate load context + /// + internal void SetProfileOptimizationRootImpl(string directoryPath) + { + AssemblyLoadContext.Default.SetProfileOptimizationRoot(directoryPath); + } + + /// + /// Start the profile optimization on the appropriate load context + /// + internal void StartProfileOptimizationImpl(string profile) + { + AssemblyLoadContext.Default.StartProfileOptimization(profile); + } + + #endregion Internal_Methods + + #region Private_Methods + /// /// The handler for the Resolving event /// @@ -235,7 +220,7 @@ namespace System.Management.Automation string asmCultureName = assemblyName.CultureName ?? string.Empty; string asmFilePath = null; - for (int i = 0; i < _probingPaths.Count; i++) + for (int i = 0; i < _probingPaths.Length; i++) { string probingPath = _probingPaths[i]; string asmCulturePath = Path.Combine(probingPath, asmCultureName); @@ -247,7 +232,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; @@ -279,303 +264,9 @@ namespace System.Management.Automation } } - // Raise AssemblyLoad event - OnAssemblyLoaded(asmLoaded); return asmLoaded; } - /// - /// Load an IL or NI assembly from its file path. - /// - internal Assembly LoadFrom(string assemblyPath) - { - ValidateAssemblyPath(assemblyPath, "assemblyPath"); - - Assembly asmLoaded; - AssemblyName assemblyName = GetAssemblyName(assemblyPath); - - // Probe the assembly cache - if (TryGetAssemblyFromCache(assemblyName, out asmLoaded)) - return asmLoaded; - - // Prepare to load the assembly - lock (s_syncObj) - { - // Probe the cache again in case it's already loaded - if (TryGetAssemblyFromCache(assemblyName, out asmLoaded)) - return asmLoaded; - - // Load the assembly through 'LoadFromNativeImagePath' or 'LoadFromAssemblyPath' - asmLoaded = assemblyPath.EndsWith(".ni.dll", StringComparison.OrdinalIgnoreCase) - ? _activeLoadContext.LoadFromNativeImagePath(assemblyPath, null) - : _activeLoadContext.LoadFromAssemblyPath(assemblyPath); - - if (asmLoaded != null) - { - // Add the loaded assembly to the cache - s_assemblyCache.TryAdd(assemblyName.Name, asmLoaded); - // Add its parent path to our probing paths - string parentPath = Path.GetDirectoryName(assemblyPath); - if (!_probingPaths.Contains(parentPath)) - { - _probingPaths.Add(parentPath); - } - } - } - - // Raise AssemblyLoad event - OnAssemblyLoaded(asmLoaded); - return asmLoaded; - } - - /// - /// Load assembly from byte stream. - /// - internal Assembly LoadFrom(Stream assembly) - { - if (assembly == null) - throw new ArgumentNullException("assembly"); - - Assembly asmLoaded; - AssemblyName assemblyName = GetAssemblyName(assembly); - - // Probe the assembly cache - if (TryGetAssemblyFromCache(assemblyName, out asmLoaded)) - return asmLoaded; - - // Prepare to load the assembly - lock (s_syncObj) - { - // Probe the cache again in case it's already loaded - if (TryGetAssemblyFromCache(assemblyName, out asmLoaded)) - return asmLoaded; - - // Load the assembly through 'LoadFromStream' - asmLoaded = _activeLoadContext.LoadFromStream(assembly); - if (asmLoaded != null) - { - // Add the loaded assembly to the cache - s_assemblyCache.TryAdd(assemblyName.Name, asmLoaded); - } - } - - // Raise AssemblyLoad event - OnAssemblyLoaded(asmLoaded); - return asmLoaded; - } - - /// - /// Get the current loaded assemblies - /// - internal IEnumerable GetAssemblies(string namespaceQualifiedTypeName) - { - // If 'namespaceQualifiedTypeName' is specified and it's a CoreCLR framework type, - // then we only return that specific TPA assembly. - if (!string.IsNullOrEmpty(namespaceQualifiedTypeName)) - { - string tpaStrongName; - if (_coreClrTypeCatalog.TryGetValue(namespaceQualifiedTypeName, out tpaStrongName)) - { - try - { - return new Assembly[] { GetTrustedPlatformAssembly(tpaStrongName) }; - } - catch (FileNotFoundException) - { - // It's possible that the type catalog generated in OPS contains more entries than - // the one generated in windows build. This is because in OPS we have more freedom - // to control what packages to depend on, such as Json.NET. - // If we deploy the PSALC.dll generated from OPS to NanoServer, then it's possible - // that 'GetTrustedPlatformAssembly(tpaStrongName)' may fail for such entries. In - // this case, we ignore the exception and return our cached assemblies. - } - } - } - - // Otherwise, we return all assemblies from the AssemblyCache - return s_assemblyCache.Values; - } - - /// - /// Try adding a new assembly to the cache - /// - /// - /// This is for adding a dynamic assembly to the cache. - /// PowerShell generates dynamic assemblies by directly emitting IL, and this API - /// is to add such assemblies to the cache so that types in them are discoverable. - /// - internal bool TryAddAssemblyToCache(Assembly assembly) - { - AssemblyName asmName = assembly.GetName(); - bool success = s_assemblyCache.TryAdd(asmName.Name, assembly); - // Raise AssemblyLoad event - if (success) { OnAssemblyLoaded(assembly); } - return success; - } - - /// - /// Probe for the assembly file with the specified short name for metadata analysis purpose - /// - internal string ProbeAssemblyFileForMetadataAnalysis(string assemblyShortName, string additionalSearchPath) - { - if (string.IsNullOrEmpty(assemblyShortName)) - { - throw new ArgumentNullException("assemblyShortName"); - } - - bool useAdditionalSearchPath = false; - if (!string.IsNullOrEmpty(additionalSearchPath)) - { - if (!Path.IsPathRooted(additionalSearchPath)) - { - throw new ArgumentException(AbsolutePathRequired, "additionalSearchPath"); - } - useAdditionalSearchPath = Directory.Exists(additionalSearchPath); - } - - // Construct the probing paths for searching the specified assembly file - string[] metadataProbingPaths = _basePaths; - if (useAdditionalSearchPath) - { - var searchPaths = new List() { additionalSearchPath }; - searchPaths.AddRange(_basePaths); - metadataProbingPaths = searchPaths.ToArray(); - } - - for (int i = 0; i < metadataProbingPaths.Length; i++) - { - string metadataProbingPath = metadataProbingPaths[i]; - for (int k = 0; k < _extensions.Length; k++) - { - string asmFileName = assemblyShortName + _extensions[k]; - string asmFilePath = Path.Combine(metadataProbingPath, asmFileName); - if (File.Exists(asmFilePath)) - { - return asmFilePath; - } - } - } - - return null; - } - - /// - /// Get the namespace-qualified type names of all available CoreCLR .NET types. - /// This is used for type name auto-completion in PS engine. - /// - internal IEnumerable GetAvailableDotNetTypes() - { - return _coreClrTypeCatalog.Keys; - } - - /// - /// Set the profile optimization root on the appropriate load context - /// - /// - /// When using PS ALC as a full fledged ALC in OPS, we don't enable profile optimization. - /// This is because PS assemblies will be recorded in the profile, and the next time OPS - /// starts up, the default context will load the PS assemblies pretty early to ngen them - /// in another CPU core, so our Load override won't track the loading of them, and thus - /// OPS will fail to work. - /// The root cause is that dotnet.exe put all PS assemblies in TPA list. If PS assemblies - /// are not in TPA list, then we can enable profile optimization without a problem. - /// - internal void SetProfileOptimizationRootImpl(string directoryPath) - { - if (_useResolvingHandlerOnly) - _activeLoadContext.SetProfileOptimizationRoot(directoryPath); - } - - /// - /// Start the profile optimization on the appropriate load context - /// - /// - /// When using PS ALC as a full fledged ALC in OPS, we don't enable profile optimization. - /// This is because PS assemblies will be recorded in the profile, and the next time OPS - /// starts up, the default context will load the PS assemblies pretty early to ngen them - /// in another CPU core, so our Load override won't track the loading of them, and thus - /// OPS will fail to work. - /// The root cause is that dotnet.exe put all PS assemblies in TPA list. If PS assemblies - /// are not in TPA list, then we can enable profile optimization without a problem. - /// - internal void StartProfileOptimizationImpl(string profile) - { - if (_useResolvingHandlerOnly) - _activeLoadContext.StartProfileOptimization(profile); - } - - #endregion Protected_Internal_Methods - - #region Private_Methods - - /// - /// Handle the AssemblyLoad event - /// - private void OnAssemblyLoaded(Assembly assemblyLoaded) - { - Action assemblyLoadHandler = AssemblyLoad; - if (assemblyLoaded != null && assemblyLoadHandler != null) - { - try - { - assemblyLoadHandler(assemblyLoaded); - } - catch - { - // Catch all exceptions, same behavior as AppDomain.AssemblyLoad - } - } - } - - /// - /// Validate assembly path value for the specified parameter - /// - private void ValidateAssemblyPath(string assemblyPath, string parameterName) - { - if (string.IsNullOrEmpty(assemblyPath)) - { - throw new ArgumentNullException(parameterName); - } - - if (!Path.IsPathRooted(assemblyPath)) - { - throw new ArgumentException(AbsolutePathRequired, parameterName); - } - - if (!File.Exists(assemblyPath)) - { - ThrowFileNotFoundException( - AssemblyPathDoesNotExist, - assemblyPath); - } - - if (!string.Equals(Path.GetExtension(assemblyPath), ".DLL", StringComparison.OrdinalIgnoreCase)) - { - ThrowFileLoadException( - InvalidAssemblyExtensionName, - assemblyPath); - } - } - - /// - /// Get AssemblyName of an assembly stream - /// - private AssemblyName GetAssemblyName(Stream assembly) - { - if (assembly == null) - throw new ArgumentNullException("assembly"); - - string strongAssemblyName = null; - using (PEReader peReader = new PEReader(assembly, PEStreamOptions.LeaveOpen | PEStreamOptions.PrefetchMetadata)) - { - MetadataReader metadataReader = peReader.GetMetadataReader(); - strongAssemblyName = AssemblyMetadataHelper.GetAssemblyStrongName(metadataReader); - } - - assembly.Seek(0, SeekOrigin.Begin); - return new AssemblyName(strongAssemblyName); - } - /// /// Try to get the specified assembly from cache /// @@ -689,8 +380,6 @@ namespace System.Management.Automation /// public class PowerShellAssemblyLoadContextInitializer { - private static object[] s_emptyArray = new object[0]; - /// /// Create a singleton of PowerShellAssemblyLoadContext. /// Then register to the Resolving event of the load context that loads this assembly. @@ -708,106 +397,7 @@ namespace System.Management.Automation if (string.IsNullOrEmpty(basePaths)) throw new ArgumentNullException("basePaths"); - PowerShellAssemblyLoadContext.InitializeSingleton(basePaths, useResolvingHandlerOnly: true); - } - - /// - /// Create a singleton of PowerShellAssemblyLoadContext. - /// Then load System.Management.Automation and call the WSManPluginManagedEntryWrapper delegate. - /// - /// - /// This method is used by the native host of the PSRP plugin. - /// - /// - /// Passed to delegate. - /// - public static int WSManPluginWrapper(IntPtr wkrPtrs) - { - string basePaths = System.IO.Path.GetDirectoryName(typeof(PowerShellAssemblyLoadContextInitializer).GetTypeInfo().Assembly.Location); - string entryAssemblyName = "System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"; - string entryTypeName = "System.Management.Automation.Remoting.WSManPluginManagedEntryWrapper"; - string entryMethodName = "InitPlugin"; - object[] args = { wkrPtrs }; - - var psLoadContext = PowerShellAssemblyLoadContext.InitializeSingleton(basePaths, useResolvingHandlerOnly: false); - var entryAssembly = psLoadContext.LoadFromAssemblyName(new AssemblyName(entryAssemblyName)); - var entryType = entryAssembly.GetType(entryTypeName, throwOnError: true, ignoreCase: true); - var methodInfo = entryType.GetMethod(entryMethodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.IgnoreCase); - - return (int)methodInfo.Invoke(null, args); - } - - /// - /// Create a singleton of PowerShellAssemblyLoadContext. - /// Then load the assembly containing the actual entry point using it. - /// - /// - /// Base directory paths that are separated by semicolon ';'. - /// They will be the default paths to probe assemblies. - /// - /// - /// Name of the assembly that contains the actual entry point. - /// - /// - /// The assembly that contains the actual entry point. - /// - public static Assembly InitializeAndLoadEntryAssembly(string basePaths, AssemblyName entryAssemblyName) - { - if (string.IsNullOrEmpty(basePaths)) - throw new ArgumentNullException("basePaths"); - - if (entryAssemblyName == null) - throw new ArgumentNullException("entryAssemblyName"); - - var psLoadContext = PowerShellAssemblyLoadContext.InitializeSingleton(basePaths, useResolvingHandlerOnly: false); - return psLoadContext.LoadFromAssemblyName(entryAssemblyName); - } - - /// - /// Create a singleton of PowerShellAssemblyLoadContext. - /// Then call into the actual entry point based on the given assembly name, type name, method name and arguments. - /// - /// - /// Base directory paths that are separated by semicolon ';'. - /// They will be the default paths to probe assemblies. - /// - /// - /// Name of the assembly that contains the actual entry point. - /// - /// - /// Name of the type that contains the actual entry point. - /// - /// - /// Name of the actual entry point method. - /// - /// - /// An array of arguments passed to the entry point method. - /// - /// - /// The return value of running the entry point method. - /// - public static object InitializeAndCallEntryMethod(string basePaths, AssemblyName entryAssemblyName, string entryTypeName, string entryMethodName, object[] args) - { - if (string.IsNullOrEmpty(basePaths)) - throw new ArgumentNullException("basePaths"); - - if (entryAssemblyName == null) - throw new ArgumentNullException("entryAssemblyName"); - - if (string.IsNullOrEmpty(entryTypeName)) - throw new ArgumentNullException("entryTypeName"); - - if (string.IsNullOrEmpty(entryMethodName)) - throw new ArgumentNullException("entryMethodName"); - - args = args ?? s_emptyArray; - - var psLoadContext = PowerShellAssemblyLoadContext.InitializeSingleton(basePaths, useResolvingHandlerOnly: false); - var entryAssembly = psLoadContext.LoadFromAssemblyName(entryAssemblyName); - var entryType = entryAssembly.GetType(entryTypeName, throwOnError: true, ignoreCase: true); - var methodInfo = entryType.GetMethod(entryMethodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.IgnoreCase); - - return methodInfo.Invoke(null, args); + PowerShellAssemblyLoadContext.InitializeSingleton(basePaths); } } } diff --git a/src/System.Management.Automation/CoreCLR/CorePsExtensions.cs b/src/System.Management.Automation/CoreCLR/CorePsExtensions.cs index 7434907a2a..20d0032cce 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsExtensions.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsExtensions.cs @@ -5,9 +5,8 @@ Copyright (c) Microsoft Corporation. All rights reserved. namespace Microsoft.PowerShell.CoreCLR { - using System.IO; + using System; using System.Reflection; - using System.Management.Automation; /// /// AssemblyExtensions @@ -19,19 +18,10 @@ namespace Microsoft.PowerShell.CoreCLR /// /// The path of the file that contains the manifest of the assembly. /// The loaded assembly. + [ObsoleteAttribute("This method is obsolete. Call Assembly.LoadFrom instead", false)] public static Assembly LoadFrom(string assemblyPath) { - return ClrFacade.LoadFrom(assemblyPath); - } - - /// - /// Load an assembly given its byte stream - /// - /// The byte stream of assembly - /// The loaded assembly - public static Assembly LoadFrom(Stream assembly) - { - return ClrFacade.LoadFrom(assembly); + return Assembly.LoadFrom(assemblyPath); } } } diff --git a/src/System.Management.Automation/cimSupport/cmdletization/EnumWriter.cs b/src/System.Management.Automation/cimSupport/cmdletization/EnumWriter.cs index d07c1621b6..571c3790ab 100644 --- a/src/System.Management.Automation/cimSupport/cmdletization/EnumWriter.cs +++ b/src/System.Management.Automation/cimSupport/cmdletization/EnumWriter.cs @@ -65,7 +65,7 @@ namespace Microsoft.PowerShell.Cmdletization eb.DefineLiteral(name, integerValue); } - ClrFacade.CreateEnumType(eb); + eb.CreateTypeInfo(); } } } diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index 65d8ee8f15..a6b46b7862 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -35,18 +35,10 @@ namespace System.Management.Automation { static CompletionCompleters() { -#if CORECLR - ClrFacade.AddAssemblyLoadHandler(UpdateTypeCacheOnAssemblyLoad); -#else AppDomain.CurrentDomain.AssemblyLoad += UpdateTypeCacheOnAssemblyLoad; -#endif } -#if CORECLR - static void UpdateTypeCacheOnAssemblyLoad(Assembly loadedAssembly) -#else private static void UpdateTypeCacheOnAssemblyLoad(object sender, AssemblyLoadEventArgs args) -#endif { // Just null out the cache - we'll rebuild it the next time someone tries to complete a type. // We could rebuild it now, but we could be loading multiple assemblies (e.g. dependent assemblies) @@ -5862,34 +5854,11 @@ namespace System.Management.Automation #endregion Process_TypeAccelerators - #region Process_LoadedAssemblies - - var assembliesExcludingPSGenerated = ClrFacade.GetAssemblies(); - var allPublicTypes = assembliesExcludingPSGenerated.SelectMany(assembly => - { - try - { - return assembly.GetTypes().Where(TypeResolver.IsPublic); - } - catch (ReflectionTypeLoadException) - { - } - return Type.EmptyTypes; - }); - - foreach (var type in allPublicTypes) - { - HandleNamespace(entries, type.Namespace); - HandleType(entries, type.FullName, type.Name, type); - } - - #endregion Process_LoadedAssemblies - #region Process_CoreCLR_TypeCatalog #if CORECLR - // In CoreCLR, we have namespace-qualified type names of all available .NET types stored in TypeCatalog of the AssemblyLoadContext. + // In CoreCLR, we have namespace-qualified type names of all available .NET Core types stored in TypeCatalog. // Populate the type completion cache using the namespace-qualified type names. - foreach (string fullTypeName in ClrFacade.GetAvailableCoreClrDotNetTypes()) + foreach (string fullTypeName in ClrFacade.AvailableDotNetTypeNames) { var typeCompInString = new TypeCompletionInStringFormat { FullTypeName = fullTypeName }; HandleNamespace(entries, typeCompInString.Namespace); @@ -5898,6 +5867,32 @@ namespace System.Management.Automation #endif #endregion Process_CoreCLR_TypeCatalog + #region Process_LoadedAssemblies + + foreach (Assembly assembly in ClrFacade.GetAssemblies()) + { +#if CORECLR + // Ignore the assemblies that are already covered by the type catalog + if (ClrFacade.AvailableDotNetAssemblyNames.Contains(assembly.FullName)) { continue; } +#endif + try + { + foreach (Type type in assembly.GetTypes()) + { + // Ignore non-public types + if (!TypeResolver.IsPublic(type)) { continue; } + + HandleNamespace(entries, type.Namespace); + HandleType(entries, type.FullName, type.Name, type); + } + } + catch (ReflectionTypeLoadException) + { + } + } + + #endregion Process_LoadedAssemblies + var grouping = entries.Values.GroupBy(t => t.Key.Count(c => c == '.')).OrderBy(g => g.Key).ToArray(); var localTypeCache = new TypeCompletionMapping[grouping.Last().Key + 1][]; foreach (var group in grouping) diff --git a/src/System.Management.Automation/engine/CommandInfo.cs b/src/System.Management.Automation/engine/CommandInfo.cs index d07e073fdc..168fe41cd9 100644 --- a/src/System.Management.Automation/engine/CommandInfo.cs +++ b/src/System.Management.Automation/engine/CommandInfo.cs @@ -7,6 +7,7 @@ using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Management.Automation.Language; using System.Management.Automation.Runspaces; +using System.Reflection; using System.Runtime.ExceptionServices; using Microsoft.PowerShell.Commands; @@ -227,7 +228,7 @@ namespace System.Management.Automation else if (Module.Path.EndsWith(StringLiterals.DependentWorkflowAssemblyExtension, StringComparison.OrdinalIgnoreCase)) { // Binary module (.dll) - Module.SetVersion(ClrFacade.GetAssemblyName(Module.Path).Version); + Module.SetVersion(AssemblyName.GetAssemblyName(Module.Path).Version); } } diff --git a/src/System.Management.Automation/engine/ExecutionContext.cs b/src/System.Management.Automation/engine/ExecutionContext.cs index 184407858c..0200aa5920 100644 --- a/src/System.Management.Automation/engine/ExecutionContext.cs +++ b/src/System.Management.Automation/engine/ExecutionContext.cs @@ -1375,7 +1375,7 @@ namespace System.Management.Automation try { - loadedAssembly = ClrFacade.LoadFrom(filename); + loadedAssembly = Assembly.LoadFrom(filename); return loadedAssembly; } catch (FileNotFoundException fileNotFound) diff --git a/src/System.Management.Automation/engine/InitialSessionState.cs b/src/System.Management.Automation/engine/InitialSessionState.cs index dec502579f..5af8be7ebe 100644 --- a/src/System.Management.Automation/engine/InitialSessionState.cs +++ b/src/System.Management.Automation/engine/InitialSessionState.cs @@ -4467,7 +4467,7 @@ namespace System.Management.Automation.Runspaces { s_PSSnapInTracer.WriteLine("Loading assembly for psSnapIn {0}", fileName); - Assembly assembly = ClrFacade.LoadFrom(fileName); + Assembly assembly = Assembly.LoadFrom(fileName); if (assembly == null) { s_PSSnapInTracer.TraceError("Loading assembly for psSnapIn {0} failed", fileName); @@ -5543,7 +5543,7 @@ if($paths) { try { - AssemblyName assemblyName = ClrFacade.GetAssemblyName(psSnapInInfo.AbsoluteModulePath); + AssemblyName assemblyName = AssemblyName.GetAssemblyName(psSnapInInfo.AbsoluteModulePath); if (!string.Equals(assemblyName.FullName, psSnapInInfo.AssemblyName, StringComparison.OrdinalIgnoreCase)) { @@ -5552,7 +5552,7 @@ if($paths) { throw new PSSnapInException(psSnapInInfo.Name, message); } - assembly = ClrFacade.LoadFrom(psSnapInInfo.AbsoluteModulePath); + assembly = Assembly.LoadFrom(psSnapInInfo.AbsoluteModulePath); } catch (FileLoadException e) { diff --git a/src/System.Management.Automation/engine/Utils.cs b/src/System.Management.Automation/engine/Utils.cs index 6c1b3bd945..9b8c4294af 100644 --- a/src/System.Management.Automation/engine/Utils.cs +++ b/src/System.Management.Automation/engine/Utils.cs @@ -175,7 +175,7 @@ namespace System.Management.Automation try { - p = ClrFacade.SecureStringToCoTaskMemUnicode(ss); + p = Marshal.SecureStringToCoTaskMemUnicode(ss); s = Marshal.PtrToStringUni(p); } finally diff --git a/src/System.Management.Automation/engine/hostifaces/LocalPipeline.cs b/src/System.Management.Automation/engine/hostifaces/LocalPipeline.cs index 3f3b54c3d1..0a583a9a50 100644 --- a/src/System.Management.Automation/engine/hostifaces/LocalPipeline.cs +++ b/src/System.Management.Automation/engine/hostifaces/LocalPipeline.cs @@ -217,8 +217,8 @@ namespace System.Management.Automation.Runspaces finally { NestedPipelineExecutionThread = oldNestedPipelineThread; - ClrFacade.SetCurrentThreadCulture(oldCurrentCulture); - ClrFacade.SetCurrentThreadUiCulture(oldCurrentUICulture); + Thread.CurrentThread.CurrentCulture = oldCurrentCulture; + Thread.CurrentThread.CurrentUICulture = oldCurrentUICulture; } break; } diff --git a/src/System.Management.Automation/engine/parser/Compiler.cs b/src/System.Management.Automation/engine/parser/Compiler.cs index bb7fc7dc08..34c0828daa 100644 --- a/src/System.Management.Automation/engine/parser/Compiler.cs +++ b/src/System.Management.Automation/engine/parser/Compiler.cs @@ -2370,7 +2370,7 @@ namespace System.Management.Automation.Language #endif if (File.Exists(assemblyFileName)) { - assembly = ClrFacade.LoadFrom(assemblyFileName); + assembly = Assembly.LoadFrom(assemblyFileName); } } catch diff --git a/src/System.Management.Automation/engine/remoting/fanin/InitialSessionStateProvider.cs b/src/System.Management.Automation/engine/remoting/fanin/InitialSessionStateProvider.cs index c11c23ffeb..fc82d5c4f4 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/InitialSessionStateProvider.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/InitialSessionStateProvider.cs @@ -700,7 +700,7 @@ namespace System.Management.Automation.Remoting //Rooted path of dll is provided. assemblyPath = assemblyName; } - result = ClrFacade.LoadFrom(assemblyPath); + result = Assembly.LoadFrom(assemblyPath); } catch (FileLoadException e) { diff --git a/src/System.Management.Automation/engine/remoting/fanin/WSManNativeAPI.cs b/src/System.Management.Automation/engine/remoting/fanin/WSManNativeAPI.cs index 5145ee0d52..9b0e3afe1a 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/WSManNativeAPI.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/WSManNativeAPI.cs @@ -348,7 +348,7 @@ namespace System.Management.Automation.Remoting.Client _cred.userName = name; if (null != pwd) { - _cred.password = ClrFacade.SecureStringToCoTaskMemUnicode(pwd); + _cred.password = Marshal.SecureStringToCoTaskMemUnicode(pwd); } _data = MarshalledObject.Create(_cred); diff --git a/src/System.Management.Automation/engine/remoting/fanin/WSManPlugin.cs b/src/System.Management.Automation/engine/remoting/fanin/WSManPlugin.cs index 8ea46369a7..ab5aa753be 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/WSManPlugin.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/WSManPlugin.cs @@ -383,7 +383,7 @@ namespace System.Management.Automation.Remoting if (Platform.IsWindows) { SafeWaitHandle safeWaitHandle = new SafeWaitHandle(requestDetails.shutdownNotificationHandle, false); // Owned by WinRM - ClrFacade.SetSafeWaitHandle(eventWaitHandle, safeWaitHandle); + eventWaitHandle.SafeWaitHandle = safeWaitHandle; } else { @@ -1496,7 +1496,7 @@ namespace System.Management.Automation.Remoting if (retrievingLocaleSucceeded && ((uint)WSManNativeApi.WSManDataType.WSMAN_DATA_TYPE_TEXT == localeData.Type)) { CultureInfo uiCultureToUse = new CultureInfo(localeData.Text); - ClrFacade.SetCurrentThreadUiCulture(uiCultureToUse); + Thread.CurrentThread.CurrentUICulture = uiCultureToUse; } } // ignore if there is any exception constructing the culture.. @@ -1510,7 +1510,7 @@ namespace System.Management.Automation.Remoting if (retrievingDataLocaleSucceeded && ((uint)WSManNativeApi.WSManDataType.WSMAN_DATA_TYPE_TEXT == dataLocaleData.Type)) { CultureInfo cultureToUse = new CultureInfo(dataLocaleData.Text); - ClrFacade.SetCurrentThreadCulture(cultureToUse); + Thread.CurrentThread.CurrentCulture = cultureToUse; } } // ignore if there is any exception constructing the culture.. diff --git a/src/System.Management.Automation/engine/remoting/fanin/WSManPluginShellSession.cs b/src/System.Management.Automation/engine/remoting/fanin/WSManPluginShellSession.cs index 73792fa7ff..9f88316c8a 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/WSManPluginShellSession.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/WSManPluginShellSession.cs @@ -266,7 +266,7 @@ namespace System.Management.Automation.Remoting // Wrap the provided handle so it can be passed to the registration function SafeWaitHandle safeWaitHandle = new SafeWaitHandle(creationRequestDetails.shutdownNotificationHandle, false); // Owned by WinRM EventWaitHandle eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset); - ClrFacade.SetSafeWaitHandle(eventWaitHandle, safeWaitHandle); + eventWaitHandle.SafeWaitHandle = safeWaitHandle; // Register shutdown notification handle this.registeredShutDownWaitHandle = ThreadPool.RegisterWaitForSingleObject( diff --git a/src/System.Management.Automation/engine/remoting/fanin/WSManPluginTransportManager.cs b/src/System.Management.Automation/engine/remoting/fanin/WSManPluginTransportManager.cs index 2d2c2e4e48..6dc38bf9e8 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/WSManPluginTransportManager.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/WSManPluginTransportManager.cs @@ -337,7 +337,7 @@ namespace System.Management.Automation.Remoting // Wrap the provided handle so it can be passed to the registration function SafeWaitHandle safeWaitHandle = new SafeWaitHandle(requestDetails.shutdownNotificationHandle, false); // Owned by WinRM EventWaitHandle eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset); - ClrFacade.SetSafeWaitHandle(eventWaitHandle, safeWaitHandle); + eventWaitHandle.SafeWaitHandle = safeWaitHandle; _registeredShutDownWaitHandle = ThreadPool.RegisterWaitForSingleObject( eventWaitHandle, diff --git a/src/System.Management.Automation/help/UpdateHelpCommand.cs b/src/System.Management.Automation/help/UpdateHelpCommand.cs index 8cc6aa12ae..3659485db5 100644 --- a/src/System.Management.Automation/help/UpdateHelpCommand.cs +++ b/src/System.Management.Automation/help/UpdateHelpCommand.cs @@ -379,7 +379,7 @@ namespace Microsoft.PowerShell.Commands destPaths.Add(module.ModuleBase); #if !CORECLR // Side-By-Side directories are not present in OneCore environments. - if (IsSystemModule(module.ModuleName) && ClrFacade.Is64BitOperatingSystem()) + if (IsSystemModule(module.ModuleName) && Environment.Is64BitOperatingSystem) { string path = Utils.GetApplicationBase(Utils.DefaultPowerShellShellID).Replace("System32", "SysWOW64"); diff --git a/src/System.Management.Automation/minishell/api/RunspaceConfiguration.cs b/src/System.Management.Automation/minishell/api/RunspaceConfiguration.cs index 526191614e..1c92270fd5 100644 --- a/src/System.Management.Automation/minishell/api/RunspaceConfiguration.cs +++ b/src/System.Management.Automation/minishell/api/RunspaceConfiguration.cs @@ -246,7 +246,7 @@ namespace System.Management.Automation.Runspaces if (assembly == null) throw PSTraceSource.NewArgumentNullException("assembly"); - object[] attributes = ClrFacade.GetCustomAttributes(assembly); + object[] attributes = assembly.GetCustomAttributes(typeof(RunspaceConfigurationTypeAttribute), false); if (attributes == null || attributes.Length == 0) { diff --git a/src/System.Management.Automation/resources/ParserStrings.resx b/src/System.Management.Automation/resources/ParserStrings.resx index 2cb1226f57..141c1f278a 100644 --- a/src/System.Management.Automation/resources/ParserStrings.resx +++ b/src/System.Management.Automation/resources/ParserStrings.resx @@ -1345,9 +1345,6 @@ ModuleVersion : Version of module to import. If used, ModuleName must represent Cannot run a document in PowerShell Core: {0}. - - 'PowerShellAssemblyLoadContext' is not initialized. - Multiple type constraints are not allowed on a method parameter. diff --git a/src/System.Management.Automation/security/SecureStringHelper.cs b/src/System.Management.Automation/security/SecureStringHelper.cs index 7801daa204..f96881b1de 100644 --- a/src/System.Management.Automation/security/SecureStringHelper.cs +++ b/src/System.Management.Automation/security/SecureStringHelper.cs @@ -85,7 +85,7 @@ namespace Microsoft.PowerShell if (s.Length > 0) { - IntPtr ptr = ClrFacade.SecureStringToCoTaskMemUnicode(s); + IntPtr ptr = Marshal.SecureStringToCoTaskMemUnicode(s); try { diff --git a/src/System.Management.Automation/security/SecuritySupport.cs b/src/System.Management.Automation/security/SecuritySupport.cs index 57d29343f8..d42557b23c 100644 --- a/src/System.Management.Automation/security/SecuritySupport.cs +++ b/src/System.Management.Automation/security/SecuritySupport.cs @@ -1565,7 +1565,7 @@ namespace System.Management.Automation { var processModule = PsUtils.GetMainModule(currentProcess); hostname = string.Concat("PowerShell_", processModule.FileName, "_", - ClrFacade.GetProcessModuleFileVersionInfo(processModule).ProductVersion); + processModule.FileVersionInfo.ProductVersion); } catch (ComponentModel.Win32Exception) { diff --git a/src/System.Management.Automation/singleshell/config/RunspaceConfigForSingleShell.cs b/src/System.Management.Automation/singleshell/config/RunspaceConfigForSingleShell.cs index a05dec3423..55895079c4 100644 --- a/src/System.Management.Automation/singleshell/config/RunspaceConfigForSingleShell.cs +++ b/src/System.Management.Automation/singleshell/config/RunspaceConfigForSingleShell.cs @@ -640,7 +640,7 @@ namespace System.Management.Automation.Runspaces try { - AssemblyName assemblyName = ClrFacade.GetAssemblyName(mshsnapinInfo.AbsoluteModulePath); + AssemblyName assemblyName = AssemblyName.GetAssemblyName(mshsnapinInfo.AbsoluteModulePath); if (string.Compare(assemblyName.FullName, mshsnapinInfo.AssemblyName, StringComparison.OrdinalIgnoreCase) != 0) { string message = StringUtil.Format(ConsoleInfoErrorStrings.PSSnapInAssemblyNameMismatch, mshsnapinInfo.AbsoluteModulePath, mshsnapinInfo.AssemblyName); @@ -648,7 +648,7 @@ namespace System.Management.Automation.Runspaces throw new PSSnapInException(mshsnapinInfo.Name, message); } - assembly = ClrFacade.LoadFrom(mshsnapinInfo.AbsoluteModulePath); + assembly = Assembly.LoadFrom(mshsnapinInfo.AbsoluteModulePath); } catch (FileLoadException e) { diff --git a/src/System.Management.Automation/utils/ClrFacade.cs b/src/System.Management.Automation/utils/ClrFacade.cs index d037485bc4..bfd4797a12 100644 --- a/src/System.Management.Automation/utils/ClrFacade.cs +++ b/src/System.Management.Automation/utils/ClrFacade.cs @@ -10,7 +10,6 @@ using System.IO; using System.Linq; using System.Management.Automation.Language; using System.Reflection; -using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; @@ -37,6 +36,18 @@ namespace System.Management.Automation /// internal static class ClrFacade { + /// + /// Initialize powershell AssemblyLoadContext and register the 'Resolving' event, if it's not done already. + /// If powershell is hosted by a native host such as DSC, then PS ALC might be initialized via 'SetPowerShellAssemblyLoadContext' before loading S.M.A. + /// + static ClrFacade() + { + if (PowerShellAssemblyLoadContext.Instance == null) + { + PowerShellAssemblyLoadContext.InitializeSingleton(string.Empty); + } + } + /// /// We need it to avoid calling lookups inside dynamic assemblies with PS Types, so we exclude it from GetAssemblies(). /// We use this convention for names to archive it. @@ -45,20 +56,6 @@ namespace System.Management.Automation #region Process - /// - /// Facade for ProcessModule FileVersionInfo - /// - /// - /// FileVersionInfo - internal static FileVersionInfo GetProcessModuleFileVersionInfo(ProcessModule processModule) - { -#if CORECLR - return FileVersionInfo.GetVersionInfo(processModule.FileName); -#else - return processModule.FileVersionInfo; -#endif - } - /// /// Facade for Process.Handle to get SafeHandle /// @@ -73,46 +70,6 @@ namespace System.Management.Automation #endif } - /// - /// Facade for Process.Handle to get raw handle - /// - internal static IntPtr GetRawProcessHandle(Process process) - { -#if CORECLR - try - { - return process.SafeHandle.DangerousGetHandle(); - } - catch (InvalidOperationException) - { - // It's possible that the process has already exited when we try to get its handle. - // In that case, InvalidOperationException will be thrown from Process.SafeHandle, - // and we return the invalid zero pointer. - return IntPtr.Zero; - } -#else - return process.Handle; -#endif - } - -#if CORECLR - /// - /// Facade for ProcessStartInfo.Environment - /// - internal static IDictionary GetProcessEnvironment(ProcessStartInfo startInfo) - { - return startInfo.Environment; - } -#else - /// - /// Facade for ProcessStartInfo.EnvironmentVariables - /// - internal static System.Collections.Specialized.StringDictionary GetProcessEnvironment(ProcessStartInfo startInfo) - { - return startInfo.EnvironmentVariables; - } -#endif - #endregion Process #region Marshal @@ -171,32 +128,9 @@ namespace System.Management.Automation #endif } - /// - /// Facade for SecureStringToCoTaskMemUnicode - /// - internal static IntPtr SecureStringToCoTaskMemUnicode(SecureString s) - { -#if CORECLR - return SecureStringMarshal.SecureStringToCoTaskMemUnicode(s); -#else - return Marshal.SecureStringToCoTaskMemUnicode(s); -#endif - } - #endregion Marshal #region Assembly - /// - /// Facade for AssemblyName.GetAssemblyName(string) - /// - internal static AssemblyName GetAssemblyName(string assemblyPath) - { -#if CORECLR // AssemblyName.GetAssemblyName(assemblyPath) is not in CoreCLR - return AssemblyLoadContext.GetAssemblyName(assemblyPath); -#else - return AssemblyName.GetAssemblyName(assemblyPath); -#endif - } internal static IEnumerable GetAssemblies(TypeResolutionState typeResolutionState, TypeName typeName) { @@ -217,112 +151,29 @@ namespace System.Management.Automation /// internal static IEnumerable GetAssemblies(string namespaceQualifiedTypeName = null) { + return #if CORECLR - return PSAssemblyLoadContext.GetAssemblies(namespaceQualifiedTypeName); -#else - return AppDomain.CurrentDomain.GetAssemblies().Where(a => !(a.FullName.Length > 0 && a.FullName[0] == FIRST_CHAR_PSASSEMBLY_MARK)); -#endif - } - - /// - /// Facade for Assembly.LoadFrom - /// - [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFrom")] - internal static Assembly LoadFrom(string assemblyPath) - { -#if CORECLR - return PSAssemblyLoadContext.LoadFrom(assemblyPath); -#else - return Assembly.LoadFrom(assemblyPath); -#endif - } - - /// - /// Facade for EnumBuilder.CreateTypeInfo - /// - /// - /// In Core PowerShell, we need to track the dynamic assemblies that powershell generates. - /// - internal static void CreateEnumType(EnumBuilder enumBuilder) - { -#if CORECLR - // Create the enum type and add the dynamic assembly to assembly cache. - TypeInfo enumTypeinfo = enumBuilder.CreateTypeInfo(); - PSAssemblyLoadContext.TryAddAssemblyToCache(enumTypeinfo.Assembly); -#else - enumBuilder.CreateTypeInfo(); + PSAssemblyLoadContext.GetAssembly(namespaceQualifiedTypeName) ?? #endif + AppDomain.CurrentDomain.GetAssemblies().Where(a => !(a.FullName.Length > 0 && a.FullName[0] == FIRST_CHAR_PSASSEMBLY_MARK)); } #if CORECLR /// - /// Probe (look for) the assembly file with the specified short name. - /// - /// - /// In Core PowerShell, we need to analyze the metadata of assembly files for binary modules. Sometimes we - /// need to find an assembly file that is referenced by the assembly file that is being processed. To find - /// the reference assembly file, we need to probe the PSBase and the additional searching path if it's specified. - /// - internal static string ProbeAssemblyPath(string assemblyShortName, string additionalSearchPath = null) - { - if (string.IsNullOrWhiteSpace(assemblyShortName)) - { - throw new ArgumentNullException("assemblyShortName"); - } - - return PSAssemblyLoadContext.ProbeAssemblyFileForMetadataAnalysis(assemblyShortName, additionalSearchPath); - } - - /// - /// Get the namespace-qualified type names of all available CoreCLR .NET types. + /// Get the namespace-qualified type names of all available .NET Core types shipped with PowerShell Core. /// This is used for type name auto-completion in PS engine. /// - internal static IEnumerable GetAvailableCoreClrDotNetTypes() - { - return PSAssemblyLoadContext.GetAvailableDotNetTypes(); - } + internal static IEnumerable AvailableDotNetTypeNames => PSAssemblyLoadContext.AvailableDotNetTypeNames; /// - /// Load assembly from byte stream. + /// Get the assembly names of all available .NET Core assemblies shipped with PowerShell Core. + /// This is used for type name auto-completion in PS engine. /// - internal static Assembly LoadFrom(Stream assembly) - { - return PSAssemblyLoadContext.LoadFrom(assembly); - } + internal static HashSet AvailableDotNetAssemblyNames => PSAssemblyLoadContext.AvailableDotNetAssemblyNames; - /// - /// Add the AssemblyLoad handler - /// - internal static void AddAssemblyLoadHandler(Action handler) - { - PSAssemblyLoadContext.AssemblyLoad += handler; - } - - private static PowerShellAssemblyLoadContext PSAssemblyLoadContext - { - get - { - if (PowerShellAssemblyLoadContext.Instance == null) - { - throw new InvalidOperationException(ParserStrings.LoadContextNotInitialized); - } - return PowerShellAssemblyLoadContext.Instance; - } - } + private static PowerShellAssemblyLoadContext PSAssemblyLoadContext => PowerShellAssemblyLoadContext.Instance; #endif - /// - /// Facade for Assembly.GetCustomAttributes - /// - internal static object[] GetCustomAttributes(Assembly assembly) - { -#if CORECLR // Assembly.GetCustomAttributes(Type, Boolean) is not in CORE CLR - return assembly.GetCustomAttributes(typeof(T)).ToArray(); -#else - return assembly.GetCustomAttributes(typeof(T), false); -#endif - } - #endregion Assembly #region Encoding @@ -592,48 +443,6 @@ namespace System.Management.Automation #endregion Security - #region Culture - - /// - /// Facade for CultureInfo.GetCultureInfo(string). - /// - internal static CultureInfo GetCultureInfo(string cultureName) - { -#if CORECLR - return new CultureInfo(cultureName); -#else - return CultureInfo.GetCultureInfo(cultureName); -#endif - } - - /// - /// Facade for setting CurrentCulture for the CurrentThread - /// - internal static void SetCurrentThreadCulture(CultureInfo cultureInfo) - { -#if CORECLR - CultureInfo.CurrentCulture = cultureInfo; -#else - // Setters for 'CultureInfo.CurrentCulture' is introduced in .NET 4.6 - Thread.CurrentThread.CurrentCulture = cultureInfo; -#endif - } - - /// - /// Facade for setting CurrentUICulture for the CurrentThread - /// - internal static void SetCurrentThreadUiCulture(CultureInfo uiCultureInfo) - { -#if CORECLR - CultureInfo.CurrentUICulture = uiCultureInfo; -#else - // Setters for 'CultureInfo.CurrentUICulture' is introduced in .NET 4.6 - Thread.CurrentThread.CurrentUICulture = uiCultureInfo; -#endif - } - - #endregion Culture - #region Misc /// @@ -716,19 +525,6 @@ namespace System.Management.Automation #endif } - /// - /// Manual implementation of the is 64bit processor check - /// - /// - internal static bool Is64BitOperatingSystem() - { -#if CORECLR - return (8 == IntPtr.Size); // Pointers are 8 bytes on 64-bit machines -#else - return Environment.Is64BitOperatingSystem; -#endif - } - /// /// Facade for FormatterServices.GetUninitializedObject. /// @@ -768,20 +564,6 @@ namespace System.Management.Automation #endif } - /// - /// Facade for setting WaitHandle.SafeWaitHandle. - /// - /// - /// - internal static void SetSafeWaitHandle(WaitHandle waitHandle, SafeWaitHandle value) - { -#if CORECLR - waitHandle.SetSafeWaitHandle(value); -#else - waitHandle.SafeWaitHandle = value; -#endif - } - /// /// Facade for ProfileOptimization.SetProfileRoot /// diff --git a/src/System.Management.Automation/utils/CryptoUtils.cs b/src/System.Management.Automation/utils/CryptoUtils.cs index f3bcb747a6..8e7fe44ded 100644 --- a/src/System.Management.Automation/utils/CryptoUtils.cs +++ b/src/System.Management.Automation/utils/CryptoUtils.cs @@ -995,7 +995,7 @@ namespace System.Management.Automation.Internal if (_rsaCryptoProvider.CanEncrypt) { - IntPtr ptr = ClrFacade.SecureStringToCoTaskMemUnicode(secureString); + IntPtr ptr = Marshal.SecureStringToCoTaskMemUnicode(secureString); if (ptr != IntPtr.Zero) { diff --git a/src/TypeCatalogGen/TypeCatalogGen.cs b/src/TypeCatalogGen/TypeCatalogGen.cs index 7d3522a6f5..2078ab07dd 100644 --- a/src/TypeCatalogGen/TypeCatalogGen.cs +++ b/src/TypeCatalogGen/TypeCatalogGen.cs @@ -424,7 +424,7 @@ using System.Runtime.Loader; namespace System.Management.Automation {{ - internal partial class PowerShellAssemblyLoadContext : AssemblyLoadContext + internal partial class PowerShellAssemblyLoadContext {{ private Dictionary InitializeTypeCatalog() {{ diff --git a/src/powershell/Program.cs b/src/powershell/Program.cs index 573e1ead87..20b84250da 100644 --- a/src/powershell/Program.cs +++ b/src/powershell/Program.cs @@ -3,7 +3,6 @@ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ using System; -using System.Management.Automation; using System.Reflection; namespace Microsoft.PowerShell @@ -21,19 +20,7 @@ namespace Microsoft.PowerShell /// public static int Main(string[] args) { -#if CORECLR - // PowerShell has to set the ALC here, since we don't own the native host - string appBase = System.IO.Path.GetDirectoryName(typeof(ManagedPSEntry).GetTypeInfo().Assembly.Location); - return (int)PowerShellAssemblyLoadContextInitializer. - InitializeAndCallEntryMethod( - appBase, - new AssemblyName("Microsoft.PowerShell.ConsoleHost, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"), - "Microsoft.PowerShell.UnmanagedPSEntry", - "Start", - new object[] { string.Empty, args, args.Length }); -#else return UnmanagedPSEntry.Start(string.Empty, args, args.Length); -#endif } } }