Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Common/Product/SharedProject/CommonProjectNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1549,7 +1549,7 @@ protected override ConfigProvider CreateConfigProvider() {
/// Returns resolved value of the current working directory property.
/// </summary>
public string GetWorkingDirectory() {
string workDir = CommonUtils.UnquotePath(GetProjectProperty(CommonConstants.WorkingDirectory, resetCache: false));
var workDir = CommonUtils.UnquotePath(GetProjectProperty(CommonConstants.WorkingDirectory, resetCache: false));
return CommonUtils.GetAbsoluteDirectoryPath(ProjectHome, workDir);
}

Expand Down
8 changes: 7 additions & 1 deletion Common/Product/SharedProject/CommonUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ internal static Uri MakeUri(string path, bool isDirectory, UriKind kind, string
}

return new Uri(path, kind);

} catch (UriFormatException ex) {
throw new ArgumentException("Path was invalid", throwParameterName, ex);
} catch (ArgumentException ex) {
Expand All @@ -62,6 +61,13 @@ public static string NormalizePath(string path) {
return null;
}

const string MdhaPrefix = "mdha:";

// webkit debugger prepends with 'mdha'
if (path.StartsWith(MdhaPrefix, StringComparison.OrdinalIgnoreCase)) {
path = path.Substring(MdhaPrefix.Length);
}

var uri = MakeUri(path, false, UriKind.RelativeOrAbsolute);
if (uri.IsAbsoluteUri) {
if (uri.IsFile) {
Expand Down
1 change: 0 additions & 1 deletion Common/Product/SharedProject/ProjectNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4496,7 +4496,6 @@ public virtual int IsDocumentInProject(string mkDoc, out int found, VSDOCUMENTPR
}

return VSConstants.S_OK;

}

protected virtual bool IncludeNonMemberItemInProject(HierarchyNode node) {
Expand Down
17 changes: 4 additions & 13 deletions Nodejs/Product/Nodejs/Debugger/DebugEngine/AD7Engine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,6 @@ public sealed class AD7Engine : IDebugEngine2, IDebugEngineLaunch2, IDebugProgra
/// </summary>
public const string WaitOnNormalExitSetting = "WAIT_ON_NORMAL_EXIT";

/// <summary>
/// Specifies if the output should be redirected to the visual studio output window.
/// </summary>
public const string RedirectOutputSetting = "REDIRECT_OUTPUT";

/// <summary>
/// Specifies options which should be passed to the Node runtime before the script. If
/// the interpreter options should include a semicolon then it should be escaped as a double
Expand Down Expand Up @@ -517,11 +512,6 @@ int IDebugEngineLaunch2.LaunchSuspended(string pszServer, IDebugPort2 port, stri
debugOptions |= NodeDebugOptions.WaitOnNormalExit;
}
break;
case RedirectOutputSetting:
if (Boolean.TryParse(setting[1], out value) && value) {
debugOptions |= NodeDebugOptions.RedirectOutput;
}
break;
case DirMappingSetting:
string[] dirs = setting[1].Split('|');
if (dirs.Length == 2) {
Expand Down Expand Up @@ -565,9 +555,10 @@ int IDebugEngineLaunch2.LaunchSuspended(string pszServer, IDebugPort2 port, stri

AttachEvents(_process);

var adProcessId = new AD_PROCESS_ID();
adProcessId.ProcessIdType = (uint)enum_AD_PROCESS_ID.AD_PROCESS_ID_SYSTEM;
adProcessId.dwProcessId = (uint)_process.Id;
var adProcessId = new AD_PROCESS_ID() {
ProcessIdType = (uint)enum_AD_PROCESS_ID.AD_PROCESS_ID_SYSTEM,
dwProcessId = (uint)_process.Id
};

EngineUtils.RequireOk(port.GetProcess(adProcessId, out process));
LiveLogger.WriteLine("AD7Engine LaunchSuspended returning S_OK");
Expand Down
2 changes: 1 addition & 1 deletion Nodejs/Product/Nodejs/Debugger/NodeConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
//*********************************************************//

namespace Microsoft.NodejsTools.Debugger {
sealed class NodeConstants {
internal sealed class NodeConstants {
public const string ScriptWrapBegin = "(function (exports, require, module, __filename, __dirname) { ";
public const string ScriptWrapEnd = "\n});";
}
Expand Down
12 changes: 1 addition & 11 deletions Nodejs/Product/Nodejs/Debugger/NodeDebugOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

namespace Microsoft.NodejsTools.Debugger {
[Flags]
enum NodeDebugOptions {
internal enum NodeDebugOptions {
None,
/// <summary>
/// Passing this flag to the debugger will cause it to wait for input on an abnormal (non-zero)
Expand All @@ -29,15 +29,5 @@ enum NodeDebugOptions {
/// Passing this flag to the debugger will cause it to wait for input on a normal (zero) exit code.
/// </summary>
WaitOnNormalExit = 0x02,
/// <summary>
/// Passing this flag will cause output to standard out to be redirected via the debugger
/// so it can be outputted in the Visual Studio debug output window.
/// </summary>
RedirectOutput = 0x04,

/// <summary>
/// Set if you do not want to create a window
/// </summary>
CreateNoWindow = 0x40
}
}
146 changes: 89 additions & 57 deletions Nodejs/Product/Nodejs/Debugger/NodeDebugger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ namespace Microsoft.NodejsTools.Debugger {
/// <summary>
/// Handles all interactions with a Node process which is being debugged.
/// </summary>
sealed class NodeDebugger : IDisposable {
public readonly int MainThreadId = 1;
internal sealed class NodeDebugger : IDisposable {
public const int MainThreadId = 1;
private readonly Dictionary<int, NodeBreakpointBinding> _breakpointBindings = new Dictionary<int, NodeBreakpointBinding>();
private readonly IDebuggerClient _client;
private readonly IDebuggerConnection _connection;
Expand Down Expand Up @@ -76,6 +76,13 @@ private NodeDebugger() {
_fileNameMapper = new LocalFileNameMapper();
}

public NodeDebugger(Uri debuggerEndpointUri, int id)
: this() {
_debuggerEndpointUri = debuggerEndpointUri;
_id = id;
_attached = true;
}

public NodeDebugger(
string exe,
string script,
Expand All @@ -87,71 +94,102 @@ public NodeDebugger(
bool createNodeWindow = true)
: this() {
// Select debugger port for a local connection
ushort debuggerPortOrDefault = NodejsConstants.DefaultDebuggerPort;
if (debuggerPort != null) {
debuggerPortOrDefault = debuggerPort.Value;
} else {
List<int> activeConnections =
(from listener in IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners()
select listener.Port).ToList();
if (activeConnections.Contains(debuggerPortOrDefault)) {
debuggerPortOrDefault = (ushort)Enumerable.Range(new Random().Next(5859, 6000), 60000).Except(activeConnections).First();
}
var debuggerPortOrDefault = debuggerPort ?? GetDebuggerPort();
_debuggerEndpointUri = new UriBuilder { Scheme = "tcp", Host = "localhost", Port = debuggerPortOrDefault }.Uri;

_process = StartNodeProcessWithDebug(exe, script, dir, env, interpreterOptions, debugOptions, debuggerPortOrDefault, createNodeWindow);
}

private static ushort GetDebuggerPort() {
var debuggerPortOrDefault = NodejsConstants.DefaultDebuggerPort;

var activeConnections = (from listener in IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners()
select listener.Port).ToList();

if (activeConnections.Contains(debuggerPortOrDefault)) {
debuggerPortOrDefault = (ushort)Enumerable.Range(new Random().Next(5859, 6000), 60000).Except(activeConnections).First();
}

_debuggerEndpointUri = new UriBuilder { Scheme = "tcp", Host = "localhost", Port = debuggerPortOrDefault }.Uri;
return debuggerPortOrDefault;
}

public static NodeProcess StartNodeProcessWithDebug(
string exe,
string script,
string dir,
string env,
string interpreterOptions,
NodeDebugOptions debugOptions,
ushort? debuggerPort = null,
bool createNodeWindow = true) {
// Select debugger port for a local connection
var debuggerPortOrDefault = debuggerPort ?? GetDebuggerPort();

// Node usage: node [options] [ -e script | script.js ] [arguments]
var allArgs = $"--debug-brk={debuggerPortOrDefault} --nolazy {interpreterOptions} \"{CommonUtils.UnquotePath(script)}\""; /* unquote the path so we can safely add quotes */

return StartNodeProcess(exe, dir, env, debugOptions, debuggerPortOrDefault, allArgs, createNodeWindow);
}

public static NodeProcess StartNodeProcessWithInspect(
string exe,
string script,
string dir,
string env,
string interpreterOptions,
NodeDebugOptions debugOptions,
ushort? debuggerPort = null,
bool createNodeWindow = true) {
// Select debugger port for a local connection
var debuggerPortOrDefault = debuggerPort ?? GetDebuggerPort();

// Node usage: node [options] [ -e script | script.js ] [arguments]
string allArgs = string.Format(CultureInfo.InvariantCulture,
"--debug-brk={0} --nolazy {1} {2}",
debuggerPortOrDefault,
interpreterOptions,
script
);
var allArgs = $"--inspect={debuggerPortOrDefault} --debug-brk --nolazy {interpreterOptions} \"{CommonUtils.UnquotePath(script)}\""; /* unquote the path so we can safely add quotes */

return StartNodeProcess(exe, dir, env, debugOptions, debuggerPortOrDefault, allArgs, createNodeWindow);
}

// starts the nodeprocess in debug mode without hooking up our debugger, this way we can attach the WebKit debugger as a next step.
private static NodeProcess StartNodeProcess(
string exe,
string dir,
string env,
NodeDebugOptions
debugOptions,
ushort debuggerPortOrDefault,
string allArgs,
bool createNodeWindow) {
var psi = new ProcessStartInfo(exe, allArgs) {
CreateNoWindow = !createNodeWindow,
WorkingDirectory = dir,
UseShellExecute = false
};

if (env != null) {
string[] envValues = env.Split('\0');
foreach (string curValue in envValues) {
string[] nameValue = curValue.Split(new[] { '=' }, 2);
if (nameValue.Length == 2 && !String.IsNullOrWhiteSpace(nameValue[0])) {
var envValues = env.Split('\0');
foreach (var curValue in envValues) {
var nameValue = curValue.Split(new[] { '=' }, 2);
if (nameValue.Length == 2 && !string.IsNullOrWhiteSpace(nameValue[0])) {
psi.EnvironmentVariables[nameValue[0]] = nameValue[1];
}
}
}

_process = new NodeProcess(
return new NodeProcess(
psi,
debugOptions.HasFlag(NodeDebugOptions.WaitOnAbnormalExit),
debugOptions.HasFlag(NodeDebugOptions.WaitOnNormalExit),
true);
}

public NodeDebugger(Uri debuggerEndpointUri, int id)
: this() {
_debuggerEndpointUri = debuggerEndpointUri;
_id = id;
_attached = true;
waitOnAbnormal: debugOptions.HasFlag(NodeDebugOptions.WaitOnAbnormalExit),
waitOnNormal: debugOptions.HasFlag(NodeDebugOptions.WaitOnNormalExit),
enableRaisingEvents: true,
debuggerPort: debuggerPortOrDefault);
}

#region Public Process API

public int Id {
get { return _id != null ? _id.Value : _process.Id; }
}
public int Id => _id != null ? _id.Value : _process.Id;

private NodeThread MainThread {
get { return _threads[MainThreadId]; }
}
private NodeThread MainThread => _threads[MainThreadId];

public bool HasExited {
get { return !_connection.Connected; }
}
public bool HasExited => !_connection.Connected;

/// <summary>
/// Gets or sets a value indicating whether executed remote debugging process.
Expand Down Expand Up @@ -236,7 +274,7 @@ public async Task BreakAllAsync() {
// We need to get the backtrace before we break, so we request the backtrace
// and follow up with firing the appropriate event for the break
tokenSource = new CancellationTokenSource(_timeout);
bool running = await PerformBacktraceAsync(tokenSource.Token).ConfigureAwait(false);
var running = await PerformBacktraceAsync(tokenSource.Token).ConfigureAwait(false);
Debug.Assert(!running);

// Fallback to firing step complete event
Expand Down Expand Up @@ -424,16 +462,12 @@ public void ClearExceptionTreatment() {
/// <summary>
/// Gets a next command identifier.
/// </summary>
private int CommandId {
get { return Interlocked.Increment(ref _commandId); }
}
private int CommandId => Interlocked.Increment(ref _commandId);

/// <summary>
/// Gets a source mapper.
/// </summary>
public SourceMapper SourceMapper {
get { return _sourceMapper; }
}
public SourceMapper SourceMapper => _sourceMapper;

/// <summary>
/// Gets or sets a file name mapper.
Expand Down Expand Up @@ -650,7 +684,7 @@ private async Task<bool> ProcessBreakpointBreakAsync(
}

SetBreakpointCommand result = await SetBreakpointAsync(breakpoint, cancellationToken: cancellationToken).ConfigureAwait(false);

// Treat rebound breakpoint binding as fully bound
NodeBreakpointBinding reboundbreakpointBinding = CreateBreakpointBinding(breakpoint, result.BreakpointId, result.ScriptId, breakpoint.GetPosition(SourceMapper).FileName, result.Line, result.Column, true);
HandleBindBreakpointSuccess(reboundbreakpointBinding, breakpoint);
Expand Down Expand Up @@ -712,7 +746,7 @@ private void OnExceptionEvent(object sender, ExceptionEventArgs args) {

var lookupCommand = new LookupCommand(CommandId, _resultFactory, new[] { exception.ErrorNumber.Value });
string errorCodeFromLookup = null;

if (await TrySendRequestAsync(lookupCommand).ConfigureAwait(false)) {
errorCodeFromLookup = lookupCommand.Results[errorNumber][0].StringValue;
_errorCodes[errorNumber] = errorCodeFromLookup;
Expand Down Expand Up @@ -1049,7 +1083,7 @@ internal async Task UpdateBreakpointBindingAsync(

internal async Task<int?> GetBreakpointHitCountAsync(int breakpointId, CancellationToken cancellationToken = new CancellationToken()) {
var listBreakpointsCommand = new ListBreakpointsCommand(CommandId);

int hitCount;
if (await TrySendRequestAsync(listBreakpointsCommand, cancellationToken).ConfigureAwait(false) &&
listBreakpointsCommand.Breakpoints.TryGetValue(breakpointId, out hitCount)) {
Expand Down Expand Up @@ -1160,7 +1194,7 @@ internal async Task<NodeEvaluationResult> SetVariableValueAsync(
return false;
}
}

#endregion

#region Debugging Events
Expand Down Expand Up @@ -1211,7 +1245,7 @@ private bool GetOrAddModule(NodeModule module, out NodeModule value, NodeStackFr
javaScriptFileName = FileNameMapper.GetLocalFileName(javaScriptFileName);

// Try to get mapping for JS file
if(stackFrame != null) {
if (stackFrame != null) {
line = stackFrame.Line;
column = stackFrame.Column;
}
Expand Down Expand Up @@ -1255,8 +1289,6 @@ public NodeModule GetModuleForFilePath(string filePath) {
internal void Close() {
}



#region IDisposable

public void Dispose() {
Expand Down
2 changes: 1 addition & 1 deletion Nodejs/Product/Nodejs/Debugger/NodeEvaluationResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ namespace Microsoft.NodejsTools.Debugger {
/// <summary>
/// Represents the result of an evaluation of an expression against a given stack frame.
/// </summary>
class NodeEvaluationResult {
internal class NodeEvaluationResult {
private readonly Regex _stringLengthExpression = new Regex(@"\.\.\. \(length: ([0-9]+)\)$", RegexOptions.Compiled);

/// <summary>
Expand Down
Loading