@@ -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 ($Script) {
@@ -65,6 +217,9 @@ internal class BreakpointService
65
217
$null)
66
218
67
219
if (-not $cmdCtor) {
220
+ if ($PSBoundParameters.ContainsKey('RunspaceId')) {
221
+ throw 'Failed to find constructor for CommandBreakpoint.'
222
+ }
68
223
Microsoft.PowerShell.Utility\Set-PSBreakpoint @PSBoundParameters
69
224
return
70
225
}
@@ -82,15 +237,24 @@ internal class BreakpointService
82
237
$null)
83
238
84
239
if (-not $lineCtor) {
240
+ if ($PSBoundParameters.ContainsKey('RunspaceId')) {
241
+ throw 'Failed to find constructor for LineBreakpoint.'
242
+ }
85
243
Microsoft.PowerShell.Utility\Set-PSBreakpoint @PSBoundParameters
86
244
return
87
245
}
88
246
89
247
$b = $lineCtor.Invoke(@($Script, $Line, $Column, $Action))
90
248
}
91
249
92
- [Runspace]::DefaultRunspace.Debugger.SetBreakpoints(
93
- [System.Management.Automation.Breakpoint[]]@($b))
250
+ $runspace = if ($PSBoundParameters.ContainsKey('RunspaceId')) {
251
+ Get-Runspace -Id $RunspaceId
252
+ }
253
+ else {
254
+ [Runspace]::DefaultRunspace
255
+ }
256
+
257
+ $runspace.Debugger.SetBreakpoints([System.Management.Automation.Breakpoint[]]@($b))
94
258
95
259
$b
96
260
" ;
@@ -128,10 +292,14 @@ public async Task<IReadOnlyList<Breakpoint>> GetBreakpointsAsync()
128
292
}
129
293
130
294
// Legacy behavior
131
- PSCommand psCommand = new PSCommand ( ) . AddCommand ( @"Microsoft.PowerShell.Utility\Get-PSBreakpoint" ) ;
295
+ PSCommand psCommand = new PSCommand ( ) . AddScript ( _getPSBreakpointLegacy , useLocalScope : true ) ;
296
+ if ( _debugStateService . RunspaceId is not null )
297
+ {
298
+ psCommand . AddParameter ( "RunspaceId" , _debugStateService . RunspaceId . Value ) ;
299
+ }
132
300
return await _executionService
133
- . ExecutePSCommandAsync < Breakpoint > ( psCommand , CancellationToken . None )
134
- . ConfigureAwait ( false ) ;
301
+ . ExecutePSCommandAsync < Breakpoint > ( psCommand , CancellationToken . None )
302
+ . ConfigureAwait ( false ) ;
135
303
}
136
304
137
305
public async Task < IReadOnlyList < BreakpointDetails > > SetBreakpointsAsync ( IReadOnlyList < BreakpointDetails > breakpoints )
@@ -211,6 +379,11 @@ public async Task<IReadOnlyList<BreakpointDetails>> SetBreakpointsAsync(IReadOnl
211
379
{
212
380
psCommand . AddParameter ( "Action" , actionScriptBlock ) ;
213
381
}
382
+
383
+ if ( _debugStateService . RunspaceId is not null )
384
+ {
385
+ psCommand . AddParameter ( "RunspaceId" , _debugStateService . RunspaceId . Value ) ;
386
+ }
214
387
}
215
388
216
389
// If no PSCommand was created then there are no breakpoints to set.
@@ -335,14 +508,17 @@ public async Task RemoveAllBreakpointsAsync(string scriptPath = null)
335
508
}
336
509
337
510
// Legacy behavior
338
- PSCommand psCommand = new PSCommand ( ) . AddCommand ( @"Microsoft.PowerShell.Utility\Get-PSBreakpoint" ) ;
339
-
511
+ PSCommand psCommand = new PSCommand ( ) . AddScript ( _getPSBreakpointLegacy , useLocalScope : true ) ;
512
+ if ( _debugStateService . RunspaceId is not null )
513
+ {
514
+ psCommand . AddParameter ( "RunspaceId" , _debugStateService . RunspaceId . Value ) ;
515
+ }
340
516
if ( ! string . IsNullOrEmpty ( scriptPath ) )
341
517
{
342
518
psCommand . AddParameter ( "Script" , scriptPath ) ;
343
519
}
344
520
345
- psCommand . AddCommand ( @"Microsoft.PowerShell.Utility\Remove-PSBreakpoint" ) ;
521
+ psCommand . AddScript ( _removePSBreakpointLegacy , useLocalScope : true ) ;
346
522
await _executionService . ExecutePSCommandAsync < object > ( psCommand , CancellationToken . None ) . ConfigureAwait ( false ) ;
347
523
}
348
524
catch ( Exception e )
@@ -378,8 +554,12 @@ public async Task RemoveBreakpointsAsync(IEnumerable<Breakpoint> breakpoints)
378
554
if ( breakpointIds . Any ( ) )
379
555
{
380
556
PSCommand psCommand = new PSCommand ( )
381
- . AddCommand ( @"Microsoft.PowerShell.Utility\Remove-PSBreakpoint" )
557
+ . AddScript ( _removePSBreakpointLegacy , useLocalScope : true )
382
558
. AddParameter ( "Id" , breakpoints . Select ( b => b . Id ) . ToArray ( ) ) ;
559
+ if ( _debugStateService . RunspaceId is not null )
560
+ {
561
+ psCommand . AddParameter ( "RunspaceId" , _debugStateService . RunspaceId . Value ) ;
562
+ }
383
563
await _executionService . ExecutePSCommandAsync < object > ( psCommand , CancellationToken . None ) . ConfigureAwait ( false ) ;
384
564
}
385
565
}
0 commit comments