Index: pkg/analyzer/lib/src/summary/bazel_summary.dart |
diff --git a/pkg/analyzer/lib/src/summary/bazel_summary.dart b/pkg/analyzer/lib/src/summary/bazel_summary.dart |
index 5697784da960565d4ed954b6e8a9ab9d3c45cb5c..6c65cc2ea4cf2f2f7ab3052dc80edd5a3b9054f7 100644 |
--- a/pkg/analyzer/lib/src/summary/bazel_summary.dart |
+++ b/pkg/analyzer/lib/src/summary/bazel_summary.dart |
@@ -8,7 +8,12 @@ import 'package:analyzer/file_system/file_system.dart'; |
import 'package:analyzer/src/generated/engine.dart'; |
import 'package:analyzer/src/generated/source.dart'; |
import 'package:analyzer/src/generated/utilities_collection.dart'; |
+import 'package:analyzer/src/summary/format.dart'; |
import 'package:analyzer/src/summary/idl.dart'; |
+import 'package:analyzer/src/summary/link.dart'; |
+import 'package:analyzer/src/summary/package_bundle_reader.dart'; |
+import 'package:analyzer/src/summary/summarize_elements.dart'; |
+import 'package:analyzer/src/util/fast_uri.dart'; |
import 'package:convert/convert.dart'; |
import 'package:crypto/crypto.dart'; |
import 'package:meta/meta.dart'; |
@@ -29,9 +34,16 @@ class Package { |
final PackageBundle unlinked; |
final Set<String> _unitUris = new Set<String>(); |
+ PackageBundle _linked; |
+ |
Package(this.unlinkedFile, this.unlinked) { |
_unitUris.addAll(unlinked.unlinkedUnitUris); |
} |
+ |
+ PackageBundle get linked => _linked; |
+ |
+ @override |
+ String toString() => '$unlinkedFile'; |
} |
/** |
@@ -45,6 +57,7 @@ class SummaryProvider { |
final ResourceProvider provider; |
final GetOutputFolder getOutputFolder; |
final AnalysisContext context; |
+ final PackageBundle sdkBundle; |
/** |
* Mapping from bundle paths to corresponding [Package]s. The packages in |
@@ -53,7 +66,14 @@ class SummaryProvider { |
*/ |
final Map<Folder, List<Package>> folderToPackagesMap = {}; |
- SummaryProvider(this.provider, this.getOutputFolder, this.context); |
+ /** |
+ * Mapping from [Uri]s to corresponding [_LinkNode]s. |
+ */ |
+ final Map<Uri, _LinkNode> uriToNodeMap = {}; |
+ |
+ SummaryProvider(this.provider, this.getOutputFolder, AnalysisContext context) |
+ : context = context, |
+ sdkBundle = context.sourceFactory.dartSdk?.getLinkedBundle(); |
/** |
* Return the complete list of [Package]s that are required to provide all |
@@ -68,8 +88,29 @@ class SummaryProvider { |
* bundles are not built, or out of date, etc, then `null` is returned. |
*/ |
List<Package> getLinkedPackages(Source source) { |
- // TODO(scheglov) implement |
- return null; |
+ // Find the node that contains the source. |
+ _LinkNode node = _getLinkNodeForUri(source.uri); |
+ if (node == null) { |
+ return null; |
+ } |
+ |
+ // Compute all transitive dependencies. |
+ node.computeTransitiveDependencies(); |
+ List<_LinkNode> nodes = node.transitiveDependencies.toList(); |
+ nodes.forEach((dependency) => dependency.computeTransitiveDependencies()); |
+ |
+ // Fail if any dependency cannot be resolved. |
+ if (node.failed) { |
+ return null; |
+ } |
+ |
+ _link(nodes); |
+ |
+ // Create successfully linked packages. |
+ return nodes |
+ .map((node) => node.package) |
+ .where((package) => package.linked != null) |
+ .toList(); |
} |
/** |
@@ -103,6 +144,20 @@ class SummaryProvider { |
} |
/** |
+ * Return the node for the given [uri], or `null` if there is no unlinked |
+ * bundle that contains [uri]. |
+ */ |
+ _LinkNode _getLinkNodeForUri(Uri uri) { |
+ return uriToNodeMap.putIfAbsent(uri, () { |
+ Package package = getUnlinkedForUri(uri); |
+ if (package == null) { |
+ return null; |
+ } |
+ return new _LinkNode(this, package); |
+ }); |
+ } |
+ |
+ /** |
* Return all consistent unlinked [Package]s in the given [folder]. Some of |
* the returned packages might be already linked. |
*/ |
@@ -154,6 +209,56 @@ class SummaryProvider { |
} |
/** |
+ * Link the given [nodes]. |
+ */ |
+ void _link(List<_LinkNode> nodes) { |
+ // Fill the store with bundles. |
+ // Append the linked SDK bundle. |
+ // Append unlinked and (if read from a cache) linked package bundles. |
+ SummaryDataStore store = new SummaryDataStore(const <String>[]); |
+ store.addBundle(null, sdkBundle); |
+ for (_LinkNode node in nodes) { |
+ store.addBundle(null, node.package.unlinked); |
+ if (node.package.linked != null) { |
+ store.addBundle(null, node.package.linked); |
+ } |
+ } |
+ |
+ // Prepare URIs to link. |
+ Map<String, _LinkNode> uriToNode = <String, _LinkNode>{}; |
+ for (_LinkNode node in nodes) { |
+ if (!node.isReady) { |
+ for (String uri in node.package.unlinked.unlinkedUnitUris) { |
+ uriToNode[uri] = node; |
+ } |
+ } |
+ } |
+ Set<String> libraryUris = uriToNode.keys.toSet(); |
+ |
+ // Perform linking. |
+ Map<String, LinkedLibraryBuilder> linkedLibraries = |
+ link(libraryUris, (String uri) { |
+ return store.linkedMap[uri]; |
+ }, (String uri) { |
+ return store.unlinkedMap[uri]; |
+ }, context.declaredVariables.get, context.analysisOptions.strongMode); |
+ |
+ // Assemble newly linked bundles. |
+ for (_LinkNode node in nodes) { |
+ if (!node.isReady) { |
+ PackageBundleAssembler assembler = new PackageBundleAssembler(); |
+ linkedLibraries.forEach((uri, linkedLibrary) { |
+ if (identical(uriToNode[uri], node)) { |
+ assembler.addLinkedLibrary(uri, linkedLibrary); |
+ } |
+ }); |
+ List<int> bytes = assembler.assemble().toBuffer(); |
+ node.package._linked = new PackageBundle.fromBuffer(bytes); |
+ } |
+ } |
+ } |
+ |
+ /** |
* Read the unlinked [Package] from the given [file], or return `null` if the |
* file does not exist, or it cannot be read, or is not consistent with the |
* constituent sources on the file system. |
@@ -172,3 +277,92 @@ class SummaryProvider { |
return null; |
} |
} |
+ |
+/** |
+ * Information about a single [Package]. |
+ */ |
+class _LinkNode { |
+ final SummaryProvider linker; |
+ final Package package; |
+ |
+ bool failed = false; |
+ Set<_LinkNode> transitiveDependencies; |
+ |
+ List<_LinkNode> _dependencies; |
+ |
+ _LinkNode(this.linker, this.package); |
+ |
+ /** |
+ * Retrieve the dependencies of this node. |
+ */ |
+ List<_LinkNode> get dependencies { |
+ if (_dependencies == null) { |
+ Set<_LinkNode> dependencies = new Set<_LinkNode>(); |
+ |
+ void appendDependency(String uriStr) { |
+ Uri uri = FastUri.parse(uriStr); |
+ if (!uri.hasScheme) { |
+ // A relative path in this package, skip it. |
+ } else if (uri.scheme == 'dart') { |
+ // Dependency on the SDK is implicit and always added. |
+ // The SDK linked bundle is precomputed before linking packages. |
+ } else if (uri.scheme == 'package') { |
+ _LinkNode packageNode = linker._getLinkNodeForUri(uri); |
+ if (packageNode == null) { |
+ failed = true; |
+ } |
+ if (packageNode != null) { |
+ dependencies.add(packageNode); |
+ } |
+ } else { |
+ failed = true; |
+ } |
+ } |
+ |
+ for (UnlinkedUnit unit in package.unlinked.unlinkedUnits) { |
+ for (UnlinkedImport import in unit.imports) { |
+ if (!import.isImplicit) { |
+ appendDependency(import.uri); |
+ } |
+ } |
+ for (UnlinkedExportPublic export in unit.publicNamespace.exports) { |
+ appendDependency(export.uri); |
+ } |
+ } |
+ |
+ _dependencies = dependencies.toList(); |
+ } |
+ return _dependencies; |
+ } |
+ |
+ /** |
+ * Return `true` is the node is ready - has the linked bundle or failed (does |
+ * not have all required dependencies). |
+ */ |
+ bool get isReady => package.linked != null || failed; |
+ |
+ /** |
+ * Compute the set of existing transitive dependencies for this node. |
+ * If any dependency cannot be resolved, then set [failed] to `true`. |
+ * Only unlinked bundle is used, so this method can be called before linking. |
+ */ |
+ void computeTransitiveDependencies() { |
+ if (transitiveDependencies == null) { |
+ transitiveDependencies = new Set<_LinkNode>(); |
+ |
+ void appendDependencies(_LinkNode node) { |
+ if (transitiveDependencies.add(node)) { |
+ node.dependencies.forEach(appendDependencies); |
+ } |
+ } |
+ |
+ appendDependencies(this); |
+ if (transitiveDependencies.any((node) => node.failed)) { |
+ failed = true; |
+ } |
+ } |
+ } |
+ |
+ @override |
+ String toString() => package.toString(); |
+} |