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

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

Issue 2698133002: Remove PubSummaryManager. (Closed)
Patch Set: Created 3 years, 10 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
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 import 'dart:async';
6 import 'dart:collection';
7 import 'dart:core';
8
9 import 'package:analyzer/dart/ast/ast.dart';
10 import 'package:analyzer/dart/ast/token.dart';
11 import 'package:analyzer/error/listener.dart';
12 import 'package:analyzer/file_system/file_system.dart';
13 import 'package:analyzer/src/dart/scanner/reader.dart';
14 import 'package:analyzer/src/dart/scanner/scanner.dart';
15 import 'package:analyzer/src/generated/engine.dart';
16 import 'package:analyzer/src/generated/parser.dart';
17 import 'package:analyzer/src/generated/source.dart';
18 import 'package:analyzer/src/generated/utilities_collection.dart';
19 import 'package:analyzer/src/summary/api_signature.dart';
20 import 'package:analyzer/src/summary/format.dart';
21 import 'package:analyzer/src/summary/idl.dart';
22 import 'package:analyzer/src/summary/link.dart';
23 import 'package:analyzer/src/summary/package_bundle_reader.dart'
24 show ResynthesizerResultProvider, SummaryDataStore;
25 import 'package:analyzer/src/summary/summarize_ast.dart'
26 show serializeAstUnlinked;
27 import 'package:analyzer/src/summary/summarize_elements.dart'
28 show PackageBundleAssembler;
29 import 'package:convert/convert.dart';
30 import 'package:crypto/crypto.dart';
31 import 'package:meta/meta.dart';
32 import 'package:path/path.dart' as pathos;
33
34 /**
35 * Unlinked and linked information about a [PubPackage].
36 */
37 class LinkedPubPackage {
38 final PubPackage package;
39 final PackageBundle unlinked;
40 final PackageBundle linked;
41
42 final String linkedHash;
43
44 LinkedPubPackage(this.package, this.unlinked, this.linked, this.linkedHash);
45
46 @override
47 String toString() => package.toString();
48 }
49
50 /**
51 * A package in the pub cache.
52 */
53 class PubPackage {
54 final String name;
55 final Folder libFolder;
56
57 PubPackage(this.name, this.libFolder);
58
59 Folder get folder => libFolder.parent;
60
61 @override
62 int get hashCode => libFolder.hashCode;
63
64 @override
65 bool operator ==(other) {
66 return other is PubPackage && other.libFolder == libFolder;
67 }
68
69 @override
70 String toString() => '($name in $folder)';
71 }
72
73 /**
74 * Class that manages summaries for pub packages.
75 *
76 * The client should call [getLinkedBundles] after creating a new
77 * [AnalysisContext] and configuring its source factory, but before computing
78 * any analysis results. The returned linked bundles can be used to create and
79 * configure [ResynthesizerResultProvider] for the context.
80 */
81 class PubSummaryManager {
82 static const UNLINKED_NAME = 'unlinked.ds';
83 static const UNLINKED_SPEC_NAME = 'unlinked_spec.ds';
84
85 /**
86 * If `true` (by default), then linking new bundles is allowed.
87 * Otherwise only using existing cached bundles can be used.
88 */
89 final bool allowLinking;
90
91 /**
92 * See [PackageBundleAssembler.currentMajorVersion].
93 */
94 final int majorVersion;
95
96 final ResourceProvider resourceProvider;
97
98 /**
99 * The name of the temporary file that is used for atomic writes.
100 */
101 final String tempFileName;
102
103 /**
104 * The map from [PubPackage]s to their unlinked [PackageBundle]s in the pub
105 * cache.
106 */
107 final Map<PubPackage, PackageBundle> unlinkedBundleMap =
108 new HashMap<PubPackage, PackageBundle>();
109
110 /**
111 * The map from linked file paths to the corresponding linked bundles.
112 */
113 final Map<String, PackageBundle> linkedBundleMap =
114 new HashMap<String, PackageBundle>();
115
116 /**
117 * The set of packages to compute unlinked summaries for.
118 */
119 final Set<PubPackage> packagesToComputeUnlinked = new Set<PubPackage>();
120
121 /**
122 * The set of already processed packages, which we have already checked
123 * for their unlinked bundle existence, or scheduled its computing.
124 */
125 final Set<PubPackage> seenPackages = new Set<PubPackage>();
126
127 /**
128 * The [Completer] that completes when computing of all scheduled unlinked
129 * bundles is complete.
130 */
131 Completer _onUnlinkedCompleteCompleter;
132
133 PubSummaryManager(this.resourceProvider, this.tempFileName,
134 {@visibleForTesting this.allowLinking: true,
135 @visibleForTesting this.majorVersion:
136 PackageBundleAssembler.currentMajorVersion});
137
138 /**
139 * The [Future] that completes when computing of all scheduled unlinked
140 * bundles is complete.
141 */
142 Future get onUnlinkedComplete {
143 if (packagesToComputeUnlinked.isEmpty) {
144 return new Future.value();
145 }
146 _onUnlinkedCompleteCompleter ??= new Completer();
147 return _onUnlinkedCompleteCompleter.future;
148 }
149
150 /**
151 * Return the [pathos.Context] corresponding to the [resourceProvider].
152 */
153 pathos.Context get pathContext => resourceProvider.pathContext;
154
155 /**
156 * Complete when the unlinked bundles for the package with the given [name]
157 * and the [libFolder] are computed and written to the files.
158 *
159 * This method is intended to be used for generating unlinked bundles for
160 * the `Flutter` packages.
161 */
162 Future<Null> computeUnlinkedForFolder(String name, Folder libFolder) async {
163 PubPackage package = new PubPackage(name, libFolder);
164 _scheduleUnlinked(package);
165 await onUnlinkedComplete;
166 }
167
168 /**
169 * Return the list of linked [LinkedPubPackage]s that can be provided at this
170 * time for a subset of the packages used by the given [context]. If
171 * information about some of the used packages is not available yet, schedule
172 * its computation, so that it might be available later for other contexts
173 * referencing the same packages.
174 */
175 List<LinkedPubPackage> getLinkedBundles(AnalysisContext context) {
176 return new _ContextLinker(this, context).getLinkedBundles();
177 }
178
179 /**
180 * Return all available unlinked [PackageBundle]s for the given [context],
181 * maybe an empty map, but not `null`.
182 */
183 @visibleForTesting
184 Map<PubPackage, PackageBundle> getUnlinkedBundles(AnalysisContext context) {
185 bool strong = context.analysisOptions.strongMode;
186 Map<PubPackage, PackageBundle> unlinkedBundles =
187 new HashMap<PubPackage, PackageBundle>();
188 Map<String, List<Folder>> packageMap = context.sourceFactory.packageMap;
189 if (packageMap != null) {
190 packageMap.forEach((String packageName, List<Folder> libFolders) {
191 if (libFolders.length == 1) {
192 Folder libFolder = libFolders.first;
193 PubPackage package = new PubPackage(packageName, libFolder);
194 PackageBundle unlinkedBundle =
195 _getUnlinkedOrSchedule(package, strong);
196 if (unlinkedBundle != null) {
197 unlinkedBundles[package] = unlinkedBundle;
198 }
199 }
200 });
201 }
202 return unlinkedBundles;
203 }
204
205 /**
206 * Compute unlinked bundle for a package from [packagesToComputeUnlinked],
207 * and schedule delayed computation for the next package, if any.
208 */
209 void _computeNextUnlinked() {
210 if (packagesToComputeUnlinked.isNotEmpty) {
211 PubPackage package = packagesToComputeUnlinked.first;
212 _computeUnlinked(package, false);
213 _computeUnlinked(package, true);
214 packagesToComputeUnlinked.remove(package);
215 _scheduleNextUnlinked();
216 } else {
217 if (_onUnlinkedCompleteCompleter != null) {
218 _onUnlinkedCompleteCompleter.complete(true);
219 _onUnlinkedCompleteCompleter = null;
220 }
221 }
222 }
223
224 /**
225 * Compute the unlinked bundle for the package with the given path, put
226 * it in the [unlinkedBundleMap] and store into the [resourceProvider].
227 *
228 * TODO(scheglov) Consider moving into separate isolate(s).
229 */
230 void _computeUnlinked(PubPackage package, bool strong) {
231 Folder libFolder = package.libFolder;
232 String libPath = libFolder.path + pathContext.separator;
233 PackageBundleAssembler assembler = new PackageBundleAssembler();
234
235 /**
236 * Return the `package` [Uri] for the given [path] in the `lib` folder
237 * of the current package.
238 */
239 Uri getUri(String path) {
240 String pathInLib = path.substring(libPath.length);
241 String uriPath = pathos.posix.joinAll(pathContext.split(pathInLib));
242 String uriStr = 'package:${package.name}/$uriPath';
243 return Uri.parse(uriStr);
244 }
245
246 /**
247 * If the given [file] is a Dart file, add its unlinked unit.
248 */
249 void addDartFile(File file) {
250 String path = file.path;
251 if (AnalysisEngine.isDartFileName(path)) {
252 Uri uri = getUri(path);
253 Source source = file.createSource(uri);
254 CompilationUnit unit = _parse(source, strong);
255 UnlinkedUnitBuilder unlinkedUnit = serializeAstUnlinked(unit);
256 assembler.addUnlinkedUnit(source, unlinkedUnit);
257 }
258 }
259
260 /**
261 * Visit the [folder] recursively.
262 */
263 void addDartFiles(Folder folder) {
264 List<Resource> children = folder.getChildren();
265 for (Resource child in children) {
266 if (child is File) {
267 addDartFile(child);
268 }
269 }
270 for (Resource child in children) {
271 if (child is Folder) {
272 addDartFiles(child);
273 }
274 }
275 }
276
277 try {
278 addDartFiles(libFolder);
279 PackageBundleBuilder bundleWriter = assembler.assemble();
280 bundleWriter.majorVersion = majorVersion;
281 List<int> bytes = bundleWriter.toBuffer();
282 String fileName = _getUnlinkedName(strong);
283 _writeAtomic(package.folder, fileName, bytes);
284 } on FileSystemException {
285 // Ignore file system exceptions.
286 }
287 }
288
289 /**
290 * Return the name of the file for an unlinked bundle, in strong or spec mode.
291 */
292 String _getUnlinkedName(bool strong) {
293 if (strong) {
294 return UNLINKED_NAME;
295 } else {
296 return UNLINKED_SPEC_NAME;
297 }
298 }
299
300 /**
301 * Return the unlinked [PackageBundle] for the given [package]. If the bundle
302 * has not been compute yet, return `null` and schedule its computation.
303 */
304 PackageBundle _getUnlinkedOrSchedule(PubPackage package, bool strong) {
305 // Try to find in the cache.
306 PackageBundle bundle = unlinkedBundleMap[package];
307 if (bundle != null) {
308 return bundle;
309 }
310
311 // Try to read from the file system.
312 String fileName = _getUnlinkedName(strong);
313 File file = package.folder.getChildAssumingFile(fileName);
314 if (file.exists) {
315 try {
316 List<int> bytes = file.readAsBytesSync();
317 bundle = new PackageBundle.fromBuffer(bytes);
318 } on FileSystemException {
319 // Ignore file system exceptions.
320 }
321 }
322
323 // Verify compatibility and consistency.
324 bool isInPubCache = isPathInPubCache(pathContext, package.folder.path);
325 if (bundle != null &&
326 bundle.majorVersion == majorVersion &&
327 (isInPubCache || _isConsistent(package, bundle))) {
328 unlinkedBundleMap[package] = bundle;
329 return bundle;
330 }
331
332 // Schedule computation in the background, if in the pub cache.
333 if (isInPubCache) {
334 if (seenPackages.add(package)) {
335 _scheduleUnlinked(package);
336 }
337 }
338
339 // The bundle is not available.
340 return null;
341 }
342
343 /**
344 * Return `true` if content hashes for the [package] library files are the
345 * same the hashes in the unlinked [bundle].
346 */
347 bool _isConsistent(PubPackage package, PackageBundle bundle) {
348 List<String> actualHashes = <String>[];
349
350 /**
351 * If the given [file] is a Dart file, add its content hash.
352 */
353 void hashDartFile(File file) {
354 String path = file.path;
355 if (AnalysisEngine.isDartFileName(path)) {
356 List<int> fileBytes = file.readAsBytesSync();
357 List<int> hashBytes = md5.convert(fileBytes).bytes;
358 String hashHex = hex.encode(hashBytes);
359 actualHashes.add(hashHex);
360 }
361 }
362
363 /**
364 * Visit the [folder] recursively.
365 */
366 void hashDartFiles(Folder folder) {
367 List<Resource> children = folder.getChildren();
368 for (Resource child in children) {
369 if (child is File) {
370 hashDartFile(child);
371 } else if (child is Folder) {
372 hashDartFiles(child);
373 }
374 }
375 }
376
377 // Recursively compute hashes of the `lib` folder Dart files.
378 try {
379 hashDartFiles(package.libFolder);
380 } on FileSystemException {
381 return false;
382 }
383
384 // Compare sorted actual and bundle unit hashes.
385 List<String> bundleHashes = bundle.unlinkedUnitHashes.toList()..sort();
386 actualHashes.sort();
387 return listsEqual(actualHashes, bundleHashes);
388 }
389
390 /**
391 * Parse the given [source] into AST.
392 */
393 CompilationUnit _parse(Source source, bool strong) {
394 String code = source.contents.data;
395 AnalysisErrorListener errorListener = AnalysisErrorListener.NULL_LISTENER;
396 CharSequenceReader reader = new CharSequenceReader(code);
397 Scanner scanner = new Scanner(source, reader, errorListener);
398 scanner.scanGenericMethodComments = strong;
399 Token token = scanner.tokenize();
400 LineInfo lineInfo = new LineInfo(scanner.lineStarts);
401 Parser parser = new Parser(source, errorListener);
402 parser.parseGenericMethodComments = strong;
403 CompilationUnit unit = parser.parseCompilationUnit(token);
404 unit.lineInfo = lineInfo;
405 return unit;
406 }
407
408 /**
409 * Schedule delayed computation of the next package unlinked bundle from the
410 * set of [packagesToComputeUnlinked]. We delay each computation because we
411 * want operations in analysis server to proceed, and computing bundles of
412 * packages is a background task.
413 */
414 void _scheduleNextUnlinked() {
415 new Future.delayed(new Duration(milliseconds: 10), _computeNextUnlinked);
416 }
417
418 /**
419 * Schedule computing unlinked bundles for the given [package].
420 */
421 void _scheduleUnlinked(PubPackage package) {
422 if (packagesToComputeUnlinked.isEmpty) {
423 _scheduleNextUnlinked();
424 }
425 packagesToComputeUnlinked.add(package);
426 }
427
428 /**
429 * Atomically write the given [bytes] into the file in the [folder].
430 */
431 void _writeAtomic(Folder folder, String fileName, List<int> bytes) {
432 String filePath = folder.getChildAssumingFile(fileName).path;
433 File tempFile = folder.getChildAssumingFile(tempFileName);
434 tempFile.writeAsBytesSync(bytes);
435 tempFile.renameSync(filePath);
436 }
437
438 /**
439 * If the given [uri] has the `package` scheme, return the name of the
440 * package that contains the referenced resource. Otherwise return `null`.
441 *
442 * For example `package:foo/bar.dart` => `foo`.
443 */
444 static String getPackageName(String uri) {
445 const String PACKAGE_SCHEME = 'package:';
446 if (uri.startsWith(PACKAGE_SCHEME)) {
447 int index = uri.indexOf('/');
448 if (index != -1) {
449 return uri.substring(PACKAGE_SCHEME.length, index);
450 }
451 }
452 return null;
453 }
454
455 /**
456 * Return `true` if the given absolute [path] is in the pub cache.
457 */
458 static bool isPathInPubCache(pathos.Context pathContext, String path) {
459 List<String> parts = pathContext.split(path);
460 for (int i = 0; i < parts.length - 1; i++) {
461 if (parts[i] == '.pub-cache') {
462 return true;
463 }
464 if (parts[i] == 'Pub' && parts[i + 1] == 'Cache') {
465 return true;
466 }
467 }
468 return false;
469 }
470 }
471
472 class _ContextLinker {
473 final PubSummaryManager manager;
474 final AnalysisContext context;
475
476 final strong;
477 final _ListedPackages listedPackages;
478 final PackageBundle sdkBundle;
479
480 final List<_LinkNode> nodes = <_LinkNode>[];
481 final Map<String, _LinkNode> packageToNode = <String, _LinkNode>{};
482
483 _ContextLinker(this.manager, AnalysisContext context)
484 : context = context,
485 strong = context.analysisOptions.strongMode,
486 listedPackages = new _ListedPackages(context.sourceFactory),
487 sdkBundle = context.sourceFactory.dartSdk.getLinkedBundle();
488
489 /**
490 * Return the list of linked [LinkedPubPackage]s that can be provided at this
491 * time for a subset of the packages used by the [context].
492 */
493 List<LinkedPubPackage> getLinkedBundles() {
494 // Stopwatch timer = new Stopwatch()..start();
495
496 if (sdkBundle == null) {
497 return const <LinkedPubPackage>[];
498 }
499
500 Map<PubPackage, PackageBundle> unlinkedBundles =
501 manager.getUnlinkedBundles(context);
502
503 // TODO(scheglov) remove debug output after optimizing
504 // print('LOADED ${unlinkedBundles.length} unlinked bundles'
505 // ' in ${timer.elapsedMilliseconds} ms');
506 // timer..reset();
507
508 // If no unlinked bundles, there is nothing we can try to link.
509 if (unlinkedBundles.isEmpty) {
510 return const <LinkedPubPackage>[];
511 }
512
513 // Create nodes for packages.
514 unlinkedBundles.forEach((package, unlinked) {
515 _LinkNode node = new _LinkNode(this, package, unlinked);
516 nodes.add(node);
517 packageToNode[package.name] = node;
518 });
519
520 // Compute transitive dependencies, mark some nodes as failed.
521 for (_LinkNode node in nodes) {
522 node.computeTransitiveDependencies();
523 }
524
525 // Attempt to read existing linked bundles.
526 for (_LinkNode node in nodes) {
527 _readLinked(node);
528 }
529
530 // Link new packages, if allowed.
531 if (manager.allowLinking) {
532 _link();
533 }
534
535 // Create successfully linked packages.
536 List<LinkedPubPackage> linkedPackages = <LinkedPubPackage>[];
537 for (_LinkNode node in nodes) {
538 if (node.linked != null) {
539 linkedPackages.add(new LinkedPubPackage(
540 node.package, node.unlinked, node.linked, node.linkedHash));
541 }
542 }
543
544 // TODO(scheglov) remove debug output after optimizing
545 // print('LINKED ${linkedPackages.length} bundles'
546 // ' in ${timer.elapsedMilliseconds} ms');
547
548 // Done.
549 return linkedPackages;
550 }
551
552 String _getDeclaredVariable(String name) {
553 return context.declaredVariables.get(name);
554 }
555
556 /**
557 * Return the name of the file for a linked bundle, in strong or spec mode.
558 */
559 String _getLinkedName(String hash) {
560 if (strong) {
561 return 'linked_$hash.ds';
562 } else {
563 return 'linked_spec_$hash.ds';
564 }
565 }
566
567 void _link() {
568 // Fill the store with bundles.
569 // Append the linked SDK bundle.
570 // Append unlinked and (if read from a cache) linked package bundles.
571 SummaryDataStore store = new SummaryDataStore(const <String>[]);
572 store.addBundle(null, sdkBundle);
573 for (_LinkNode node in nodes) {
574 store.addBundle(null, node.unlinked);
575 if (node.linked != null) {
576 store.addBundle(null, node.linked);
577 }
578 }
579
580 // Prepare URIs to link.
581 Map<String, _LinkNode> uriToNode = <String, _LinkNode>{};
582 for (_LinkNode node in nodes) {
583 if (!node.isReady) {
584 for (String uri in node.unlinked.unlinkedUnitUris) {
585 uriToNode[uri] = node;
586 }
587 }
588 }
589 Set<String> libraryUris = uriToNode.keys.toSet();
590
591 // Perform linking.
592 Map<String, LinkedLibraryBuilder> linkedLibraries =
593 link(libraryUris, (String uri) {
594 return store.linkedMap[uri];
595 }, (String uri) {
596 return store.unlinkedMap[uri];
597 }, _getDeclaredVariable, strong);
598
599 // Assemble newly linked bundles.
600 for (_LinkNode node in nodes) {
601 if (!node.isReady) {
602 PackageBundleAssembler assembler = new PackageBundleAssembler();
603 linkedLibraries.forEach((uri, linkedLibrary) {
604 if (identical(uriToNode[uri], node)) {
605 assembler.addLinkedLibrary(uri, linkedLibrary);
606 }
607 });
608 List<int> bytes = assembler.assemble().toBuffer();
609 node.linkedNewBytes = bytes;
610 node.linked = new PackageBundle.fromBuffer(bytes);
611 }
612 }
613
614 // Write newly linked bundles.
615 for (_LinkNode node in nodes) {
616 _writeLinked(node);
617 }
618 }
619
620 /**
621 * Attempt to find the linked bundle that corresponds to the given [node]
622 * with all its transitive dependencies and put it into [_LinkNode.linked].
623 */
624 void _readLinked(_LinkNode node) {
625 String hash = node.linkedHash;
626 if (hash != null) {
627 String fileName = _getLinkedName(hash);
628 File file = node.package.folder.getChildAssumingFile(fileName);
629 // Try to find in the cache.
630 PackageBundle linked = manager.linkedBundleMap[file.path];
631 if (linked != null) {
632 node.linked = linked;
633 return;
634 }
635 // Try to read from the file system.
636 if (file.exists) {
637 try {
638 List<int> bytes = file.readAsBytesSync();
639 linked = new PackageBundle.fromBuffer(bytes);
640 manager.linkedBundleMap[file.path] = linked;
641 node.linked = linked;
642 } on FileSystemException {
643 // Ignore file system exceptions.
644 }
645 }
646 }
647 }
648
649 /**
650 * If a new linked bundle was linked for the given [node], write the bundle
651 * into the memory cache and the file system.
652 */
653 void _writeLinked(_LinkNode node) {
654 String hash = node.linkedHash;
655 if (hash != null && node.linkedNewBytes != null) {
656 String fileName = _getLinkedName(hash);
657 File file = node.package.folder.getChildAssumingFile(fileName);
658 manager.linkedBundleMap[file.path] = node.linked;
659 manager._writeAtomic(node.package.folder, fileName, node.linkedNewBytes);
660 }
661 }
662 }
663
664 /**
665 * Information about a package to link.
666 */
667 class _LinkNode {
668 final _ContextLinker linker;
669 final PubPackage package;
670 final PackageBundle unlinked;
671
672 bool failed = false;
673 Set<_LinkNode> transitiveDependencies;
674
675 List<_LinkNode> _dependencies;
676 String _linkedHash;
677
678 List<int> linkedNewBytes;
679 PackageBundle linked;
680
681 _LinkNode(this.linker, this.package, this.unlinked);
682
683 /**
684 * Retrieve the dependencies of this node.
685 */
686 List<_LinkNode> get dependencies {
687 if (_dependencies == null) {
688 Set<_LinkNode> dependencies = new Set<_LinkNode>();
689
690 void appendDependency(String uriStr) {
691 Uri uri = Uri.parse(uriStr);
692 if (!uri.hasScheme) {
693 // A relative path in this package, skip it.
694 } else if (uri.scheme == 'dart') {
695 // Dependency on the SDK is implicit and always added.
696 // The SDK linked bundle is precomputed before linking packages.
697 } else if (uriStr.startsWith('package:')) {
698 String package = PubSummaryManager.getPackageName(uriStr);
699 _LinkNode packageNode = linker.packageToNode[package];
700 if (packageNode == null && linker.listedPackages.isListed(uriStr)) {
701 failed = true;
702 }
703 if (packageNode != null) {
704 dependencies.add(packageNode);
705 }
706 } else {
707 failed = true;
708 }
709 }
710
711 for (UnlinkedUnit unit in unlinked.unlinkedUnits) {
712 for (UnlinkedImport import in unit.imports) {
713 if (!import.isImplicit) {
714 appendDependency(import.uri);
715 }
716 }
717 for (UnlinkedExportPublic export in unit.publicNamespace.exports) {
718 appendDependency(export.uri);
719 }
720 }
721
722 _dependencies = dependencies.toList();
723 }
724 return _dependencies;
725 }
726
727 /**
728 * Return `true` is the node is ready - has the linked bundle or failed (does
729 * not have all required dependencies).
730 */
731 bool get isReady => linked != null || failed;
732
733 /**
734 * Return the hash string that corresponds to this linked bundle in the
735 * context of its SDK bundle and transitive dependencies. Return `null` if
736 * the hash computation fails, because for example the full transitive
737 * dependencies cannot computed.
738 */
739 String get linkedHash {
740 if (_linkedHash == null && transitiveDependencies != null) {
741 ApiSignature signature = new ApiSignature();
742 // Add all unlinked API signatures.
743 List<String> signatures = <String>[];
744 signatures.add(linker.sdkBundle.apiSignature);
745 transitiveDependencies
746 .map((node) => node.unlinked.apiSignature)
747 .forEach(signatures.add);
748 signatures.sort();
749 signatures.forEach(signature.addString);
750 // Combine into a single hash.
751 appendDeclaredVariables(signature);
752 _linkedHash = signature.toHex();
753 }
754 return _linkedHash;
755 }
756
757 /**
758 * Append names and values of all referenced declared variables (even the
759 * ones without actually declared values) to the given [signature].
760 */
761 void appendDeclaredVariables(ApiSignature signature) {
762 Set<String> nameSet = new Set<String>();
763 for (_LinkNode node in transitiveDependencies) {
764 for (UnlinkedUnit unit in node.unlinked.unlinkedUnits) {
765 for (UnlinkedImport import in unit.imports) {
766 for (UnlinkedConfiguration configuration in import.configurations) {
767 nameSet.add(configuration.name);
768 }
769 }
770 for (UnlinkedExportPublic export in unit.publicNamespace.exports) {
771 for (UnlinkedConfiguration configuration in export.configurations) {
772 nameSet.add(configuration.name);
773 }
774 }
775 }
776 }
777 List<String> sortedNameList = nameSet.toList()..sort();
778 signature.addInt(sortedNameList.length);
779 for (String name in sortedNameList) {
780 signature.addString(name);
781 signature.addString(linker._getDeclaredVariable(name) ?? '');
782 }
783 }
784
785 /**
786 * Compute the set of existing transitive dependencies for this node.
787 * If any `package` dependency cannot be resolved, but it is one of the
788 * [listedPackages] then set [failed] to `true`.
789 * Only [unlinked] is used, so this method can be called before linking.
790 */
791 void computeTransitiveDependencies() {
792 if (transitiveDependencies == null) {
793 transitiveDependencies = new Set<_LinkNode>();
794
795 void appendDependencies(_LinkNode node) {
796 if (transitiveDependencies.add(node)) {
797 node.dependencies.forEach(appendDependencies);
798 }
799 }
800
801 appendDependencies(this);
802 if (transitiveDependencies.any((node) => node.failed)) {
803 failed = true;
804 }
805 }
806 }
807
808 @override
809 String toString() => package.toString();
810 }
811
812 /**
813 * The set of package names that are listed in the `.packages` file of a
814 * context. These are the only packages, references to which can
815 * be possibly resolved in the context. Nodes that reference a `package:` URI
816 * without the unlinked bundle, so without the node, cannot be linked.
817 */
818 class _ListedPackages {
819 final Set<String> names = new Set<String>();
820
821 _ListedPackages(SourceFactory sourceFactory) {
822 Map<String, List<Folder>> map = sourceFactory.packageMap;
823 if (map != null) {
824 names.addAll(map.keys);
825 }
826 }
827
828 /**
829 * Check whether the given `package:` [uri] is listed in the package map.
830 */
831 bool isListed(String uri) {
832 String package = PubSummaryManager.getPackageName(uri);
833 return names.contains(package);
834 }
835 }
OLDNEW
« no previous file with comments | « pkg/analyzer/lib/src/context/builder.dart ('k') | pkg/analyzer/test/src/command_line/arguments_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698