diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/CommonUtils.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/CommonUtils.cs index e51685365..97fdbc978 100644 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/CommonUtils.cs +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/CommonUtils.cs @@ -11,6 +11,7 @@ namespace Microsoft.PowerShell.Commands.Diagnostics.Common internal static class CommonUtilities { +#if !CORECLR // // StringArrayToString helper converts a string array into a comma-separated string. // Note this has only limited use, individual strings cannot have commas. @@ -111,13 +112,13 @@ namespace Microsoft.PowerShell.Commands.Diagnostics.Common } return formatError; } +#endif public static ResourceManager GetResourceManager() { // this naming pattern is dictated by the dotnet cli return new ResourceManager("Microsoft.PowerShell.Commands.Diagnostics.resources.GetEventResources", typeof(CommonUtilities).GetTypeInfo().Assembly); } - } } diff --git a/src/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext/CoreCLR/CorePsAssemblyLoadContext.cs b/src/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext/CoreCLR/CorePsAssemblyLoadContext.cs index 5f48ed3bc..073951c3c 100644 --- a/src/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext/CoreCLR/CorePsAssemblyLoadContext.cs +++ b/src/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext/CoreCLR/CorePsAssemblyLoadContext.cs @@ -175,7 +175,7 @@ namespace System.Management.Automation /// /// Singleton instance of PowerShellAssemblyLoadContext /// - public static PowerShellAssemblyLoadContext Instance + internal static PowerShellAssemblyLoadContext Instance { get; private set; } @@ -376,7 +376,19 @@ namespace System.Management.Automation string tpaStrongName; if (coreClrTypeCatalog.TryGetValue(namespaceQualifiedTypeName, out tpaStrongName)) { - return new Assembly[] { GetTrustedPlatformAssembly(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. + } } } @@ -459,6 +471,15 @@ namespace System.Management.Automation /// /// 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 (this.useResolvingHandlerOnly) @@ -468,6 +489,15 @@ namespace System.Management.Automation /// /// 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 (this.useResolvingHandlerOnly) @@ -620,38 +650,15 @@ namespace System.Management.Automation /// private Assembly GetTrustedPlatformAssembly(string tpaStrongName) { - Assembly asmLoaded; + // We always depend on the default context to load the TPAs that are recorded in + // the type catalog. + // - If the requested TPA is already loaded, then 'Assembly.Load' will just get + // it back from the cache of default context. + // - If the requested TPA is not loaded yet, then 'Assembly.Load' will make the + // default context to load it AssemblyName assemblyName = new AssemblyName(tpaStrongName); - - // With the current standalone-app model of OPS, .NET Core libraries and PS assemblies are mixed together in one folder. - // So when using PSALC as a full fledged ALC in OPS, some TPAs might be loaded by our Load override. In that case, if we - // alwasy call Assembly.Load here to get a TPA, we might end up with a different Assembly instance of the the same TPA - // loaded in the default load context. We want to use the same assembly instance for type resolution in PS to avoid creating - // types and running .NET code from different assembly instances of the same DLL. Therefore, we try our cache first to see - // if the requested TPA is already loaded. If so, we use that one. If not, we load it in default context using Assembly.Load. - // Once a TPA is loaded in the default context, the same Assembly instance will always be used by custom ALC's when they attempt - // to resolve an "Assembly.Load" request for the same TPA. - // - // For in-box PS of NanoServer/IoT and the share-framework host model of OPS, we don't have the mixed libraries/assemblies - // problem, and TPAs are always resolved/loaded by the default context. In those cases, checking our cache would be unnecessary, - // but it won't cause any problems. - - // Probe the assembly cache - if (TryGetAssemblyFromCache(assemblyName, out asmLoaded)) - return asmLoaded; - - // Prepare to load the assembly - lock (syncObj) - { - // Probe the cache again in case it's already loaded - if (TryGetAssemblyFromCache(assemblyName, out asmLoaded)) - return asmLoaded; - - // The requested TPA is not loaded by PS ALC, so load it in the default load context using Assembly.Load. - // There is no need to add it to our cache. It's cached in the default context. - asmLoaded = Assembly.Load(assemblyName); - return asmLoaded; - } + Assembly asmLoaded = Assembly.Load(assemblyName); + return asmLoaded; } /// diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index 4200b1055..8c3eb882b 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -5907,21 +5907,32 @@ namespace System.Management.Automation /// The namespace private static void HandleNamespace(Dictionary entryCache, string @namespace) { - if (!string.IsNullOrEmpty(@namespace)) + if (string.IsNullOrEmpty(@namespace)) { + return; + } + + int dotIndex = 0; + while (dotIndex != -1) + { + dotIndex = @namespace.IndexOf('.', dotIndex + 1); + string subNamespace = dotIndex != -1 + ? @namespace.Substring(0, dotIndex) + : @namespace; + TypeCompletionMapping entry; - if (!entryCache.TryGetValue(@namespace, out entry)) + if (!entryCache.TryGetValue(subNamespace, out entry)) { entry = new TypeCompletionMapping { - Key = @namespace, - Completions = { new NamespaceCompletion { Namespace = @namespace } } + Key = subNamespace, + Completions = { new NamespaceCompletion { Namespace = subNamespace } } }; - entryCache.Add(@namespace, entry); + entryCache.Add(subNamespace, entry); } else if (!entry.Completions.OfType().Any()) { - entry.Completions.Add(new NamespaceCompletion { Namespace = @namespace }); + entry.Completions.Add(new NamespaceCompletion { Namespace = subNamespace }); } } } diff --git a/src/System.Management.Automation/engine/Modules/ModuleCmdletBase.cs b/src/System.Management.Automation/engine/Modules/ModuleCmdletBase.cs index f60a5ff75..5bf3efbac 100644 --- a/src/System.Management.Automation/engine/Modules/ModuleCmdletBase.cs +++ b/src/System.Management.Automation/engine/Modules/ModuleCmdletBase.cs @@ -1818,7 +1818,7 @@ namespace Microsoft.PowerShell.Commands DirectoryInfo parent = null; try { - parent = ClrFacade.GetParent(moduleManifestPath); + parent = Directory.GetParent(moduleManifestPath); } catch (IOException) { diff --git a/src/System.Management.Automation/engine/Modules/TestModuleManifestCommand.cs b/src/System.Management.Automation/engine/Modules/TestModuleManifestCommand.cs index b74c18bfa..f3ade9228 100644 --- a/src/System.Management.Automation/engine/Modules/TestModuleManifestCommand.cs +++ b/src/System.Management.Automation/engine/Modules/TestModuleManifestCommand.cs @@ -241,7 +241,7 @@ namespace Microsoft.PowerShell.Commands DirectoryInfo parent = null; try { - parent = ClrFacade.GetParent(filePath); + parent = Directory.GetParent(filePath); } catch (IOException) { } catch (UnauthorizedAccessException) { } diff --git a/src/System.Management.Automation/resources/ParserStrings.resx b/src/System.Management.Automation/resources/ParserStrings.resx index 695cdc596..45c808cb5 100644 --- a/src/System.Management.Automation/resources/ParserStrings.resx +++ b/src/System.Management.Automation/resources/ParserStrings.resx @@ -1351,8 +1351,8 @@ 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 'PowerShellAssemblyLoader'. + + 'PowerShellAssemblyLoadContext' is not initialized. 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 16d085820..f9cd78eb4 100644 --- a/src/System.Management.Automation/utils/ClrFacade.cs +++ b/src/System.Management.Automation/utils/ClrFacade.cs @@ -344,6 +344,10 @@ namespace System.Management.Automation { get { + if (PowerShellAssemblyLoadContext.Instance == null) + { + throw new InvalidOperationException(ParserStrings.LoadContextNotInitialized); + } return PowerShellAssemblyLoadContext.Instance; } } @@ -512,15 +516,6 @@ namespace System.Management.Automation #endif } - /// - /// Facade for Directory.GetParent(string) - /// - internal static DirectoryInfo GetParent(string path) - { - // Porting note: this is in recent CoreCLR - return Directory.GetParent(path); - } - /// /// Facade for ManagementDateTimeConverter.ToDmtfDateTime(DateTime) /// diff --git a/src/powershell-native/nativemsh/pwrshcommon/pwrshcommon.cpp b/src/powershell-native/nativemsh/pwrshcommon/pwrshcommon.cpp index 0f9deb37e..64d0a014d 100644 --- a/src/powershell-native/nativemsh/pwrshcommon/pwrshcommon.cpp +++ b/src/powershell-native/nativemsh/pwrshcommon/pwrshcommon.cpp @@ -1454,14 +1454,14 @@ namespace NativeMsh props[nProps] = L"TRUSTED_PLATFORM_ASSEMBLIES"; std::wstring tempStr = assemblyList.str(); vals[nProps] = tempStr.c_str(); - nProps++; + nProps++; props[nProps] = L"APP_PATHS"; - vals[nProps] = hostEnvironment.GetHostDirectoryPath(); + vals[nProps] = L""; nProps++; props[nProps] = L"APP_NI_PATHS"; - vals[nProps] = hostEnvironment.GetHostDirectoryPath(); + vals[nProps] = L""; nProps++; // Create the customized AppDomainManager out of the SandboxHelper class diff --git a/test/powershell/Host/TabCompletion/BugFix.Tests.ps1 b/test/powershell/Host/TabCompletion/BugFix.Tests.ps1 new file mode 100644 index 000000000..18a64f782 --- /dev/null +++ b/test/powershell/Host/TabCompletion/BugFix.Tests.ps1 @@ -0,0 +1,9 @@ +Describe "Issue#682 - [system.manage] should work" { + + It "[system.manage" { + $result = TabExpansion2 -inputScript "[system.manage" -cursorColumn "[system.manage".Length + $result | Should Not BeNullOrEmpty + $result.CompletionMatches.Count | Should Be 1 + $result.CompletionMatches[0].CompletionText | Should Be "System.Management" + } +}