Version 2.10.0-107.0.dev

Merge commit 'd3b0979b04f594f93ad49990e51d06314c6ab816' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/correction/organize_imports.dart b/pkg/analysis_server/lib/src/services/correction/organize_imports.dart
index 9d77655..22e440b 100644
--- a/pkg/analysis_server/lib/src/services/correction/organize_imports.dart
+++ b/pkg/analysis_server/lib/src/services/correction/organize_imports.dart
@@ -6,6 +6,7 @@
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/token.dart';
 import 'package:analyzer/error/error.dart';
+import 'package:analyzer/source/line_info.dart';
 import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart'
     hide AnalysisError, Element;
@@ -58,63 +59,59 @@
   /// Organize all [Directive]s.
   void _organizeDirectives() {
     var lineInfo = unit.lineInfo;
+    var hasLibraryDirective = false;
     var directives = <_DirectiveInfo>[];
     for (var directive in unit.directives) {
+      if (directive is LibraryDirective) {
+        hasLibraryDirective = true;
+      }
       if (directive is UriBasedDirective) {
         var priority = getDirectivePriority(directive);
         if (priority != null) {
           var offset = directive.offset;
-          if (directive.beginToken.precedingComments != null) {
-            var firstComment = directive.beginToken.precedingComments;
-            var comment = firstComment;
-            // Don't connect comments that have a blank line between them
-            while (comment.next != null) {
-              var currentLine = lineInfo.getLocation(comment.offset).lineNumber;
-              var nextLine =
-                  lineInfo.getLocation(comment.next.offset).lineNumber;
-              if (nextLine - currentLine > 1) {
-                firstComment = comment.next;
-              }
-
-              comment = comment.next;
-            }
-            // Check if the comment is the first comment in the document
-            if (firstComment != unit.beginToken.precedingComments) {
-              var previousDirectiveLine = lineInfo
-                  .getLocation(directive.beginToken.previous.end)
-                  .lineNumber;
-
-              // Skip over any comments on the same line as the previous directive
-              // as they will be attached to the end of it.
-              var comment = firstComment;
-              while (comment != null &&
-                  previousDirectiveLine ==
-                      lineInfo.getLocation(comment.offset).lineNumber) {
-                comment = comment.next;
-              }
-              if (comment != null) {
-                offset = comment.offset;
-              }
-            }
-          }
-
           var end = directive.end;
-          var line = lineInfo.getLocation(end).lineNumber;
-          Token comment = directive.endToken.next.precedingComments;
-          while (comment != null) {
-            if (lineInfo.getLocation(comment.offset).lineNumber == line) {
-              end = comment.end;
-            }
-            comment = comment.next;
-          }
 
-          var text = code.substring(offset, end);
+          final leadingComment = getLeadingComment(unit, directive, lineInfo);
+          final trailingComment =
+              getTrailingComment(unit, directive, lineInfo, end);
+
+          String leadingCommentText;
+          if (leadingComment != null) {
+            leadingCommentText =
+                code.substring(leadingComment.offset, directive.offset);
+            offset = leadingComment.offset;
+          }
+          String trailingCommentText;
+          if (trailingComment != null) {
+            trailingCommentText =
+                code.substring(directive.end, trailingComment.end);
+            end = trailingComment.end;
+          }
+          String documentationText;
+          if (directive.documentationComment != null) {
+            documentationText = code.substring(
+                directive.documentationComment.offset,
+                directive.documentationComment.end);
+          }
+          String annotationText;
+          if (directive.metadata.beginToken != null) {
+            annotationText = code.substring(
+                directive.metadata.beginToken.offset,
+                directive.metadata.endToken.end);
+          }
+          var text = code.substring(
+              directive.firstTokenAfterCommentAndMetadata.offset,
+              directive.end);
           var uriContent = directive.uri.stringValue;
           directives.add(
             _DirectiveInfo(
               directive,
               priority,
+              leadingCommentText,
+              documentationText,
+              annotationText,
               uriContent,
+              trailingCommentText,
               offset,
               end,
               text,
@@ -129,13 +126,25 @@
     }
     var firstDirectiveOffset = directives.first.offset;
     var lastDirectiveEnd = directives.last.end;
+
+    // Without a library directive, the library comment is the comment of the
+    // first directive.
+    _DirectiveInfo libraryDocumentationDirective;
+    if (!hasLibraryDirective && directives.isNotEmpty) {
+      libraryDocumentationDirective = directives.first;
+    }
+
     // sort
     directives.sort();
     // append directives with grouping
     String directivesCode;
     {
       var sb = StringBuffer();
-      _DirectivePriority currentPriority;
+      if (libraryDocumentationDirective?.documentationText != null) {
+        sb.write(libraryDocumentationDirective.documentationText);
+        sb.write(endOfLine);
+      }
+      var currentPriority = directives.first.priority;
       for (var directiveInfo in directives) {
         if (!hasUnresolvedIdentifierError) {
           var directive = directiveInfo.directive;
@@ -144,12 +153,25 @@
           }
         }
         if (currentPriority != directiveInfo.priority) {
-          if (sb.length != 0) {
-            sb.write(endOfLine);
-          }
+          sb.write(endOfLine);
           currentPriority = directiveInfo.priority;
         }
+        if (directiveInfo.leadingCommentText != null) {
+          sb.write(directiveInfo.leadingCommentText);
+        }
+        if (directiveInfo != libraryDocumentationDirective &&
+            directiveInfo.documentationText != null) {
+          sb.write(directiveInfo.documentationText);
+          sb.write(endOfLine);
+        }
+        if (directiveInfo.annotationText != null) {
+          sb.write(directiveInfo.annotationText);
+          sb.write(endOfLine);
+        }
         sb.write(directiveInfo.text);
+        if (directiveInfo.trailingCommentText != null) {
+          sb.write(directiveInfo.trailingCommentText);
+        }
         sb.write(endOfLine);
       }
       directivesCode = sb.toString();
@@ -162,7 +184,7 @@
   }
 
   static _DirectivePriority getDirectivePriority(UriBasedDirective directive) {
-    var uriContent = directive.uri.stringValue;
+    var uriContent = directive.uri.stringValue ?? '';
     if (directive is ImportDirective) {
       if (uriContent.startsWith('dart:')) {
         return _DirectivePriority.IMPORT_SDK;
@@ -199,26 +221,94 @@
       return '\n';
     }
   }
+
+  /// Gets the first comment token considered to be the leading comment for this
+  /// directive.
+  ///
+  /// Leading comments for the first directive in a file are considered library
+  /// comments and not returned unless they contain blank lines, in which case
+  /// only the last part of the comment will be returned.
+  static Token getLeadingComment(
+      CompilationUnit unit, UriBasedDirective directive, LineInfo lineInfo) {
+    if (directive.beginToken.precedingComments == null) {
+      return null;
+    }
+
+    var firstComment = directive.beginToken.precedingComments;
+    var comment = firstComment;
+    // Don't connect comments that have a blank line between them
+    while (comment.next != null) {
+      var currentLine = lineInfo.getLocation(comment.offset).lineNumber;
+      var nextLine = lineInfo.getLocation(comment.next.offset).lineNumber;
+      if (nextLine - currentLine > 1) {
+        firstComment = comment.next;
+      }
+      comment = comment.next;
+    }
+
+    // Check if the comment is the first comment in the document
+    if (firstComment != unit.beginToken.precedingComments) {
+      var previousDirectiveLine =
+          lineInfo.getLocation(directive.beginToken.previous.end).lineNumber;
+
+      // Skip over any comments on the same line as the previous directive
+      // as they will be attached to the end of it.
+      var comment = firstComment;
+      while (comment != null &&
+          previousDirectiveLine ==
+              lineInfo.getLocation(comment.offset).lineNumber) {
+        comment = comment.next;
+      }
+      return comment;
+    }
+    return null;
+  }
+
+  /// Gets the last comment token considered to be the trailing comment for this
+  /// directive.
+  ///
+  /// To be considered a trailing comment, the comment must be on the same line
+  /// as the directive.
+  static Token getTrailingComment(CompilationUnit unit,
+      UriBasedDirective directive, LineInfo lineInfo, int end) {
+    var line = lineInfo.getLocation(end).lineNumber;
+    Token comment = directive.endToken.next.precedingComments;
+    while (comment != null) {
+      if (lineInfo.getLocation(comment.offset).lineNumber == line) {
+        return comment;
+      }
+      comment = comment.next;
+    }
+    return null;
+  }
 }
 
 class _DirectiveInfo implements Comparable<_DirectiveInfo> {
   final UriBasedDirective directive;
   final _DirectivePriority priority;
+  final String leadingCommentText;
+  final String documentationText;
+  final String annotationText;
   final String uri;
+  final String trailingCommentText;
 
-  /// The offset of the first token, usually the keyword.
+  /// The offset of the first token, usually the keyword but may include leading comments.
   final int offset;
 
-  /// The offset after the least token, including the end-of-line comment.
+  /// The offset after the last token, including the end-of-line comment.
   final int end;
 
-  /// The text between [offset] and [end].
+  /// The text excluding comments, documentation and annotations.
   final String text;
 
   _DirectiveInfo(
     this.directive,
     this.priority,
+    this.leadingCommentText,
+    this.documentationText,
+    this.annotationText,
     this.uri,
+    this.trailingCommentText,
     this.offset,
     this.end,
     this.text,
diff --git a/pkg/analysis_server/lib/src/services/correction/sort_members.dart b/pkg/analysis_server/lib/src/services/correction/sort_members.dart
index 9f5630a..34e0ba4 100644
--- a/pkg/analysis_server/lib/src/services/correction/sort_members.dart
+++ b/pkg/analysis_server/lib/src/services/correction/sort_members.dart
@@ -2,6 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+import 'package:analysis_server/src/services/correction/organize_imports.dart';
 import 'package:analysis_server/src/utilities/strings.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart' hide Element;
@@ -143,117 +144,10 @@
 
   /// Sorts all [Directive]s.
   void _sortUnitDirectives() {
-    var hasLibraryDirective = false;
-    var directives = <_DirectiveInfo>[];
-    for (var directive in unit.directives) {
-      if (directive is LibraryDirective) {
-        hasLibraryDirective = true;
-      }
-      if (directive is! UriBasedDirective) {
-        continue;
-      }
-      var uriDirective = directive as UriBasedDirective;
-      var uriContent = uriDirective.uri.stringValue ?? '';
-      _DirectivePriority kind;
-      if (directive is ImportDirective) {
-        if (uriContent.startsWith('dart:')) {
-          kind = _DirectivePriority.IMPORT_SDK;
-        } else if (uriContent.startsWith('package:')) {
-          kind = _DirectivePriority.IMPORT_PKG;
-        } else if (uriContent.contains('://')) {
-          kind = _DirectivePriority.IMPORT_OTHER;
-        } else {
-          kind = _DirectivePriority.IMPORT_REL;
-        }
-      }
-      if (directive is ExportDirective) {
-        if (uriContent.startsWith('dart:')) {
-          kind = _DirectivePriority.EXPORT_SDK;
-        } else if (uriContent.startsWith('package:')) {
-          kind = _DirectivePriority.EXPORT_PKG;
-        } else if (uriContent.contains('://')) {
-          kind = _DirectivePriority.EXPORT_OTHER;
-        } else {
-          kind = _DirectivePriority.EXPORT_REL;
-        }
-      }
-      if (directive is PartDirective) {
-        kind = _DirectivePriority.PART;
-      }
-      if (kind != null) {
-        String documentationText;
-        if (directive.documentationComment != null) {
-          documentationText = code.substring(
-              directive.documentationComment.offset,
-              directive.documentationComment.end);
-        }
-        String annotationText;
-        if (directive.metadata.beginToken != null) {
-          annotationText = code.substring(directive.metadata.beginToken.offset,
-              directive.metadata.endToken.end);
-        }
-        var offset = directive.firstTokenAfterCommentAndMetadata.offset;
-        var length = directive.end - offset;
-        var text = code.substring(offset, offset + length);
-        directives.add(_DirectiveInfo(directive, kind, uriContent,
-            documentationText, annotationText, text));
-      }
-    }
-    // nothing to do
-    if (directives.isEmpty) {
-      return;
-    }
-    var firstDirectiveOffset = directives[0].directive.offset;
-    var lastDirectiveEnd = directives[directives.length - 1].directive.end;
-    // Without a library directive, the library comment is the comment of the
-    // first directive.
-    _DirectiveInfo libraryDocumentationDirective;
-    if (!hasLibraryDirective && directives.isNotEmpty) {
-      libraryDocumentationDirective = directives.first;
-    }
-    // do sort
-    directives.sort();
-    // append directives with grouping
-    String directivesCode;
-    {
-      var sb = StringBuffer();
-      var endOfLine = this.endOfLine;
-      _DirectivePriority currentPriority;
-      var firstOutputDirective = true;
-      for (var directive in directives) {
-        if (currentPriority != directive.priority) {
-          if (sb.length != 0) {
-            sb.write(endOfLine);
-          }
-          currentPriority = directive.priority;
-        }
-        if (directive != libraryDocumentationDirective &&
-            directive.documentationText != null) {
-          sb.write(directive.documentationText);
-          sb.write(endOfLine);
-        }
-        if (firstOutputDirective) {
-          firstOutputDirective = false;
-          if (libraryDocumentationDirective != null &&
-              libraryDocumentationDirective.documentationText != null) {
-            sb.write(libraryDocumentationDirective.documentationText);
-            sb.write(endOfLine);
-          }
-        }
-        if (directive.annotationText != null) {
-          sb.write(directive.annotationText);
-          sb.write(endOfLine);
-        }
-        sb.write(directive.text);
-        sb.write(endOfLine);
-      }
-      directivesCode = sb.toString();
-      directivesCode = directivesCode.trimRight();
-    }
-    // prepare code
-    var beforeDirectives = code.substring(0, firstDirectiveOffset);
-    var afterDirectives = code.substring(lastDirectiveEnd);
-    code = beforeDirectives + directivesCode + afterDirectives;
+    final importOrganizer =
+        ImportOrganizer(code, unit, [], removeUnused: false);
+    importOrganizer.organize();
+    code = importOrganizer.code;
   }
 
   /// Sorts all [CompilationUnitMember]s.
@@ -360,68 +254,6 @@
   }
 }
 
-class _DirectiveInfo implements Comparable<_DirectiveInfo> {
-  final Directive directive;
-  final _DirectivePriority priority;
-  final String uri;
-  final String documentationText;
-  final String annotationText;
-  final String text;
-
-  _DirectiveInfo(this.directive, this.priority, this.uri,
-      this.documentationText, this.annotationText, this.text);
-
-  @override
-  int compareTo(_DirectiveInfo other) {
-    if (priority == other.priority) {
-      return _compareUri(uri, other.uri);
-    }
-    return priority.ordinal - other.priority.ordinal;
-  }
-
-  @override
-  String toString() => '(priority=$priority; text=$text)';
-
-  static int _compareUri(String a, String b) {
-    var aList = _splitUri(a);
-    var bList = _splitUri(b);
-    int result;
-    if ((result = aList[0].compareTo(bList[0])) != 0) return result;
-    if ((result = aList[1].compareTo(bList[1])) != 0) return result;
-    return 0;
-  }
-
-  /// Split the given [uri] like `package:some.name/and/path.dart` into a list
-  /// like `[package:some.name, and/path.dart]`.
-  static List<String> _splitUri(String uri) {
-    var index = uri.indexOf('/');
-    if (index == -1) {
-      return <String>[uri, ''];
-    }
-    return <String>[uri.substring(0, index), uri.substring(index + 1)];
-  }
-}
-
-class _DirectivePriority {
-  static const IMPORT_SDK = _DirectivePriority('IMPORT_SDK', 0);
-  static const IMPORT_PKG = _DirectivePriority('IMPORT_PKG', 1);
-  static const IMPORT_OTHER = _DirectivePriority('IMPORT_OTHER', 2);
-  static const IMPORT_REL = _DirectivePriority('IMPORT_REL', 3);
-  static const EXPORT_SDK = _DirectivePriority('EXPORT_SDK', 4);
-  static const EXPORT_PKG = _DirectivePriority('EXPORT_PKG', 5);
-  static const EXPORT_OTHER = _DirectivePriority('EXPORT_OTHER', 6);
-  static const EXPORT_REL = _DirectivePriority('EXPORT_REL', 7);
-  static const PART = _DirectivePriority('PART', 8);
-
-  final String name;
-  final int ordinal;
-
-  const _DirectivePriority(this.name, this.ordinal);
-
-  @override
-  String toString() => name;
-}
-
 class _MemberInfo {
   final _PriorityItem item;
   final String name;
diff --git a/pkg/analysis_server/test/services/correction/organize_directives_test.dart b/pkg/analysis_server/test/services/correction/organize_directives_test.dart
index c2e47ac..9315f3a 100644
--- a/pkg/analysis_server/test/services/correction/organize_directives_test.dart
+++ b/pkg/analysis_server/test/services/correction/organize_directives_test.dart
@@ -260,6 +260,162 @@
 ''');
   }
 
+  Future<void> test_sort_commentsAnnotations_library() async {
+    await _computeUnitAndErrors(r'''
+// Library docs 1
+// Library docs 2
+library foo;
+
+// Import c comment
+@annotation
+import 'c.dart'; // Trailing comment C
+// Import b comment
+@annotation
+import 'b.dart'; // Trailing comment B
+// Import a comment
+@annotation
+import 'a.dart'; // Trailing comment A
+
+/** doc */
+main() {
+}
+''');
+    // validate change
+    _assertOrganize(r'''
+// Library docs 1
+// Library docs 2
+library foo;
+
+// Import a comment
+@annotation
+import 'a.dart'; // Trailing comment A
+// Import b comment
+@annotation
+import 'b.dart'; // Trailing comment B
+// Import c comment
+@annotation
+import 'c.dart'; // Trailing comment C
+
+/** doc */
+main() {
+}
+''');
+  }
+
+  Future<void> test_sort_commentsAnnotations_noLibrary() async {
+    await _computeUnitAndErrors(r'''
+// Library docs 1
+// Library docs 2
+@annotation
+import 'c.dart'; // Trailing comment C
+// Import b comment
+@annotation
+import 'b.dart'; // Trailing comment B
+// Import a comment
+@annotation
+import 'a.dart'; // Trailing comment A
+
+/** doc */
+main() {
+}
+''');
+    // validate change
+    _assertOrganize(r'''
+// Library docs 1
+// Library docs 2
+// Import a comment
+@annotation
+import 'a.dart'; // Trailing comment A
+// Import b comment
+@annotation
+import 'b.dart'; // Trailing comment B
+@annotation
+import 'c.dart'; // Trailing comment C
+
+/** doc */
+main() {
+}
+''');
+  }
+
+  Future<void> test_sort_documentationAnnotations_library() async {
+    await _computeUnitAndErrors(r'''
+/// Library docs 1
+/// Library docs 2
+library foo;
+
+/// Import c docs
+@annotation
+import 'c.dart'; // Trailing comment C
+/// Import b docs
+@annotation
+import 'b.dart'; // Trailing comment B
+/// Import a docs
+@annotation
+import 'a.dart'; // Trailing comment A
+
+/** doc */
+main() {
+}
+''');
+    // validate change
+    _assertOrganize(r'''
+/// Library docs 1
+/// Library docs 2
+library foo;
+
+/// Import a docs
+@annotation
+import 'a.dart'; // Trailing comment A
+/// Import b docs
+@annotation
+import 'b.dart'; // Trailing comment B
+/// Import c docs
+@annotation
+import 'c.dart'; // Trailing comment C
+
+/** doc */
+main() {
+}
+''');
+  }
+
+  Future<void> test_sort_documentationAnnotations_noLibrary() async {
+    await _computeUnitAndErrors(r'''
+/// Library docs 1
+/// Library docs 2
+@annotation
+import 'c.dart'; // Trailing comment C
+/// Import b docs
+@annotation
+import 'b.dart'; // Trailing comment B
+/// Import a docs
+@annotation
+import 'a.dart'; // Trailing comment A
+
+/** doc */
+main() {
+}
+''');
+    // validate change
+    _assertOrganize(r'''
+/// Library docs 1
+/// Library docs 2
+/// Import a docs
+@annotation
+import 'a.dart'; // Trailing comment A
+/// Import b docs
+@annotation
+import 'b.dart'; // Trailing comment B
+@annotation
+import 'c.dart'; // Trailing comment C
+
+/** doc */
+main() {
+}
+''');
+  }
+
   Future<void> test_sort_hasComments() async {
     await _computeUnitAndErrors(r'''
 // header
diff --git a/pkg/analysis_server/test/services/correction/sort_members_test.dart b/pkg/analysis_server/test/services/correction/sort_members_test.dart
index 64d74cc..4c50509 100644
--- a/pkg/analysis_server/test/services/correction/sort_members_test.dart
+++ b/pkg/analysis_server/test/services/correction/sort_members_test.dart
@@ -448,10 +448,10 @@
 ''');
     // validate change
     _assertSort(r'''
-/// aaa1
-/// aaa2
 /// Library documentation comment A
 /// Library documentation comment B
+/// aaa1
+/// aaa2
 import 'a.dart';
 import 'b.dart';
 ''');
@@ -473,13 +473,13 @@
     // validate change
     _assertSort(r'''
 /**
- * aaa
- * aaa
- */
-/**
  * Library documentation comment A.
  * Library documentation comment B.
  */
+/**
+ * aaa
+ * aaa
+ */
 import 'a.dart';
 import 'b.dart';
 ''');
@@ -527,6 +527,32 @@
 ''');
   }
 
+  Future<void> test_directives_splits_comments() async {
+    // Here, the comments "b" and "ccc1" will be part of the same list
+    // of comments so need to be split.
+    await _parseTestUnit(r'''
+// copyright
+import 'b.dart'; // b
+// ccc1
+// ccc2
+import 'c.dart'; // c
+// aaa1
+// aaa2
+import 'a.dart'; // a
+''');
+
+    _assertSort(r'''
+// copyright
+// aaa1
+// aaa2
+import 'a.dart'; // a
+import 'b.dart'; // b
+// ccc1
+// ccc2
+import 'c.dart'; // c
+''');
+  }
+
   Future<void> test_mixinMembers_method() async {
     await _parseTestUnit(r'''
 mixin A {
diff --git a/runtime/include/dart_tools_api.h b/runtime/include/dart_tools_api.h
index c7ebb49..a106d42 100644
--- a/runtime/include/dart_tools_api.h
+++ b/runtime/include/dart_tools_api.h
@@ -271,23 +271,28 @@
 /**
  * Usage statistics for a space/generation at a particular moment in time.
  *
- * \param new_space Data for New Space.
+ * \param used Amount of memory used, in bytes.
  *
- * \param old_space Data for Old Space.
+ * \param capacity Memory capacity, in bytes.
+ *
+ * \param external External memory, in bytes.
+ *
+ * \param collections How many times the garbage collector has run in this
+ *   space.
+ *
+ * \param time Cumulative time spent collecting garbage in this space, in
+ *   seconds.
+ *
+ * \param avg_collection_period Average time between garbage collector running
+ *   in this space, in milliseconds.
  */
 typedef struct {
-  /**
-   * \param used_in_words Amount of memory space used, in words.
-   *
-   * \param capacity_in_words Memory space capacity, in words.
-   *
-   * \param external_in_words External memory, in words.
-   */
-  struct {
-    intptr_t used_in_words;
-    intptr_t capacity_in_words;
-    intptr_t external_in_words;
-  } new_space, old_space;
+  intptr_t used;
+  intptr_t capacity;
+  intptr_t external;
+  intptr_t collections;
+  double time;
+  double avg_collection_period;
 } Dart_GCStats;
 
 /**
@@ -297,15 +302,17 @@
  *
  * \param reason The reason for the GC event. Static lifetime.
  *
- * \param before Memory usage statistics measured before GC was performed.
+ * \param new_space Data for New Space.
  *
- * \param before Memory usage statistics measured after GC was performed.
+ * \param old_space Data for Old Space.
  */
 typedef struct {
   const char* type;
   const char* reason;
-  Dart_GCStats before;
-  Dart_GCStats after;
+  const char* isolate_id;
+
+  Dart_GCStats new_space;
+  Dart_GCStats old_space;
 } Dart_GCEvent;
 
 /**
diff --git a/runtime/vm/heap/heap.cc b/runtime/vm/heap/heap.cc
index 51031cc..dad7079 100644
--- a/runtime/vm/heap/heap.cc
+++ b/runtime/vm/heap/heap.cc
@@ -994,6 +994,14 @@
     stats_.data_[i] = 0;
 }
 
+static double AvgCollectionPeriod(int64_t run_time, intptr_t collections) {
+  if (collections <= 0 || run_time <= 0) {
+    return 0.0;
+  }
+  return MicrosecondsToMilliseconds(run_time) /
+         static_cast<double>(collections);
+}
+
 void Heap::RecordAfterGC(GCType type) {
   stats_.after_.micros_ = OS::GetCurrentMonotonicMicros();
   int64_t delta = stats_.after_.micros_ - stats_.before_.micros_;
@@ -1019,33 +1027,54 @@
   }
 #endif  // !PRODUCT
   if (gc_event_callback_ != nullptr) {
-    Dart_GCEvent event;
+    isolate_group_->ForEachIsolate([&](Isolate* isolate) {
+      if (!Isolate::IsSystemIsolate(isolate)) {
+        Dart_GCEvent event;
+        auto isolate_id = Utils::CStringUniquePtr(
+            OS::SCreate(nullptr, ISOLATE_SERVICE_ID_FORMAT_STRING,
+                        isolate->main_port()),
+            std::free);
+        int64_t isolate_uptime_micros = isolate->UptimeMicros();
 
-    event.type = GCTypeToString(stats_.type_);
-    event.reason = GCReasonToString(stats_.reason_);
+        event.isolate_id = isolate_id.get();
+        event.type = GCTypeToString(stats_.type_);
+        event.reason = GCReasonToString(stats_.reason_);
 
-    event.before.new_space.capacity_in_words =
-        stats_.before_.new_.capacity_in_words;
-    event.before.new_space.external_in_words =
-        stats_.before_.new_.external_in_words;
-    event.before.new_space.used_in_words = stats_.before_.new_.used_in_words;
-    event.before.old_space.capacity_in_words =
-        stats_.before_.old_.capacity_in_words;
-    event.before.old_space.external_in_words =
-        stats_.before_.old_.external_in_words;
-    event.before.old_space.used_in_words = stats_.before_.old_.used_in_words;
-    event.after.new_space.capacity_in_words =
-        stats_.after_.new_.capacity_in_words;
-    event.after.new_space.external_in_words =
-        stats_.after_.new_.external_in_words;
-    event.after.new_space.used_in_words = stats_.after_.new_.used_in_words;
-    event.after.old_space.capacity_in_words =
-        stats_.after_.old_.capacity_in_words;
-    event.after.old_space.external_in_words =
-        stats_.after_.old_.external_in_words;
-    event.after.old_space.used_in_words = stats_.after_.old_.used_in_words;
+        // New space - Scavenger.
+        {
+          intptr_t new_space_collections = new_space_.collections();
 
-    (*gc_event_callback_)(&event);
+          event.new_space.collections = new_space_collections;
+          event.new_space.used = stats_.after_.new_.used_in_words * kWordSize;
+          event.new_space.capacity =
+              stats_.after_.new_.capacity_in_words * kWordSize;
+          event.new_space.external =
+              stats_.after_.new_.external_in_words * kWordSize;
+          event.new_space.time =
+              MicrosecondsToSeconds(new_space_.gc_time_micros());
+          event.new_space.avg_collection_period =
+              AvgCollectionPeriod(isolate_uptime_micros, new_space_collections);
+        }
+
+        // Old space - Page.
+        {
+          intptr_t old_space_collections = old_space_.collections();
+
+          event.old_space.collections = old_space_collections;
+          event.old_space.used = stats_.after_.old_.used_in_words * kWordSize;
+          event.old_space.capacity =
+              stats_.after_.old_.capacity_in_words * kWordSize;
+          event.old_space.external =
+              stats_.after_.old_.external_in_words * kWordSize;
+          event.old_space.time =
+              MicrosecondsToSeconds(old_space_.gc_time_micros());
+          event.old_space.avg_collection_period =
+              AvgCollectionPeriod(isolate_uptime_micros, old_space_collections);
+        }
+
+        (*gc_event_callback_)(&event);
+      }
+    });
   }
 }
 
diff --git a/tests/co19/co19-co19.status b/tests/co19/co19-co19.status
index 76666e6..4bfc333 100644
--- a/tests/co19/co19-co19.status
+++ b/tests/co19/co19-co19.status
@@ -43,10 +43,9 @@
 LanguageFeatures/Simple-bounds/static/*: Skip # Not migrated to NNBD
 LanguageFeatures/Simple-bounds/static/type-aliases/*: Skip # Not migrated to NNBD
 LanguageFeatures/Spread-collections/*: Skip # Not migrated to NNBD
-LanguageFeatures/Super-bounded-types/*: Skip # Not migrated to NNBD
-LanguageFeatures/Triple-Shift/*: Skip # Not migrated to NNBD
+LanguageFeatures/Triple-Shift/*: Skip # Triple shift is not implemented yet
 LanguageFeatures/int-to-double/*: Skip # Not migrated to NNBD
-LanguageFeatures/regression/*: Skip # Not migrated to NNBD
+LanguageFeatures/regression/34560_t02/01: Skip # Type aliases are not fully implemented
 LibTest/html/CanvasRenderingContext2D/*: Skip # Not migrated to NNBD
 LibTest/html/Document/*: Skip # Not migrated to NNBD
 LibTest/html/Element/*: Skip # Not migrated to NNBD
@@ -57,7 +56,6 @@
 LibTest/html/Node/*: Skip # Not migrated to NNBD
 LibTest/html/Window/*: Skip # Not migrated to NNBD
 LibTest/io/RawDatagramSocket/*: Skip # https://github.com/dart-lang/co19/issues/195
-Utils/tests/Expect/*: Skip # Not migrated to NNBD
 
 [ $compiler == dart2analyzer ]
 Language/Classes/Instance_Methods/Operators/allowed_names_t01: Skip # Triple shift is not fully implemented
diff --git a/tools/VERSION b/tools/VERSION
index 33b7687..7ec9e7a 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 10
 PATCH 0
-PRERELEASE 106
+PRERELEASE 107
 PRERELEASE_PATCH 0
\ No newline at end of file