| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 library analyzer.src.task.driver; | 5 library analyzer.src.task.driver; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:collection'; | 8 import 'dart:collection'; |
| 9 | 9 |
| 10 import 'package:analyzer/src/context/cache.dart'; | 10 import 'package:analyzer/src/context/cache.dart'; |
| (...skipping 330 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 341 * [skipInputs], and return it. If [node] has no further inputs, return | 341 * [skipInputs], and return it. If [node] has no further inputs, return |
| 342 * `null`. | 342 * `null`. |
| 343 */ | 343 */ |
| 344 Node getNextInput(Node node, List<Node> skipInputs); | 344 Node getNextInput(Node node, List<Node> skipInputs); |
| 345 | 345 |
| 346 /** | 346 /** |
| 347 * Determine the next strongly connected component in the graph, and return | 347 * Determine the next strongly connected component in the graph, and return |
| 348 * it. The client is expected to evaluate this component before calling | 348 * it. The client is expected to evaluate this component before calling |
| 349 * [getNextStronglyConnectedComponent] again. | 349 * [getNextStronglyConnectedComponent] again. |
| 350 */ | 350 */ |
| 351 List<Node> getNextStronglyConnectedComponent() { | 351 StronglyConnectedComponent<Node> getNextStronglyConnectedComponent() { |
| 352 while (_currentIndices.isNotEmpty) { | 352 while (_currentIndices.isNotEmpty) { |
| 353 Node nextUnevaluatedInput = getNextInput(_path[_currentIndices.last], | 353 Node nextUnevaluatedInput = getNextInput(_path[_currentIndices.last], |
| 354 _provisionalDependencies[_currentIndices.last]); | 354 _provisionalDependencies[_currentIndices.last]); |
| 355 assert(!_provisionalDependencies[_currentIndices.last] | 355 assert(!_provisionalDependencies[_currentIndices.last] |
| 356 .contains(nextUnevaluatedInput)); | 356 .contains(nextUnevaluatedInput)); |
| 357 if (nextUnevaluatedInput != null) { | 357 if (nextUnevaluatedInput != null) { |
| 358 // TODO(paulberry): the call to _path.indexOf makes the algorithm | 358 // TODO(paulberry): the call to _path.indexOf makes the algorithm |
| 359 // O(n^2) in the depth of the dependency graph. If this becomes a | 359 // O(n^2) in the depth of the dependency graph. If this becomes a |
| 360 // problem, consider maintaining a map from node to index. | 360 // problem, consider maintaining a map from node to index. |
| 361 int previousIndex = _path.indexOf(nextUnevaluatedInput); | 361 int previousIndex = _path.indexOf(nextUnevaluatedInput); |
| (...skipping 25 matching lines...) Expand all Loading... |
| 387 } else { | 387 } else { |
| 388 // The node has no more inputs. Figure out if there are any more nodes | 388 // The node has no more inputs. Figure out if there are any more nodes |
| 389 // in the current strongly connected component that need to have their | 389 // in the current strongly connected component that need to have their |
| 390 // indices examined. | 390 // indices examined. |
| 391 _currentIndices.removeLast(); | 391 _currentIndices.removeLast(); |
| 392 if (_currentIndices.isEmpty || | 392 if (_currentIndices.isEmpty || |
| 393 _currentIndices.last < _contractedPath.last) { | 393 _currentIndices.last < _contractedPath.last) { |
| 394 // No more nodes in the current strongly connected component need to | 394 // No more nodes in the current strongly connected component need to |
| 395 // have their indices examined. We can now yield this component to | 395 // have their indices examined. We can now yield this component to |
| 396 // the caller. | 396 // the caller. |
| 397 List<Node> component = _path.sublist(_contractedPath.last); | 397 List<Node> nodes = _path.sublist(_contractedPath.last); |
| 398 bool containsCycle = nodes.length > 1; |
| 399 if (!containsCycle) { |
| 400 if (_provisionalDependencies.last.isNotEmpty) { |
| 401 containsCycle = true; |
| 402 } |
| 403 } |
| 398 _path.length = _contractedPath.last; | 404 _path.length = _contractedPath.last; |
| 399 _provisionalDependencies.length = _contractedPath.last; | 405 _provisionalDependencies.length = _contractedPath.last; |
| 400 _contractedPath.removeLast(); | 406 _contractedPath.removeLast(); |
| 401 return component; | 407 return new StronglyConnectedComponent<Node>(nodes, containsCycle); |
| 402 } else { | 408 } else { |
| 403 // At least one node in the current strongly connected component | 409 // At least one node in the current strongly connected component |
| 404 // still needs to have its inputs examined. So loop and allow the | 410 // still needs to have its inputs examined. So loop and allow the |
| 405 // inputs to be examined. | 411 // inputs to be examined. |
| 406 continue; | 412 continue; |
| 407 } | 413 } |
| 408 } | 414 } |
| 409 } | 415 } |
| 410 // No further strongly connected components found. | 416 // No further strongly connected components found. |
| 411 return null; | 417 return null; |
| (...skipping 27 matching lines...) Expand all Loading... |
| 439 | 445 |
| 440 /** | 446 /** |
| 441 * Initialize a newly created exception to represent a failed attempt to | 447 * Initialize a newly created exception to represent a failed attempt to |
| 442 * perform the given [task] due to the given [dependencyCycle]. | 448 * perform the given [task] due to the given [dependencyCycle]. |
| 443 */ | 449 */ |
| 444 InfiniteTaskLoopException(AnalysisTask task, this.dependencyCycle) : super( | 450 InfiniteTaskLoopException(AnalysisTask task, this.dependencyCycle) : super( |
| 445 'Infinite loop while performing task ${task.descriptor.name} for ${tas
k.target}'); | 451 'Infinite loop while performing task ${task.descriptor.name} for ${tas
k.target}'); |
| 446 } | 452 } |
| 447 | 453 |
| 448 /** | 454 /** |
| 455 * Object used by CycleAwareDependencyWalker to report a single strongly |
| 456 * connected component of nodes. |
| 457 */ |
| 458 class StronglyConnectedComponent<Node> { |
| 459 /** |
| 460 * The nodes contained in the strongly connected component. |
| 461 */ |
| 462 final List<Node> nodes; |
| 463 |
| 464 /** |
| 465 * Indicates whether the strongly component contains any cycles. Note that |
| 466 * if [nodes] has multiple elements, this will always be `true`. However, if |
| 467 * [nodes] has exactly one element, this may be either `true` or `false` |
| 468 * depending on whether the node has a dependency on itself. |
| 469 */ |
| 470 final bool containsCycle; |
| 471 |
| 472 StronglyConnectedComponent(this.nodes, this.containsCycle); |
| 473 } |
| 474 |
| 475 /** |
| 449 * A description of a single anaysis task that can be performed to advance | 476 * A description of a single anaysis task that can be performed to advance |
| 450 * analysis. | 477 * analysis. |
| 451 */ | 478 */ |
| 452 class WorkItem { | 479 class WorkItem { |
| 453 /** | 480 /** |
| 454 * The context in which the task will be performed. | 481 * The context in which the task will be performed. |
| 455 */ | 482 */ |
| 456 final InternalAnalysisContext context; | 483 final InternalAnalysisContext context; |
| 457 | 484 |
| 458 /** | 485 /** |
| (...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 688 } | 715 } |
| 689 | 716 |
| 690 @override | 717 @override |
| 691 bool moveNext() { | 718 bool moveNext() { |
| 692 if (currentItems != null && currentItems.length > 1) { | 719 if (currentItems != null && currentItems.length > 1) { |
| 693 // Yield more items. | 720 // Yield more items. |
| 694 currentItems.removeLast(); | 721 currentItems.removeLast(); |
| 695 return true; | 722 return true; |
| 696 } else { | 723 } else { |
| 697 // Get a new strongly connected component. | 724 // Get a new strongly connected component. |
| 698 currentItems = _dependencyWalker.getNextStronglyConnectedComponent(); | 725 StronglyConnectedComponent<WorkItem> nextStronglyConnectedComponent = |
| 699 if (currentItems == null) { | 726 _dependencyWalker.getNextStronglyConnectedComponent(); |
| 727 if (nextStronglyConnectedComponent == null) { |
| 728 currentItems = null; |
| 700 return false; | 729 return false; |
| 701 } | 730 } |
| 702 if (currentItems.length > 1) { | 731 currentItems = nextStronglyConnectedComponent.nodes; |
| 732 if (nextStronglyConnectedComponent.containsCycle) { |
| 703 // A cycle has been found. | 733 // A cycle has been found. |
| 704 for (WorkItem item in currentItems) { | 734 for (WorkItem item in currentItems) { |
| 705 item.dependencyCycle = currentItems.toList(); | 735 item.dependencyCycle = currentItems.toList(); |
| 706 } | 736 } |
| 707 } else { | 737 } else { |
| 708 assert(currentItems.length == 1); | 738 assert(currentItems.length == 1); |
| 709 } | 739 } |
| 710 return true; | 740 return true; |
| 711 } | 741 } |
| 712 } | 742 } |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 747 final TaskManager taskManager; | 777 final TaskManager taskManager; |
| 748 | 778 |
| 749 _WorkOrderDependencyWalker(this.taskManager, WorkItem startingNode) | 779 _WorkOrderDependencyWalker(this.taskManager, WorkItem startingNode) |
| 750 : super(startingNode); | 780 : super(startingNode); |
| 751 | 781 |
| 752 @override | 782 @override |
| 753 WorkItem getNextInput(WorkItem node, List<WorkItem> skipInputs) { | 783 WorkItem getNextInput(WorkItem node, List<WorkItem> skipInputs) { |
| 754 return node.gatherInputs(taskManager, skipInputs); | 784 return node.gatherInputs(taskManager, skipInputs); |
| 755 } | 785 } |
| 756 } | 786 } |
| OLD | NEW |