OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 } |
OLD | NEW |