Properly parse V8 lines involving eval in pkg/stack_trace. R=sigmund@google.com Review URL: https://codereview.chromium.org//130443002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/stack_trace@31644 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/pkgs/stack_trace/lib/src/frame.dart b/pkgs/stack_trace/lib/src/frame.dart index 74aafcd..8544c78 100644 --- a/pkgs/stack_trace/lib/src/frame.dart +++ b/pkgs/stack_trace/lib/src/frame.dart
@@ -14,13 +14,24 @@ r'^#\d+\s+([^\s].*) \((.+?):(\d+)(?::(\d+))?\)$'); // at VW.call$0 (http://pub.dartlang.org/stuff.dart.js:560:28) +// at VW.call$0 (eval as fn +// (http://pub.dartlang.org/stuff.dart.js:560:28), efn:3:28) // at http://pub.dartlang.org/stuff.dart.js:560:28 final _v8Frame = new RegExp( - r'^\s*at (?:([^\s].*?)(?: \[as [^\]]+\])? ' - r'\((.+):(\d+):(\d+)\)|(.+):(\d+):(\d+))$'); + r'^\s*at (?:([^\s].*?)(?: \[as [^\]]+\])? \((.*)\)|(.*))$'); -/// foo$bar$0@http://pub.dartlang.org/stuff.dart.js:560:28 -/// http://pub.dartlang.org/stuff.dart.js:560:28 +// http://pub.dartlang.org/stuff.dart.js:560:28 +final _v8UrlLocation = new RegExp(r'^(.*):(\d+):(\d+)$'); + +// eval as function (http://pub.dartlang.org/stuff.dart.js:560:28), efn:3:28 +// eval as function (http://pub.dartlang.org/stuff.dart.js:560:28) +// eval as function (eval as otherFunction +// (http://pub.dartlang.org/stuff.dart.js:560:28)) +final _v8EvalLocation = new RegExp( + r'^eval at (?:[^\s].*?) \((.*)\)(?:, .*?:\d+:\d+)?$'); + +// foo$bar$0@http://pub.dartlang.org/stuff.dart.js:560:28 +// http://pub.dartlang.org/stuff.dart.js:560:28 final _safariFrame = new RegExp(r"^(?:([0-9A-Za-z_$]*)@)?(.*):(\d*):(\d*)$"); // .VW.call$0@http://pub.dartlang.org/stuff.dart.js:560 @@ -139,20 +150,35 @@ throw new FormatException("Couldn't parse V8 stack trace line '$frame'."); } + // v8 location strings can be arbitrarily-nested, since it adds a layer of + // nesting for each eval performed on that line. + parseLocation(location, member) { + var evalMatch = _v8EvalLocation.firstMatch(location); + if (evalMatch != null) return parseLocation(evalMatch[1], member); + + + var urlMatch = _v8UrlLocation.firstMatch(location); + if (urlMatch == null) { + throw new FormatException( + "Couldn't parse V8 stack trace line '$frame'."); + } + + return new Frame( + _uriOrPathToUri(urlMatch[1]), + int.parse(urlMatch[2]), + int.parse(urlMatch[3]), + member); + } + // V8 stack frames can be in two forms. if (match[2] != null) { - // 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); + // The first form looks like " at FUNCTION (LOCATION)". + return parseLocation( + match[2], match[1].replaceAll("<anonymous>", "<fn>")); } else { - // 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>"); + // The second form looks like " at LOCATION", and is used for anonymous + // functions. + return parseLocation(match[3], "<fn>"); } }
diff --git a/pkgs/stack_trace/test/frame_test.dart b/pkgs/stack_trace/test/frame_test.dart index 9722d38..482794c 100644 --- a/pkgs/stack_trace/test/frame_test.dart +++ b/pkgs/stack_trace/test/frame_test.dart
@@ -149,6 +149,36 @@ expect(frame.member, equals('VW.call\$0')); }); + test('parses a basic eval stack frame correctly', () { + var frame = new Frame.parseV8(" at eval (eval at <anonymous> " + "(http://pub.dartlang.org/stuff.dart.js:560:28))"); + expect(frame.uri, + equals(Uri.parse("http://pub.dartlang.org/stuff.dart.js"))); + expect(frame.line, equals(560)); + expect(frame.column, equals(28)); + expect(frame.member, equals('eval')); + }); + + test('parses an eval stack frame with inner position info correctly', () { + var frame = new Frame.parseV8(" at eval (eval at <anonymous> " + "(http://pub.dartlang.org/stuff.dart.js:560:28), <anonymous>:3:28)"); + expect(frame.uri, + equals(Uri.parse("http://pub.dartlang.org/stuff.dart.js"))); + expect(frame.line, equals(560)); + expect(frame.column, equals(28)); + expect(frame.member, equals('eval')); + }); + + test('parses a nested eval stack frame correctly', () { + var frame = new Frame.parseV8(" at eval (eval at <anonymous> " + "(eval at sub (http://pub.dartlang.org/stuff.dart.js:560:28)))"); + expect(frame.uri, + equals(Uri.parse("http://pub.dartlang.org/stuff.dart.js"))); + expect(frame.line, equals(560)); + expect(frame.column, equals(28)); + expect(frame.member, equals('eval')); + }); + test('converts "<anonymous>" to "<fn>"', () { String parsedMember(String member) => new Frame.parseV8(' at $member (foo:0:0)').member;