Chromium Code Reviews| 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 60a132077929311f88955ce13ba2892d1091d6f6..e3f4eee9b9ffe183db16e2d439a3d49926b0a10b 100644 |
| --- a/pkg/analyzer/lib/src/summary/bazel_summary.dart |
| +++ b/pkg/analyzer/lib/src/summary/bazel_summary.dart |
| @@ -8,6 +8,7 @@ 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/api_signature.dart'; |
| import 'package:analyzer/src/summary/format.dart'; |
| import 'package:analyzer/src/summary/idl.dart'; |
| import 'package:analyzer/src/summary/link.dart'; |
| @@ -98,11 +99,24 @@ class Package { |
| */ |
| class SummaryProvider { |
| final ResourceProvider provider; |
| + final String tempFileName; |
| final GetOutputFolder getOutputFolder; |
| + final Folder linkedCacheFolder; |
| final AnalysisContext context; |
| final PackageBundle sdkBundle; |
| /** |
| + * If `true` (by default), then linking new bundles is allowed. |
| + * Otherwise only using existing cached bundles can be used. |
| + */ |
| + final bool allowLinking; |
| + |
| + /** |
| + * See [PackageBundleAssembler.currentMajorVersion]. |
| + */ |
| + final int majorVersion; |
| + |
| + /** |
| * Mapping from bundle paths to corresponding [Package]s. The packages in |
| * the map were consistent with their constituent sources at the moment when |
| * they were put into the map. |
| @@ -114,7 +128,16 @@ class SummaryProvider { |
| */ |
| final Map<Uri, _LinkNode> uriToNodeMap = {}; |
| - SummaryProvider(this.provider, this.getOutputFolder, AnalysisContext context) |
| + SummaryProvider( |
| + this.provider, |
| + this.tempFileName, |
| + this.getOutputFolder, |
| + this.linkedCacheFolder, |
| + AnalysisContext context, |
| + {@visibleForTesting |
| + this.allowLinking: true, |
| + @visibleForTesting |
| + this.majorVersion: PackageBundleAssembler.currentMajorVersion}) |
| : context = context, |
| sdkBundle = context.sourceFactory.dartSdk?.getLinkedBundle(); |
| @@ -147,7 +170,15 @@ class SummaryProvider { |
| return null; |
| } |
| - _link(nodes); |
| + // Read existing cached linked bundles. |
| + for (_LinkNode node in nodes) { |
| + _readLinked(node); |
| + } |
| + |
| + // Link new packages, if allowed. |
| + if (allowLinking) { |
| + _link(nodes); |
| + } |
| // Create successfully linked packages. |
| return nodes |
| @@ -187,6 +218,17 @@ class SummaryProvider { |
| } |
| /** |
| + * Return the name of the file for a linked bundle, in strong or spec mode. |
| + */ |
| + String _getLinkedName(String hash) { |
| + if (context.analysisOptions.strongMode) { |
| + return 'linked_$hash.ds'; |
| + } else { |
| + return 'linked_spec_$hash.ds'; |
| + } |
| + } |
| + |
| + /** |
| * Return the node for the given [uri], or `null` if there is no unlinked |
| * bundle that contains [uri]. |
| */ |
| @@ -297,6 +339,27 @@ class SummaryProvider { |
| }); |
| List<int> bytes = assembler.assemble().toBuffer(); |
| node.package._linked = new PackageBundle.fromBuffer(bytes); |
| + _writeLinked(node, bytes); |
| + } |
| + } |
| + } |
| + |
| + /** |
| + * Attempt to read the linked bundle that corresponds to the given [node] |
| + * with all its transitive dependencies. |
| + */ |
| + void _readLinked(_LinkNode node) { |
| + if (!node.isReady && node.linkedHash != null) { |
| + String fileName = _getLinkedName(node.linkedHash); |
| + File file = linkedCacheFolder.getChildAssumingFile(fileName); |
| + // Try to read from the file system. |
| + if (file.exists) { |
| + try { |
| + List<int> bytes = file.readAsBytesSync(); |
| + node.package._linked = new PackageBundle.fromBuffer(bytes); |
| + } on FileSystemException { |
| + // Ignore file system exceptions. |
| + } |
| } |
| } |
| } |
| @@ -310,6 +373,10 @@ class SummaryProvider { |
| try { |
| List<int> bytes = file.readAsBytesSync(); |
| PackageBundle bundle = new PackageBundle.fromBuffer(bytes); |
| + // Check the major version. |
| + if (bundle.majorVersion != majorVersion) { |
| + return null; |
| + } |
| // Check for consistency, and fail if it's not. |
| if (!_isUnlinkedBundleConsistent(bundle)) { |
| return null; |
| @@ -319,6 +386,30 @@ class SummaryProvider { |
| } on FileSystemException {} |
| return null; |
| } |
| + |
| + /** |
| + * Atomically write the given [bytes] into the file in the [folder]. |
| + */ |
| + void _writeAtomic(Folder folder, String fileName, List<int> bytes) { |
| + String filePath = folder.getChildAssumingFile(fileName).path; |
| + File tempFile = folder.getChildAssumingFile(tempFileName); |
| + tempFile.writeAsBytesSync(bytes); |
| + tempFile.renameSync(filePath); |
| + } |
| + |
| + /** |
| + * 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, List<int> bytes) { |
| + String hash = node.linkedHash; |
| + if (hash != null) { |
| + String fileName = _getLinkedName(hash); |
| +// File file = linkedCacheFolder.getChildAssumingFile(fileName); |
|
Paul Berry
2016/09/30 20:30:47
Remove commented out lines
scheglov
2016/09/30 20:38:28
Done.
|
| +// manager.linkedBundleMap[file.path] = node.linked; |
| + _writeAtomic(linkedCacheFolder, fileName, bytes); |
| + } |
| + } |
| } |
| /** |
| @@ -332,6 +423,7 @@ class _LinkNode { |
| Set<_LinkNode> transitiveDependencies; |
| List<_LinkNode> _dependencies; |
| + String _linkedHash; |
| _LinkNode(this.linker, this.package); |
| @@ -385,6 +477,58 @@ class _LinkNode { |
| bool get isReady => package.linked != null || failed; |
| /** |
| + * Return the hash string that corresponds to this linked bundle in the |
| + * context of its SDK bundle and transitive dependencies. Return `null` if |
| + * the hash computation fails, because for example the full transitive |
| + * dependencies cannot computed. |
| + */ |
| + String get linkedHash { |
|
Paul Berry
2016/09/30 20:30:47
This method and the one below are copied almost ex
scheglov
2016/09/30 20:38:28
Yes, these two methods are similar to the ones we
|
| + if (_linkedHash == null && transitiveDependencies != null && !failed) { |
| + ApiSignature signature = new ApiSignature(); |
| + // Add all unlinked API signatures. |
| + List<String> signatures = <String>[]; |
| + signatures.add(linker.sdkBundle.apiSignature); |
| + transitiveDependencies |
| + .map((node) => node.package.unlinked.apiSignature) |
| + .forEach(signatures.add); |
| + signatures.sort(); |
| + signatures.forEach(signature.addString); |
| + // Combine into a single hash. |
| + appendDeclaredVariables(signature); |
| + _linkedHash = signature.toHex(); |
| + } |
| + return _linkedHash; |
| + } |
| + |
| + /** |
| + * 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.package.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(linker.context.declaredVariables.get(name) ?? ''); |
| + } |
| + } |
| + |
| + /** |
| * 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. |