Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(728)

Side by Side Diff: packages/analyzer/lib/src/task/driver.dart

Issue 2990843002: Removed fixed dependencies (Closed)
Patch Set: Created 3 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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/exception/exception.dart';
10 import 'package:analyzer/src/context/cache.dart'; 11 import 'package:analyzer/src/context/cache.dart';
11 import 'package:analyzer/src/generated/engine.dart' 12 import 'package:analyzer/src/generated/engine.dart';
12 hide AnalysisTask, AnalysisContextImpl, WorkManager;
13 import 'package:analyzer/src/generated/java_engine.dart';
14 import 'package:analyzer/src/generated/resolver.dart'; 13 import 'package:analyzer/src/generated/resolver.dart';
15 import 'package:analyzer/src/generated/utilities_general.dart'; 14 import 'package:analyzer/src/generated/utilities_general.dart';
16 import 'package:analyzer/src/task/inputs.dart'; 15 import 'package:analyzer/src/task/inputs.dart';
17 import 'package:analyzer/src/task/manager.dart'; 16 import 'package:analyzer/src/task/manager.dart';
18 import 'package:analyzer/task/model.dart'; 17 import 'package:analyzer/task/model.dart';
19 18
20 final PerformanceTag workOrderMoveNextPerfTag = 19 final PerformanceTag analysisDriverProcessOutputs =
20 new PerformanceTag('AnalysisDriver.processOutputs');
21
22 final PerformanceTag workOrderMoveNextPerformanceTag =
21 new PerformanceTag('WorkOrder.moveNext'); 23 new PerformanceTag('WorkOrder.moveNext');
22 24
23 /** 25 /**
24 * An object that is used to cause analysis to be performed until all of the 26 * An object that is used to cause analysis to be performed until all of the
25 * required analysis information has been computed. 27 * required analysis information has been computed.
26 */ 28 */
27 class AnalysisDriver { 29 class AnalysisDriver {
28 /** 30 /**
29 * The task manager used to figure out how to compute analysis results. 31 * The task manager used to figure out how to compute analysis results.
30 */ 32 */
31 final TaskManager taskManager; 33 final TaskManager taskManager;
32 34
33 /** 35 /**
34 * The list of [WorkManager] used to figure out which analysis results to 36 * The list of [WorkManager] used to figure out which analysis results to
35 * compute. 37 * compute.
36 */ 38 */
37 final List<WorkManager> workManagers; 39 final List<WorkManager> workManagers;
38 40
39 /** 41 /**
40 * The context in which analysis is to be performed. 42 * The context in which analysis is to be performed.
41 */ 43 */
42 final InternalAnalysisContext context; 44 final InternalAnalysisContext context;
43 45
44 /** 46 /**
45 * The map of [ComputedResult] controllers. 47 * The map of [ResultChangedEvent] controllers.
46 */ 48 */
47 final Map<ResultDescriptor, 49 final Map<ResultDescriptor, StreamController<ResultChangedEvent>>
48 StreamController<ComputedResult>> resultComputedControllers = 50 resultComputedControllers =
49 <ResultDescriptor, StreamController<ComputedResult>>{}; 51 <ResultDescriptor, StreamController<ResultChangedEvent>>{};
50 52
51 /** 53 /**
52 * The work order that was previously computed but that has not yet been 54 * The work order that was previously computed but that has not yet been
53 * completed. 55 * completed.
54 */ 56 */
55 WorkOrder currentWorkOrder; 57 WorkOrder currentWorkOrder;
56 58
57 /** 59 /**
58 * Indicates whether any tasks are currently being performed (or building 60 * Indicates whether any tasks are currently being performed (or building
59 * their inputs). In debug builds, we use this to ensure that tasks don't 61 * their inputs). In debug builds, we use this to ensure that tasks don't
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
92 94
93 /** 95 /**
94 * Perform work until the given [result] has been computed for the given 96 * Perform work until the given [result] has been computed for the given
95 * [target]. Return the last [AnalysisTask] that was performed. 97 * [target]. Return the last [AnalysisTask] that was performed.
96 */ 98 */
97 AnalysisTask computeResult(AnalysisTarget target, ResultDescriptor result) { 99 AnalysisTask computeResult(AnalysisTarget target, ResultDescriptor result) {
98 assert(!isTaskRunning); 100 assert(!isTaskRunning);
99 try { 101 try {
100 isTaskRunning = true; 102 isTaskRunning = true;
101 AnalysisTask task; 103 AnalysisTask task;
102 WorkOrder workOrder = createWorkOrderForResult(target, result); 104 while (true) {
103 if (workOrder != null) { 105 try {
104 while (workOrder.moveNext()) { 106 WorkOrder workOrder = createWorkOrderForResult(target, result);
105 // AnalysisTask previousTask = task; 107 if (workOrder != null) {
106 // String message = workOrder.current.toString(); 108 while (workOrder.moveNext()) {
107 task = performWorkItem(workOrder.current); 109 task = performWorkItem(workOrder.current);
108 // if (task == null) { 110 }
109 // throw new AnalysisException(message, previousTask.caughtException) ; 111 }
110 // } 112 break;
113 } on ModificationTimeMismatchError {
114 // Cache inconsistency was detected and fixed by invalidating
115 // corresponding results in cache. Computation must be restarted.
111 } 116 }
112 } 117 }
113 return task; 118 return task;
114 } finally { 119 } finally {
115 isTaskRunning = false; 120 isTaskRunning = false;
116 } 121 }
117 } 122 }
118 123
119 /** 124 /**
120 * Return the work order describing the work that should be getting worked on, 125 * Return the work order describing the work that should be getting worked on,
(...skipping 24 matching lines...) Expand all
145 if (workOrder != null) { 150 if (workOrder != null) {
146 return workOrder; 151 return workOrder;
147 } 152 }
148 } 153 }
149 } 154 }
150 } 155 }
151 156
152 /** 157 /**
153 * Create a work order that will produce the given [result] for the given 158 * Create a work order that will produce the given [result] for the given
154 * [target]. Return the work order that was created, or `null` if the result 159 * [target]. Return the work order that was created, or `null` if the result
155 * has already been computed. 160 * has either already been computed or cannot be computed.
156 */ 161 */
157 WorkOrder createWorkOrderForResult( 162 WorkOrder createWorkOrderForResult(
158 AnalysisTarget target, ResultDescriptor result) { 163 AnalysisTarget target, ResultDescriptor result) {
159 CacheEntry entry = context.getCacheEntry(target); 164 CacheEntry entry = context.getCacheEntry(target);
160 CacheState state = entry.getState(result); 165 CacheState state = entry.getState(result);
161 if (state == CacheState.VALID || 166 if (state == CacheState.VALID ||
162 state == CacheState.ERROR || 167 state == CacheState.ERROR ||
163 state == CacheState.IN_PROCESS) { 168 state == CacheState.IN_PROCESS) {
164 return null; 169 return null;
165 } 170 }
171 if (context.aboutToComputeResult(entry, result)) {
172 return null;
173 }
166 TaskDescriptor taskDescriptor = taskManager.findTask(target, result); 174 TaskDescriptor taskDescriptor = taskManager.findTask(target, result);
175 if (taskDescriptor == null) {
176 return null;
177 }
167 try { 178 try {
168 WorkItem workItem = new WorkItem(context, target, taskDescriptor, result); 179 WorkItem workItem =
180 new WorkItem(context, target, taskDescriptor, result, 0, null);
169 return new WorkOrder(taskManager, workItem); 181 return new WorkOrder(taskManager, workItem);
170 } catch (exception, stackTrace) { 182 } catch (exception, stackTrace) {
171 throw new AnalysisException( 183 throw new AnalysisException(
172 'Could not create work order (target = $target; taskDescriptor = $task Descriptor; result = $result)', 184 'Could not create work order (target = $target; taskDescriptor = $task Descriptor; result = $result)',
173 new CaughtException(exception, stackTrace)); 185 new CaughtException(exception, stackTrace));
174 } 186 }
175 } 187 }
176 188
177 /** 189 /**
178 * Create a work order that will produce the required analysis results for 190 * Create a work order that will produce the required analysis results for
(...skipping 16 matching lines...) Expand all
195 } 207 }
196 } 208 }
197 } 209 }
198 return null; 210 return null;
199 } 211 }
200 212
201 /** 213 /**
202 * Return the stream that is notified when a new value for the given 214 * Return the stream that is notified when a new value for the given
203 * [descriptor] is computed. 215 * [descriptor] is computed.
204 */ 216 */
205 Stream<ComputedResult> onResultComputed(ResultDescriptor descriptor) { 217 Stream<ResultChangedEvent> onResultComputed(ResultDescriptor descriptor) {
206 return resultComputedControllers 218 return resultComputedControllers.putIfAbsent(descriptor, () {
207 .putIfAbsent(descriptor, 219 return new StreamController<ResultChangedEvent>.broadcast(sync: true);
208 () => new StreamController<ComputedResult>.broadcast(sync: true)) 220 }).stream;
209 .stream;
210 } 221 }
211 222
212 /** 223 /**
213 * Perform the next analysis task, and return `true` if there is more work to 224 * Perform the next analysis task, and return `true` if there is more work to
214 * be done in order to compute all of the required analysis information. 225 * be done in order to compute all of the required analysis information.
215 */ 226 */
216 bool performAnalysisTask() { 227 bool performAnalysisTask() {
217 // 228 //
218 // TODO(brianwilkerson) This implementaiton does not allow us to prioritize 229 // TODO(brianwilkerson) This implementaiton does not allow us to prioritize
219 // work across contexts. What we need is a way for an external client to ask 230 // work across contexts. What we need is a way for an external client to ask
(...skipping 13 matching lines...) Expand all
233 // the client choosing a context and telling it do to some work, the client 244 // the client choosing a context and telling it do to some work, the client
234 // simply says "do some work", and the engine chooses the best thing to do 245 // simply says "do some work", and the engine chooses the best thing to do
235 // next regardless of what context it's in. 246 // next regardless of what context it's in.
236 // 247 //
237 assert(!isTaskRunning); 248 assert(!isTaskRunning);
238 try { 249 try {
239 isTaskRunning = true; 250 isTaskRunning = true;
240 if (currentWorkOrder == null) { 251 if (currentWorkOrder == null) {
241 currentWorkOrder = createNextWorkOrder(); 252 currentWorkOrder = createNextWorkOrder();
242 } else if (currentWorkOrder.moveNext()) { 253 } else if (currentWorkOrder.moveNext()) {
243 performWorkItem(currentWorkOrder.current); 254 try {
255 performWorkItem(currentWorkOrder.current);
256 } on ModificationTimeMismatchError {
257 reset();
258 return true;
259 }
244 } else { 260 } else {
245 currentWorkOrder = createNextWorkOrder(); 261 currentWorkOrder = createNextWorkOrder();
246 } 262 }
247 return currentWorkOrder != null; 263 return currentWorkOrder != null;
248 } finally { 264 } finally {
249 isTaskRunning = false; 265 isTaskRunning = false;
250 } 266 }
251 } 267 }
252 268
253 /** 269 /**
254 * Perform the given work item. 270 * Perform the given work item.
255 * Return the performed [AnalysisTask]. 271 * Return the performed [AnalysisTask].
256 */ 272 */
257 AnalysisTask performWorkItem(WorkItem item) { 273 AnalysisTask performWorkItem(WorkItem item) {
258 if (item.exception != null) { 274 if (item.exception != null) {
259 // Mark all of the results that the task would have computed as being in 275 // Mark all of the results that the task would have computed as being in
260 // ERROR with the exception recorded on the work item. 276 // ERROR with the exception recorded on the work item.
261 CacheEntry targetEntry = context.getCacheEntry(item.target); 277 CacheEntry targetEntry = context.getCacheEntry(item.target);
262 targetEntry.setErrorState(item.exception, item.descriptor.results); 278 targetEntry.setErrorState(item.exception, item.descriptor.results);
263 return null; 279 return null;
264 } 280 }
265 // Otherwise, perform the task. 281 // Otherwise, perform the task.
266 AnalysisTask task = item.buildTask(); 282 AnalysisTask task = item.buildTask();
267 _onTaskStartedController.add(task); 283 _onTaskStartedController.add(task);
268 task.perform(); 284 task.perform();
269 AnalysisTarget target = task.target; 285 analysisDriverProcessOutputs.makeCurrentWhile(() {
270 CacheEntry entry = context.getCacheEntry(target); 286 AnalysisTarget target = task.target;
271 if (task.caughtException == null) { 287 CacheEntry entry = context.getCacheEntry(target);
272 List<TargetedResult> dependedOn = item.inputTargetedResults.toList(); 288 if (task.caughtException == null) {
273 Map<ResultDescriptor, dynamic> outputs = task.outputs; 289 List<TargetedResult> dependedOn =
274 for (ResultDescriptor result in task.descriptor.results) { 290 context.analysisOptions.trackCacheDependencies
275 // TODO(brianwilkerson) We could check here that a value was produced 291 ? item.inputTargetedResults.toList()
276 // and throw an exception if not (unless we want to allow null values). 292 : const <TargetedResult>[];
277 entry.setValue(result, outputs[result], dependedOn); 293 Map<ResultDescriptor, dynamic> outputs = task.outputs;
294 List<ResultDescriptor> results = task.descriptor.results;
295 int resultLength = results.length;
296 for (int i = 0; i < resultLength; i++) {
297 ResultDescriptor result = results[i];
298 // TODO(brianwilkerson) We could check here that a value was produced
299 // and throw an exception if not (unless we want to allow null values) .
300 entry.setValue(result, outputs[result], dependedOn);
301 }
302 outputs.forEach((ResultDescriptor descriptor, value) {
303 StreamController<ResultChangedEvent> controller =
304 resultComputedControllers[descriptor];
305 if (controller != null) {
306 ResultChangedEvent event = new ResultChangedEvent(
307 context, target, descriptor, value, true);
308 controller.add(event);
309 }
310 });
311 for (WorkManager manager in workManagers) {
312 manager.resultsComputed(target, outputs);
313 }
314 } else {
315 entry.setErrorState(task.caughtException, item.descriptor.results);
278 } 316 }
279 outputs.forEach((ResultDescriptor descriptor, value) { 317 });
280 StreamController<ComputedResult> controller =
281 resultComputedControllers[descriptor];
282 if (controller != null) {
283 ComputedResult event =
284 new ComputedResult(context, descriptor, target, value);
285 controller.add(event);
286 }
287 });
288 for (WorkManager manager in workManagers) {
289 manager.resultsComputed(target, outputs);
290 }
291 } else {
292 entry.setErrorState(task.caughtException, item.descriptor.results);
293 }
294 _onTaskCompletedController.add(task); 318 _onTaskCompletedController.add(task);
295 return task; 319 return task;
296 } 320 }
297 321
298 /** 322 /**
299 * Reset the state of the driver in response to a change in the state of one 323 * Reset the state of the driver in response to a change in the state of one
300 * or more analysis targets. This will cause any analysis that was currently 324 * or more analysis targets. This will cause any analysis that was currently
301 * in process to be stopped and for analysis to resume based on the new state. 325 * in process to be stopped and for analysis to resume based on the new state.
302 */ 326 */
303 void reset() { 327 void reset() {
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
382 406
383 /** 407 /**
384 * Determine the next strongly connected component in the graph, and return 408 * Determine the next strongly connected component in the graph, and return
385 * it. The client is expected to evaluate this component before calling 409 * it. The client is expected to evaluate this component before calling
386 * [getNextStronglyConnectedComponent] again. 410 * [getNextStronglyConnectedComponent] again.
387 */ 411 */
388 StronglyConnectedComponent<Node> getNextStronglyConnectedComponent() { 412 StronglyConnectedComponent<Node> getNextStronglyConnectedComponent() {
389 while (_currentIndices.isNotEmpty) { 413 while (_currentIndices.isNotEmpty) {
390 Node nextUnevaluatedInput = getNextInput(_path[_currentIndices.last], 414 Node nextUnevaluatedInput = getNextInput(_path[_currentIndices.last],
391 _provisionalDependencies[_currentIndices.last]); 415 _provisionalDependencies[_currentIndices.last]);
416 // If the assertion below fails, it indicates that [getNextInput] did not
417 // skip an input that we asked it to skip.
392 assert(!_provisionalDependencies[_currentIndices.last] 418 assert(!_provisionalDependencies[_currentIndices.last]
393 .contains(nextUnevaluatedInput)); 419 .contains(nextUnevaluatedInput));
394 if (nextUnevaluatedInput != null) { 420 if (nextUnevaluatedInput != null) {
395 // TODO(paulberry): the call to _path.indexOf makes the algorithm 421 // TODO(paulberry): the call to _path.indexOf makes the algorithm
396 // O(n^2) in the depth of the dependency graph. If this becomes a 422 // O(n^2) in the depth of the dependency graph. If this becomes a
397 // problem, consider maintaining a map from node to index. 423 // problem, consider maintaining a map from node to index.
398 int previousIndex = _path.indexOf(nextUnevaluatedInput); 424 int previousIndex = _path.indexOf(nextUnevaluatedInput);
399 if (previousIndex != -1) { 425 if (previousIndex != -1) {
400 // Update contractedPath to indicate that all nodes in the path 426 // Update contractedPath to indicate that all nodes in the path
401 // between previousIndex and currentIndex are part of the same 427 // between previousIndex and currentIndex are part of the same
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
466 CacheEntry getCacheEntry(AnalysisTarget target); 492 CacheEntry getCacheEntry(AnalysisTarget target);
467 } 493 }
468 494
469 /** 495 /**
470 * An exception indicating that an attempt was made to perform a task on a 496 * An exception indicating that an attempt was made to perform a task on a
471 * target while gathering the inputs to perform the same task for the same 497 * target while gathering the inputs to perform the same task for the same
472 * target. 498 * target.
473 */ 499 */
474 class InfiniteTaskLoopException extends AnalysisException { 500 class InfiniteTaskLoopException extends AnalysisException {
475 /** 501 /**
502 * A concrete cyclic path of [TargetedResults] within the [dependencyCycle],
503 * `null` if no such path exists. All nodes in the path are in the
504 * dependencyCycle, but the path is not guaranteed to cover the
505 * entire cycle.
506 */
507 final List<TargetedResult> cyclicPath;
508
509 /**
476 * If a dependency cycle was found while computing the inputs for the task, 510 * If a dependency cycle was found while computing the inputs for the task,
477 * the set of [WorkItem]s contained in the cycle (if there are overlapping 511 * the set of [WorkItem]s contained in the cycle (if there are overlapping
478 * cycles, this is the set of all [WorkItem]s in the entire strongly 512 * cycles, this is the set of all [WorkItem]s in the entire strongly
479 * connected component). Otherwise, `null`. 513 * connected component). Otherwise, `null`.
480 */ 514 */
481 final List<WorkItem> dependencyCycle; 515 final List<WorkItem> dependencyCycle;
482 516
483 /** 517 /**
484 * Initialize a newly created exception to represent a failed attempt to 518 * Initialize a newly created exception to represent a failed attempt to
485 * perform the given [task] due to the given [dependencyCycle]. 519 * perform the given [task] due to the given [dependencyCycle].
486 */ 520 */
487 InfiniteTaskLoopException(AnalysisTask task, this.dependencyCycle) 521 InfiniteTaskLoopException(AnalysisTask task, List<WorkItem> dependencyCycle,
488 : super( 522 [List<TargetedResult> cyclicPath])
489 'Infinite loop while performing task ${task.descriptor.name} for ${t ask.target}'); 523 : this.dependencyCycle = dependencyCycle,
524 this.cyclicPath = cyclicPath,
525 super(_composeMessage(task, dependencyCycle, cyclicPath));
526
527 /**
528 * Compose an error message based on the data we have available.
529 */
530 static String _composeMessage(AnalysisTask task,
531 List<WorkItem> dependencyCycle, List<TargetedResult> cyclicPath) {
532 StringBuffer buffer = new StringBuffer();
533 buffer.write('Infinite loop while performing task ');
534 buffer.write(task.descriptor.name);
535 buffer.write(' for ');
536 buffer.writeln(task.target);
537 buffer.writeln(' Dependency Cycle:');
538 for (WorkItem item in dependencyCycle) {
539 buffer.write(' ');
540 buffer.writeln(item);
541 }
542 if (cyclicPath != null) {
543 buffer.writeln(' Cyclic Path:');
544 for (TargetedResult result in cyclicPath) {
545 buffer.write(' ');
546 buffer.writeln(result);
547 }
548 }
549 return buffer.toString();
550 }
490 } 551 }
491 552
492 /** 553 /**
493 * Object used by CycleAwareDependencyWalker to report a single strongly 554 * Object used by [CycleAwareDependencyWalker] to report a single strongly
494 * connected component of nodes. 555 * connected component of nodes.
495 */ 556 */
496 class StronglyConnectedComponent<Node> { 557 class StronglyConnectedComponent<Node> {
497 /** 558 /**
498 * The nodes contained in the strongly connected component. 559 * The nodes contained in the strongly connected component.
499 */ 560 */
500 final List<Node> nodes; 561 final List<Node> nodes;
501 562
502 /** 563 /**
503 * Indicates whether the strongly component contains any cycles. Note that 564 * Indicates whether the strongly component contains any cycles. Note that
504 * if [nodes] has multiple elements, this will always be `true`. However, if 565 * if [nodes] has multiple elements, this will always be `true`. However, if
505 * [nodes] has exactly one element, this may be either `true` or `false` 566 * [nodes] has exactly one element, this may be either `true` or `false`
506 * depending on whether the node has a dependency on itself. 567 * depending on whether the node has a dependency on itself.
507 */ 568 */
508 final bool containsCycle; 569 final bool containsCycle;
509 570
510 StronglyConnectedComponent(this.nodes, this.containsCycle); 571 StronglyConnectedComponent(this.nodes, this.containsCycle);
511 } 572 }
512 573
513 /** 574 /**
514 * A description of a single anaysis task that can be performed to advance 575 * A description of a single analysis task that can be performed to advance
515 * analysis. 576 * analysis.
516 */ 577 */
517 class WorkItem { 578 class WorkItem {
518 /** 579 /**
519 * The context in which the task will be performed. 580 * The context in which the task will be performed.
520 */ 581 */
521 final InternalAnalysisContext context; 582 final InternalAnalysisContext context;
522 583
523 /** 584 /**
524 * The target for which a task is to be performed. 585 * The target for which a task is to be performed.
525 */ 586 */
526 final AnalysisTarget target; 587 final AnalysisTarget target;
527 588
528 /** 589 /**
529 * A description of the task to be performed. 590 * A description of the task to be performed.
530 */ 591 */
531 final TaskDescriptor descriptor; 592 final TaskDescriptor descriptor;
532 593
533 /** 594 /**
534 * The [ResultDescriptor] which was led to this work item being spawned. 595 * The [ResultDescriptor] which was led to this work item being spawned.
535 */ 596 */
536 final ResultDescriptor spawningResult; 597 final ResultDescriptor spawningResult;
537 598
538 /** 599 /**
600 * The level of this item in its [WorkOrder].
601 */
602 final int level;
603
604 /**
605 * The work order that this item is part of, may be `null`.
606 */
607 WorkOrder workOrder;
608
609 /**
539 * An iterator used to iterate over the descriptors of the inputs to the task, 610 * An iterator used to iterate over the descriptors of the inputs to the task,
540 * or `null` if all of the inputs have been collected and the task can be 611 * or `null` if all of the inputs have been collected and the task can be
541 * created. 612 * created.
542 */ 613 */
543 TaskInputBuilder builder; 614 TopLevelTaskInputBuilder builder;
544 615
545 /** 616 /**
546 * The [TargetedResult]s outputs of this task depends on. 617 * The [TargetedResult]s outputs of this task depends on.
547 */ 618 */
548 final HashSet<TargetedResult> inputTargetedResults = 619 final HashSet<TargetedResult> inputTargetedResults =
549 new HashSet<TargetedResult>(); 620 new HashSet<TargetedResult>();
550 621
551 /** 622 /**
552 * The inputs to the task that have been computed. 623 * The inputs to the task that have been computed.
553 */ 624 */
554 Map<String, dynamic> inputs; 625 Map<String, dynamic> inputs = const <String, dynamic>{};
555 626
556 /** 627 /**
557 * The exception that was found while trying to populate the inputs. If this 628 * The exception that was found while trying to populate the inputs. If this
558 * field is non-`null`, then the task cannot be performed and all of the 629 * field is non-`null`, then the task cannot be performed and all of the
559 * results that this task would have computed need to be marked as being in 630 * results that this task would have computed need to be marked as being in
560 * ERROR with this exception. 631 * ERROR with this exception.
561 */ 632 */
562 CaughtException exception = null; 633 CaughtException exception = null;
563 634
564 /** 635 /**
565 * If a dependency cycle was found while computing the inputs for the task, 636 * If a dependency cycle was found while computing the inputs for the task,
566 * the set of [WorkItem]s contained in the cycle (if there are overlapping 637 * the set of [WorkItem]s contained in the cycle (if there are overlapping
567 * cycles, this is the set of all [WorkItem]s in the entire strongly 638 * cycles, this is the set of all [WorkItem]s in the entire strongly
568 * connected component). Otherwise, `null`. 639 * connected component). Otherwise, `null`.
569 */ 640 */
570 List<WorkItem> dependencyCycle; 641 List<WorkItem> dependencyCycle;
571 642
572 /** 643 /**
573 * Initialize a newly created work item to compute the inputs for the task 644 * Initialize a newly created work item to compute the inputs for the task
574 * described by the given descriptor. 645 * described by the given descriptor.
575 */ 646 */
576 WorkItem(this.context, this.target, this.descriptor, this.spawningResult) { 647 WorkItem(this.context, this.target, this.descriptor, this.spawningResult,
648 this.level, this.workOrder) {
577 AnalysisTarget actualTarget = 649 AnalysisTarget actualTarget =
578 identical(target, AnalysisContextTarget.request) 650 identical(target, AnalysisContextTarget.request)
579 ? new AnalysisContextTarget(context) 651 ? new AnalysisContextTarget(context)
580 : target; 652 : target;
653 // print('${'\t' * level}$spawningResult of $actualTarget');
581 Map<String, TaskInput> inputDescriptors = 654 Map<String, TaskInput> inputDescriptors =
582 descriptor.createTaskInputs(actualTarget); 655 descriptor.createTaskInputs(actualTarget);
583 builder = new TopLevelTaskInputBuilder(inputDescriptors); 656 builder = new TopLevelTaskInputBuilder(inputDescriptors);
584 if (!builder.moveNext()) { 657 if (!builder.moveNext()) {
585 builder = null; 658 builder = null;
586 } 659 }
587 inputs = new HashMap<String, dynamic>();
588 } 660 }
589 661
590 @override 662 @override
591 int get hashCode => 663 int get hashCode =>
592 JenkinsSmiHash.hash2(descriptor.hashCode, target.hashCode); 664 JenkinsSmiHash.hash2(descriptor.hashCode, target.hashCode);
593 665
594 @override 666 @override
595 bool operator ==(other) { 667 bool operator ==(other) {
596 if (other is WorkItem) { 668 if (other is WorkItem) {
597 return this.descriptor == other.descriptor && this.target == other.target; 669 return this.descriptor == other.descriptor && this.target == other.target;
(...skipping 27 matching lines...) Expand all
625 * returned if some of the inputs cannot be computed and the task cannot be 697 * returned if some of the inputs cannot be computed and the task cannot be
626 * performed. Callers can differentiate between these cases by checking the 698 * performed. Callers can differentiate between these cases by checking the
627 * [exception] field. If the field is `null`, then the task can be performed; 699 * [exception] field. If the field is `null`, then the task can be performed;
628 * if the field is non-`null` then the task cannot be performed and all of the 700 * if the field is non-`null` then the task cannot be performed and all of the
629 * tasks' results should be marked as being in ERROR. 701 * tasks' results should be marked as being in ERROR.
630 */ 702 */
631 WorkItem gatherInputs(TaskManager taskManager, List<WorkItem> skipInputs) { 703 WorkItem gatherInputs(TaskManager taskManager, List<WorkItem> skipInputs) {
632 while (builder != null) { 704 while (builder != null) {
633 AnalysisTarget inputTarget = builder.currentTarget; 705 AnalysisTarget inputTarget = builder.currentTarget;
634 ResultDescriptor inputResult = builder.currentResult; 706 ResultDescriptor inputResult = builder.currentResult;
707
708 // TODO(scheglov) record information to debug
709 // https://github.com/dart-lang/sdk/issues/24939
710 if (inputTarget == null || inputResult == null) {
711 try {
712 String message =
713 'Invalid input descriptor ($inputTarget, $inputResult) for $this';
714 if (workOrder != null) {
715 message += '\nPath:\n' + workOrder.workItems.join('|\n');
716 }
717 throw new AnalysisException(message);
718 } catch (exception, stackTrace) {
719 this.exception = new CaughtException(exception, stackTrace);
720 AnalysisEngine.instance.logger
721 .logError('Task failed: $this', this.exception);
722 }
723 return null;
724 }
725
635 inputTargetedResults.add(new TargetedResult(inputTarget, inputResult)); 726 inputTargetedResults.add(new TargetedResult(inputTarget, inputResult));
636 CacheEntry inputEntry = context.getCacheEntry(inputTarget); 727 CacheEntry inputEntry = context.getCacheEntry(inputTarget);
637 CacheState inputState = inputEntry.getState(inputResult); 728 CacheState inputState = inputEntry.getState(inputResult);
638 if (skipInputs.any((WorkItem item) => 729 if (inputState == CacheState.ERROR) {
639 item.target == inputTarget && item.spawningResult == inputResult)) {
640 // This input is being skipped due to a circular dependency. Tell the
641 // builder that it's not available so we can move on to other inputs.
642 builder.currentValueNotAvailable();
643 } else if (inputState == CacheState.ERROR) {
644 exception = inputEntry.exception; 730 exception = inputEntry.exception;
645 return null; 731 return null;
646 } else if (inputState == CacheState.IN_PROCESS) { 732 } else if (inputState == CacheState.IN_PROCESS) {
647 // 733 //
648 // TODO(brianwilkerson) Implement this case. 734 // TODO(brianwilkerson) Implement this case.
649 // 735 //
650 // One possibility would be to return a WorkItem that would perform a 736 // One possibility would be to return a WorkItem that would perform a
651 // no-op task in order to cause us to come back to this work item on the 737 // no-op task in order to cause us to come back to this work item on the
652 // next iteration. It would be more efficient, in general, to push this 738 // next iteration. It would be more efficient, in general, to push this
653 // input onto a waiting list and proceed to the next input so that work 739 // input onto a waiting list and proceed to the next input so that work
654 // could proceed, but given that the only result that can currently be 740 // could proceed, but given that the only result that can currently be
655 // IN_PROCESS is CONTENT, I don't know that it's worth the extra effort 741 // IN_PROCESS is CONTENT, I don't know that it's worth the extra effort
656 // to implement the general solution at this point. 742 // to implement the general solution at this point.
657 // 743 //
658 throw new UnimplementedError(); 744 throw new UnimplementedError();
659 } else if (inputState != CacheState.VALID) { 745 } else if (inputState != CacheState.VALID) {
660 try { 746 if (context.aboutToComputeResult(inputEntry, inputResult)) {
661 TaskDescriptor descriptor = 747 inputState = CacheState.VALID;
662 taskManager.findTask(inputTarget, inputResult); 748 builder.currentValue = inputEntry.getValue(inputResult);
663 return new WorkItem(context, inputTarget, descriptor, inputResult); 749 } else {
664 } on AnalysisException catch (exception, stackTrace) { 750 try {
665 this.exception = new CaughtException(exception, stackTrace); 751 TaskDescriptor descriptor =
666 return null; 752 taskManager.findTask(inputTarget, inputResult);
753 if (descriptor == null) {
754 throw new AnalysisException(
755 'Cannot find task to build $inputResult for $inputTarget');
756 }
757 if (skipInputs.any((WorkItem item) =>
758 item.target == inputTarget && item.descriptor == descriptor)) {
759 // This input is being skipped due to a circular dependency. Tell
760 // the builder that it's not available so we can move on to other
761 // inputs.
762 builder.currentValueNotAvailable();
763 } else {
764 return new WorkItem(context, inputTarget, descriptor, inputResult,
765 level + 1, workOrder);
766 }
767 } on AnalysisException catch (exception, stackTrace) {
768 this.exception = new CaughtException(exception, stackTrace);
769 return null;
770 } catch (exception, stackTrace) {
771 this.exception = new CaughtException(exception, stackTrace);
772 throw new AnalysisException(
773 'Cannot create work order to build $inputResult for $inputTarget ',
774 this.exception);
775 }
667 } 776 }
668 } else { 777 } else {
669 builder.currentValue = inputEntry.getValue(inputResult); 778 builder.currentValue = inputEntry.getValue(inputResult);
670 if (builder.flushOnAccess) { 779 if (builder.flushOnAccess) {
671 inputEntry.setState(inputResult, CacheState.FLUSHED); 780 inputEntry.setState(inputResult, CacheState.FLUSHED);
672 } 781 }
673 } 782 }
674 if (!builder.moveNext()) { 783 if (!builder.moveNext()) {
675 inputs = builder.inputValue; 784 inputs = builder.inputValue;
676 builder = null; 785 builder = null;
(...skipping 24 matching lines...) Expand all
701 * 810 *
702 * Null if the [_dependencyWalker] hasn't been used yet. 811 * Null if the [_dependencyWalker] hasn't been used yet.
703 */ 812 */
704 List<WorkItem> currentItems; 813 List<WorkItem> currentItems;
705 814
706 /** 815 /**
707 * Initialize a newly created work order to compute the result described by 816 * Initialize a newly created work order to compute the result described by
708 * the given work item. 817 * the given work item.
709 */ 818 */
710 WorkOrder(TaskManager taskManager, WorkItem item) 819 WorkOrder(TaskManager taskManager, WorkItem item)
711 : _dependencyWalker = new _WorkOrderDependencyWalker(taskManager, item); 820 : _dependencyWalker = new _WorkOrderDependencyWalker(taskManager, item) {
821 item.workOrder = this;
822 }
712 823
713 @override 824 @override
714 WorkItem get current { 825 WorkItem get current {
715 if (currentItems == null) { 826 if (currentItems == null) {
716 return null; 827 return null;
717 } else { 828 } else {
718 return currentItems.last; 829 return currentItems.last;
719 } 830 }
720 } 831 }
721 832
833 List<WorkItem> get workItems => _dependencyWalker._path;
834
722 @override 835 @override
723 bool moveNext() { 836 bool moveNext() {
724 return workOrderMoveNextPerfTag.makeCurrentWhile(() { 837 return workOrderMoveNextPerformanceTag.makeCurrentWhile(() {
725 if (currentItems != null && currentItems.length > 1) { 838 if (currentItems != null && currentItems.length > 1) {
726 // Yield more items. 839 // Yield more items.
727 currentItems.removeLast(); 840 currentItems.removeLast();
728 return true; 841 return true;
729 } else { 842 } else {
730 // Get a new strongly connected component. 843 // Get a new strongly connected component.
731 StronglyConnectedComponent<WorkItem> nextStronglyConnectedComponent = 844 StronglyConnectedComponent<WorkItem> nextStronglyConnectedComponent =
732 _dependencyWalker.getNextStronglyConnectedComponent(); 845 _dependencyWalker.getNextStronglyConnectedComponent();
733 if (nextStronglyConnectedComponent == null) { 846 if (nextStronglyConnectedComponent == null) {
734 currentItems = null; 847 currentItems = null;
735 return false; 848 return false;
736 } 849 }
737 currentItems = nextStronglyConnectedComponent.nodes; 850 currentItems = nextStronglyConnectedComponent.nodes;
738 if (nextStronglyConnectedComponent.containsCycle) { 851 if (nextStronglyConnectedComponent.containsCycle) {
739 // A cycle has been found. 852 // A cycle has been found.
740 for (WorkItem item in currentItems) { 853 for (WorkItem item in currentItems) {
741 item.dependencyCycle = currentItems.toList(); 854 item.dependencyCycle = currentItems.toList();
742 } 855 }
743 } else { 856 } else {
744 assert(currentItems.length == 1); 857 assert(currentItems.length == 1);
745 } 858 }
746 return true; 859 return true;
747 } 860 }
748 }); 861 });
749 } 862 }
750 } 863 }
751 864
752 /** 865 /**
753 * Specilaization of [CycleAwareDependencyWalker] for use by [WorkOrder]. 866 * Specialization of [CycleAwareDependencyWalker] for use by [WorkOrder].
754 */ 867 */
755 class _WorkOrderDependencyWalker extends CycleAwareDependencyWalker<WorkItem> { 868 class _WorkOrderDependencyWalker extends CycleAwareDependencyWalker<WorkItem> {
756 /** 869 /**
757 * The task manager used to build work items. 870 * The task manager used to build work items.
758 */ 871 */
759 final TaskManager taskManager; 872 final TaskManager taskManager;
760 873
761 _WorkOrderDependencyWalker(this.taskManager, WorkItem startingNode) 874 _WorkOrderDependencyWalker(this.taskManager, WorkItem startingNode)
762 : super(startingNode); 875 : super(startingNode);
763 876
764 @override 877 @override
765 WorkItem getNextInput(WorkItem node, List<WorkItem> skipInputs) { 878 WorkItem getNextInput(WorkItem node, List<WorkItem> skipInputs) {
766 return node.gatherInputs(taskManager, skipInputs); 879 return node.gatherInputs(taskManager, skipInputs);
767 } 880 }
768 } 881 }
OLDNEW
« no previous file with comments | « packages/analyzer/lib/src/task/dart_work_manager.dart ('k') | packages/analyzer/lib/src/task/general.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698