From 66c7abedfeb8f95475ef9b10c8edec29023d354c Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 19 May 2022 13:12:36 -0700 Subject: [PATCH] Fix startup bug when zero profiles are present The service would fail to fully start if zero profiles were present, because that meant we attempted to invoke an empty command, which then threw `PSInvalidOperationException: No commands are specified`. This wasn't caught by our task executor, since it happened in a delegate which directly invoked the command. --- .../Services/PowerShell/Host/PsesInternalHost.cs | 10 +++++----- .../PowerShell/Utility/PowerShellExtensions.cs | 10 ++++++++-- .../Utility/PSCommandExtensions.cs | 5 +---- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs b/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs index aa87675e6..271518add 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs @@ -113,12 +113,12 @@ public PsesInternalHost( _cancellationContext = new CancellationContext(); // Default stack size on .NET Framework is 524288 (512KB) (as reported by GetProcessDefaultStackSize) - // this leaves very little room in the stack. Windows PowerShell internally sets the value based on - // PipelineMaxStackSizeMB as seen here: https://github.com/PowerShell/PowerShell/issues/1187, + // this leaves very little room in the stack. Windows PowerShell internally sets the value based on + // PipelineMaxStackSizeMB as seen here: https://github.com/PowerShell/PowerShell/issues/1187, // which has default of 10 and multiplies that by 1_000_000, so the default stack size is - // 10_000_000 (~10MB) when starting in normal console host. + // 10_000_000 (~10MB) when starting in normal console host. // - // For PS7 the value is ignored by .NET because settings the stack size is not supported, but we can + // For PS7 the value is ignored by .NET because settings the stack size is not supported, but we can // still provide 0, which means fallback to the default in both .NET and .NET Framework. int maxStackSize = VersionUtils.IsPS5 ? 10_000_000 : 0; _pipelineThread = new Thread(Run, maxStackSize) @@ -433,7 +433,7 @@ internal Task LoadHostProfilesAsync(CancellationToken cancellationToken) // NOTE: This is a special task run on startup! return ExecuteDelegateAsync( "LoadProfiles", - new PowerShellExecutionOptions { ThrowOnError = false }, + executionOptions: null, (pwsh, _) => pwsh.LoadProfiles(_hostInfo.ProfilePaths), cancellationToken); } diff --git a/src/PowerShellEditorServices/Services/PowerShell/Utility/PowerShellExtensions.cs b/src/PowerShellEditorServices/Services/PowerShell/Utility/PowerShellExtensions.cs index 173d1d6ae..d7e85f3e4 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Utility/PowerShellExtensions.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Utility/PowerShellExtensions.cs @@ -2,13 +2,13 @@ // Licensed under the MIT License. using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Reflection; using System.Text; using Microsoft.Extensions.Logging; using Microsoft.PowerShell.EditorServices.Hosting; using Microsoft.PowerShell.EditorServices.Utility; -using System.Collections.Generic; namespace Microsoft.PowerShell.EditorServices.Services.PowerShell.Utility { @@ -211,7 +211,13 @@ public static void LoadProfiles(this PowerShell pwsh, ProfilePathInfo profilePat // NOTE: This must be set before the profiles are loaded. pwsh.Runspace.SessionStateProxy.SetVariable("PROFILE", profileVariable); - pwsh.InvokeCommand(psCommand); + // NOTE: Because it's possible there are no profiles defined, we might have an empty + // command. Since this is being executed directly, we can't rely on `ThrowOnError = + // false` to avoid an exception here. Instead, we must just not execute it. + if (psCommand.Commands.Count > 0) + { + pwsh.InvokeCommand(psCommand); + } } public static void ImportModule(this PowerShell pwsh, string moduleNameOrPath) diff --git a/src/PowerShellEditorServices/Utility/PSCommandExtensions.cs b/src/PowerShellEditorServices/Utility/PSCommandExtensions.cs index 5b9763829..154710003 100644 --- a/src/PowerShellEditorServices/Utility/PSCommandExtensions.cs +++ b/src/PowerShellEditorServices/Utility/PSCommandExtensions.cs @@ -77,10 +77,7 @@ public static PSCommand AddProfileLoadIfExists(this PSCommand psCommand, PSObjec if (File.Exists(profilePath)) { - psCommand - .AddCommand(profilePath, useLocalScope: false) - .AddOutputCommand() - .AddStatement(); + psCommand.AddCommand(profilePath, useLocalScope: false).AddOutputCommand().AddStatement(); } return psCommand;