Properly parse jsshell and d8 stack frames. It turns out jsshell and d8 can use native paths where Firefox and Chrome usually use URLs. This CL accommodates that. R=rnystrom@google.com BUG=14897 Review URL: https://codereview.chromium.org//63603005 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/stack_trace@30079 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/pkgs/stack_trace/lib/src/frame.dart b/pkgs/stack_trace/lib/src/frame.dart index b15f651..3bead03 100644 --- a/pkgs/stack_trace/lib/src/frame.dart +++ b/pkgs/stack_trace/lib/src/frame.dart
@@ -136,14 +136,17 @@ // V8 stack frames can be in two forms. if (match[2] != null) { - // The first form looks like " at FUNCTION (URI:LINE:COL)" - var uri = Uri.parse(match[2]); + // The first form looks like " at FUNCTION (PATH:LINE:COL)". PATH is + // usually an absolute URL, but it can be a path if the stack frame came + // from d8. + var uri = _uriOrPathToUri(match[2]); var member = match[1].replaceAll("<anonymous>", "<fn>"); return new Frame(uri, int.parse(match[3]), int.parse(match[4]), member); } else { - // The second form looks like " at URI:LINE:COL", and is used for - // anonymous functions. - var uri = Uri.parse(match[5]); + // The second form looks like " at PATH:LINE:COL", and is used for + // anonymous functions. PATH is usually an absolute URL, but it can be a + // path if the stack frame came from d8. + var uri = _uriOrPathToUri(match[5]); return new Frame(uri, int.parse(match[6]), int.parse(match[7]), "<fn>"); } } @@ -162,7 +165,8 @@ "Couldn't parse Firefox stack trace line '$frame'."); } - var uri = Uri.parse(match[3]); + // Normally this is a URI, but in a jsshell trace it can be a path. + var uri = _uriOrPathToUri(match[3]); var member = match[1]; member += new List.filled('/'.allMatches(match[2]).length, ".<fn>").join(); if (member == '') member = '<fn>'; @@ -215,6 +219,30 @@ return new Frame(uri, line, column, match[4]); } + /// A regular expression matching an absolute URI. + static final _uriRegExp = new RegExp(r'^[a-zA-Z][-+.a-zA-Z\d]*://'); + + /// A regular expression matching a Windows path. + static final _windowsRegExp = new RegExp(r'^([a-zA-Z]:[\\/]|\\\\)'); + + /// Converts [uriOrPath], which can be a URI, a Windows path, or a Posix path, + /// to a URI (absolute if possible). + static Uri _uriOrPathToUri(String uriOrPath) { + if (uriOrPath.contains(_uriRegExp)) { + return Uri.parse(uriOrPath); + } else if (uriOrPath.contains(_windowsRegExp)) { + return new Uri.file(uriOrPath, windows: true); + } else if (uriOrPath.startsWith('/')) { + return new Uri.file(uriOrPath, windows: false); + } + + // As far as I've seen, Firefox and V8 both always report absolute paths in + // their stack frames. However, if we do get a relative path, we should + // handle it gracefully. + if (uriOrPath.contains('\\')) return path.windows.toUri(uriOrPath); + return Uri.parse(uriOrPath); + } + Frame(this.uri, this.line, this.column, this.member); String toString() => '$location in $member';
diff --git a/pkgs/stack_trace/test/frame_test.dart b/pkgs/stack_trace/test/frame_test.dart index 97cf48a..87a8c4d 100644 --- a/pkgs/stack_trace/test/frame_test.dart +++ b/pkgs/stack_trace/test/frame_test.dart
@@ -72,6 +72,52 @@ expect(frame.member, equals('VW.call\$0')); }); + test('parses a stack frame with an absolute POSIX path correctly', () { + var frame = new Frame.parseV8(" at VW.call\$0 " + "(/path/to/stuff.dart.js:560:28)"); + expect(frame.uri, equals(Uri.parse("file:///path/to/stuff.dart.js"))); + expect(frame.line, equals(560)); + expect(frame.column, equals(28)); + expect(frame.member, equals('VW.call\$0')); + }); + + test('parses a stack frame with an absolute Windows path correctly', () { + var frame = new Frame.parseV8(" at VW.call\$0 " + r"(C:\path\to\stuff.dart.js:560:28)"); + expect(frame.uri, equals(Uri.parse("file:///C:/path/to/stuff.dart.js"))); + expect(frame.line, equals(560)); + expect(frame.column, equals(28)); + expect(frame.member, equals('VW.call\$0')); + }); + + test('parses a stack frame with a Windows UNC path correctly', () { + var frame = new Frame.parseV8(" at VW.call\$0 " + r"(\\mount\path\to\stuff.dart.js:560:28)"); + expect(frame.uri, + equals(Uri.parse("file://mount/path/to/stuff.dart.js"))); + expect(frame.line, equals(560)); + expect(frame.column, equals(28)); + expect(frame.member, equals('VW.call\$0')); + }); + + test('parses a stack frame with a relative POSIX path correctly', () { + var frame = new Frame.parseV8(" at VW.call\$0 " + "(path/to/stuff.dart.js:560:28)"); + expect(frame.uri, equals(Uri.parse("path/to/stuff.dart.js"))); + expect(frame.line, equals(560)); + expect(frame.column, equals(28)); + expect(frame.member, equals('VW.call\$0')); + }); + + test('parses a stack frame with a relative Windows path correctly', () { + var frame = new Frame.parseV8(" at VW.call\$0 " + r"(path\to\stuff.dart.js:560:28)"); + expect(frame.uri, equals(Uri.parse("path/to/stuff.dart.js"))); + expect(frame.line, equals(560)); + expect(frame.column, equals(28)); + expect(frame.member, equals('VW.call\$0')); + }); + test('parses an anonymous stack frame correctly', () { var frame = new Frame.parseV8( " at http://pub.dartlang.org/stuff.dart.js:560:28"); @@ -135,6 +181,52 @@ expect(frame.member, equals('VW.call\$0')); }); + test('parses a stack frame with an absolute POSIX path correctly', () { + var frame = new Frame.parseFirefox( + ".VW.call\$0@/path/to/stuff.dart.js:560"); + expect(frame.uri, equals(Uri.parse("file:///path/to/stuff.dart.js"))); + expect(frame.line, equals(560)); + expect(frame.column, isNull); + expect(frame.member, equals('VW.call\$0')); + }); + + test('parses a stack frame with an absolute Windows path correctly', () { + var frame = new Frame.parseFirefox( + r".VW.call$0@C:\path\to\stuff.dart.js:560"); + expect(frame.uri, equals(Uri.parse("file:///C:/path/to/stuff.dart.js"))); + expect(frame.line, equals(560)); + expect(frame.column, isNull); + expect(frame.member, equals('VW.call\$0')); + }); + + test('parses a stack frame with a Windows UNC path correctly', () { + var frame = new Frame.parseFirefox( + r".VW.call$0@\\mount\path\to\stuff.dart.js:560"); + expect(frame.uri, + equals(Uri.parse("file://mount/path/to/stuff.dart.js"))); + expect(frame.line, equals(560)); + expect(frame.column, isNull); + expect(frame.member, equals('VW.call\$0')); + }); + + test('parses a stack frame with a relative POSIX path correctly', () { + var frame = new Frame.parseFirefox( + ".VW.call\$0@path/to/stuff.dart.js:560"); + expect(frame.uri, equals(Uri.parse("path/to/stuff.dart.js"))); + expect(frame.line, equals(560)); + expect(frame.column, isNull); + expect(frame.member, equals('VW.call\$0')); + }); + + test('parses a stack frame with a relative Windows path correctly', () { + var frame = new Frame.parseFirefox( + r".VW.call$0@path\to\stuff.dart.js:560"); + expect(frame.uri, equals(Uri.parse("path/to/stuff.dart.js"))); + expect(frame.line, equals(560)); + expect(frame.column, isNull); + expect(frame.member, equals('VW.call\$0')); + }); + test('parses a simple anonymous stack frame correctly', () { var frame = new Frame.parseFirefox( "@http://pub.dartlang.org/stuff.dart.js:560");