@@ -35,7 +35,7 @@ internal class DebugService
35
35
private readonly ILogger _logger ;
36
36
private readonly IInternalPowerShellExecutionService _executionService ;
37
37
private readonly BreakpointService _breakpointService ;
38
- private readonly RemoteFileManagerService remoteFileManager ;
38
+ private readonly RemoteFileManagerService _remoteFileManager ;
39
39
40
40
private readonly PsesInternalHost _psesHost ;
41
41
@@ -45,7 +45,7 @@ internal class DebugService
45
45
private int nextVariableId ;
46
46
private string temporaryScriptListingPath ;
47
47
private List < VariableDetailsBase > variables ;
48
- private VariableContainerDetails globalScopeVariables ;
48
+ internal VariableContainerDetails globalScopeVariables ; // Internal for unit testing.
49
49
private VariableContainerDetails scriptScopeVariables ;
50
50
private VariableContainerDetails localScopeVariables ;
51
51
private StackFrameDetails [ ] stackFrameDetails ;
@@ -80,28 +80,8 @@ internal class DebugService
80
80
81
81
/// <summary>
82
82
/// Initializes a new instance of the DebugService class and uses
83
- /// the given PowerShellContext for all future operations.
83
+ /// the given execution service for all future operations.
84
84
/// </summary>
85
- /// <param name="powerShellContext">
86
- /// The PowerShellContext to use for all debugging operations.
87
- /// </param>
88
- /// <param name="logger">An ILogger implementation used for writing log messages.</param>
89
- //public DebugService(PowerShellContextService powerShellContext, ILogger logger)
90
- // : this(powerShellContext, null, logger)
91
- //{
92
- //}
93
-
94
- /// <summary>
95
- /// Initializes a new instance of the DebugService class and uses
96
- /// the given PowerShellContext for all future operations.
97
- /// </summary>
98
- /// <param name="powerShellContext">
99
- /// The PowerShellContext to use for all debugging operations.
100
- /// </param>
101
- //// <param name = "remoteFileManager" >
102
- //// A RemoteFileManagerService instance to use for accessing files in remote sessions.
103
- //// </param>
104
- /// <param name="logger">An ILogger implementation used for writing log messages.</param>
105
85
public DebugService (
106
86
IInternalPowerShellExecutionService executionService ,
107
87
IPowerShellDebugContext debugContext ,
@@ -120,8 +100,7 @@ public DebugService(
120
100
_debugContext . DebuggerStopped += OnDebuggerStopAsync ;
121
101
_debugContext . DebuggerResuming += OnDebuggerResuming ;
122
102
_debugContext . BreakpointUpdated += OnBreakpointUpdated ;
123
-
124
- this . remoteFileManager = remoteFileManager ;
103
+ _remoteFileManager = remoteFileManager ;
125
104
126
105
invocationTypeScriptPositionProperty =
127
106
typeof ( InvocationInfo )
@@ -150,32 +129,27 @@ public async Task<BreakpointDetails[]> SetLineBreakpointsAsync(
150
129
151
130
string scriptPath = scriptFile . FilePath ;
152
131
// Make sure we're using the remote script path
153
- if ( _psesHost . CurrentRunspace . IsOnRemoteMachine && remoteFileManager is not null )
132
+ if ( _psesHost . CurrentRunspace . IsOnRemoteMachine && _remoteFileManager is not null )
154
133
{
155
- if ( ! remoteFileManager . IsUnderRemoteTempPath ( scriptPath ) )
134
+ if ( ! _remoteFileManager . IsUnderRemoteTempPath ( scriptPath ) )
156
135
{
157
136
_logger . LogTrace ( $ "Could not set breakpoints for local path '{ scriptPath } ' in a remote session.") ;
158
-
159
137
return Array . Empty < BreakpointDetails > ( ) ;
160
138
}
161
139
162
- string mappedPath = remoteFileManager . GetMappedPath ( scriptPath , _psesHost . CurrentRunspace ) ;
163
-
164
- scriptPath = mappedPath ;
140
+ scriptPath = _remoteFileManager . GetMappedPath ( scriptPath , _psesHost . CurrentRunspace ) ;
165
141
}
166
- else if ( temporaryScriptListingPath is not null
167
- && temporaryScriptListingPath . Equals ( scriptPath , StringComparison . CurrentCultureIgnoreCase ) )
142
+ else if ( temporaryScriptListingPath ? . Equals ( scriptPath , StringComparison . CurrentCultureIgnoreCase ) == true )
168
143
{
169
144
_logger . LogTrace ( $ "Could not set breakpoint on temporary script listing path '{ scriptPath } '.") ;
170
-
171
145
return Array . Empty < BreakpointDetails > ( ) ;
172
146
}
173
147
174
148
// Fix for issue #123 - file paths that contain wildcard chars [ and ] need to
175
149
// quoted and have those wildcard chars escaped.
176
150
string escapedScriptPath = PathUtils . WildcardEscapePath ( scriptPath ) ;
177
151
178
- if ( dscBreakpoints is null || ! dscBreakpoints . IsDscResourcePath ( escapedScriptPath ) )
152
+ if ( dscBreakpoints ? . IsDscResourcePath ( escapedScriptPath ) != true )
179
153
{
180
154
if ( clearExisting )
181
155
{
@@ -185,10 +159,8 @@ public async Task<BreakpointDetails[]> SetLineBreakpointsAsync(
185
159
return ( await _breakpointService . SetBreakpointsAsync ( escapedScriptPath , breakpoints ) . ConfigureAwait ( false ) ) . ToArray ( ) ;
186
160
}
187
161
188
- return await dscBreakpoints . SetLineBreakpointsAsync (
189
- _executionService ,
190
- escapedScriptPath ,
191
- breakpoints )
162
+ return await dscBreakpoints
163
+ . SetLineBreakpointsAsync ( _executionService , escapedScriptPath , breakpoints )
192
164
. ConfigureAwait ( false ) ;
193
165
}
194
166
@@ -362,7 +334,7 @@ public VariableDetailsBase GetVariableFromExpression(string variableExpression)
362
334
variableName ,
363
335
StringComparison . CurrentCultureIgnoreCase ) ) ;
364
336
365
- if ( resolvedVariable is not null && resolvedVariable . IsExpandable )
337
+ if ( resolvedVariable ? . IsExpandable == true )
366
338
{
367
339
// Continue by searching in this variable's children.
368
340
variableList = GetVariables ( resolvedVariable . Id ) ;
@@ -383,6 +355,7 @@ public VariableDetailsBase GetVariableFromExpression(string variableExpression)
383
355
/// <param name="value">The new string value. This value must not be null. If you want to set the variable to $null
384
356
/// pass in the string "$null".</param>
385
357
/// <returns>The string representation of the value the variable was set to.</returns>
358
+ /// <exception cref="InvalidPowerShellExpressionException"></exception>
386
359
public async Task < string > SetVariableAsync ( int variableContainerReferenceId , string name , string value )
387
360
{
388
361
Validate . IsNotNull ( nameof ( name ) , name ) ;
@@ -476,20 +449,18 @@ public async Task<string> SetVariableAsync(int variableContainerReferenceId, str
476
449
{
477
450
_logger . LogTrace ( $ "Setting variable '{ name } ' using conversion to value: { expressionResult ?? "<null>" } ") ;
478
451
479
- psVariable . Value = await _executionService . ExecuteDelegateAsync < object > (
452
+ psVariable . Value = await _executionService . ExecuteDelegateAsync (
480
453
"PS debugger argument converter" ,
481
454
ExecutionOptions . Default ,
482
- ( pwsh , cancellationToken ) =>
455
+ ( pwsh , _ ) =>
483
456
{
484
457
var engineIntrinsics = ( EngineIntrinsics ) pwsh . Runspace . SessionStateProxy . GetVariable ( "ExecutionContext" ) ;
485
458
486
459
// TODO: This is almost (but not quite) the same as LanguagePrimitives.Convert(), which does not require the pipeline thread.
487
460
// We should investigate changing it.
488
461
return argTypeConverterAttr . Transform ( engineIntrinsics , expressionResult ) ;
489
-
490
462
} ,
491
463
CancellationToken . None ) . ConfigureAwait ( false ) ;
492
-
493
464
}
494
465
else
495
466
{
@@ -634,17 +605,14 @@ private async Task FetchStackFramesAndVariablesAsync(string scriptNameOverride)
634
605
nextVariableId = VariableDetailsBase . FirstVariableId ;
635
606
variables = new List < VariableDetailsBase >
636
607
{
637
-
638
608
// Create a dummy variable for index 0, should never see this.
639
609
new VariableDetails ( "Dummy" , null )
640
610
} ;
641
611
642
612
// Must retrieve in order of broadest to narrowest scope for efficient
643
613
// deduplication: global, script, local.
644
614
globalScopeVariables = await FetchVariableContainerAsync ( VariableContainerDetails . GlobalScopeName ) . ConfigureAwait ( false ) ;
645
-
646
615
scriptScopeVariables = await FetchVariableContainerAsync ( VariableContainerDetails . ScriptScopeName ) . ConfigureAwait ( false ) ;
647
-
648
616
localScopeVariables = await FetchVariableContainerAsync ( VariableContainerDetails . LocalScopeName ) . ConfigureAwait ( false ) ;
649
617
650
618
await FetchStackFramesAsync ( scriptNameOverride ) . ConfigureAwait ( false ) ;
@@ -662,9 +630,7 @@ private Task<VariableContainerDetails> FetchVariableContainerAsync(string scope)
662
630
663
631
private async Task < VariableContainerDetails > FetchVariableContainerAsync ( string scope , bool autoVarsOnly )
664
632
{
665
- PSCommand psCommand = new PSCommand ( )
666
- . AddCommand ( "Get-Variable" )
667
- . AddParameter ( "Scope" , scope ) ;
633
+ PSCommand psCommand = new PSCommand ( ) . AddCommand ( "Get-Variable" ) . AddParameter ( "Scope" , scope ) ;
668
634
669
635
var scopeVariableContainer = new VariableContainerDetails ( nextVariableId ++ , "Scope: " + scope ) ;
670
636
variables . Add ( scopeVariableContainer ) ;
@@ -674,17 +640,13 @@ private async Task<VariableContainerDetails> FetchVariableContainerAsync(string
674
640
{
675
641
results = await _executionService . ExecutePSCommandAsync < PSObject > ( psCommand , CancellationToken . None ) . ConfigureAwait ( false ) ;
676
642
}
677
- catch ( CmdletInvocationException ex )
643
+ // It's possible to be asked to run `Get-Variable -Scope N` where N is a number that
644
+ // exceeds the available scopes. In this case, the command throws this exception, but
645
+ // there's nothing we can do about it, nor can we know the number of scopes that exist,
646
+ // and we shouldn't crash the debugger, so we just return no results instead. All other
647
+ // exceptions should be thrown again.
648
+ catch ( CmdletInvocationException ex ) when ( ex . ErrorRecord . CategoryInfo . Reason . Equals ( "PSArgumentOutOfRangeException" ) )
678
649
{
679
- // It's possible to be asked to run `Get-Variable -Scope N` where N is a number that
680
- // exceeds the available scopes. In this case, the command throws this exception,
681
- // but there's nothing we can do about it, nor can we know the number of scopes that
682
- // exist, and we shouldn't crash the debugger, so we just return no results instead.
683
- // All other exceptions should be thrown again.
684
- if ( ! ex . ErrorRecord . CategoryInfo . Reason . Equals ( "PSArgumentOutOfRangeException" ) )
685
- {
686
- throw ;
687
- }
688
650
results = null ;
689
651
}
690
652
@@ -749,8 +711,8 @@ private static bool ShouldAddAsVariable(VariableInfo variableInfo)
749
711
{
750
712
// Filter built-in constant or readonly variables like $true, $false, $null, etc.
751
713
ScopedItemOptions variableScope = variableInfo . Variable . Options ;
752
- var constantAllScope = ScopedItemOptions . AllScope | ScopedItemOptions . Constant ;
753
- var readonlyAllScope = ScopedItemOptions . AllScope | ScopedItemOptions . ReadOnly ;
714
+ const ScopedItemOptions constantAllScope = ScopedItemOptions . AllScope | ScopedItemOptions . Constant ;
715
+ const ScopedItemOptions readonlyAllScope = ScopedItemOptions . AllScope | ScopedItemOptions . ReadOnly ;
754
716
if ( ( ( variableScope & constantAllScope ) == constantAllScope )
755
717
|| ( ( variableScope & readonlyAllScope ) == readonlyAllScope ) )
756
718
{
@@ -909,11 +871,11 @@ private async Task FetchStackFramesAsync(string scriptNameOverride)
909
871
stackFrameDetailsEntry . ScriptPath = scriptNameOverride ;
910
872
}
911
873
else if ( isOnRemoteMachine
912
- && remoteFileManager is not null
874
+ && _remoteFileManager is not null
913
875
&& ! string . Equals ( stackFrameScriptPath , StackFrameDetails . NoFileScriptPath ) )
914
876
{
915
877
stackFrameDetailsEntry . ScriptPath =
916
- remoteFileManager . GetMappedPath ( stackFrameScriptPath , _psesHost . CurrentRunspace ) ;
878
+ _remoteFileManager . GetMappedPath ( stackFrameScriptPath , _psesHost . CurrentRunspace ) ;
917
879
}
918
880
919
881
stackFrameDetailList . Add ( stackFrameDetailsEntry ) ;
@@ -956,7 +918,7 @@ internal async void OnDebuggerStopAsync(object sender, DebuggerStopEventArgs e)
956
918
string localScriptPath = e . InvocationInfo . ScriptName ;
957
919
958
920
// If there's no ScriptName, get the "list" of the current source
959
- if ( remoteFileManager is not null && string . IsNullOrEmpty ( localScriptPath ) )
921
+ if ( _remoteFileManager is not null && string . IsNullOrEmpty ( localScriptPath ) )
960
922
{
961
923
// Get the current script listing and create the buffer
962
924
PSCommand command = new PSCommand ( ) . AddScript ( $ "list 1 { int . MaxValue } ") ;
@@ -977,7 +939,7 @@ await _executionService.ExecutePSCommandAsync<PSObject>(
977
939
. Where ( s => s is not null ) ) ;
978
940
979
941
temporaryScriptListingPath =
980
- remoteFileManager . CreateTemporaryFile (
942
+ _remoteFileManager . CreateTemporaryFile (
981
943
$ "[{ _psesHost . CurrentRunspace . SessionDetails . ComputerName } ] { TemporaryScriptFileName } ",
982
944
scriptListing ,
983
945
_psesHost . CurrentRunspace ) ;
@@ -1000,19 +962,18 @@ await _executionService.ExecutePSCommandAsync<PSObject>(
1000
962
// If this is a remote connection and the debugger stopped at a line
1001
963
// in a script file, get the file contents
1002
964
if ( _psesHost . CurrentRunspace . IsOnRemoteMachine
1003
- && remoteFileManager is not null
965
+ && _remoteFileManager is not null
1004
966
&& ! noScriptName )
1005
967
{
1006
968
localScriptPath =
1007
- await remoteFileManager . FetchRemoteFileAsync (
969
+ await _remoteFileManager . FetchRemoteFileAsync (
1008
970
e . InvocationInfo . ScriptName ,
1009
971
_psesHost . CurrentRunspace ) . ConfigureAwait ( false ) ;
1010
972
}
1011
973
1012
974
if ( stackFrameDetails . Length > 0 )
1013
975
{
1014
976
// Augment the top stack frame with details from the stop event
1015
-
1016
977
if ( invocationTypeScriptPositionProperty . GetValue ( e . InvocationInfo ) is IScriptExtent scriptExtent )
1017
978
{
1018
979
stackFrameDetails [ 0 ] . StartLineNumber = scriptExtent . StartLineNumber ;
@@ -1054,9 +1015,9 @@ private void OnBreakpointUpdated(object sender, BreakpointUpdatedEventArgs e)
1054
1015
// TODO: This could be either a path or a script block!
1055
1016
string scriptPath = lineBreakpoint . Script ;
1056
1017
if ( _psesHost . CurrentRunspace . IsOnRemoteMachine
1057
- && remoteFileManager is not null )
1018
+ && _remoteFileManager is not null )
1058
1019
{
1059
- string mappedPath = remoteFileManager . GetMappedPath ( scriptPath , _psesHost . CurrentRunspace ) ;
1020
+ string mappedPath = _remoteFileManager . GetMappedPath ( scriptPath , _psesHost . CurrentRunspace ) ;
1060
1021
1061
1022
if ( mappedPath is null )
1062
1023
{
0 commit comments