@@ -18,6 +18,154 @@ namespace Microsoft.PowerShell.EditorServices.Services
18
18
{
19
19
internal class BreakpointService
20
20
{
21
+ private const string _getPSBreakpointLegacy = @"
22
+ [CmdletBinding()]
23
+ param (
24
+ [Parameter()]
25
+ [string]
26
+ $Script,
27
+
28
+ [Parameter()]
29
+ [int]
30
+ $RunspaceId = [Runspace]::DefaultRunspace.Id
31
+ )
32
+
33
+ $runspace = if ($PSBoundParameters.ContainsKey('RunspaceId')) {
34
+ Get-Runspace -Id $RunspaceId
35
+ $null = $PSBoundParameters.Remove('RunspaceId')
36
+ }
37
+ else {
38
+ [Runspace]::DefaultRunspace
39
+ }
40
+
41
+ $debugger = $runspace.Debugger
42
+ $getBreakpointsMeth = $debugger.GetType().GetMethod(
43
+ 'GetBreakpoints',
44
+ [System.Reflection.BindingFlags]'NonPublic, Public, Instance',
45
+ $null,
46
+ [type[]]@(),
47
+ $null)
48
+
49
+ $runspaceIdProp = [System.Management.Automation.PSNoteProperty]::new(
50
+ 'RunspaceId',
51
+ $runspaceId)
52
+
53
+ @(
54
+ if (-not $getBreakpointsMeth) {
55
+ if ($RunspaceId -ne [Runspace]::DefaultRunspace.Id) {
56
+ throw 'Failed to find GetBreakpoints method on Debugger.'
57
+ }
58
+
59
+ Microsoft.PowerShell.Utility\Get-PSBreakpoint @PSBoundParameters
60
+ }
61
+ else {
62
+ $getBreakpointsMeth.Invoke($debugger, @()) | Where-Object {
63
+ if ($Script) {
64
+ $_.Script -eq $Script
65
+ }
66
+ else {
67
+ $true
68
+ }
69
+ }
70
+ }
71
+ ) | ForEach-Object {
72
+ $_.PSObject.Properties.Add($runspaceIdProp)
73
+ $_
74
+ }
75
+ " ;
76
+
77
+ private const string _removePSBreakpointLegacy = @"
78
+ [CmdletBinding(DefaultParameterSetName = 'Breakpoint')]
79
+ param (
80
+ [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Breakpoint')]
81
+ [System.Management.Automation.Breakpoint[]]
82
+ $Breakpoint,
83
+
84
+ [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Id')]
85
+ [int[]]
86
+ $Id,
87
+
88
+ [Parameter(ParameterSetName = 'Id')]
89
+ [int]
90
+ $RunspaceId = [Runspace]::DefaultRunspace.Id
91
+ )
92
+
93
+ begin {
94
+ $removeBreakpointMeth = [Runspace]::DefaultRunspace.Debugger.GetType().GetMethod(
95
+ 'RemoveBreakpoint',
96
+ [System.Reflection.BindingFlags]'NonPublic, Public, Instance',
97
+ $null,
98
+ [type[]]@([System.Management.Automation.Breakpoint]),
99
+ $null)
100
+ $getBreakpointMeth = [Runspace]::DefaultRunspace.Debugger.GetType().GetMethod(
101
+ 'GetBreakpoint',
102
+ [System.Reflection.BindingFlags]'NonPublic, Public, Instance',
103
+ $null,
104
+ [type[]]@([int]),
105
+ $null)
106
+
107
+ $breakpointCollection = [System.Collections.Generic.List[System.Management.Automation.Breakpoint]]::new()
108
+ }
109
+
110
+ process {
111
+ if ($PSCmdlet.ParameterSetName -eq 'Id') {
112
+ $runspace = Get-Runspace -Id $RunspaceId
113
+ $runspaceProp = [System.Management.Automation.PSNoteProperty]::new(
114
+ 'Runspace',
115
+ $Runspace)
116
+
117
+ $breakpoints = if ($getBreakpointMeth) {
118
+ foreach ($breakpointId in $Id) {
119
+ $getBreakpointMeth.Invoke($runspace.Debugger, @($breakpointId))
120
+ }
121
+ }
122
+ elseif ($runspace -eq [Runspace]::DefaultRunspace) {
123
+ Microsoft.PowerShell.Utility\Get-PSBreakpoint -Id $Id
124
+ }
125
+ else {
126
+ throw 'Failed to find GetBreakpoint method on Debugger.'
127
+ }
128
+
129
+ $breakpoints | ForEach-Object {
130
+ $_.PSObject.Properties.Add($runspaceProp)
131
+ $breakpointCollection.Add($_)
132
+ }
133
+ }
134
+ else {
135
+ foreach ($b in $Breakpoint) {
136
+ # RunspaceId may be set by _getPSBreakpointLegacy when
137
+ # targeting a breakpoint in a specific runspace.
138
+ $runspace = if ($b.PSObject.Properties.Match('RunspaceId')) {
139
+ Get-Runspace -Id $b.RunspaceId
140
+ }
141
+ else {
142
+ [Runspace]::DefaultRunspace
143
+ }
144
+
145
+ $b.PSObject.Properties.Add(
146
+ [System.Management.Automation.PSNoteProperty]::new('Runspace', $runspace))
147
+ $breakpointCollection.Add($b)
148
+ }
149
+ }
150
+ }
151
+
152
+ end {
153
+ foreach ($b in $breakpointCollection) {
154
+ if ($removeBreakpointMeth) {
155
+ $removeBreakpointMeth.Invoke($b.Runspace.Debugger, @($b))
156
+ }
157
+ elseif ($b.Runspace -eq [Runspace]::DefaultRunspace) {
158
+ # If we don't have the method, we can only remove breakpoints
159
+ # from the default runspace using Remove-PSBreakpoint.
160
+ $b | Microsoft.PowerShell.Utility\Remove-PSBreakpoint
161
+ }
162
+ else {
163
+ throw 'Failed to find RemoveBreakpoint method on Debugger.'
164
+ }
165
+ }
166
+ }
167
+ " ;
168
+
21
169
/// <summary>
22
170
/// Code used on WinPS 5.1 to set breakpoints without Script path validation.
23
171
/// It uses reflection because the APIs were not public until 7.0 but just in
@@ -45,7 +193,11 @@ internal class BreakpointService
45
193
46
194
[Parameter(ParameterSetName = 'Command', Mandatory = $true)]
47
195
[string]
48
- $Command
196
+ $Command,
197
+
198
+ [Parameter()]
199
+ [int]
200
+ $RunspaceId
49
201
)
50
202
51
203
if ($PSCmdlet.ParameterSetName -eq 'Command') {
@@ -56,6 +208,9 @@ internal class BreakpointService
56
208
$null)
57
209
58
210
if (-not $cmdCtor) {
211
+ if ($PSBoundParameters.ContainsKey('RunspaceId')) {
212
+ throw 'Failed to find constructor for CommandBreakpoint.'
213
+ }
59
214
Microsoft.PowerShell.Utility\Set-PSBreakpoint @PSBoundParameters
60
215
return
61
216
}
@@ -73,15 +228,24 @@ internal class BreakpointService
73
228
$null)
74
229
75
230
if (-not $lineCtor) {
231
+ if ($PSBoundParameters.ContainsKey('RunspaceId')) {
232
+ throw 'Failed to find constructor for LineBreakpoint.'
233
+ }
76
234
Microsoft.PowerShell.Utility\Set-PSBreakpoint @PSBoundParameters
77
235
return
78
236
}
79
237
80
238
$b = $lineCtor.Invoke(@($Script, $Line, $Column, $Action))
81
239
}
82
240
83
- [Runspace]::DefaultRunspace.Debugger.SetBreakpoints(
84
- [System.Management.Automation.Breakpoint[]]@($b))
241
+ $runspace = if ($PSBoundParameters.ContainsKey('RunspaceId')) {
242
+ Get-Runspace -Id $RunspaceId
243
+ }
244
+ else {
245
+ [Runspace]::DefaultRunspace
246
+ }
247
+
248
+ $runspace.Debugger.SetBreakpoints([System.Management.Automation.Breakpoint[]]@($b))
85
249
86
250
$b
87
251
" ;
@@ -119,10 +283,14 @@ public async Task<IReadOnlyList<Breakpoint>> GetBreakpointsAsync()
119
283
}
120
284
121
285
// Legacy behavior
122
- PSCommand psCommand = new PSCommand ( ) . AddCommand ( @"Microsoft.PowerShell.Utility\Get-PSBreakpoint" ) ;
286
+ PSCommand psCommand = new PSCommand ( ) . AddScript ( _getPSBreakpointLegacy , useLocalScope : true ) ;
287
+ if ( _debugStateService . RunspaceId is not null )
288
+ {
289
+ psCommand . AddParameter ( "RunspaceId" , _debugStateService . RunspaceId . Value ) ;
290
+ }
123
291
return await _executionService
124
- . ExecutePSCommandAsync < Breakpoint > ( psCommand , CancellationToken . None )
125
- . ConfigureAwait ( false ) ;
292
+ . ExecutePSCommandAsync < Breakpoint > ( psCommand , CancellationToken . None )
293
+ . ConfigureAwait ( false ) ;
126
294
}
127
295
128
296
public async Task < IReadOnlyList < BreakpointDetails > > SetBreakpointsAsync ( string escapedScriptPath , IReadOnlyList < BreakpointDetails > breakpoints )
@@ -202,6 +370,11 @@ public async Task<IReadOnlyList<BreakpointDetails>> SetBreakpointsAsync(string e
202
370
{
203
371
psCommand . AddParameter ( "Action" , actionScriptBlock ) ;
204
372
}
373
+
374
+ if ( _debugStateService . RunspaceId is not null )
375
+ {
376
+ psCommand . AddParameter ( "RunspaceId" , _debugStateService . RunspaceId . Value ) ;
377
+ }
205
378
}
206
379
207
380
// If no PSCommand was created then there are no breakpoints to set.
@@ -317,14 +490,17 @@ public async Task RemoveAllBreakpointsAsync(string scriptPath = null)
317
490
}
318
491
319
492
// Legacy behavior
320
- PSCommand psCommand = new PSCommand ( ) . AddCommand ( @"Microsoft.PowerShell.Utility\Get-PSBreakpoint" ) ;
321
-
493
+ PSCommand psCommand = new PSCommand ( ) . AddScript ( _getPSBreakpointLegacy , useLocalScope : true ) ;
494
+ if ( _debugStateService . RunspaceId is not null )
495
+ {
496
+ psCommand . AddParameter ( "RunspaceId" , _debugStateService . RunspaceId . Value ) ;
497
+ }
322
498
if ( ! string . IsNullOrEmpty ( scriptPath ) )
323
499
{
324
500
psCommand . AddParameter ( "Script" , scriptPath ) ;
325
501
}
326
502
327
- psCommand . AddCommand ( @"Microsoft.PowerShell.Utility\Remove-PSBreakpoint" ) ;
503
+ psCommand . AddScript ( _removePSBreakpointLegacy , useLocalScope : true ) ;
328
504
await _executionService . ExecutePSCommandAsync < object > ( psCommand , CancellationToken . None ) . ConfigureAwait ( false ) ;
329
505
}
330
506
catch ( Exception e )
@@ -360,8 +536,12 @@ public async Task RemoveBreakpointsAsync(IEnumerable<Breakpoint> breakpoints)
360
536
if ( breakpointIds . Any ( ) )
361
537
{
362
538
PSCommand psCommand = new PSCommand ( )
363
- . AddCommand ( @"Microsoft.PowerShell.Utility\Remove-PSBreakpoint" )
539
+ . AddScript ( _removePSBreakpointLegacy , useLocalScope : true )
364
540
. AddParameter ( "Id" , breakpoints . Select ( b => b . Id ) . ToArray ( ) ) ;
541
+ if ( _debugStateService . RunspaceId is not null )
542
+ {
543
+ psCommand . AddParameter ( "RunspaceId" , _debugStateService . RunspaceId . Value ) ;
544
+ }
365
545
await _executionService . ExecutePSCommandAsync < object > ( psCommand , CancellationToken . None ) . ConfigureAwait ( false ) ;
366
546
}
367
547
}
0 commit comments