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'; |
11 import 'package:analyzer/src/generated/engine.dart' | 11 import 'package:analyzer/src/generated/engine.dart' |
12 hide AnalysisTask, AnalysisContextImpl; | 12 hide AnalysisTask, AnalysisContextImpl, WorkManager; |
13 import 'package:analyzer/src/generated/java_engine.dart'; | 13 import 'package:analyzer/src/generated/java_engine.dart'; |
14 import 'package:analyzer/src/generated/resolver.dart'; | 14 import 'package:analyzer/src/generated/resolver.dart'; |
15 import 'package:analyzer/src/generated/utilities_general.dart'; | 15 import 'package:analyzer/src/generated/utilities_general.dart'; |
16 import 'package:analyzer/src/task/inputs.dart'; | 16 import 'package:analyzer/src/task/inputs.dart'; |
17 import 'package:analyzer/src/task/manager.dart'; | 17 import 'package:analyzer/src/task/manager.dart'; |
18 import 'package:analyzer/task/model.dart'; | 18 import 'package:analyzer/task/model.dart'; |
19 | 19 |
20 final PerformanceTag workOrderMoveNextPerfTag = | 20 final PerformanceTag workOrderMoveNextPerfTag = |
21 new PerformanceTag('WorkOrder.moveNext'); | 21 new PerformanceTag('WorkOrder.moveNext'); |
22 | 22 |
(...skipping 14 matching lines...) Expand all Loading... |
37 final List<WorkManager> workManagers; | 37 final List<WorkManager> workManagers; |
38 | 38 |
39 /** | 39 /** |
40 * The context in which analysis is to be performed. | 40 * The context in which analysis is to be performed. |
41 */ | 41 */ |
42 final InternalAnalysisContext context; | 42 final InternalAnalysisContext context; |
43 | 43 |
44 /** | 44 /** |
45 * The map of [ComputedResult] controllers. | 45 * The map of [ComputedResult] controllers. |
46 */ | 46 */ |
47 final Map<ResultDescriptor, StreamController<ComputedResult>> resultComputedCo
ntrollers = | 47 final Map<ResultDescriptor, |
| 48 StreamController<ComputedResult>> resultComputedControllers = |
48 <ResultDescriptor, StreamController<ComputedResult>>{}; | 49 <ResultDescriptor, StreamController<ComputedResult>>{}; |
49 | 50 |
50 /** | 51 /** |
51 * The work order that was previously computed but that has not yet been | 52 * The work order that was previously computed but that has not yet been |
52 * completed. | 53 * completed. |
53 */ | 54 */ |
54 WorkOrder currentWorkOrder; | 55 WorkOrder currentWorkOrder; |
55 | 56 |
56 /** | 57 /** |
57 * Indicates whether any tasks are currently being performed (or building | 58 * Indicates whether any tasks are currently being performed (or building |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
190 } | 191 } |
191 } | 192 } |
192 return null; | 193 return null; |
193 } | 194 } |
194 | 195 |
195 /** | 196 /** |
196 * Return the stream that is notified when a new value for the given | 197 * Return the stream that is notified when a new value for the given |
197 * [descriptor] is computed. | 198 * [descriptor] is computed. |
198 */ | 199 */ |
199 Stream<ComputedResult> onResultComputed(ResultDescriptor descriptor) { | 200 Stream<ComputedResult> onResultComputed(ResultDescriptor descriptor) { |
200 return resultComputedControllers.putIfAbsent(descriptor, () => | 201 return resultComputedControllers |
201 new StreamController<ComputedResult>.broadcast(sync: true)).stream; | 202 .putIfAbsent(descriptor, |
| 203 () => new StreamController<ComputedResult>.broadcast(sync: true)) |
| 204 .stream; |
202 } | 205 } |
203 | 206 |
204 /** | 207 /** |
205 * Perform the next analysis task, and return `true` if there is more work to | 208 * Perform the next analysis task, and return `true` if there is more work to |
206 * be done in order to compute all of the required analysis information. | 209 * be done in order to compute all of the required analysis information. |
207 */ | 210 */ |
208 bool performAnalysisTask() { | 211 bool performAnalysisTask() { |
209 // | 212 // |
210 // TODO(brianwilkerson) This implementaiton does not allow us to prioritize | 213 // TODO(brianwilkerson) This implementaiton does not allow us to prioritize |
211 // work across contexts. What we need is a way for an external client to ask | 214 // work across contexts. What we need is a way for an external client to ask |
(...skipping 257 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
469 * the set of [WorkItem]s contained in the cycle (if there are overlapping | 472 * the set of [WorkItem]s contained in the cycle (if there are overlapping |
470 * cycles, this is the set of all [WorkItem]s in the entire strongly | 473 * cycles, this is the set of all [WorkItem]s in the entire strongly |
471 * connected component). Otherwise, `null`. | 474 * connected component). Otherwise, `null`. |
472 */ | 475 */ |
473 final List<WorkItem> dependencyCycle; | 476 final List<WorkItem> dependencyCycle; |
474 | 477 |
475 /** | 478 /** |
476 * Initialize a newly created exception to represent a failed attempt to | 479 * Initialize a newly created exception to represent a failed attempt to |
477 * perform the given [task] due to the given [dependencyCycle]. | 480 * perform the given [task] due to the given [dependencyCycle]. |
478 */ | 481 */ |
479 InfiniteTaskLoopException(AnalysisTask task, this.dependencyCycle) : super( | 482 InfiniteTaskLoopException(AnalysisTask task, this.dependencyCycle) |
480 'Infinite loop while performing task ${task.descriptor.name} for ${tas
k.target}'); | 483 : super( |
| 484 'Infinite loop while performing task ${task.descriptor.name} for ${t
ask.target}'); |
481 } | 485 } |
482 | 486 |
483 /** | 487 /** |
484 * Object used by CycleAwareDependencyWalker to report a single strongly | 488 * Object used by CycleAwareDependencyWalker to report a single strongly |
485 * connected component of nodes. | 489 * connected component of nodes. |
486 */ | 490 */ |
487 class StronglyConnectedComponent<Node> { | 491 class StronglyConnectedComponent<Node> { |
488 /** | 492 /** |
489 * The nodes contained in the strongly connected component. | 493 * The nodes contained in the strongly connected component. |
490 */ | 494 */ |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
558 * cycles, this is the set of all [WorkItem]s in the entire strongly | 562 * cycles, this is the set of all [WorkItem]s in the entire strongly |
559 * connected component). Otherwise, `null`. | 563 * connected component). Otherwise, `null`. |
560 */ | 564 */ |
561 List<WorkItem> dependencyCycle; | 565 List<WorkItem> dependencyCycle; |
562 | 566 |
563 /** | 567 /** |
564 * Initialize a newly created work item to compute the inputs for the task | 568 * Initialize a newly created work item to compute the inputs for the task |
565 * described by the given descriptor. | 569 * described by the given descriptor. |
566 */ | 570 */ |
567 WorkItem(this.context, this.target, this.descriptor, this.spawningResult) { | 571 WorkItem(this.context, this.target, this.descriptor, this.spawningResult) { |
568 AnalysisTarget actualTarget = identical( | 572 AnalysisTarget actualTarget = |
569 target, AnalysisContextTarget.request) | 573 identical(target, AnalysisContextTarget.request) |
570 ? new AnalysisContextTarget(context) | 574 ? new AnalysisContextTarget(context) |
571 : target; | 575 : target; |
572 Map<String, TaskInput> inputDescriptors = | 576 Map<String, TaskInput> inputDescriptors = |
573 descriptor.createTaskInputs(actualTarget); | 577 descriptor.createTaskInputs(actualTarget); |
574 builder = new TopLevelTaskInputBuilder(inputDescriptors); | 578 builder = new TopLevelTaskInputBuilder(inputDescriptors); |
575 if (!builder.moveNext()) { | 579 if (!builder.moveNext()) { |
576 builder = null; | 580 builder = null; |
577 } | 581 } |
578 inputs = new HashMap<String, dynamic>(); | 582 inputs = new HashMap<String, dynamic>(); |
579 } | 583 } |
580 | 584 |
581 @override | 585 @override |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
665 } | 669 } |
666 } | 670 } |
667 return null; | 671 return null; |
668 } | 672 } |
669 | 673 |
670 @override | 674 @override |
671 String toString() => 'Run $descriptor on $target'; | 675 String toString() => 'Run $descriptor on $target'; |
672 } | 676 } |
673 | 677 |
674 /** | 678 /** |
675 * [AnalysisDriver] uses [WorkManager]s to select results to compute. | |
676 * | |
677 * They know specific of the targets and results they care about, | |
678 * so they can request analysis results in optimal order. | |
679 */ | |
680 abstract class WorkManager { | |
681 /** | |
682 * Notifies the managers that the given set of priority [targets] was set. | |
683 */ | |
684 void applyPriorityTargets(List<AnalysisTarget> targets); | |
685 | |
686 /** | |
687 * Return the next [TargetedResult] that this work manager wants to be | |
688 * computed, or `null` if this manager doesn't need any new results. | |
689 */ | |
690 TargetedResult getNextResult(); | |
691 | |
692 /** | |
693 * Return the priority if the next work order this work manager want to be | |
694 * computed. The [AnalysisDriver] will perform the work order with | |
695 * the highest priority. | |
696 * | |
697 * Even if the returned value is [WorkOrderPriority.NONE], it still does not | |
698 * guarantee that [getNextResult] will return not `null`. | |
699 */ | |
700 WorkOrderPriority getNextResultPriority(); | |
701 | |
702 /** | |
703 * Notifies the manager that the given [outputs] were produced for | |
704 * the given [target]. | |
705 */ | |
706 void resultsComputed( | |
707 AnalysisTarget target, Map<ResultDescriptor, dynamic> outputs); | |
708 } | |
709 | |
710 /** | |
711 * A description of the work to be done to compute a desired analysis result. | 679 * A description of the work to be done to compute a desired analysis result. |
712 * The class implements a lazy depth-first traversal of the work item's input. | 680 * The class implements a lazy depth-first traversal of the work item's input. |
713 */ | 681 */ |
714 class WorkOrder implements Iterator<WorkItem> { | 682 class WorkOrder implements Iterator<WorkItem> { |
715 /** | 683 /** |
716 * The dependency walker which is being used to determine what work to do | 684 * The dependency walker which is being used to determine what work to do |
717 * next. | 685 * next. |
718 */ | 686 */ |
719 final _WorkOrderDependencyWalker _dependencyWalker; | 687 final _WorkOrderDependencyWalker _dependencyWalker; |
720 | 688 |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
767 } else { | 735 } else { |
768 assert(currentItems.length == 1); | 736 assert(currentItems.length == 1); |
769 } | 737 } |
770 return true; | 738 return true; |
771 } | 739 } |
772 }); | 740 }); |
773 } | 741 } |
774 } | 742 } |
775 | 743 |
776 /** | 744 /** |
777 * The priorities of work orders returned by [WorkManager]s. | |
778 */ | |
779 enum WorkOrderPriority { | |
780 /** | |
781 * Responding to an user's action. | |
782 */ | |
783 INTERACTIVE, | |
784 | |
785 /** | |
786 * Computing information for priority sources. | |
787 */ | |
788 PRIORITY, | |
789 | |
790 /** | |
791 * A work should be done, but without any special urgency. | |
792 */ | |
793 NORMAL, | |
794 | |
795 /** | |
796 * Nothing to do. | |
797 */ | |
798 NONE | |
799 } | |
800 | |
801 /** | |
802 * Specilaization of [CycleAwareDependencyWalker] for use by [WorkOrder]. | 745 * Specilaization of [CycleAwareDependencyWalker] for use by [WorkOrder]. |
803 */ | 746 */ |
804 class _WorkOrderDependencyWalker extends CycleAwareDependencyWalker<WorkItem> { | 747 class _WorkOrderDependencyWalker extends CycleAwareDependencyWalker<WorkItem> { |
805 /** | 748 /** |
806 * The task manager used to build work items. | 749 * The task manager used to build work items. |
807 */ | 750 */ |
808 final TaskManager taskManager; | 751 final TaskManager taskManager; |
809 | 752 |
810 _WorkOrderDependencyWalker(this.taskManager, WorkItem startingNode) | 753 _WorkOrderDependencyWalker(this.taskManager, WorkItem startingNode) |
811 : super(startingNode); | 754 : super(startingNode); |
812 | 755 |
813 @override | 756 @override |
814 WorkItem getNextInput(WorkItem node, List<WorkItem> skipInputs) { | 757 WorkItem getNextInput(WorkItem node, List<WorkItem> skipInputs) { |
815 return node.gatherInputs(taskManager, skipInputs); | 758 return node.gatherInputs(taskManager, skipInputs); |
816 } | 759 } |
817 } | 760 } |
OLD | NEW |