Merge branch 'master' into null_safety
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 39e7fb3..2f189e6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,11 @@
-## 1.9.4-dev
+## 1.9.5
 
-* Added support for firefox anonymous stack traces
+* Parse the format for `data:` URIs that the Dart VM has used since `2.2.0`.
+
+## 1.9.4
+
+* Add support for firefox anonymous stack traces.
+* Add support for chrome eval stack traces without a column.
 * Change the argument type to `Chain.capture` from `Function(dynamic, Chain)` to
   `Function(Object, Chain)`. Existing functions which take `dynamic` are still
   fine, but new uses can have a safer type.
diff --git a/lib/src/frame.dart b/lib/src/frame.dart
index d26df63..18e4f98 100644
--- a/lib/src/frame.dart
+++ b/lib/src/frame.dart
@@ -21,7 +21,8 @@
     RegExp(r'^\s*at (?:(\S.*?)(?: \[as [^\]]+\])? \((.*)\)|(.*))$');
 
 // https://example.com/stuff.dart.js:560:28
-final _v8UrlLocation = RegExp(r'^(.*):(\d+):(\d+)|native$');
+// https://example.com/stuff.dart.js:560
+final _v8UrlLocation = RegExp(r'^(.*?):(\d+)(?::(\d+))?$|native$');
 
 // eval as function (https://example.com/stuff.dart.js:560:28), efn:3:28
 // eval as function (https://example.com/stuff.dart.js:560:28)
@@ -148,7 +149,9 @@
         var member = match[1]!
             .replaceAll(_asyncBody, '<async>')
             .replaceAll('<anonymous closure>', '<fn>');
-        var uri = Uri.parse(match[2]!);
+        var uri = match[2]!.startsWith('<data:')
+            ? Uri.dataFromString('')
+            : Uri.parse(match[2]!);
 
         var lineAndColumn = match[3]!.split(':');
         var line =
@@ -179,8 +182,10 @@
           var urlMatch = _v8UrlLocation.firstMatch(location);
           if (urlMatch == null) return UnparsedFrame(frame);
 
-          return Frame(_uriOrPathToUri(urlMatch[1]!), int.parse(urlMatch[2]!),
-              int.parse(urlMatch[3]!), member);
+          final uri = _uriOrPathToUri(urlMatch[1]!);
+          final line = int.parse(urlMatch[2]!);
+          final column = urlMatch[3] != null ? int.parse(urlMatch[3]) : null;
+          return Frame(uri, line, column, member);
         }
 
         // V8 stack frames can be in two forms.
diff --git a/test/frame_test.dart b/test/frame_test.dart
index d917b11..8788039 100644
--- a/test/frame_test.dart
+++ b/test/frame_test.dart
@@ -89,6 +89,16 @@
       expect(frame.member, equals('VW.call\$0'));
     });
 
+    test('parses a stack frame with a : in the authority', () {
+      var frame = Frame.parseV8('    at VW.call\$0 '
+          '(http://localhost:8080/stuff.dart.js:560:28)');
+      expect(
+          frame.uri, equals(Uri.parse('http://localhost:8080/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 POSIX path correctly', () {
       var frame = Frame.parseV8('    at VW.call\$0 '
           '(/path/to/stuff.dart.js:560:28)');
@@ -221,8 +231,6 @@
       expectIsUnparsed(
           (text) => Frame.parseV8(text), '    at dart:async/future.dart');
       expectIsUnparsed(
-          (text) => Frame.parseV8(text), '    at dart:async/future.dart:10');
-      expectIsUnparsed(
           (text) => Frame.parseV8(text), 'dart:async/future.dart:10:15');
     });
   });
@@ -560,11 +568,17 @@
           equals(path.join('foo', 'bar.dart')));
     });
 
-    test('truncates data: URIs', () {
+    test('truncates legacy data: URIs', () {
       var frame = Frame.parseVM(
           '#0 Foo (data:application/dart;charset=utf-8,blah:0:0)');
       expect(frame.library, equals('data:...'));
     });
+
+    test('truncates data: URIs', () {
+      var frame = Frame.parseVM(
+          '#0      main (<data:application/dart;charset=utf-8>:1:15)');
+      expect(frame.library, equals('data:...'));
+    });
   });
 
   group('.location', () {
diff --git a/test/trace_test.dart b/test/trace_test.dart
index be785e8..a10c69d 100644
--- a/test/trace_test.dart
+++ b/test/trace_test.dart
@@ -15,6 +15,15 @@
   test('a native stack trace is parseable', () => Trace.current());
 
   group('.parse', () {
+    test('.parse parses a V8 stack trace with eval statment correctly', () {
+      var trace = Trace.parse(r'''Error
+    at Object.eval (eval at Foo (main.dart.js:588), <anonymous>:3:47)''');
+      expect(trace.frames[0].uri, Uri.parse('main.dart.js'));
+      expect(trace.frames[0].member, equals('Object.eval'));
+      expect(trace.frames[0].line, equals(588));
+      expect(trace.frames[0].column, isNull);
+    });
+
     test('.parse parses a VM stack trace correctly', () {
       var trace = Trace.parse(
           '#0      Foo._bar (file:///home/nweiz/code/stuff.dart:42:21)\n'