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' hide AnalysisTask; | 11 import 'package:analyzer/src/generated/engine.dart' hide AnalysisTask; |
12 import 'package:analyzer/src/generated/java_engine.dart'; | 12 import 'package:analyzer/src/generated/java_engine.dart'; |
13 import 'package:analyzer/src/generated/resolver.dart'; | 13 import 'package:analyzer/src/generated/resolver.dart'; |
14 import 'package:analyzer/src/task/inputs.dart'; | 14 import 'package:analyzer/src/task/inputs.dart'; |
15 import 'package:analyzer/src/task/manager.dart'; | 15 import 'package:analyzer/src/task/manager.dart'; |
16 import 'package:analyzer/task/model.dart'; | 16 import 'package:analyzer/task/model.dart'; |
17 | 17 |
18 /** | 18 /** |
19 * An object that is used to cause analysis to be performed until all of the | 19 * An object that is used to cause analysis to be performed until all of the |
20 * required analysis information has been computed. | 20 * required analysis information has been computed. |
21 */ | 21 */ |
22 class AnalysisDriver { | 22 class AnalysisDriver { |
23 /** | 23 /** |
24 * The task manager used to figure out how to compute analysis results. | 24 * The task manager used to figure out how to compute analysis results. |
25 */ | 25 */ |
26 final TaskManager taskManager; | 26 final TaskManager taskManager; |
27 | 27 |
28 // /** | 28 /** |
29 // * The list of [WorkManager] used to figure out which analysis results to | 29 * The list of [WorkManager] used to figure out which analysis results to |
30 // * compute. | 30 * compute. |
31 // */ | 31 */ |
32 // final List<WorkManager> workManagers; | 32 final List<WorkManager> workManagers; |
33 | 33 |
34 /** | 34 /** |
35 * The context in which analysis is to be performed. | 35 * The context in which analysis is to be performed. |
36 */ | 36 */ |
37 final InternalAnalysisContext context; | 37 final InternalAnalysisContext context; |
38 | 38 |
39 /** | 39 /** |
40 * The work order that was previously computed but that has not yet been | 40 * The work order that was previously computed but that has not yet been |
41 * completed. | 41 * completed. |
42 */ | 42 */ |
43 WorkOrder currentWorkOrder; | 43 WorkOrder currentWorkOrder; |
44 | 44 |
45 /** | 45 /** |
46 * The controller that is notified when a task is started. | 46 * The controller that is notified when a task is started. |
47 */ | 47 */ |
48 StreamController<AnalysisTask> _onTaskStartedController; | 48 StreamController<AnalysisTask> _onTaskStartedController; |
49 | 49 |
50 /** | 50 /** |
51 * The controller that is notified when a task is complete. | 51 * The controller that is notified when a task is complete. |
52 */ | 52 */ |
53 StreamController<AnalysisTask> _onTaskCompletedController; | 53 StreamController<AnalysisTask> _onTaskCompletedController; |
54 | 54 |
55 /** | 55 /** |
56 * Initialize a newly created driver to use the tasks know to the given | 56 * Initialize a newly created driver to use the tasks know to the given |
57 * [taskManager] to perform analysis in the given [context]. | 57 * [taskManager] to perform analysis in the given [context]. |
58 */ | 58 */ |
59 AnalysisDriver(this.taskManager, this.context) { | 59 AnalysisDriver(this.taskManager, this.workManagers, this.context) { |
60 _onTaskStartedController = new StreamController.broadcast(); | 60 _onTaskStartedController = new StreamController.broadcast(); |
61 _onTaskCompletedController = new StreamController.broadcast(); | 61 _onTaskCompletedController = new StreamController.broadcast(); |
62 } | 62 } |
63 | 63 |
64 /** | 64 /** |
65 * The stream that is notified when a task is complete. | 65 * The stream that is notified when a task is complete. |
66 */ | 66 */ |
67 Stream<AnalysisTask> get onTaskCompleted => _onTaskCompletedController.stream; | 67 Stream<AnalysisTask> get onTaskCompleted => _onTaskCompletedController.stream; |
68 | 68 |
69 /** | 69 /** |
(...skipping 14 matching lines...) Expand all Loading... |
84 } | 84 } |
85 } | 85 } |
86 return task; | 86 return task; |
87 } | 87 } |
88 | 88 |
89 /** | 89 /** |
90 * Return the work order describing the work that should be getting worked on, | 90 * Return the work order describing the work that should be getting worked on, |
91 * or `null` if there is currently no work to be done. | 91 * or `null` if there is currently no work to be done. |
92 */ | 92 */ |
93 WorkOrder createNextWorkOrder() { | 93 WorkOrder createNextWorkOrder() { |
94 // | 94 while (true) { |
95 // TODO(brianwilkerson) This is an inefficient implementation. We need to | 95 // Find the WorkManager with the highest priority. |
96 // port over the concept of the WorkManager to manage the list of sources | 96 WorkOrderPriority highestPriority = null; |
97 // for which some work needs to be performed so that we do not waste time | 97 WorkManager highestManager = null; |
98 // repeatedly looking at the same completed sources to see whether there is | 98 for (WorkManager manager in workManagers) { |
99 // work that needs to be done. | 99 WorkOrderPriority priority = manager.getNextResultPriority(); |
100 // | 100 if (highestPriority == null || highestPriority.index > priority.index) { |
101 for (AnalysisTarget target in context.priorityTargets) { | 101 highestPriority = priority; |
102 WorkOrder workOrder = createWorkOrderForTarget(target, true); | 102 highestManager = manager; |
103 if (workOrder != null) { | 103 } |
104 return workOrder; | 104 } |
| 105 // Nothing to do. |
| 106 if (highestPriority == WorkOrderPriority.NONE) { |
| 107 return null; |
| 108 } |
| 109 // Create a new WorkOrder. |
| 110 TargetedResult request = highestManager.getNextResult(); |
| 111 if (request != null) { |
| 112 WorkOrder workOrder = |
| 113 createWorkOrderForResult(request.target, request.result); |
| 114 if (workOrder != null) { |
| 115 return workOrder; |
| 116 } |
105 } | 117 } |
106 } | 118 } |
107 // TODO(brianwilkerson) Add a third priority, corresponding to | |
108 // AnalysisContextImpl._pendingFutureSources to support code completion. | |
109 for (AnalysisTarget target in context.explicitTargets) { | |
110 WorkOrder workOrder = createWorkOrderForTarget(target, false); | |
111 if (workOrder != null) { | |
112 return workOrder; | |
113 } | |
114 } | |
115 return null; | |
116 } | 119 } |
117 | 120 |
118 /** | 121 /** |
119 * Create a work order that will produce the given [result] for the given | 122 * Create a work order that will produce the given [result] for the given |
120 * [target]. Return the work order that was created, or `null` if the result | 123 * [target]. Return the work order that was created, or `null` if the result |
121 * has already been computed. | 124 * has already been computed. |
122 */ | 125 */ |
123 WorkOrder createWorkOrderForResult( | 126 WorkOrder createWorkOrderForResult( |
124 AnalysisTarget target, ResultDescriptor result) { | 127 AnalysisTarget target, ResultDescriptor result) { |
125 CacheEntry entry = context.getCacheEntry(target); | 128 CacheEntry entry = context.getCacheEntry(target); |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
217 task.perform(); | 220 task.perform(); |
218 CacheEntry entry = context.getCacheEntry(task.target); | 221 CacheEntry entry = context.getCacheEntry(task.target); |
219 if (task.caughtException == null) { | 222 if (task.caughtException == null) { |
220 List<TargetedResult> dependedOn = item.inputTargetedResults.toList(); | 223 List<TargetedResult> dependedOn = item.inputTargetedResults.toList(); |
221 Map<ResultDescriptor, dynamic> outputs = task.outputs; | 224 Map<ResultDescriptor, dynamic> outputs = task.outputs; |
222 for (ResultDescriptor result in task.descriptor.results) { | 225 for (ResultDescriptor result in task.descriptor.results) { |
223 // TODO(brianwilkerson) We could check here that a value was produced | 226 // TODO(brianwilkerson) We could check here that a value was produced |
224 // and throw an exception if not (unless we want to allow null values). | 227 // and throw an exception if not (unless we want to allow null values). |
225 entry.setValue(result, outputs[result], dependedOn); | 228 entry.setValue(result, outputs[result], dependedOn); |
226 } | 229 } |
| 230 for (WorkManager manager in workManagers) { |
| 231 manager.resultsComputed(task.target, outputs); |
| 232 } |
227 } else { | 233 } else { |
228 entry.setErrorState(task.caughtException, item.descriptor.results); | 234 entry.setErrorState(task.caughtException, item.descriptor.results); |
229 } | 235 } |
230 _onTaskCompletedController.add(task); | 236 _onTaskCompletedController.add(task); |
231 return task; | 237 return task; |
232 } | 238 } |
233 | 239 |
234 /** | 240 /** |
235 * Reset the state of the driver in response to a change in the state of one | 241 * Reset the state of the driver in response to a change in the state of one |
236 * or more analysis targets. This will cause any analysis that was currently | 242 * or more analysis targets. This will cause any analysis that was currently |
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
395 } | 401 } |
396 } | 402 } |
397 return null; | 403 return null; |
398 } | 404 } |
399 | 405 |
400 @override | 406 @override |
401 String toString() => 'Run $descriptor on $target'; | 407 String toString() => 'Run $descriptor on $target'; |
402 } | 408 } |
403 | 409 |
404 /** | 410 /** |
| 411 * [AnalysisDriver] uses [WorkManager]s to select results to compute. |
| 412 * |
| 413 * They know specific of the targets and results they care about, |
| 414 * so they can request analysis results in optimal order. |
| 415 */ |
| 416 abstract class WorkManager { |
| 417 /** |
| 418 * Return the next [TargetedResult] that this work manager wants to be |
| 419 * computed, or `null` if this manager doesn't need any new results. |
| 420 */ |
| 421 TargetedResult getNextResult(); |
| 422 |
| 423 /** |
| 424 * Return the priority if the next work order this work manager want to be |
| 425 * computed. The [AnalysisDriver] will perform the work order with |
| 426 * the highest priority. |
| 427 * |
| 428 * Even if the returned value is [WorkOrderPriority.NONE], it still does not |
| 429 * guarantee that [getNextResult] will return not `null`. |
| 430 */ |
| 431 WorkOrderPriority getNextResultPriority(); |
| 432 |
| 433 /** |
| 434 * Notifies the manager that the given [outputs] were produced for |
| 435 * the given [target]. |
| 436 */ |
| 437 void resultsComputed( |
| 438 AnalysisTarget target, Map<ResultDescriptor, dynamic> outputs); |
| 439 } |
| 440 |
| 441 /** |
| 442 * The priorities of work orders returned by [WorkManager]s. |
| 443 */ |
| 444 enum WorkOrderPriority { |
| 445 /** |
| 446 * Responding to an user's action. |
| 447 */ |
| 448 INTERACTIVE, |
| 449 |
| 450 /** |
| 451 * Computing information for priority sources. |
| 452 */ |
| 453 PRIORITY, |
| 454 |
| 455 /** |
| 456 * A work should be done, but without any special urgency. |
| 457 */ |
| 458 NORMAL, |
| 459 |
| 460 /** |
| 461 * Nothing to do. |
| 462 */ |
| 463 NONE |
| 464 } |
| 465 |
| 466 /** |
405 * A description of the work to be done to compute a desired analysis result. | 467 * A description of the work to be done to compute a desired analysis result. |
406 * The class implements a lazy depth-first traversal of the work item's input. | 468 * The class implements a lazy depth-first traversal of the work item's input. |
407 */ | 469 */ |
408 class WorkOrder implements Iterator<WorkItem> { | 470 class WorkOrder implements Iterator<WorkItem> { |
409 /** | 471 /** |
410 * The task manager used to build work items. | 472 * The task manager used to build work items. |
411 */ | 473 */ |
412 final TaskManager taskManager; | 474 final TaskManager taskManager; |
413 | 475 |
414 /** | 476 /** |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
468 TaskDescriptor descriptor = currentItem.descriptor; | 530 TaskDescriptor descriptor = currentItem.descriptor; |
469 AnalysisTarget target = currentItem.target; | 531 AnalysisTarget target = currentItem.target; |
470 for (WorkItem item in pendingItems) { | 532 for (WorkItem item in pendingItems) { |
471 if (item.descriptor == descriptor && item.target == target) { | 533 if (item.descriptor == descriptor && item.target == target) { |
472 return true; | 534 return true; |
473 } | 535 } |
474 } | 536 } |
475 return false; | 537 return false; |
476 } | 538 } |
477 } | 539 } |
478 | |
479 /** | |
480 * [AnalysisDriver] uses [WorkManager]s to select results to compute. | |
481 * | |
482 * They know specific of the targets and results they care about, | |
483 * so they can request analysis results in optimal order. | |
484 */ | |
485 abstract class WorkManager { | |
486 /** | |
487 * Return the next [TargetedResult] that this work manager wants to be | |
488 * computed, or `null` if this manager doesn't need any new results. | |
489 */ | |
490 TargetedResult getNextResult(); | |
491 | |
492 /** | |
493 * Notifies the manager that the given [outputs] were produced for | |
494 * the given [target]. | |
495 */ | |
496 void resultsComputed( | |
497 AnalysisTarget target, Map<ResultDescriptor, dynamic> outputs); | |
498 } | |
OLD | NEW |