From 9d18d81e5f54bb481efb70320af80228c966c265 Mon Sep 17 00:00:00 2001 From: Patrick Meinecke Date: Mon, 15 Aug 2022 17:13:10 -0400 Subject: [PATCH 1/3] Fix cancelling a debug task `CancelDebugExecution` was supposed to exit early when the runspace was unusable. Basically forgot the `!` in the `if` statement. --- .../Services/PowerShell/Execution/SynchronousPowerShellTask.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PowerShellEditorServices/Services/PowerShell/Execution/SynchronousPowerShellTask.cs b/src/PowerShellEditorServices/Services/PowerShell/Execution/SynchronousPowerShellTask.cs index 0661ce962..2fdefbfdc 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Execution/SynchronousPowerShellTask.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Execution/SynchronousPowerShellTask.cs @@ -382,7 +382,7 @@ internal void MaybeAddToHistory() private void CancelDebugExecution() { - if (_pwsh.Runspace.RunspaceStateInfo.IsUsable()) + if (!_pwsh.Runspace.RunspaceStateInfo.IsUsable()) { return; } From d490e7a95336b43c0061dce58d2a88951cae6cc7 Mon Sep 17 00:00:00 2001 From: Patrick Meinecke Date: Mon, 15 Aug 2022 17:15:39 -0400 Subject: [PATCH 2/3] Abort execution when in a nested debug frame When attempting to step, if we're in a nested debug frame that isn't a REPL we should stop execution before trying to exit the REPL. --- .../Debugging/PowerShellDebugContext.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/PowerShellEditorServices/Services/PowerShell/Debugging/PowerShellDebugContext.cs b/src/PowerShellEditorServices/Services/PowerShell/Debugging/PowerShellDebugContext.cs index 36aaad0e2..236403e5a 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Debugging/PowerShellDebugContext.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Debugging/PowerShellDebugContext.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.PowerShell.EditorServices.Services.PowerShell.Context; using Microsoft.PowerShell.EditorServices.Services.PowerShell.Host; using Microsoft.PowerShell.EditorServices.Services.PowerShell.Utility; @@ -145,15 +146,27 @@ public void SetDebugResuming(DebuggerResumeAction debuggerResumeAction) // TODO: We need to assign cancellation tokens to each frame, because the current // logic results in a deadlock here when we try to cancel the scopes...which // includes ourself (hence running it in a separate thread). - Task.Run(() => _psesHost.UnwindCallStack()); + _ = Task.Run(() => _psesHost.UnwindCallStack()); return; } // Otherwise we're continuing or stepping (i.e. resuming) so we need to cancel the // debugger REPL. - if (_psesHost.CurrentFrame.IsRepl) + PowerShellFrameType frameType = _psesHost.CurrentFrame.FrameType; + if (frameType.HasFlag(PowerShellFrameType.Repl)) { _psesHost.CancelIdleParentTask(); + return; + } + + // If the user is running something via the REPL like `while ($true) { sleep 1 }` + // and then tries to step, we want to stop that so that execution can resume. + // + // This also applies to anything we're running on debugger stop like watch variables. + if (frameType.HasFlag(PowerShellFrameType.Debug | PowerShellFrameType.Nested)) + { + _psesHost.ForceSetExit(); + _psesHost.CancelIdleParentTask(); } } From 60e37b4270ffa08f02ec82744de2b5b178271e3d Mon Sep 17 00:00:00 2001 From: Patrick Meinecke Date: Mon, 15 Aug 2022 17:17:19 -0400 Subject: [PATCH 3/3] Write exception messages when `watch` fails Instead of a blank watch message we now write the exception message that was thrown from the attempt. --- .../Services/DebugAdapter/DebugService.cs | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/DebugService.cs b/src/PowerShellEditorServices/Services/DebugAdapter/DebugService.cs index 5d32451ed..906b9fe4d 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/DebugService.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/DebugService.cs @@ -497,10 +497,22 @@ public async Task EvaluateExpressionAsync( bool writeResultAsOutput) { PSCommand command = new PSCommand().AddScript(expressionString); - IReadOnlyList results = await _executionService.ExecutePSCommandAsync( - command, - CancellationToken.None, - new PowerShellExecutionOptions { WriteOutputToHost = writeResultAsOutput, ThrowOnError = !writeResultAsOutput }).ConfigureAwait(false); + IReadOnlyList results; + try + { + results = await _executionService.ExecutePSCommandAsync( + command, + CancellationToken.None, + new PowerShellExecutionOptions { WriteOutputToHost = writeResultAsOutput, ThrowOnError = !writeResultAsOutput }).ConfigureAwait(false); + } + catch (Exception e) + { + // If a watch expression throws we want to show the exception. + // TODO: Show the exception as an expandable object. + return new VariableDetails( + expressionString, + $"{e.GetType().Name}: {e.Message}"); + } // Since this method should only be getting invoked in the debugger, // we can assume that Out-String will be getting used to format results