[kernel] Constant coverage recorded in dill

This adds the recorded constant coverage to the dill file.

runtimes for constant evaluation:
dart2js: No difference proven at 95.0% confidence
flutter gallery: No difference proven at 95.0% confidence
big internal app: No difference proven at 95.0% confidence

sdk sizes:
vm: ~0.0182%
dart2js: ~0.0137%
flutter: ~0.0197%

compile sizes:
dart2js: ~0.0179%
flutter gallery: ~0.0162%
big internal app: ~0.0414%

TEST=test was fixed.

Change-Id: I347751e4d96d4d62140d26ebe37960f46a0dfbfa
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/171948
Commit-Queue: Jens Johansen <jensj@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
index ad8d0b8..3a23482 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
@@ -808,8 +808,8 @@
             isSynthetic: true,
             isConst: isConst,
             reference: referenceFrom?.reference)
-          ..isNonNullableByDefault =
-              cls.enclosingLibrary.isNonNullableByDefault,
+          ..isNonNullableByDefault = cls.enclosingLibrary.isNonNullableByDefault
+          ..fileUri = cls.fileUri,
         // If the constructor is constant, the default values must be part of
         // the outline expressions. We pass on the original constructor and
         // cloned function nodes to ensure that the default values are computed
@@ -1254,6 +1254,15 @@
     ticker.logMs("Evaluated constants");
     constantCoverageForTesting = coverage;
 
+    coverage.constructorCoverage.forEach((Uri fileUri, Set<Reference> value) {
+      Source source = uriToSource[fileUri];
+      if (source != null && fileUri != null) {
+        source.constantCoverageConstructors ??= new Set<Reference>();
+        source.constantCoverageConstructors.addAll(value);
+      }
+    });
+    ticker.logMs("Added constant coverage");
+
     if (loader.target.context.options
         .isExperimentEnabledGlobally(ExperimentalFlag.valueClass)) {
       valueClass.transformComponent(
diff --git a/pkg/kernel/binary.md b/pkg/kernel/binary.md
index cafad21..d2d2c47 100644
--- a/pkg/kernel/binary.md
+++ b/pkg/kernel/binary.md
@@ -99,6 +99,10 @@
   List<UInt> lineStarts;
 
   List<Byte> importUriUtf8Bytes;
+
+  // List of constructors evaluated *by* this library. Note that these can be
+  // in other libraries.
+  List<ConstructorReference> constructorCoverage;
 }
 
 type String {
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index 7b9ce66..2ba1722 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -9937,6 +9937,8 @@
 
   final Uri fileUri;
 
+  Set<Reference> constantCoverageConstructors;
+
   String cachedText;
 
   Source(this.lineStarts, this.source, this.importUri, this.fileUri);
diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart
index 5d9958f..9461731 100644
--- a/pkg/kernel/lib/binary/ast_from_binary.dart
+++ b/pkg/kernel/lib/binary/ast_from_binary.dart
@@ -841,7 +841,19 @@
       String importUriString = readString();
       Uri importUri =
           importUriString.isEmpty ? null : Uri.parse(importUriString);
-      uriToSource[uri] = new Source(lineStarts, sourceCode, importUri, uri);
+
+      Set<Reference> coverageConstructors;
+      {
+        int constructorCoverageCount = readUInt30();
+        coverageConstructors =
+            constructorCoverageCount == 0 ? null : new Set<Reference>();
+        for (int j = 0; j < constructorCoverageCount; ++j) {
+          coverageConstructors.add(readMemberReference());
+        }
+      }
+
+      uriToSource[uri] = new Source(lineStarts, sourceCode, importUri, uri)
+        ..constantCoverageConstructors = coverageConstructors;
     }
 
     // Read index.
@@ -861,8 +873,32 @@
       dst.addAll(src);
     } else {
       src.forEach((Uri key, Source value) {
-        if (value.source.isNotEmpty || !dst.containsKey(key)) {
+        Source originalDestinationSource = dst[key];
+        Source mergeFrom;
+        Source mergeTo;
+        if (value.source.isNotEmpty || originalDestinationSource == null) {
           dst[key] = value;
+          mergeFrom = originalDestinationSource;
+          mergeTo = value;
+        } else {
+          mergeFrom = value;
+          mergeTo = originalDestinationSource;
+        }
+
+        // TODO(jensj): Find out what the right thing to do is --- it probably
+        // depends on what we do if read the same library twice - do we merge or
+        // do we overwrite, and should we even support such a thing?
+
+        // Merge coverage. Note that mergeFrom might be null.
+        if (mergeTo.constantCoverageConstructors == null) {
+          mergeTo.constantCoverageConstructors =
+              mergeFrom?.constantCoverageConstructors;
+        } else if (mergeFrom?.constantCoverageConstructors == null) {
+          // Nothing to do.
+        } else {
+          // Bot are non-null: Merge.
+          mergeTo.constantCoverageConstructors
+              .addAll(mergeFrom.constantCoverageConstructors);
         }
       });
     }
diff --git a/pkg/kernel/lib/binary/ast_to_binary.dart b/pkg/kernel/lib/binary/ast_to_binary.dart
index 038a584..20881c8 100644
--- a/pkg/kernel/lib/binary/ast_to_binary.dart
+++ b/pkg/kernel/lib/binary/ast_to_binary.dart
@@ -794,19 +794,33 @@
 
       writeByteList(source.source);
 
-      List<int> lineStarts = source.lineStarts;
-      writeUInt30(lineStarts.length);
-      int previousLineStart = 0;
-      for (int j = 0; j < lineStarts.length; ++j) {
-        int lineStart = lineStarts[j];
-        writeUInt30(lineStart - previousLineStart);
-        previousLineStart = lineStart;
+      {
+        List<int> lineStarts = source.lineStarts;
+        writeUInt30(lineStarts.length);
+        int previousLineStart = 0;
+        for (int j = 0; j < lineStarts.length; ++j) {
+          int lineStart = lineStarts[j];
+          writeUInt30(lineStart - previousLineStart);
+          previousLineStart = lineStart;
+        }
       }
 
       String importUriAsString =
           source.importUri == null ? "" : "${source.importUri}";
       outputStringViaBuffer(importUriAsString, buffer);
 
+      {
+        Set<Reference> coverage = source.constantCoverageConstructors;
+        if (coverage == null || coverage.isEmpty) {
+          writeUInt30(0);
+        } else {
+          writeUInt30(coverage.length);
+          for (Reference reference in coverage) {
+            writeNonNullReference(reference);
+          }
+        }
+      }
+
       i++;
     }
 
diff --git a/pkg/vm/lib/transformations/type_flow/transformer.dart b/pkg/vm/lib/transformations/type_flow/transformer.dart
index 68688fd..06a0665 100644
--- a/pkg/vm/lib/transformations/type_flow/transformer.dart
+++ b/pkg/vm/lib/transformations/type_flow/transformer.dart
@@ -1230,6 +1230,12 @@
 
   void transform(Component component) {
     component.transformChildren(this);
+    for (Source source in component.uriToSource.values) {
+      source?.constantCoverageConstructors?.removeWhere((Reference reference) {
+        Member node = reference.asMember;
+        return !shaker.isMemberUsed(node) && !_preserveSpecialMember(node);
+      });
+    }
   }
 
   @override