Index: pkg/analyzer/lib/src/summary/pub_summary.dart |
diff --git a/pkg/analyzer/lib/src/summary/pub_summary.dart b/pkg/analyzer/lib/src/summary/pub_summary.dart |
index 0113564b25f583e216346968e291730261545cfb..171c2ff12da4986e4a97479d5eac4bae4ab2d30a 100644 |
--- a/pkg/analyzer/lib/src/summary/pub_summary.dart |
+++ b/pkg/analyzer/lib/src/summary/pub_summary.dart |
@@ -33,12 +33,6 @@ import 'package:meta/meta.dart'; |
import 'package:path/path.dart' as pathos; |
/** |
- * Return the raw string value of the variable with the given [name], |
- * or `null` of the variable is not defined. |
- */ |
-typedef String _GetDeclaredVariable(String name); |
- |
-/** |
* Unlinked and linked information about a [PubPackage]. |
*/ |
class LinkedPubPackage { |
@@ -180,94 +174,7 @@ class PubSummaryManager { |
* referencing the same packages. |
*/ |
List<LinkedPubPackage> getLinkedBundles(AnalysisContext context) { |
-// Stopwatch timer = new Stopwatch()..start(); |
- |
- _GetDeclaredVariable getDeclaredVariable = context.declaredVariables.get; |
- SourceFactory sourceFactory = context.sourceFactory; |
- _ListedPackages listedPackages = new _ListedPackages(sourceFactory); |
- |
- PackageBundle sdkBundle = sourceFactory.dartSdk.getLinkedBundle(); |
- if (sdkBundle == null) { |
- return const <LinkedPubPackage>[]; |
- } |
- |
- bool strong = context.analysisOptions.strongMode; |
- Map<PubPackage, PackageBundle> unlinkedBundles = |
- getUnlinkedBundles(context); |
- |
- // TODO(scheglov) remove debug output after optimizing |
-// print('LOADED ${unlinkedBundles.length} unlinked bundles' |
-// ' in ${timer.elapsedMilliseconds} ms'); |
-// timer..reset(); |
- |
- // If no unlinked bundles, there is nothing we can try to link. |
- if (unlinkedBundles.isEmpty) { |
- return const <LinkedPubPackage>[]; |
- } |
- |
- // Create graph nodes for packages. |
- List<_LinkNode> nodes = <_LinkNode>[]; |
- Map<String, _LinkNode> packageToNode = <String, _LinkNode>{}; |
- unlinkedBundles.forEach((package, unlinked) { |
- _LinkNode node = new _LinkNode(sdkBundle, getDeclaredVariable, |
- listedPackages, package, unlinked, packageToNode); |
- nodes.add(node); |
- packageToNode[package.name] = node; |
- }); |
- |
- // Compute transitive dependencies, mark some nodes as failed. |
- for (_LinkNode node in nodes) { |
- node.computeTransitiveDependencies(); |
- } |
- |
- // Attempt to read existing linked bundles. |
- for (_LinkNode node in nodes) { |
- _readLinked(node, strong); |
- } |
- |
- // Link new bundles, if allowed. |
- if (allowLinking) { |
- // 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.unlinked); |
- if (node.linked != null) { |
- store.addBundle(null, node.linked); |
- } |
- } |
- |
- // Link each package node. |
- for (_LinkNode node in nodes) { |
- if (!node.isEvaluated) { |
- new _LinkWalker(getDeclaredVariable, listedPackages, store, strong) |
- .walk(node); |
- } |
- } |
- |
- // Write newly linked bundles. |
- for (_LinkNode node in nodes) { |
- _writeLinked(node, strong); |
- } |
- } |
- |
- // Create successfully linked packages. |
- List<LinkedPubPackage> linkedPackages = <LinkedPubPackage>[]; |
- for (_LinkNode node in nodes) { |
- if (node.linked != null) { |
- linkedPackages.add(new LinkedPubPackage( |
- node.package, node.unlinked, node.linked, node.linkedHash)); |
- } |
- } |
- |
- // TODO(scheglov) remove debug output after optimizing |
-// print('LINKED ${linkedPackages.length} bundles' |
-// ' in ${timer.elapsedMilliseconds} ms'); |
- |
- // Done. |
- return linkedPackages; |
+ return new _ContextLinker(this, context).getLinkedBundles(); |
} |
/** |
@@ -381,17 +288,6 @@ class PubSummaryManager { |
} |
/** |
- * Return the name of the file for a linked bundle, in strong or spec mode. |
- */ |
- String _getLinkedName(String hash, bool strong) { |
- if (strong) { |
- return 'linked_$hash.ds'; |
- } else { |
- return 'linked_spec_$hash.ds'; |
- } |
- } |
- |
- /** |
* Return the name of the file for an unlinked bundle, in strong or spec mode. |
*/ |
String _getUnlinkedName(bool strong) { |
@@ -511,35 +407,6 @@ class PubSummaryManager { |
} |
/** |
- * Attempt to find the linked bundle that corresponds to the given [node] |
- * with all its transitive dependencies and put it into [_LinkNode.linked]. |
- */ |
- void _readLinked(_LinkNode node, bool strong) { |
- String hash = node.linkedHash; |
- if (hash != null) { |
- String fileName = _getLinkedName(hash, strong); |
- File file = node.package.folder.getChildAssumingFile(fileName); |
- // Try to find in the cache. |
- PackageBundle linked = linkedBundleMap[file.path]; |
- if (linked != null) { |
- node.linked = linked; |
- return; |
- } |
- // Try to read from the file system. |
- if (file.exists) { |
- try { |
- List<int> bytes = file.readAsBytesSync(); |
- linked = new PackageBundle.fromBuffer(bytes); |
- linkedBundleMap[file.path] = linked; |
- node.linked = linked; |
- } on FileSystemException { |
- // Ignore file system exceptions. |
- } |
- } |
- } |
- } |
- |
- /** |
* Schedule delayed computation of the next package unlinked bundle from the |
* set of [packagesToComputeUnlinked]. We delay each computation because we |
* want operations in analysis server to proceed, and computing bundles of |
@@ -570,20 +437,6 @@ class PubSummaryManager { |
} |
/** |
- * If a new linked bundle was linked for the given [node], write the bundle |
- * into the memory cache and the file system. |
- */ |
- void _writeLinked(_LinkNode node, bool strong) { |
- String hash = node.linkedHash; |
- if (hash != null && node.linkedNewBytes != null) { |
- String fileName = _getLinkedName(hash, strong); |
- File file = node.package.folder.getChildAssumingFile(fileName); |
- linkedBundleMap[file.path] = node.linked; |
- _writeAtomic(node.package.folder, fileName, node.linkedNewBytes); |
- } |
- } |
- |
- /** |
* If the given [uri] has the `package` scheme, return the name of the |
* package that contains the referenced resource. Otherwise return `null`. |
* |
@@ -617,33 +470,270 @@ class PubSummaryManager { |
} |
} |
+class _ContextLinker { |
+ final PubSummaryManager manager; |
+ final AnalysisContext context; |
+ |
+ final strong; |
+ final _ListedPackages listedPackages; |
+ final PackageBundle sdkBundle; |
+ |
+ final List<_LinkNode> nodes = <_LinkNode>[]; |
+ final Map<String, _LinkNode> packageToNode = <String, _LinkNode>{}; |
+ |
+ _ContextLinker(this.manager, AnalysisContext context) |
+ : context = context, |
+ strong = context.analysisOptions.strongMode, |
+ listedPackages = new _ListedPackages(context.sourceFactory), |
+ sdkBundle = context.sourceFactory.dartSdk.getLinkedBundle(); |
+ |
+ /** |
+ * Return the list of linked [LinkedPubPackage]s that can be provided at this |
+ * time for a subset of the packages used by the [context]. |
+ */ |
+ List<LinkedPubPackage> getLinkedBundles() { |
+// Stopwatch timer = new Stopwatch()..start(); |
+ |
+ if (sdkBundle == null) { |
+ return const <LinkedPubPackage>[]; |
+ } |
+ |
+ Map<PubPackage, PackageBundle> unlinkedBundles = |
+ manager.getUnlinkedBundles(context); |
+ |
+ // TODO(scheglov) remove debug output after optimizing |
+// print('LOADED ${unlinkedBundles.length} unlinked bundles' |
+// ' in ${timer.elapsedMilliseconds} ms'); |
+// timer..reset(); |
+ |
+ // If no unlinked bundles, there is nothing we can try to link. |
+ if (unlinkedBundles.isEmpty) { |
+ return const <LinkedPubPackage>[]; |
+ } |
+ |
+ // Create nodes for packages. |
+ unlinkedBundles.forEach((package, unlinked) { |
+ _LinkNode node = new _LinkNode(this, package, unlinked); |
+ nodes.add(node); |
+ packageToNode[package.name] = node; |
+ }); |
+ |
+ // Compute transitive dependencies, mark some nodes as failed. |
+ for (_LinkNode node in nodes) { |
+ node.computeTransitiveDependencies(); |
+ } |
+ |
+ // Attempt to read existing linked bundles. |
+ for (_LinkNode node in nodes) { |
+ _readLinked(node); |
+ } |
+ |
+ // Link new packages, if allowed. |
+ if (manager.allowLinking) { |
+ _link(); |
+ } |
+ |
+ // Create successfully linked packages. |
+ List<LinkedPubPackage> linkedPackages = <LinkedPubPackage>[]; |
+ for (_LinkNode node in nodes) { |
+ if (node.linked != null) { |
+ linkedPackages.add(new LinkedPubPackage( |
+ node.package, node.unlinked, node.linked, node.linkedHash)); |
+ } |
+ } |
+ |
+ // TODO(scheglov) remove debug output after optimizing |
+// print('LINKED ${linkedPackages.length} bundles' |
+// ' in ${timer.elapsedMilliseconds} ms'); |
+ |
+ // Done. |
+ return linkedPackages; |
+ } |
+ |
+ String _getDeclaredVariable(String name) { |
+ return context.declaredVariables.get(name); |
+ } |
+ |
+ /** |
+ * Return the name of the file for a linked bundle, in strong or spec mode. |
+ */ |
+ String _getLinkedName(String hash) { |
+ if (strong) { |
+ return 'linked_$hash.ds'; |
+ } else { |
+ return 'linked_spec_$hash.ds'; |
+ } |
+ } |
+ |
+ void _link() { |
+ // 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.unlinked); |
+ if (node.linked != null) { |
+ store.addBundle(null, node.linked); |
+ } |
+ } |
+ |
+ // Prepare URIs to link. |
+ Map<String, _LinkNode> uriToNode = <String, _LinkNode>{}; |
+ for (_LinkNode node in nodes) { |
+ if (!node.isReady) { |
+ for (String uri in node.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]; |
+ }, _getDeclaredVariable, strong); |
+ |
+ // 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.linkedNewBytes = bytes; |
+ node.linked = new PackageBundle.fromBuffer(bytes); |
+ } |
+ } |
+ |
+ // Write newly linked bundles. |
+ for (_LinkNode node in nodes) { |
+ _writeLinked(node); |
+ } |
+ } |
+ |
+ /** |
+ * Attempt to find the linked bundle that corresponds to the given [node] |
+ * with all its transitive dependencies and put it into [_LinkNode.linked]. |
+ */ |
+ void _readLinked(_LinkNode node) { |
+ String hash = node.linkedHash; |
+ if (hash != null) { |
+ String fileName = _getLinkedName(hash); |
+ File file = node.package.folder.getChildAssumingFile(fileName); |
+ // Try to find in the cache. |
+ PackageBundle linked = manager.linkedBundleMap[file.path]; |
+ if (linked != null) { |
+ node.linked = linked; |
+ return; |
+ } |
+ // Try to read from the file system. |
+ if (file.exists) { |
+ try { |
+ List<int> bytes = file.readAsBytesSync(); |
+ linked = new PackageBundle.fromBuffer(bytes); |
+ manager.linkedBundleMap[file.path] = linked; |
+ node.linked = linked; |
+ } on FileSystemException { |
+ // Ignore file system exceptions. |
+ } |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * If a new linked bundle was linked for the given [node], write the bundle |
+ * into the memory cache and the file system. |
+ */ |
+ void _writeLinked(_LinkNode node) { |
+ String hash = node.linkedHash; |
+ if (hash != null && node.linkedNewBytes != null) { |
+ String fileName = _getLinkedName(hash); |
+ File file = node.package.folder.getChildAssumingFile(fileName); |
+ manager.linkedBundleMap[file.path] = node.linked; |
+ manager._writeAtomic(node.package.folder, fileName, node.linkedNewBytes); |
+ } |
+ } |
+} |
+ |
/** |
- * Specialization of [Node] for linking packages in proper dependency order. |
+ * Information about a package to link. |
*/ |
-class _LinkNode extends Node<_LinkNode> { |
- final PackageBundle sdkBundle; |
- final _GetDeclaredVariable getDeclaredVariable; |
- final _ListedPackages listedPackages; |
+class _LinkNode { |
+ final _ContextLinker linker; |
final PubPackage package; |
final PackageBundle unlinked; |
- final Map<String, _LinkNode> packageToNode; |
bool failed = false; |
Set<_LinkNode> transitiveDependencies; |
+ |
+ List<_LinkNode> _dependencies; |
String _linkedHash; |
List<int> linkedNewBytes; |
PackageBundle linked; |
- _LinkNode(this.sdkBundle, this.getDeclaredVariable, this.listedPackages, |
- this.package, this.unlinked, this.packageToNode); |
+ _LinkNode(this.linker, this.package, this.unlinked); |
- @override |
- bool get isEvaluated => linked != null || failed; |
+ /** |
+ * 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 (uriStr.startsWith('package:')) { |
+ String package = PubSummaryManager.getPackageName(uriStr); |
+ _LinkNode packageNode = linker.packageToNode[package]; |
+ if (packageNode == null && linker.listedPackages.isListed(uriStr)) { |
+ failed = true; |
+ } |
+ if (packageNode != null) { |
+ dependencies.add(packageNode); |
+ } |
+ } else { |
+ failed = true; |
+ } |
+ } |
+ |
+ for (UnlinkedUnit unit in 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 => linked != null || failed; |
/** |
* Return the hash string that corresponds to this linked bundle in the |
- * context of its [sdkBundle] and transitive dependencies. Return `null` if |
+ * context of its SDK bundle and transitive dependencies. Return `null` if |
* the hash computation fails, because for example the full transitive |
* dependencies cannot computed. |
*/ |
@@ -652,56 +742,45 @@ class _LinkNode extends Node<_LinkNode> { |
ApiSignature signature = new ApiSignature(); |
// Add all unlinked API signatures. |
List<String> signatures = <String>[]; |
- signatures.add(sdkBundle.apiSignature); |
+ signatures.add(linker.sdkBundle.apiSignature); |
transitiveDependencies |
.map((node) => node.unlinked.apiSignature) |
.forEach(signatures.add); |
signatures.sort(); |
signatures.forEach(signature.addString); |
// Combine into a single hash. |
- _appendDeclaredVariables(signature); |
+ appendDeclaredVariables(signature); |
_linkedHash = signature.toHex(); |
} |
return _linkedHash; |
} |
- @override |
- List<_LinkNode> computeDependencies() { |
- 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 (uriStr.startsWith('package:')) { |
- String package = PubSummaryManager.getPackageName(uriStr); |
- _LinkNode packageNode = packageToNode[package]; |
- if (packageNode == null && listedPackages.isListed(uriStr)) { |
- failed = true; |
+ /** |
+ * Append names and values of all referenced declared variables (even the |
+ * ones without actually declared values) to the given [signature]. |
+ */ |
+ void appendDeclaredVariables(ApiSignature signature) { |
+ Set<String> nameSet = new Set<String>(); |
+ for (_LinkNode node in transitiveDependencies) { |
+ for (UnlinkedUnit unit in node.unlinked.unlinkedUnits) { |
+ for (UnlinkedImport import in unit.imports) { |
+ for (UnlinkedConfiguration configuration in import.configurations) { |
+ nameSet.add(configuration.name); |
+ } |
} |
- if (packageNode != null) { |
- dependencies.add(packageNode); |
+ for (UnlinkedExportPublic export in unit.publicNamespace.exports) { |
+ for (UnlinkedConfiguration configuration in export.configurations) { |
+ nameSet.add(configuration.name); |
+ } |
} |
- } else { |
- failed = true; |
} |
} |
- |
- for (UnlinkedUnit unit in unlinked.unlinkedUnits) { |
- for (UnlinkedImport import in unit.imports) { |
- if (!import.isImplicit) { |
- appendDependency(import.uri); |
- } |
- } |
- for (UnlinkedExportPublic export in unit.publicNamespace.exports) { |
- appendDependency(export.uri); |
- } |
+ List<String> sortedNameList = nameSet.toList()..sort(); |
+ signature.addInt(sortedNameList.length); |
+ for (String name in sortedNameList) { |
+ signature.addString(name); |
+ signature.addString(linker._getDeclaredVariable(name) ?? ''); |
} |
- |
- return dependencies.toList(); |
} |
/** |
@@ -729,83 +808,6 @@ class _LinkNode extends Node<_LinkNode> { |
@override |
String toString() => package.toString(); |
- |
- /** |
- * Append names and values of all referenced declared variables (even the |
- * ones without actually declared values) to the given [signature]. |
- */ |
- void _appendDeclaredVariables(ApiSignature signature) { |
- Set<String> nameSet = new Set<String>(); |
- for (_LinkNode node in transitiveDependencies) { |
- for (UnlinkedUnit unit in node.unlinked.unlinkedUnits) { |
- for (UnlinkedImport import in unit.imports) { |
- for (UnlinkedConfiguration configuration in import.configurations) { |
- nameSet.add(configuration.name); |
- } |
- } |
- for (UnlinkedExportPublic export in unit.publicNamespace.exports) { |
- for (UnlinkedConfiguration configuration in export.configurations) { |
- nameSet.add(configuration.name); |
- } |
- } |
- } |
- } |
- List<String> sortedNameList = nameSet.toList()..sort(); |
- signature.addInt(sortedNameList.length); |
- for (String name in sortedNameList) { |
- signature.addString(name); |
- signature.addString(getDeclaredVariable(name) ?? ''); |
- } |
- } |
-} |
- |
-/** |
- * Specialization of [DependencyWalker] for linking packages. |
- */ |
-class _LinkWalker extends DependencyWalker<_LinkNode> { |
- final _GetDeclaredVariable getDeclaredVariable; |
- final _ListedPackages listedPackages; |
- final SummaryDataStore store; |
- final bool strong; |
- |
- _LinkWalker( |
- this.getDeclaredVariable, this.listedPackages, this.store, this.strong); |
- |
- @override |
- void evaluate(_LinkNode node) { |
- evaluateScc([node]); |
- } |
- |
- @override |
- void evaluateScc(List<_LinkNode> scc) { |
- Map<String, _LinkNode> uriToNode = <String, _LinkNode>{}; |
- for (_LinkNode node in scc) { |
- for (String uri in node.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]; |
- }, getDeclaredVariable, strong); |
- // Assemble linked bundles and put them into the store. |
- for (_LinkNode node in scc) { |
- PackageBundleAssembler assembler = new PackageBundleAssembler(); |
- linkedLibraries.forEach((uri, linkedLibrary) { |
- if (identical(uriToNode[uri], node)) { |
- assembler.addLinkedLibrary(uri, linkedLibrary); |
- } |
- }); |
- List<int> bytes = assembler.assemble().toBuffer(); |
- node.linkedNewBytes = bytes; |
- node.linked = new PackageBundle.fromBuffer(bytes); |
- store.addBundle(null, node.linked); |
- } |
- } |
} |
/** |