Even better stack chains with VM async/await.

It turns out the VM can generate frames that have the previous
method's name in the <..._async_body> member.

R=rnystrom@google.com

Review URL: https://codereview.chromium.org//936643002
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 47bd9b1..43d547a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 1.2.1
+
+* Further improve stack chains when using the VM's async/await implementation.
+
 ## 1.2.0
 
 * Add a `terse` argument to `Trace.foldFrames()` and `Chain.foldFrames()`. This
diff --git a/lib/src/frame.dart b/lib/src/frame.dart
index 8ecd19f..c06fd60 100644
--- a/lib/src/frame.dart
+++ b/lib/src/frame.dart
@@ -57,6 +57,10 @@
 final _friendlyFrame = new RegExp(
     r'^(\S+)(?: (\d+)(?::(\d+))?)?\s+([^\d]\S*)$');
 
+/// A regular expression that matches asynchronous member names generated by the
+/// VM.
+final _asyncBody = new RegExp(r'<(<anonymous closure>|[^>]+)_async_body>');
+
 final _initialDot = new RegExp(r"^\.");
 
 /// A single stack frame. Each frame points to a precise location in Dart code.
@@ -137,7 +141,7 @@
     // Get the pieces out of the regexp match. Function, URI and line should
     // always be found. The column is optional.
     var member = match[1]
-        .replaceAll("<<anonymous closure>_async_body>", "<async>")
+        .replaceAll(_asyncBody, "<async>")
         .replaceAll("<anonymous closure>", "<fn>");
     var uri = Uri.parse(match[2]);
 
diff --git a/lib/src/vm_trace.dart b/lib/src/vm_trace.dart
index 89474f5..7991160 100644
--- a/lib/src/vm_trace.dart
+++ b/lib/src/vm_trace.dart
@@ -23,8 +23,9 @@
     return frames.map((frame) {
       var number = padRight("#${i++}", 8);
       var member = frame.member
-          .replaceAll("<fn>", "<anonymous closure>")
-          .replaceAll("<async>", "<<anonymous closure>_async_body>");
+          .replaceAllMapped(new RegExp(r"[^.]+\.<async>"),
+              (match) => "${match[1]}.<${match[1]}_async_body>")
+          .replaceAll("<fn>", "<anonymous closure>");
       var line = frame.line == null ? 0 : frame.line;
       var column = frame.column == null ? 0 : frame.column;
       return "$number$member (${frame.uri}:$line:$column)\n";
diff --git a/pubspec.yaml b/pubspec.yaml
index 01b1550..35c419e 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -7,7 +7,7 @@
 #
 # When the major version is upgraded, you *must* update that version constraint
 # in pub to stay in sync with this.
-version: 1.2.0
+version: 1.2.1-dev
 author: "Dart Team <misc@dartlang.org>"
 homepage: http://github.com/dart-lang/stack_trace
 description: >
diff --git a/test/frame_test.dart b/test/frame_test.dart
index 638d1d7..c2cb80e 100644
--- a/test/frame_test.dart
+++ b/test/frame_test.dart
@@ -56,9 +56,9 @@
       expect(frame.member, equals('Foo.<async>'));
     });
 
-    test('converts "<<anonymous closure>_async_body>" to "<async>"', () {
+    test('converts "<function_name_async_body>" to "<async>"', () {
       var frame = new Frame.parseVM(
-          '#0 Foo.<<anonymous closure>_async_body> (foo:0:0)');
+          '#0 Foo.<function_name_async_body> (foo:0:0)');
       expect(frame.member, equals('Foo.<async>'));
     });