Skip to content

Commit b126eda

Browse files
committed
Fix debugging script blocks that aren't in files
The code relied on running `list 1 <MaxInt>` but that wasn't being run in the debug context because we allow-list commands to prevent pollution of the history, and missed it. Like the `prompt` and interactive commands (which `list` could be but is not when we run it) we need to check for this exact `list` command and run it under the debugger. Moreover, we also weren't locking the `debugInfoHandle`, nor were we correctly checking if `scriptListingLines` was empty (it was never null), and our shortcut to skip allocation was broken. Actually we can't skip allocation, but we can at least skip superfluous conversions.
1 parent 9e19755 commit b126eda

File tree

2 files changed

+23
-12
lines changed

2 files changed

+23
-12
lines changed

src/PowerShellEditorServices/Services/DebugAdapter/DebugService.cs

+16-5
Original file line numberDiff line numberDiff line change
@@ -930,13 +930,24 @@ internal async void OnDebuggerStopAsync(object sender, DebuggerStopEventArgs e)
930930
if (_remoteFileManager is not null && string.IsNullOrEmpty(localScriptPath))
931931
{
932932
// Get the current script listing and create the buffer
933-
PSCommand command = new PSCommand().AddScript($"list 1 {int.MaxValue}");
933+
IReadOnlyList<PSObject> scriptListingLines;
934+
await debugInfoHandle.WaitAsync().ConfigureAwait(false);
935+
try
936+
{
937+
// This command must be run through `ExecuteInDebugger`!
938+
PSCommand psCommand = new PSCommand().AddScript($"list 1 {int.MaxValue}");
934939

935-
IReadOnlyList<PSObject> scriptListingLines =
936-
await _executionService.ExecutePSCommandAsync<PSObject>(
937-
command, CancellationToken.None).ConfigureAwait(false);
940+
scriptListingLines =
941+
await _executionService.ExecutePSCommandAsync<PSObject>(
942+
psCommand,
943+
CancellationToken.None).ConfigureAwait(false);
944+
}
945+
finally
946+
{
947+
debugInfoHandle.Release();
948+
}
938949

939-
if (scriptListingLines is not null)
950+
if (scriptListingLines.Count > 0)
940951
{
941952
int linePrefixLength = 0;
942953

src/PowerShellEditorServices/Services/PowerShell/Execution/SynchronousPowerShellTask.cs

+7-7
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public override IReadOnlyList<TResult> Run(CancellationToken cancellationToken)
7575
// the latter we must not send through it else they pollute the history as this
7676
// PowerShell API does not let us exclude them from it.
7777
return _pwsh.Runspace.Debugger.InBreakpoint
78-
&& (PowerShellExecutionOptions.AddToHistory || IsPromptCommand(_psCommand) || _pwsh.Runspace.RunspaceIsRemote)
78+
&& (PowerShellExecutionOptions.AddToHistory || IsPromptOrListCommand(_psCommand) || _pwsh.Runspace.RunspaceIsRemote)
7979
? ExecuteInDebugger(cancellationToken)
8080
: ExecuteNormally(cancellationToken);
8181
}
@@ -87,7 +87,7 @@ public override IReadOnlyList<TResult> Run(CancellationToken cancellationToken)
8787

8888
public override string ToString() => _psCommand.GetInvocationText();
8989

90-
private static bool IsPromptCommand(PSCommand command)
90+
private static bool IsPromptOrListCommand(PSCommand command)
9191
{
9292
if (command.Commands.Count is not 1
9393
|| command.Commands[0] is { IsScript: false } or { Parameters.Count: > 0 })
@@ -96,7 +96,8 @@ private static bool IsPromptCommand(PSCommand command)
9696
}
9797

9898
string commandText = command.Commands[0].CommandText;
99-
return commandText.Equals("prompt", StringComparison.OrdinalIgnoreCase);
99+
return commandText.Equals("prompt", StringComparison.OrdinalIgnoreCase)
100+
|| commandText.Equals($"list 1 {int.MaxValue}", StringComparison.OrdinalIgnoreCase);
100101
}
101102

102103
private IReadOnlyList<TResult> ExecuteNormally(CancellationToken cancellationToken)
@@ -301,11 +302,10 @@ private IReadOnlyList<TResult> ExecuteInDebugger(CancellationToken cancellationT
301302
return Array.Empty<TResult>();
302303
}
303304

304-
// If we've been asked for a PSObject, no need to allocate a new collection
305-
if (typeof(TResult) == typeof(PSObject)
306-
&& outputCollection is IReadOnlyList<TResult> resultCollection)
305+
// If we've been asked for a PSObject, no need to convert
306+
if (typeof(TResult) == typeof(PSObject))
307307
{
308-
return resultCollection;
308+
return new List<PSObject>(outputCollection) as IReadOnlyList<TResult>;
309309
}
310310

311311
// Otherwise, convert things over

0 commit comments

Comments
 (0)