| 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 |