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

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

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

Powered by Google App Engine
This is Rietveld 408576698