@@ -36,8 +36,8 @@ namespace Microsoft.NodejsTools.Debugger {
3636 /// <summary>
3737 /// Handles all interactions with a Node process which is being debugged.
3838 /// </summary>
39- sealed class NodeDebugger : IDisposable {
40- public readonly int MainThreadId = 1 ;
39+ internal sealed class NodeDebugger : IDisposable {
40+ public const int MainThreadId = 1 ;
4141 private readonly Dictionary < int , NodeBreakpointBinding > _breakpointBindings = new Dictionary < int , NodeBreakpointBinding > ( ) ;
4242 private readonly IDebuggerClient _client ;
4343 private readonly IDebuggerConnection _connection ;
@@ -76,6 +76,13 @@ private NodeDebugger() {
7676 _fileNameMapper = new LocalFileNameMapper ( ) ;
7777 }
7878
79+ public NodeDebugger ( Uri debuggerEndpointUri , int id )
80+ : this ( ) {
81+ _debuggerEndpointUri = debuggerEndpointUri ;
82+ _id = id ;
83+ _attached = true ;
84+ }
85+
7986 public NodeDebugger (
8087 string exe ,
8188 string script ,
@@ -87,71 +94,102 @@ public NodeDebugger(
8794 bool createNodeWindow = true )
8895 : this ( ) {
8996 // Select debugger port for a local connection
90- ushort debuggerPortOrDefault = NodejsConstants . DefaultDebuggerPort ;
91- if ( debuggerPort != null ) {
92- debuggerPortOrDefault = debuggerPort . Value ;
93- } else {
94- List < int > activeConnections =
95- ( from listener in IPGlobalProperties . GetIPGlobalProperties ( ) . GetActiveTcpListeners ( )
96- select listener . Port ) . ToList ( ) ;
97- if ( activeConnections . Contains ( debuggerPortOrDefault ) ) {
98- debuggerPortOrDefault = ( ushort ) Enumerable . Range ( new Random ( ) . Next ( 5859 , 6000 ) , 60000 ) . Except ( activeConnections ) . First ( ) ;
99- }
97+ var debuggerPortOrDefault = debuggerPort ?? GetDebuggerPort ( ) ;
98+ _debuggerEndpointUri = new UriBuilder { Scheme = "tcp" , Host = "localhost" , Port = debuggerPortOrDefault } . Uri ;
99+
100+ _process = StartNodeProcessWithDebug ( exe , script , dir , env , interpreterOptions , debugOptions , debuggerPortOrDefault , createNodeWindow ) ;
101+ }
102+
103+ private static ushort GetDebuggerPort ( ) {
104+ var debuggerPortOrDefault = NodejsConstants . DefaultDebuggerPort ;
105+
106+ var activeConnections = ( from listener in IPGlobalProperties . GetIPGlobalProperties ( ) . GetActiveTcpListeners ( )
107+ select listener . Port ) . ToList ( ) ;
108+
109+ if ( activeConnections . Contains ( debuggerPortOrDefault ) ) {
110+ debuggerPortOrDefault = ( ushort ) Enumerable . Range ( new Random ( ) . Next ( 5859 , 6000 ) , 60000 ) . Except ( activeConnections ) . First ( ) ;
100111 }
101112
102- _debuggerEndpointUri = new UriBuilder { Scheme = "tcp" , Host = "localhost" , Port = debuggerPortOrDefault } . Uri ;
113+ return debuggerPortOrDefault ;
114+ }
115+
116+ public static NodeProcess StartNodeProcessWithDebug (
117+ string exe ,
118+ string script ,
119+ string dir ,
120+ string env ,
121+ string interpreterOptions ,
122+ NodeDebugOptions debugOptions ,
123+ ushort ? debuggerPort = null ,
124+ bool createNodeWindow = true ) {
125+ // Select debugger port for a local connection
126+ var debuggerPortOrDefault = debuggerPort ?? GetDebuggerPort ( ) ;
127+
128+ // Node usage: node [options] [ -e script | script.js ] [arguments]
129+ var allArgs = $ "--debug-brk={ debuggerPortOrDefault } --nolazy { interpreterOptions } \" { CommonUtils . UnquotePath ( script ) } \" "; /* unquote the path so we can safely add quotes */
130+
131+ return StartNodeProcess ( exe , dir , env , debugOptions , debuggerPortOrDefault , allArgs , createNodeWindow ) ;
132+ }
133+
134+ public static NodeProcess StartNodeProcessWithInspect (
135+ string exe ,
136+ string script ,
137+ string dir ,
138+ string env ,
139+ string interpreterOptions ,
140+ NodeDebugOptions debugOptions ,
141+ ushort ? debuggerPort = null ,
142+ bool createNodeWindow = true ) {
143+ // Select debugger port for a local connection
144+ var debuggerPortOrDefault = debuggerPort ?? GetDebuggerPort ( ) ;
103145
104146 // Node usage: node [options] [ -e script | script.js ] [arguments]
105- string allArgs = string . Format ( CultureInfo . InvariantCulture ,
106- "--debug-brk={0} --nolazy {1} {2}" ,
107- debuggerPortOrDefault ,
108- interpreterOptions ,
109- script
110- ) ;
147+ var allArgs = $ "--inspect={ debuggerPortOrDefault } --debug-brk --nolazy { interpreterOptions } \" { CommonUtils . UnquotePath ( script ) } \" "; /* unquote the path so we can safely add quotes */
111148
149+ return StartNodeProcess ( exe , dir , env , debugOptions , debuggerPortOrDefault , allArgs , createNodeWindow ) ;
150+ }
151+
152+ // starts the nodeprocess in debug mode without hooking up our debugger, this way we can attach the WebKit debugger as a next step.
153+ private static NodeProcess StartNodeProcess (
154+ string exe ,
155+ string dir ,
156+ string env ,
157+ NodeDebugOptions
158+ debugOptions ,
159+ ushort debuggerPortOrDefault ,
160+ string allArgs ,
161+ bool createNodeWindow ) {
112162 var psi = new ProcessStartInfo ( exe , allArgs ) {
113163 CreateNoWindow = ! createNodeWindow ,
114164 WorkingDirectory = dir ,
115165 UseShellExecute = false
116166 } ;
117167
118168 if ( env != null ) {
119- string [ ] envValues = env . Split ( '\0 ' ) ;
120- foreach ( string curValue in envValues ) {
121- string [ ] nameValue = curValue . Split ( new [ ] { '=' } , 2 ) ;
122- if ( nameValue . Length == 2 && ! String . IsNullOrWhiteSpace ( nameValue [ 0 ] ) ) {
169+ var envValues = env . Split ( '\0 ' ) ;
170+ foreach ( var curValue in envValues ) {
171+ var nameValue = curValue . Split ( new [ ] { '=' } , 2 ) ;
172+ if ( nameValue . Length == 2 && ! string . IsNullOrWhiteSpace ( nameValue [ 0 ] ) ) {
123173 psi . EnvironmentVariables [ nameValue [ 0 ] ] = nameValue [ 1 ] ;
124174 }
125175 }
126176 }
127177
128- _process = new NodeProcess (
178+ return new NodeProcess (
129179 psi ,
130- debugOptions . HasFlag ( NodeDebugOptions . WaitOnAbnormalExit ) ,
131- debugOptions . HasFlag ( NodeDebugOptions . WaitOnNormalExit ) ,
132- true ) ;
133- }
134-
135- public NodeDebugger ( Uri debuggerEndpointUri , int id )
136- : this ( ) {
137- _debuggerEndpointUri = debuggerEndpointUri ;
138- _id = id ;
139- _attached = true ;
180+ waitOnAbnormal : debugOptions . HasFlag ( NodeDebugOptions . WaitOnAbnormalExit ) ,
181+ waitOnNormal : debugOptions . HasFlag ( NodeDebugOptions . WaitOnNormalExit ) ,
182+ enableRaisingEvents : true ,
183+ debuggerPort : debuggerPortOrDefault ) ;
140184 }
141185
142186 #region Public Process API
143187
144- public int Id {
145- get { return _id != null ? _id . Value : _process . Id ; }
146- }
188+ public int Id => _id != null ? _id . Value : _process . Id ;
147189
148- private NodeThread MainThread {
149- get { return _threads [ MainThreadId ] ; }
150- }
190+ private NodeThread MainThread => _threads [ MainThreadId ] ;
151191
152- public bool HasExited {
153- get { return ! _connection . Connected ; }
154- }
192+ public bool HasExited => ! _connection . Connected ;
155193
156194 /// <summary>
157195 /// Gets or sets a value indicating whether executed remote debugging process.
@@ -236,7 +274,7 @@ public async Task BreakAllAsync() {
236274 // We need to get the backtrace before we break, so we request the backtrace
237275 // and follow up with firing the appropriate event for the break
238276 tokenSource = new CancellationTokenSource ( _timeout ) ;
239- bool running = await PerformBacktraceAsync ( tokenSource . Token ) . ConfigureAwait ( false ) ;
277+ var running = await PerformBacktraceAsync ( tokenSource . Token ) . ConfigureAwait ( false ) ;
240278 Debug . Assert ( ! running ) ;
241279
242280 // Fallback to firing step complete event
@@ -424,16 +462,12 @@ public void ClearExceptionTreatment() {
424462 /// <summary>
425463 /// Gets a next command identifier.
426464 /// </summary>
427- private int CommandId {
428- get { return Interlocked . Increment ( ref _commandId ) ; }
429- }
465+ private int CommandId => Interlocked . Increment ( ref _commandId ) ;
430466
431467 /// <summary>
432468 /// Gets a source mapper.
433469 /// </summary>
434- public SourceMapper SourceMapper {
435- get { return _sourceMapper ; }
436- }
470+ public SourceMapper SourceMapper => _sourceMapper ;
437471
438472 /// <summary>
439473 /// Gets or sets a file name mapper.
@@ -650,7 +684,7 @@ private async Task<bool> ProcessBreakpointBreakAsync(
650684 }
651685
652686 SetBreakpointCommand result = await SetBreakpointAsync ( breakpoint , cancellationToken : cancellationToken ) . ConfigureAwait ( false ) ;
653-
687+
654688 // Treat rebound breakpoint binding as fully bound
655689 NodeBreakpointBinding reboundbreakpointBinding = CreateBreakpointBinding ( breakpoint , result . BreakpointId , result . ScriptId , breakpoint . GetPosition ( SourceMapper ) . FileName , result . Line , result . Column , true ) ;
656690 HandleBindBreakpointSuccess ( reboundbreakpointBinding , breakpoint ) ;
@@ -712,7 +746,7 @@ private void OnExceptionEvent(object sender, ExceptionEventArgs args) {
712746
713747 var lookupCommand = new LookupCommand ( CommandId , _resultFactory , new [ ] { exception . ErrorNumber . Value } ) ;
714748 string errorCodeFromLookup = null ;
715-
749+
716750 if ( await TrySendRequestAsync ( lookupCommand ) . ConfigureAwait ( false ) ) {
717751 errorCodeFromLookup = lookupCommand . Results [ errorNumber ] [ 0 ] . StringValue ;
718752 _errorCodes [ errorNumber ] = errorCodeFromLookup ;
@@ -1049,7 +1083,7 @@ internal async Task UpdateBreakpointBindingAsync(
10491083
10501084 internal async Task < int ? > GetBreakpointHitCountAsync ( int breakpointId , CancellationToken cancellationToken = new CancellationToken ( ) ) {
10511085 var listBreakpointsCommand = new ListBreakpointsCommand ( CommandId ) ;
1052-
1086+
10531087 int hitCount ;
10541088 if ( await TrySendRequestAsync ( listBreakpointsCommand , cancellationToken ) . ConfigureAwait ( false ) &&
10551089 listBreakpointsCommand . Breakpoints . TryGetValue ( breakpointId , out hitCount ) ) {
@@ -1160,7 +1194,7 @@ internal async Task<NodeEvaluationResult> SetVariableValueAsync(
11601194 return false ;
11611195 }
11621196 }
1163-
1197+
11641198 #endregion
11651199
11661200 #region Debugging Events
@@ -1211,7 +1245,7 @@ private bool GetOrAddModule(NodeModule module, out NodeModule value, NodeStackFr
12111245 javaScriptFileName = FileNameMapper . GetLocalFileName ( javaScriptFileName ) ;
12121246
12131247 // Try to get mapping for JS file
1214- if ( stackFrame != null ) {
1248+ if ( stackFrame != null ) {
12151249 line = stackFrame . Line ;
12161250 column = stackFrame . Column ;
12171251 }
@@ -1255,8 +1289,6 @@ public NodeModule GetModuleForFilePath(string filePath) {
12551289 internal void Close ( ) {
12561290 }
12571291
1258-
1259-
12601292 #region IDisposable
12611293
12621294 public void Dispose ( ) {
0 commit comments