@@ -18,6 +18,154 @@ namespace Microsoft.PowerShell.EditorServices.Services
1818{ 
1919    internal  class  BreakpointService 
2020    { 
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+ 
21169        /// <summary> 
22170        /// Code used on WinPS 5.1 to set breakpoints without Script path validation. 
23171        /// It uses reflection because the APIs were not public until 7.0 but just in 
@@ -45,7 +193,11 @@ internal class BreakpointService
45193
46194                [Parameter(ParameterSetName = 'Command', Mandatory = $true)] 
47195                [string] 
48-                 $Command 
196+                 $Command, 
197+ 
198+                 [Parameter()] 
199+                 [int] 
200+                 $RunspaceId 
49201            ) 
50202
51203            if ($Script) { 
@@ -65,6 +217,9 @@ internal class BreakpointService
65217                    $null) 
66218
67219                if (-not $cmdCtor) { 
220+                     if ($PSBoundParameters.ContainsKey('RunspaceId')) { 
221+                         throw 'Failed to find constructor for CommandBreakpoint.' 
222+                     } 
68223                    Microsoft.PowerShell.Utility\Set-PSBreakpoint @PSBoundParameters 
69224                    return 
70225                } 
@@ -82,15 +237,24 @@ internal class BreakpointService
82237                    $null) 
83238
84239                if (-not $lineCtor) { 
240+                     if ($PSBoundParameters.ContainsKey('RunspaceId')) { 
241+                         throw 'Failed to find constructor for LineBreakpoint.' 
242+                     } 
85243                    Microsoft.PowerShell.Utility\Set-PSBreakpoint @PSBoundParameters 
86244                    return 
87245                } 
88246
89247                $b = $lineCtor.Invoke(@($Script, $Line, $Column, $Action)) 
90248            } 
91249
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)) 
94258
95259            $b 
96260        " ; 
@@ -128,10 +292,14 @@ public async Task<IReadOnlyList<Breakpoint>> GetBreakpointsAsync()
128292            } 
129293
130294            // 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+             } 
132300            return  await  _executionService 
133-                 . ExecutePSCommandAsync < Breakpoint > ( psCommand ,  CancellationToken . None ) 
134-                 . ConfigureAwait ( false ) ; 
301+                      . ExecutePSCommandAsync < Breakpoint > ( psCommand ,  CancellationToken . None ) 
302+                      . ConfigureAwait ( false ) ; 
135303        } 
136304
137305        public  async  Task < IReadOnlyList < BreakpointDetails > >  SetBreakpointsAsync ( IReadOnlyList < BreakpointDetails >  breakpoints ) 
@@ -211,6 +379,11 @@ public async Task<IReadOnlyList<BreakpointDetails>> SetBreakpointsAsync(IReadOnl
211379                { 
212380                    psCommand . AddParameter ( "Action" ,  actionScriptBlock ) ; 
213381                } 
382+ 
383+                 if  ( _debugStateService . RunspaceId  is  not null ) 
384+                 { 
385+                     psCommand . AddParameter ( "RunspaceId" ,  _debugStateService . RunspaceId . Value ) ; 
386+                 } 
214387            } 
215388
216389            // If no PSCommand was created then there are no breakpoints to set. 
@@ -335,14 +508,17 @@ public async Task RemoveAllBreakpointsAsync(string scriptPath = null)
335508                } 
336509
337510                // 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+                 } 
340516                if  ( ! string . IsNullOrEmpty ( scriptPath ) ) 
341517                { 
342518                    psCommand . AddParameter ( "Script" ,  scriptPath ) ; 
343519                } 
344520
345-                 psCommand . AddCommand ( @"Microsoft.PowerShell.Utility\Remove-PSBreakpoint" ) ; 
521+                 psCommand . AddScript ( _removePSBreakpointLegacy ,   useLocalScope :   true ) ; 
346522                await  _executionService . ExecutePSCommandAsync < object > ( psCommand ,  CancellationToken . None ) . ConfigureAwait ( false ) ; 
347523            } 
348524            catch  ( Exception  e ) 
@@ -378,8 +554,12 @@ public async Task RemoveBreakpointsAsync(IEnumerable<Breakpoint> breakpoints)
378554            if  ( breakpointIds . Any ( ) ) 
379555            { 
380556                PSCommand  psCommand  =  new  PSCommand ( ) 
381-                     . AddCommand ( @"Microsoft.PowerShell.Utility\Remove-PSBreakpoint" ) 
557+                     . AddScript ( _removePSBreakpointLegacy ,   useLocalScope :   true ) 
382558                    . AddParameter ( "Id" ,  breakpoints . Select ( b =>  b . Id ) . ToArray ( ) ) ; 
559+                 if  ( _debugStateService . RunspaceId  is  not null ) 
560+                 { 
561+                     psCommand . AddParameter ( "RunspaceId" ,  _debugStateService . RunspaceId . Value ) ; 
562+                 } 
383563                await  _executionService . ExecutePSCommandAsync < object > ( psCommand ,  CancellationToken . None ) . ConfigureAwait ( false ) ; 
384564            } 
385565        } 
0 commit comments