Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(232)

Side by Side Diff: pkg/analyzer/lib/src/summary/bazel_summary.dart

Issue 2386743002: Cache linked bundles for Bazel packages in files.. (Closed)
Patch Set: Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | pkg/analyzer/test/src/summary/bazel_summary_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 import 'dart:convert'; 5 import 'dart:convert';
6 6
7 import 'package:analyzer/file_system/file_system.dart'; 7 import 'package:analyzer/file_system/file_system.dart';
8 import 'package:analyzer/src/generated/engine.dart'; 8 import 'package:analyzer/src/generated/engine.dart';
9 import 'package:analyzer/src/generated/source.dart'; 9 import 'package:analyzer/src/generated/source.dart';
10 import 'package:analyzer/src/generated/utilities_collection.dart'; 10 import 'package:analyzer/src/generated/utilities_collection.dart';
11 import 'package:analyzer/src/summary/api_signature.dart';
11 import 'package:analyzer/src/summary/format.dart'; 12 import 'package:analyzer/src/summary/format.dart';
12 import 'package:analyzer/src/summary/idl.dart'; 13 import 'package:analyzer/src/summary/idl.dart';
13 import 'package:analyzer/src/summary/link.dart'; 14 import 'package:analyzer/src/summary/link.dart';
14 import 'package:analyzer/src/summary/package_bundle_reader.dart'; 15 import 'package:analyzer/src/summary/package_bundle_reader.dart';
15 import 'package:analyzer/src/summary/summarize_elements.dart'; 16 import 'package:analyzer/src/summary/summarize_elements.dart';
16 import 'package:analyzer/src/util/fast_uri.dart'; 17 import 'package:analyzer/src/util/fast_uri.dart';
17 import 'package:convert/convert.dart'; 18 import 'package:convert/convert.dart';
18 import 'package:crypto/crypto.dart'; 19 import 'package:crypto/crypto.dart';
19 import 'package:meta/meta.dart'; 20 import 'package:meta/meta.dart';
20 21
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
91 92
92 /** 93 /**
93 * Class that reads summaries of Bazel packages. 94 * Class that reads summaries of Bazel packages.
94 * 95 *
95 * When the client needs to produce a resolution result for a new [Source], it 96 * When the client needs to produce a resolution result for a new [Source], it
96 * should call [getLinkedPackages] to check whether there is the set of 97 * should call [getLinkedPackages] to check whether there is the set of
97 * packages to resynthesize resolution results. 98 * packages to resynthesize resolution results.
98 */ 99 */
99 class SummaryProvider { 100 class SummaryProvider {
100 final ResourceProvider provider; 101 final ResourceProvider provider;
102 final String tempFileName;
101 final GetOutputFolder getOutputFolder; 103 final GetOutputFolder getOutputFolder;
104 final Folder linkedCacheFolder;
102 final AnalysisContext context; 105 final AnalysisContext context;
103 final PackageBundle sdkBundle; 106 final PackageBundle sdkBundle;
104 107
105 /** 108 /**
109 * If `true` (by default), then linking new bundles is allowed.
110 * Otherwise only using existing cached bundles can be used.
111 */
112 final bool allowLinking;
113
114 /**
115 * See [PackageBundleAssembler.currentMajorVersion].
116 */
117 final int majorVersion;
118
119 /**
106 * Mapping from bundle paths to corresponding [Package]s. The packages in 120 * Mapping from bundle paths to corresponding [Package]s. The packages in
107 * the map were consistent with their constituent sources at the moment when 121 * the map were consistent with their constituent sources at the moment when
108 * they were put into the map. 122 * they were put into the map.
109 */ 123 */
110 final Map<Folder, List<Package>> folderToPackagesMap = {}; 124 final Map<Folder, List<Package>> folderToPackagesMap = {};
111 125
112 /** 126 /**
113 * Mapping from [Uri]s to corresponding [_LinkNode]s. 127 * Mapping from [Uri]s to corresponding [_LinkNode]s.
114 */ 128 */
115 final Map<Uri, _LinkNode> uriToNodeMap = {}; 129 final Map<Uri, _LinkNode> uriToNodeMap = {};
116 130
117 SummaryProvider(this.provider, this.getOutputFolder, AnalysisContext context) 131 SummaryProvider(
132 this.provider,
133 this.tempFileName,
134 this.getOutputFolder,
135 this.linkedCacheFolder,
136 AnalysisContext context,
137 {@visibleForTesting
138 this.allowLinking: true,
139 @visibleForTesting
140 this.majorVersion: PackageBundleAssembler.currentMajorVersion})
118 : context = context, 141 : context = context,
119 sdkBundle = context.sourceFactory.dartSdk?.getLinkedBundle(); 142 sdkBundle = context.sourceFactory.dartSdk?.getLinkedBundle();
120 143
121 /** 144 /**
122 * Return the complete list of [Package]s that are required to provide all 145 * Return the complete list of [Package]s that are required to provide all
123 * resolution results for the given [source]. 146 * resolution results for the given [source].
124 * 147 *
125 * The same list of packages is returned for the same [Source], i.e. always 148 * The same list of packages is returned for the same [Source], i.e. always
126 * the full list, not a difference with a previous request. It is up to the 149 * the full list, not a difference with a previous request. It is up to the
127 * client to decide whether some of the returned packages should be excluded 150 * client to decide whether some of the returned packages should be excluded
(...skipping 12 matching lines...) Expand all
140 // Compute all transitive dependencies. 163 // Compute all transitive dependencies.
141 node.computeTransitiveDependencies(); 164 node.computeTransitiveDependencies();
142 List<_LinkNode> nodes = node.transitiveDependencies.toList(); 165 List<_LinkNode> nodes = node.transitiveDependencies.toList();
143 nodes.forEach((dependency) => dependency.computeTransitiveDependencies()); 166 nodes.forEach((dependency) => dependency.computeTransitiveDependencies());
144 167
145 // Fail if any dependency cannot be resolved. 168 // Fail if any dependency cannot be resolved.
146 if (node.failed) { 169 if (node.failed) {
147 return null; 170 return null;
148 } 171 }
149 172
150 _link(nodes); 173 // Read existing cached linked bundles.
174 for (_LinkNode node in nodes) {
175 _readLinked(node);
176 }
177
178 // Link new packages, if allowed.
179 if (allowLinking) {
180 _link(nodes);
181 }
151 182
152 // Create successfully linked packages. 183 // Create successfully linked packages.
153 return nodes 184 return nodes
154 .map((node) => node.package) 185 .map((node) => node.package)
155 .where((package) => package.linked != null) 186 .where((package) => package.linked != null)
156 .toList(); 187 .toList();
157 } 188 }
158 189
159 /** 190 /**
160 * Return the [Package] that contains information about the source with 191 * Return the [Package] that contains information about the source with
(...skipping 19 matching lines...) Expand all
180 * given [source] in [context]. 211 * given [source] in [context].
181 */ 212 */
182 String _computeSourceHashHex(Source source) { 213 String _computeSourceHashHex(Source source) {
183 String text = context.getContents(source).data; 214 String text = context.getContents(source).data;
184 List<int> bytes = UTF8.encode(text); 215 List<int> bytes = UTF8.encode(text);
185 List<int> hashBytes = md5.convert(bytes).bytes; 216 List<int> hashBytes = md5.convert(bytes).bytes;
186 return hex.encode(hashBytes); 217 return hex.encode(hashBytes);
187 } 218 }
188 219
189 /** 220 /**
221 * Return the name of the file for a linked bundle, in strong or spec mode.
222 */
223 String _getLinkedName(String hash) {
224 if (context.analysisOptions.strongMode) {
225 return 'linked_$hash.ds';
226 } else {
227 return 'linked_spec_$hash.ds';
228 }
229 }
230
231 /**
190 * Return the node for the given [uri], or `null` if there is no unlinked 232 * Return the node for the given [uri], or `null` if there is no unlinked
191 * bundle that contains [uri]. 233 * bundle that contains [uri].
192 */ 234 */
193 _LinkNode _getLinkNodeForUri(Uri uri) { 235 _LinkNode _getLinkNodeForUri(Uri uri) {
194 return uriToNodeMap.putIfAbsent(uri, () { 236 return uriToNodeMap.putIfAbsent(uri, () {
195 Package package = getUnlinkedForUri(uri); 237 Package package = getUnlinkedForUri(uri);
196 if (package == null) { 238 if (package == null) {
197 return null; 239 return null;
198 } 240 }
199 return new _LinkNode(this, package); 241 return new _LinkNode(this, package);
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after
290 for (_LinkNode node in nodes) { 332 for (_LinkNode node in nodes) {
291 if (!node.isReady) { 333 if (!node.isReady) {
292 PackageBundleAssembler assembler = new PackageBundleAssembler(); 334 PackageBundleAssembler assembler = new PackageBundleAssembler();
293 linkedLibraries.forEach((uri, linkedLibrary) { 335 linkedLibraries.forEach((uri, linkedLibrary) {
294 if (identical(uriToNode[uri], node)) { 336 if (identical(uriToNode[uri], node)) {
295 assembler.addLinkedLibrary(uri, linkedLibrary); 337 assembler.addLinkedLibrary(uri, linkedLibrary);
296 } 338 }
297 }); 339 });
298 List<int> bytes = assembler.assemble().toBuffer(); 340 List<int> bytes = assembler.assemble().toBuffer();
299 node.package._linked = new PackageBundle.fromBuffer(bytes); 341 node.package._linked = new PackageBundle.fromBuffer(bytes);
342 _writeLinked(node, bytes);
300 } 343 }
301 } 344 }
302 } 345 }
346
347 /**
348 * Attempt to read the linked bundle that corresponds to the given [node]
349 * with all its transitive dependencies.
350 */
351 void _readLinked(_LinkNode node) {
352 if (!node.isReady && node.linkedHash != null) {
353 String fileName = _getLinkedName(node.linkedHash);
354 File file = linkedCacheFolder.getChildAssumingFile(fileName);
355 // Try to read from the file system.
356 if (file.exists) {
357 try {
358 List<int> bytes = file.readAsBytesSync();
359 node.package._linked = new PackageBundle.fromBuffer(bytes);
360 } on FileSystemException {
361 // Ignore file system exceptions.
362 }
363 }
364 }
365 }
303 366
304 /** 367 /**
305 * Read the unlinked [Package] from the given [file], or return `null` if the 368 * Read the unlinked [Package] from the given [file], or return `null` if the
306 * file does not exist, or it cannot be read, or is not consistent with the 369 * file does not exist, or it cannot be read, or is not consistent with the
307 * constituent sources on the file system. 370 * constituent sources on the file system.
308 */ 371 */
309 Package _readUnlinkedPackage(File file) { 372 Package _readUnlinkedPackage(File file) {
310 try { 373 try {
311 List<int> bytes = file.readAsBytesSync(); 374 List<int> bytes = file.readAsBytesSync();
312 PackageBundle bundle = new PackageBundle.fromBuffer(bytes); 375 PackageBundle bundle = new PackageBundle.fromBuffer(bytes);
376 // Check the major version.
377 if (bundle.majorVersion != majorVersion) {
378 return null;
379 }
313 // Check for consistency, and fail if it's not. 380 // Check for consistency, and fail if it's not.
314 if (!_isUnlinkedBundleConsistent(bundle)) { 381 if (!_isUnlinkedBundleConsistent(bundle)) {
315 return null; 382 return null;
316 } 383 }
317 // OK, use the bundle. 384 // OK, use the bundle.
318 return new Package(file, bundle); 385 return new Package(file, bundle);
319 } on FileSystemException {} 386 } on FileSystemException {}
320 return null; 387 return null;
321 } 388 }
389
390 /**
391 * Atomically write the given [bytes] into the file in the [folder].
392 */
393 void _writeAtomic(Folder folder, String fileName, List<int> bytes) {
394 String filePath = folder.getChildAssumingFile(fileName).path;
395 File tempFile = folder.getChildAssumingFile(tempFileName);
396 tempFile.writeAsBytesSync(bytes);
397 tempFile.renameSync(filePath);
398 }
399
400 /**
401 * If a new linked bundle was linked for the given [node], write the bundle
402 * into the memory cache and the file system.
403 */
404 void _writeLinked(_LinkNode node, List<int> bytes) {
405 String hash = node.linkedHash;
406 if (hash != null) {
407 String fileName = _getLinkedName(hash);
408 // 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.
409 // manager.linkedBundleMap[file.path] = node.linked;
410 _writeAtomic(linkedCacheFolder, fileName, bytes);
411 }
412 }
322 } 413 }
323 414
324 /** 415 /**
325 * Information about a single [Package]. 416 * Information about a single [Package].
326 */ 417 */
327 class _LinkNode { 418 class _LinkNode {
328 final SummaryProvider linker; 419 final SummaryProvider linker;
329 final Package package; 420 final Package package;
330 421
331 bool failed = false; 422 bool failed = false;
332 Set<_LinkNode> transitiveDependencies; 423 Set<_LinkNode> transitiveDependencies;
333 424
334 List<_LinkNode> _dependencies; 425 List<_LinkNode> _dependencies;
426 String _linkedHash;
335 427
336 _LinkNode(this.linker, this.package); 428 _LinkNode(this.linker, this.package);
337 429
338 /** 430 /**
339 * Retrieve the dependencies of this node. 431 * Retrieve the dependencies of this node.
340 */ 432 */
341 List<_LinkNode> get dependencies { 433 List<_LinkNode> get dependencies {
342 if (_dependencies == null) { 434 if (_dependencies == null) {
343 Set<_LinkNode> dependencies = new Set<_LinkNode>(); 435 Set<_LinkNode> dependencies = new Set<_LinkNode>();
344 436
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
378 return _dependencies; 470 return _dependencies;
379 } 471 }
380 472
381 /** 473 /**
382 * Return `true` is the node is ready - has the linked bundle or failed (does 474 * Return `true` is the node is ready - has the linked bundle or failed (does
383 * not have all required dependencies). 475 * not have all required dependencies).
384 */ 476 */
385 bool get isReady => package.linked != null || failed; 477 bool get isReady => package.linked != null || failed;
386 478
387 /** 479 /**
480 * Return the hash string that corresponds to this linked bundle in the
481 * context of its SDK bundle and transitive dependencies. Return `null` if
482 * the hash computation fails, because for example the full transitive
483 * dependencies cannot computed.
484 */
485 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
486 if (_linkedHash == null && transitiveDependencies != null && !failed) {
487 ApiSignature signature = new ApiSignature();
488 // Add all unlinked API signatures.
489 List<String> signatures = <String>[];
490 signatures.add(linker.sdkBundle.apiSignature);
491 transitiveDependencies
492 .map((node) => node.package.unlinked.apiSignature)
493 .forEach(signatures.add);
494 signatures.sort();
495 signatures.forEach(signature.addString);
496 // Combine into a single hash.
497 appendDeclaredVariables(signature);
498 _linkedHash = signature.toHex();
499 }
500 return _linkedHash;
501 }
502
503 /**
504 * Append names and values of all referenced declared variables (even the
505 * ones without actually declared values) to the given [signature].
506 */
507 void appendDeclaredVariables(ApiSignature signature) {
508 Set<String> nameSet = new Set<String>();
509 for (_LinkNode node in transitiveDependencies) {
510 for (UnlinkedUnit unit in node.package.unlinked.unlinkedUnits) {
511 for (UnlinkedImport import in unit.imports) {
512 for (UnlinkedConfiguration configuration in import.configurations) {
513 nameSet.add(configuration.name);
514 }
515 }
516 for (UnlinkedExportPublic export in unit.publicNamespace.exports) {
517 for (UnlinkedConfiguration configuration in export.configurations) {
518 nameSet.add(configuration.name);
519 }
520 }
521 }
522 }
523 List<String> sortedNameList = nameSet.toList()..sort();
524 signature.addInt(sortedNameList.length);
525 for (String name in sortedNameList) {
526 signature.addString(name);
527 signature.addString(linker.context.declaredVariables.get(name) ?? '');
528 }
529 }
530
531 /**
388 * Compute the set of existing transitive dependencies for this node. 532 * Compute the set of existing transitive dependencies for this node.
389 * If any dependency cannot be resolved, then set [failed] to `true`. 533 * If any dependency cannot be resolved, then set [failed] to `true`.
390 * Only unlinked bundle is used, so this method can be called before linking. 534 * Only unlinked bundle is used, so this method can be called before linking.
391 */ 535 */
392 void computeTransitiveDependencies() { 536 void computeTransitiveDependencies() {
393 if (transitiveDependencies == null) { 537 if (transitiveDependencies == null) {
394 transitiveDependencies = new Set<_LinkNode>(); 538 transitiveDependencies = new Set<_LinkNode>();
395 539
396 void appendDependencies(_LinkNode node) { 540 void appendDependencies(_LinkNode node) {
397 if (transitiveDependencies.add(node)) { 541 if (transitiveDependencies.add(node)) {
398 node.dependencies.forEach(appendDependencies); 542 node.dependencies.forEach(appendDependencies);
399 } 543 }
400 } 544 }
401 545
402 appendDependencies(this); 546 appendDependencies(this);
403 if (transitiveDependencies.any((node) => node.failed)) { 547 if (transitiveDependencies.any((node) => node.failed)) {
404 failed = true; 548 failed = true;
405 } 549 }
406 } 550 }
407 } 551 }
408 552
409 @override 553 @override
410 String toString() => package.toString(); 554 String toString() => package.toString();
411 } 555 }
OLDNEW
« no previous file with comments | « no previous file | pkg/analyzer/test/src/summary/bazel_summary_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698