Read linked bundles from ByteStore.

R=brianwilkerson@google.com

Change-Id: I5be61a2d03c57d1b6bd7a765e50b235ef3f8af5b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/100983
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/analysis/file_state.dart b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
index e330665..c67f47a 100644
--- a/pkg/analyzer/lib/src/dart/analysis/file_state.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
@@ -587,7 +587,7 @@
   }
 
   @override
-  String toString() => path;
+  String toString() => path ?? '<unresolved>';
 
   /**
    * Compute the full or partial map of exported declarations for this library.
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_context.dart b/pkg/analyzer/lib/src/dart/analysis/library_context.dart
index daeaedc..421a8df 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_context.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_context.dart
@@ -9,6 +9,7 @@
 import 'package:analyzer/src/dart/analysis/byte_store.dart';
 import 'package:analyzer/src/dart/analysis/driver.dart';
 import 'package:analyzer/src/dart/analysis/file_state.dart';
+import 'package:analyzer/src/dart/analysis/library_graph.dart';
 import 'package:analyzer/src/dart/analysis/performance_logger.dart';
 import 'package:analyzer/src/dart/analysis/restricted_analysis_context.dart';
 import 'package:analyzer/src/dart/element/element.dart';
@@ -219,9 +220,72 @@
   }
 
   /// Load data required to access elements of the given [targetLibrary].
-  ///
-  /// TODO(scheglov) Implement loading cached linked bundles.
   void load2(FileState targetLibrary) {
+    var loadedBundles = Set<LibraryCycle>.identity();
+    var inputBundles = <LinkedNodeBundle>[];
+
+    void loadBundle(LibraryCycle cycle) {
+      if (!loadedBundles.add(cycle)) return;
+
+      logger.run('Prepare linked bundle', () {
+        logger.writeln('Libraries: ${cycle.libraries}');
+        cycle.directDependencies.forEach(loadBundle);
+
+        var key = cycle.transitiveSignature + '.linked_bundle';
+        var bytes = byteStore.get(key);
+
+        if (bytes == null) {
+          var inputLibraries = <link2.LinkInputLibrary>[];
+          logger.run('Prepare input libraries', () {
+            for (var libraryFile in cycle.libraries) {
+              var librarySource = libraryFile.source;
+              if (librarySource == null) continue;
+
+              var inputUnits = <link2.LinkInputUnit>[];
+              for (var file in libraryFile.libraryFiles) {
+                var isSynthetic = !file.exists;
+                inputUnits.add(
+                  link2.LinkInputUnit(file.source, isSynthetic, file.parse()),
+                );
+              }
+
+              inputLibraries.add(
+                link2.LinkInputLibrary(librarySource, inputUnits),
+              );
+            }
+            logger.writeln('Prepared ${inputLibraries.length} libraries.');
+          });
+
+          link2.LinkResult linkResult;
+          logger.run('Link libraries', () {
+            linkResult = link2.link(
+              analysisContext.analysisOptions,
+              analysisContext.sourceFactory,
+              analysisContext.declaredVariables,
+              inputBundles,
+              inputLibraries,
+            );
+            logger.writeln('Linked ${inputLibraries.length} libraries.');
+          });
+
+          bytes = linkResult.bundle.toBuffer();
+          byteStore.put(key, bytes);
+          logger.writeln('Stored ${bytes.length} bytes.');
+        } else {
+          logger.writeln('Loaded ${bytes.length} bytes.');
+        }
+
+        inputBundles.add(
+          LinkedNodeBundle.fromBuffer(bytes),
+        );
+      });
+    }
+
+    logger.run('Prepare linked bundles', () {
+      var libraryCycle = targetLibrary.libraryCycle;
+      loadBundle(libraryCycle);
+    });
+
     var rootReference = Reference.root();
     rootReference.getChild('dart:core').getChild('dynamic').element =
         DynamicElementImpl.instance;
@@ -232,39 +296,11 @@
       rootReference,
     );
 
-    var inputLibraries = <link2.LinkInputLibrary>[];
-    var transitiveFiles = targetLibrary.transitiveFiles;
-    for (var libraryFile in transitiveFiles) {
-      if (libraryFile.isPart) {
-        elementFactory.partUriSet.add(libraryFile.uriStr);
-      }
-
-      // TODO(scheglov) Why do we even we such invalid files in transitive?
-      var librarySource = libraryFile.source;
-      if (librarySource == null) continue;
-
-      var inputUnits = <link2.LinkInputUnit>[];
-      for (var file in libraryFile.libraryFiles) {
-        var isSynthetic = !file.exists;
-        inputUnits.add(
-          link2.LinkInputUnit(file.source, isSynthetic, file.parse()),
-        );
-      }
-      inputLibraries.add(
-        link2.LinkInputLibrary(librarySource, inputUnits),
+    for (var bundle in inputBundles) {
+      elementFactory.addBundle(
+        LinkedBundleContext(elementFactory, bundle),
       );
     }
-    var linkResult = link2.link(
-      analysisContext.analysisOptions,
-      analysisContext.sourceFactory,
-      analysisContext.declaredVariables,
-      [],
-      inputLibraries,
-    );
-
-    elementFactory.addBundle(
-      LinkedBundleContext(elementFactory, linkResult.bundle),
-    );
 
     var dartCore = elementFactory.libraryOfUri('dart:core');
     var dartAsync = elementFactory.libraryOfUri('dart:async');
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_graph.dart b/pkg/analyzer/lib/src/dart/analysis/library_graph.dart
index 7b0bcaf..3e79078 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_graph.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_graph.dart
@@ -8,7 +8,6 @@
 import 'package:analyzer/src/summary/api_signature.dart';
 import 'package:analyzer/src/summary/link.dart' as graph
     show DependencyWalker, Node;
-import 'package:meta/meta.dart';
 
 /// Ensure that the [FileState.libraryCycle] for the [file] and anything it
 /// depends on is computed.
@@ -23,7 +22,6 @@
   final List<FileState> libraries = [];
 
   /// The library cycles that this cycle references directly.
-  @visibleForTesting
   final Set<LibraryCycle> directDependencies = new Set<LibraryCycle>();
 
   /// The cycles that use this cycle, used to [invalidate] transitively.
@@ -35,11 +33,11 @@
   /// transitive signatures of the cycles that the [libraries] reference
   /// directly.  So, indirectly it is based on the transitive closure of all
   /// files that [libraries] reference (but we don't compute these files).
-  String _transitiveSignature;
+  String transitiveSignature;
 
   /// The map from a library in [libraries] to its transitive signature.
   ///
-  /// It is almost the same as [_transitiveSignature], but is also based on
+  /// It is almost the same as [transitiveSignature], but is also based on
   /// the URI of this specific library.  Currently we store each linked library
   /// with its own key, so we need unique keys.  However practically we never
   /// can use just *one* library of a cycle, we always use the whole cycle.
@@ -49,7 +47,7 @@
 
   LibraryCycle();
 
-  LibraryCycle.external() : _transitiveSignature = '<external>';
+  LibraryCycle.external() : transitiveSignature = '<external>';
 
   /// Invalidate this cycle and any cycles that directly or indirectly use it.
   ///
@@ -125,6 +123,8 @@
     for (var node in scc) {
       cycle.libraries.add(node.file);
 
+      signature.addString(node.file.uriStr);
+
       signature.addInt(node.file.libraryFiles.length);
       for (var file in node.file.libraryFiles) {
         signature.addBytes(file.apiSignature);
@@ -132,13 +132,13 @@
     }
 
     // Compute the general library cycle signature.
-    cycle._transitiveSignature = signature.toHex();
+    cycle.transitiveSignature = signature.toHex();
 
     // Compute library specific signatures.
     for (var node in scc) {
       var librarySignatureBuilder = new ApiSignature()
         ..addString(node.file.uriStr)
-        ..addString(cycle._transitiveSignature);
+        ..addString(cycle.transitiveSignature);
       var librarySignature = librarySignatureBuilder.toHex();
 
       node.file.internal_setLibraryCycle(
@@ -166,7 +166,7 @@
 
       if (cycle.directDependencies.add(referencedCycle)) {
         referencedCycle._directUsers.add(cycle);
-        signature.addString(referencedCycle._transitiveSignature);
+        signature.addString(referencedCycle.transitiveSignature);
       }
     }
   }
diff --git a/pkg/analyzer/lib/src/summary2/linked_element_factory.dart b/pkg/analyzer/lib/src/summary2/linked_element_factory.dart
index 414d97a..64cb319 100644
--- a/pkg/analyzer/lib/src/summary2/linked_element_factory.dart
+++ b/pkg/analyzer/lib/src/summary2/linked_element_factory.dart
@@ -19,7 +19,6 @@
   final AnalysisSession analysisSession;
   final Reference rootReference;
   final Map<String, LinkedLibraryContext> libraryMap = {};
-  final Set<String> partUriSet = Set<String>();
 
   CoreTypes _coreTypes;
 
@@ -72,7 +71,8 @@
   }
 
   bool isLibraryUri(String uriStr) {
-    return !partUriSet.contains(uriStr);
+    var libraryContext = libraryMap[uriStr];
+    return !libraryContext.definingUnit.hasPartOfDirective;
   }
 
   LibraryElementImpl libraryOfUri(String uriStr) {
diff --git a/pkg/analyzer/lib/src/summary2/linked_unit_context.dart b/pkg/analyzer/lib/src/summary2/linked_unit_context.dart
index 09a4027..dabace1 100644
--- a/pkg/analyzer/lib/src/summary2/linked_unit_context.dart
+++ b/pkg/analyzer/lib/src/summary2/linked_unit_context.dart
@@ -71,6 +71,15 @@
       this.data,
       this.tokensContext);
 
+  bool get hasPartOfDirective {
+    for (var directive in unit_withDirectives.directives) {
+      if (directive is PartOfDirective) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   CompilationUnit get unit => _unit;
 
   CompilationUnit get unit_withDeclarations {