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:async'; | 5 import 'dart:async'; |
6 import 'dart:collection'; | 6 import 'dart:collection'; |
7 import 'dart:core'; | 7 import 'dart:core'; |
8 | 8 |
9 import 'package:analyzer/dart/ast/ast.dart'; | 9 import 'package:analyzer/dart/ast/ast.dart'; |
10 import 'package:analyzer/dart/ast/token.dart'; | 10 import 'package:analyzer/dart/ast/token.dart'; |
(...skipping 15 matching lines...) Expand all Loading... |
26 show serializeAstUnlinked; | 26 show serializeAstUnlinked; |
27 import 'package:analyzer/src/summary/summarize_elements.dart' | 27 import 'package:analyzer/src/summary/summarize_elements.dart' |
28 show PackageBundleAssembler; | 28 show PackageBundleAssembler; |
29 import 'package:analyzer/src/util/fast_uri.dart'; | 29 import 'package:analyzer/src/util/fast_uri.dart'; |
30 import 'package:convert/convert.dart'; | 30 import 'package:convert/convert.dart'; |
31 import 'package:crypto/crypto.dart'; | 31 import 'package:crypto/crypto.dart'; |
32 import 'package:meta/meta.dart'; | 32 import 'package:meta/meta.dart'; |
33 import 'package:path/path.dart' as pathos; | 33 import 'package:path/path.dart' as pathos; |
34 | 34 |
35 /** | 35 /** |
36 * Return the raw string value of the variable with the given [name], | |
37 * or `null` of the variable is not defined. | |
38 */ | |
39 typedef String _GetDeclaredVariable(String name); | |
40 | |
41 /** | |
42 * Unlinked and linked information about a [PubPackage]. | 36 * Unlinked and linked information about a [PubPackage]. |
43 */ | 37 */ |
44 class LinkedPubPackage { | 38 class LinkedPubPackage { |
45 final PubPackage package; | 39 final PubPackage package; |
46 final PackageBundle unlinked; | 40 final PackageBundle unlinked; |
47 final PackageBundle linked; | 41 final PackageBundle linked; |
48 | 42 |
49 final String linkedHash; | 43 final String linkedHash; |
50 | 44 |
51 LinkedPubPackage(this.package, this.unlinked, this.linked, this.linkedHash); | 45 LinkedPubPackage(this.package, this.unlinked, this.linked, this.linkedHash); |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
173 } | 167 } |
174 | 168 |
175 /** | 169 /** |
176 * Return the list of linked [LinkedPubPackage]s that can be provided at this | 170 * Return the list of linked [LinkedPubPackage]s that can be provided at this |
177 * time for a subset of the packages used by the given [context]. If | 171 * time for a subset of the packages used by the given [context]. If |
178 * information about some of the used packages is not available yet, schedule | 172 * information about some of the used packages is not available yet, schedule |
179 * its computation, so that it might be available later for other contexts | 173 * its computation, so that it might be available later for other contexts |
180 * referencing the same packages. | 174 * referencing the same packages. |
181 */ | 175 */ |
182 List<LinkedPubPackage> getLinkedBundles(AnalysisContext context) { | 176 List<LinkedPubPackage> getLinkedBundles(AnalysisContext context) { |
183 // Stopwatch timer = new Stopwatch()..start(); | 177 return new _ContextLinker(this, context).getLinkedBundles(); |
184 | |
185 _GetDeclaredVariable getDeclaredVariable = context.declaredVariables.get; | |
186 SourceFactory sourceFactory = context.sourceFactory; | |
187 _ListedPackages listedPackages = new _ListedPackages(sourceFactory); | |
188 | |
189 PackageBundle sdkBundle = sourceFactory.dartSdk.getLinkedBundle(); | |
190 if (sdkBundle == null) { | |
191 return const <LinkedPubPackage>[]; | |
192 } | |
193 | |
194 bool strong = context.analysisOptions.strongMode; | |
195 Map<PubPackage, PackageBundle> unlinkedBundles = | |
196 getUnlinkedBundles(context); | |
197 | |
198 // TODO(scheglov) remove debug output after optimizing | |
199 // print('LOADED ${unlinkedBundles.length} unlinked bundles' | |
200 // ' in ${timer.elapsedMilliseconds} ms'); | |
201 // timer..reset(); | |
202 | |
203 // If no unlinked bundles, there is nothing we can try to link. | |
204 if (unlinkedBundles.isEmpty) { | |
205 return const <LinkedPubPackage>[]; | |
206 } | |
207 | |
208 // Create graph nodes for packages. | |
209 List<_LinkNode> nodes = <_LinkNode>[]; | |
210 Map<String, _LinkNode> packageToNode = <String, _LinkNode>{}; | |
211 unlinkedBundles.forEach((package, unlinked) { | |
212 _LinkNode node = new _LinkNode(sdkBundle, getDeclaredVariable, | |
213 listedPackages, package, unlinked, packageToNode); | |
214 nodes.add(node); | |
215 packageToNode[package.name] = node; | |
216 }); | |
217 | |
218 // Compute transitive dependencies, mark some nodes as failed. | |
219 for (_LinkNode node in nodes) { | |
220 node.computeTransitiveDependencies(); | |
221 } | |
222 | |
223 // Attempt to read existing linked bundles. | |
224 for (_LinkNode node in nodes) { | |
225 _readLinked(node, strong); | |
226 } | |
227 | |
228 // Link new bundles, if allowed. | |
229 if (allowLinking) { | |
230 // Fill the store with bundles. | |
231 // Append the linked SDK bundle. | |
232 // Append unlinked and (if read from a cache) linked package bundles. | |
233 SummaryDataStore store = new SummaryDataStore(const <String>[]); | |
234 store.addBundle(null, sdkBundle); | |
235 for (_LinkNode node in nodes) { | |
236 store.addBundle(null, node.unlinked); | |
237 if (node.linked != null) { | |
238 store.addBundle(null, node.linked); | |
239 } | |
240 } | |
241 | |
242 // Link each package node. | |
243 for (_LinkNode node in nodes) { | |
244 if (!node.isEvaluated) { | |
245 new _LinkWalker(getDeclaredVariable, listedPackages, store, strong) | |
246 .walk(node); | |
247 } | |
248 } | |
249 | |
250 // Write newly linked bundles. | |
251 for (_LinkNode node in nodes) { | |
252 _writeLinked(node, strong); | |
253 } | |
254 } | |
255 | |
256 // Create successfully linked packages. | |
257 List<LinkedPubPackage> linkedPackages = <LinkedPubPackage>[]; | |
258 for (_LinkNode node in nodes) { | |
259 if (node.linked != null) { | |
260 linkedPackages.add(new LinkedPubPackage( | |
261 node.package, node.unlinked, node.linked, node.linkedHash)); | |
262 } | |
263 } | |
264 | |
265 // TODO(scheglov) remove debug output after optimizing | |
266 // print('LINKED ${linkedPackages.length} bundles' | |
267 // ' in ${timer.elapsedMilliseconds} ms'); | |
268 | |
269 // Done. | |
270 return linkedPackages; | |
271 } | 178 } |
272 | 179 |
273 /** | 180 /** |
274 * Return all available unlinked [PackageBundle]s for the given [context], | 181 * Return all available unlinked [PackageBundle]s for the given [context], |
275 * maybe an empty map, but not `null`. | 182 * maybe an empty map, but not `null`. |
276 */ | 183 */ |
277 @visibleForTesting | 184 @visibleForTesting |
278 Map<PubPackage, PackageBundle> getUnlinkedBundles(AnalysisContext context) { | 185 Map<PubPackage, PackageBundle> getUnlinkedBundles(AnalysisContext context) { |
279 bool strong = context.analysisOptions.strongMode; | 186 bool strong = context.analysisOptions.strongMode; |
280 Map<PubPackage, PackageBundle> unlinkedBundles = | 187 Map<PubPackage, PackageBundle> unlinkedBundles = |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
374 bundleWriter.majorVersion = majorVersion; | 281 bundleWriter.majorVersion = majorVersion; |
375 List<int> bytes = bundleWriter.toBuffer(); | 282 List<int> bytes = bundleWriter.toBuffer(); |
376 String fileName = _getUnlinkedName(strong); | 283 String fileName = _getUnlinkedName(strong); |
377 _writeAtomic(package.folder, fileName, bytes); | 284 _writeAtomic(package.folder, fileName, bytes); |
378 } on FileSystemException { | 285 } on FileSystemException { |
379 // Ignore file system exceptions. | 286 // Ignore file system exceptions. |
380 } | 287 } |
381 } | 288 } |
382 | 289 |
383 /** | 290 /** |
384 * Return the name of the file for a linked bundle, in strong or spec mode. | |
385 */ | |
386 String _getLinkedName(String hash, bool strong) { | |
387 if (strong) { | |
388 return 'linked_$hash.ds'; | |
389 } else { | |
390 return 'linked_spec_$hash.ds'; | |
391 } | |
392 } | |
393 | |
394 /** | |
395 * Return the name of the file for an unlinked bundle, in strong or spec mode. | 291 * Return the name of the file for an unlinked bundle, in strong or spec mode. |
396 */ | 292 */ |
397 String _getUnlinkedName(bool strong) { | 293 String _getUnlinkedName(bool strong) { |
398 if (strong) { | 294 if (strong) { |
399 return UNLINKED_NAME; | 295 return UNLINKED_NAME; |
400 } else { | 296 } else { |
401 return UNLINKED_SPEC_NAME; | 297 return UNLINKED_SPEC_NAME; |
402 } | 298 } |
403 } | 299 } |
404 | 300 |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
504 Token token = scanner.tokenize(); | 400 Token token = scanner.tokenize(); |
505 LineInfo lineInfo = new LineInfo(scanner.lineStarts); | 401 LineInfo lineInfo = new LineInfo(scanner.lineStarts); |
506 Parser parser = new Parser(source, errorListener); | 402 Parser parser = new Parser(source, errorListener); |
507 parser.parseGenericMethodComments = strong; | 403 parser.parseGenericMethodComments = strong; |
508 CompilationUnit unit = parser.parseCompilationUnit(token); | 404 CompilationUnit unit = parser.parseCompilationUnit(token); |
509 unit.lineInfo = lineInfo; | 405 unit.lineInfo = lineInfo; |
510 return unit; | 406 return unit; |
511 } | 407 } |
512 | 408 |
513 /** | 409 /** |
514 * Attempt to find the linked bundle that corresponds to the given [node] | |
515 * with all its transitive dependencies and put it into [_LinkNode.linked]. | |
516 */ | |
517 void _readLinked(_LinkNode node, bool strong) { | |
518 String hash = node.linkedHash; | |
519 if (hash != null) { | |
520 String fileName = _getLinkedName(hash, strong); | |
521 File file = node.package.folder.getChildAssumingFile(fileName); | |
522 // Try to find in the cache. | |
523 PackageBundle linked = linkedBundleMap[file.path]; | |
524 if (linked != null) { | |
525 node.linked = linked; | |
526 return; | |
527 } | |
528 // Try to read from the file system. | |
529 if (file.exists) { | |
530 try { | |
531 List<int> bytes = file.readAsBytesSync(); | |
532 linked = new PackageBundle.fromBuffer(bytes); | |
533 linkedBundleMap[file.path] = linked; | |
534 node.linked = linked; | |
535 } on FileSystemException { | |
536 // Ignore file system exceptions. | |
537 } | |
538 } | |
539 } | |
540 } | |
541 | |
542 /** | |
543 * Schedule delayed computation of the next package unlinked bundle from the | 410 * Schedule delayed computation of the next package unlinked bundle from the |
544 * set of [packagesToComputeUnlinked]. We delay each computation because we | 411 * set of [packagesToComputeUnlinked]. We delay each computation because we |
545 * want operations in analysis server to proceed, and computing bundles of | 412 * want operations in analysis server to proceed, and computing bundles of |
546 * packages is a background task. | 413 * packages is a background task. |
547 */ | 414 */ |
548 void _scheduleNextUnlinked() { | 415 void _scheduleNextUnlinked() { |
549 new Future.delayed(new Duration(milliseconds: 10), _computeNextUnlinked); | 416 new Future.delayed(new Duration(milliseconds: 10), _computeNextUnlinked); |
550 } | 417 } |
551 | 418 |
552 /** | 419 /** |
(...skipping 10 matching lines...) Expand all Loading... |
563 * Atomically write the given [bytes] into the file in the [folder]. | 430 * Atomically write the given [bytes] into the file in the [folder]. |
564 */ | 431 */ |
565 void _writeAtomic(Folder folder, String fileName, List<int> bytes) { | 432 void _writeAtomic(Folder folder, String fileName, List<int> bytes) { |
566 String filePath = folder.getChildAssumingFile(fileName).path; | 433 String filePath = folder.getChildAssumingFile(fileName).path; |
567 File tempFile = folder.getChildAssumingFile(tempFileName); | 434 File tempFile = folder.getChildAssumingFile(tempFileName); |
568 tempFile.writeAsBytesSync(bytes); | 435 tempFile.writeAsBytesSync(bytes); |
569 tempFile.renameSync(filePath); | 436 tempFile.renameSync(filePath); |
570 } | 437 } |
571 | 438 |
572 /** | 439 /** |
573 * If a new linked bundle was linked for the given [node], write the bundle | |
574 * into the memory cache and the file system. | |
575 */ | |
576 void _writeLinked(_LinkNode node, bool strong) { | |
577 String hash = node.linkedHash; | |
578 if (hash != null && node.linkedNewBytes != null) { | |
579 String fileName = _getLinkedName(hash, strong); | |
580 File file = node.package.folder.getChildAssumingFile(fileName); | |
581 linkedBundleMap[file.path] = node.linked; | |
582 _writeAtomic(node.package.folder, fileName, node.linkedNewBytes); | |
583 } | |
584 } | |
585 | |
586 /** | |
587 * If the given [uri] has the `package` scheme, return the name of the | 440 * If the given [uri] has the `package` scheme, return the name of the |
588 * package that contains the referenced resource. Otherwise return `null`. | 441 * package that contains the referenced resource. Otherwise return `null`. |
589 * | 442 * |
590 * For example `package:foo/bar.dart` => `foo`. | 443 * For example `package:foo/bar.dart` => `foo`. |
591 */ | 444 */ |
592 static String getPackageName(String uri) { | 445 static String getPackageName(String uri) { |
593 const String PACKAGE_SCHEME = 'package:'; | 446 const String PACKAGE_SCHEME = 'package:'; |
594 if (uri.startsWith(PACKAGE_SCHEME)) { | 447 if (uri.startsWith(PACKAGE_SCHEME)) { |
595 int index = uri.indexOf('/'); | 448 int index = uri.indexOf('/'); |
596 if (index != -1) { | 449 if (index != -1) { |
(...skipping 13 matching lines...) Expand all Loading... |
610 return true; | 463 return true; |
611 } | 464 } |
612 if (parts[i] == 'Pub' && parts[i + 1] == 'Cache') { | 465 if (parts[i] == 'Pub' && parts[i + 1] == 'Cache') { |
613 return true; | 466 return true; |
614 } | 467 } |
615 } | 468 } |
616 return false; | 469 return false; |
617 } | 470 } |
618 } | 471 } |
619 | 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 |
620 /** | 665 /** |
621 * Specialization of [Node] for linking packages in proper dependency order. | 666 * Information about a package to link. |
622 */ | 667 */ |
623 class _LinkNode extends Node<_LinkNode> { | 668 class _LinkNode { |
624 final PackageBundle sdkBundle; | 669 final _ContextLinker linker; |
625 final _GetDeclaredVariable getDeclaredVariable; | |
626 final _ListedPackages listedPackages; | |
627 final PubPackage package; | 670 final PubPackage package; |
628 final PackageBundle unlinked; | 671 final PackageBundle unlinked; |
629 final Map<String, _LinkNode> packageToNode; | |
630 | 672 |
631 bool failed = false; | 673 bool failed = false; |
632 Set<_LinkNode> transitiveDependencies; | 674 Set<_LinkNode> transitiveDependencies; |
| 675 |
| 676 List<_LinkNode> _dependencies; |
633 String _linkedHash; | 677 String _linkedHash; |
634 | 678 |
635 List<int> linkedNewBytes; | 679 List<int> linkedNewBytes; |
636 PackageBundle linked; | 680 PackageBundle linked; |
637 | 681 |
638 _LinkNode(this.sdkBundle, this.getDeclaredVariable, this.listedPackages, | 682 _LinkNode(this.linker, this.package, this.unlinked); |
639 this.package, this.unlinked, this.packageToNode); | 683 |
640 | 684 /** |
641 @override | 685 * Retrieve the dependencies of this node. |
642 bool get isEvaluated => linked != null || failed; | 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; |
643 | 733 |
644 /** | 734 /** |
645 * Return the hash string that corresponds to this linked bundle in the | 735 * Return the hash string that corresponds to this linked bundle in the |
646 * context of its [sdkBundle] and transitive dependencies. Return `null` if | 736 * context of its SDK bundle and transitive dependencies. Return `null` if |
647 * the hash computation fails, because for example the full transitive | 737 * the hash computation fails, because for example the full transitive |
648 * dependencies cannot computed. | 738 * dependencies cannot computed. |
649 */ | 739 */ |
650 String get linkedHash { | 740 String get linkedHash { |
651 if (_linkedHash == null && transitiveDependencies != null) { | 741 if (_linkedHash == null && transitiveDependencies != null) { |
652 ApiSignature signature = new ApiSignature(); | 742 ApiSignature signature = new ApiSignature(); |
653 // Add all unlinked API signatures. | 743 // Add all unlinked API signatures. |
654 List<String> signatures = <String>[]; | 744 List<String> signatures = <String>[]; |
655 signatures.add(sdkBundle.apiSignature); | 745 signatures.add(linker.sdkBundle.apiSignature); |
656 transitiveDependencies | 746 transitiveDependencies |
657 .map((node) => node.unlinked.apiSignature) | 747 .map((node) => node.unlinked.apiSignature) |
658 .forEach(signatures.add); | 748 .forEach(signatures.add); |
659 signatures.sort(); | 749 signatures.sort(); |
660 signatures.forEach(signature.addString); | 750 signatures.forEach(signature.addString); |
661 // Combine into a single hash. | 751 // Combine into a single hash. |
662 _appendDeclaredVariables(signature); | 752 appendDeclaredVariables(signature); |
663 _linkedHash = signature.toHex(); | 753 _linkedHash = signature.toHex(); |
664 } | 754 } |
665 return _linkedHash; | 755 return _linkedHash; |
666 } | 756 } |
667 | 757 |
668 @override | 758 /** |
669 List<_LinkNode> computeDependencies() { | 759 * Append names and values of all referenced declared variables (even the |
670 Set<_LinkNode> dependencies = new Set<_LinkNode>(); | 760 * ones without actually declared values) to the given [signature]. |
671 | 761 */ |
672 void appendDependency(String uriStr) { | 762 void appendDeclaredVariables(ApiSignature signature) { |
673 Uri uri = FastUri.parse(uriStr); | 763 Set<String> nameSet = new Set<String>(); |
674 if (!uri.hasScheme) { | 764 for (_LinkNode node in transitiveDependencies) { |
675 // A relative path in this package, skip it. | 765 for (UnlinkedUnit unit in node.unlinked.unlinkedUnits) { |
676 } else if (uri.scheme == 'dart') { | 766 for (UnlinkedImport import in unit.imports) { |
677 // Dependency on the SDK is implicit and always added. | 767 for (UnlinkedConfiguration configuration in import.configurations) { |
678 // The SDK linked bundle is precomputed before linking packages. | 768 nameSet.add(configuration.name); |
679 } else if (uriStr.startsWith('package:')) { | 769 } |
680 String package = PubSummaryManager.getPackageName(uriStr); | |
681 _LinkNode packageNode = packageToNode[package]; | |
682 if (packageNode == null && listedPackages.isListed(uriStr)) { | |
683 failed = true; | |
684 } | 770 } |
685 if (packageNode != null) { | 771 for (UnlinkedExportPublic export in unit.publicNamespace.exports) { |
686 dependencies.add(packageNode); | 772 for (UnlinkedConfiguration configuration in export.configurations) { |
| 773 nameSet.add(configuration.name); |
| 774 } |
687 } | 775 } |
688 } else { | |
689 failed = true; | |
690 } | 776 } |
691 } | 777 } |
692 | 778 List<String> sortedNameList = nameSet.toList()..sort(); |
693 for (UnlinkedUnit unit in unlinked.unlinkedUnits) { | 779 signature.addInt(sortedNameList.length); |
694 for (UnlinkedImport import in unit.imports) { | 780 for (String name in sortedNameList) { |
695 if (!import.isImplicit) { | 781 signature.addString(name); |
696 appendDependency(import.uri); | 782 signature.addString(linker._getDeclaredVariable(name) ?? ''); |
697 } | |
698 } | |
699 for (UnlinkedExportPublic export in unit.publicNamespace.exports) { | |
700 appendDependency(export.uri); | |
701 } | |
702 } | 783 } |
703 | |
704 return dependencies.toList(); | |
705 } | 784 } |
706 | 785 |
707 /** | 786 /** |
708 * Compute the set of existing transitive dependencies for this node. | 787 * Compute the set of existing transitive dependencies for this node. |
709 * If any `package` dependency cannot be resolved, but it is one of the | 788 * If any `package` dependency cannot be resolved, but it is one of the |
710 * [listedPackages] then set [failed] to `true`. | 789 * [listedPackages] then set [failed] to `true`. |
711 * Only [unlinked] is used, so this method can be called before linking. | 790 * Only [unlinked] is used, so this method can be called before linking. |
712 */ | 791 */ |
713 void computeTransitiveDependencies() { | 792 void computeTransitiveDependencies() { |
714 if (transitiveDependencies == null) { | 793 if (transitiveDependencies == null) { |
715 transitiveDependencies = new Set<_LinkNode>(); | 794 transitiveDependencies = new Set<_LinkNode>(); |
716 | 795 |
717 void appendDependencies(_LinkNode node) { | 796 void appendDependencies(_LinkNode node) { |
718 if (transitiveDependencies.add(node)) { | 797 if (transitiveDependencies.add(node)) { |
719 node.dependencies.forEach(appendDependencies); | 798 node.dependencies.forEach(appendDependencies); |
720 } | 799 } |
721 } | 800 } |
722 | 801 |
723 appendDependencies(this); | 802 appendDependencies(this); |
724 if (transitiveDependencies.any((node) => node.failed)) { | 803 if (transitiveDependencies.any((node) => node.failed)) { |
725 failed = true; | 804 failed = true; |
726 } | 805 } |
727 } | 806 } |
728 } | 807 } |
729 | 808 |
730 @override | 809 @override |
731 String toString() => package.toString(); | 810 String toString() => package.toString(); |
732 | |
733 /** | |
734 * Append names and values of all referenced declared variables (even the | |
735 * ones without actually declared values) to the given [signature]. | |
736 */ | |
737 void _appendDeclaredVariables(ApiSignature signature) { | |
738 Set<String> nameSet = new Set<String>(); | |
739 for (_LinkNode node in transitiveDependencies) { | |
740 for (UnlinkedUnit unit in node.unlinked.unlinkedUnits) { | |
741 for (UnlinkedImport import in unit.imports) { | |
742 for (UnlinkedConfiguration configuration in import.configurations) { | |
743 nameSet.add(configuration.name); | |
744 } | |
745 } | |
746 for (UnlinkedExportPublic export in unit.publicNamespace.exports) { | |
747 for (UnlinkedConfiguration configuration in export.configurations) { | |
748 nameSet.add(configuration.name); | |
749 } | |
750 } | |
751 } | |
752 } | |
753 List<String> sortedNameList = nameSet.toList()..sort(); | |
754 signature.addInt(sortedNameList.length); | |
755 for (String name in sortedNameList) { | |
756 signature.addString(name); | |
757 signature.addString(getDeclaredVariable(name) ?? ''); | |
758 } | |
759 } | |
760 } | 811 } |
761 | 812 |
762 /** | 813 /** |
763 * Specialization of [DependencyWalker] for linking packages. | |
764 */ | |
765 class _LinkWalker extends DependencyWalker<_LinkNode> { | |
766 final _GetDeclaredVariable getDeclaredVariable; | |
767 final _ListedPackages listedPackages; | |
768 final SummaryDataStore store; | |
769 final bool strong; | |
770 | |
771 _LinkWalker( | |
772 this.getDeclaredVariable, this.listedPackages, this.store, this.strong); | |
773 | |
774 @override | |
775 void evaluate(_LinkNode node) { | |
776 evaluateScc([node]); | |
777 } | |
778 | |
779 @override | |
780 void evaluateScc(List<_LinkNode> scc) { | |
781 Map<String, _LinkNode> uriToNode = <String, _LinkNode>{}; | |
782 for (_LinkNode node in scc) { | |
783 for (String uri in node.unlinked.unlinkedUnitUris) { | |
784 uriToNode[uri] = node; | |
785 } | |
786 } | |
787 Set<String> libraryUris = uriToNode.keys.toSet(); | |
788 // Perform linking. | |
789 Map<String, LinkedLibraryBuilder> linkedLibraries = | |
790 link(libraryUris, (String uri) { | |
791 return store.linkedMap[uri]; | |
792 }, (String uri) { | |
793 return store.unlinkedMap[uri]; | |
794 }, getDeclaredVariable, strong); | |
795 // Assemble linked bundles and put them into the store. | |
796 for (_LinkNode node in scc) { | |
797 PackageBundleAssembler assembler = new PackageBundleAssembler(); | |
798 linkedLibraries.forEach((uri, linkedLibrary) { | |
799 if (identical(uriToNode[uri], node)) { | |
800 assembler.addLinkedLibrary(uri, linkedLibrary); | |
801 } | |
802 }); | |
803 List<int> bytes = assembler.assemble().toBuffer(); | |
804 node.linkedNewBytes = bytes; | |
805 node.linked = new PackageBundle.fromBuffer(bytes); | |
806 store.addBundle(null, node.linked); | |
807 } | |
808 } | |
809 } | |
810 | |
811 /** | |
812 * The set of package names that are listed in the `.packages` file of a | 814 * The set of package names that are listed in the `.packages` file of a |
813 * context. These are the only packages, references to which can | 815 * context. These are the only packages, references to which can |
814 * be possibly resolved in the context. Nodes that reference a `package:` URI | 816 * be possibly resolved in the context. Nodes that reference a `package:` URI |
815 * without the unlinked bundle, so without the node, cannot be linked. | 817 * without the unlinked bundle, so without the node, cannot be linked. |
816 */ | 818 */ |
817 class _ListedPackages { | 819 class _ListedPackages { |
818 final Set<String> names = new Set<String>(); | 820 final Set<String> names = new Set<String>(); |
819 | 821 |
820 _ListedPackages(SourceFactory sourceFactory) { | 822 _ListedPackages(SourceFactory sourceFactory) { |
821 Map<String, List<Folder>> map = sourceFactory.packageMap; | 823 Map<String, List<Folder>> map = sourceFactory.packageMap; |
822 if (map != null) { | 824 if (map != null) { |
823 names.addAll(map.keys); | 825 names.addAll(map.keys); |
824 } | 826 } |
825 } | 827 } |
826 | 828 |
827 /** | 829 /** |
828 * Check whether the given `package:` [uri] is listed in the package map. | 830 * Check whether the given `package:` [uri] is listed in the package map. |
829 */ | 831 */ |
830 bool isListed(String uri) { | 832 bool isListed(String uri) { |
831 String package = PubSummaryManager.getPackageName(uri); | 833 String package = PubSummaryManager.getPackageName(uri); |
832 return names.contains(package); | 834 return names.contains(package); |
833 } | 835 } |
834 } | 836 } |
OLD | NEW |