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

Unified Diff: packages/analyzer/lib/src/task/driver.dart

Issue 2990843002: Removed fixed dependencies (Closed)
Patch Set: Created 3 years, 5 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 side-by-side diff with in-line comments
Download patch
« 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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: packages/analyzer/lib/src/task/driver.dart
diff --git a/packages/analyzer/lib/src/task/driver.dart b/packages/analyzer/lib/src/task/driver.dart
index 3231454d46fd79688b02692f2a3d02666af9ba35..bf6da4e2a2f08dddf919538ce3a88c69c7091bbe 100644
--- a/packages/analyzer/lib/src/task/driver.dart
+++ b/packages/analyzer/lib/src/task/driver.dart
@@ -7,17 +7,19 @@ library analyzer.src.task.driver;
import 'dart:async';
import 'dart:collection';
+import 'package:analyzer/exception/exception.dart';
import 'package:analyzer/src/context/cache.dart';
-import 'package:analyzer/src/generated/engine.dart'
- hide AnalysisTask, AnalysisContextImpl, WorkManager;
-import 'package:analyzer/src/generated/java_engine.dart';
+import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/resolver.dart';
import 'package:analyzer/src/generated/utilities_general.dart';
import 'package:analyzer/src/task/inputs.dart';
import 'package:analyzer/src/task/manager.dart';
import 'package:analyzer/task/model.dart';
-final PerformanceTag workOrderMoveNextPerfTag =
+final PerformanceTag analysisDriverProcessOutputs =
+ new PerformanceTag('AnalysisDriver.processOutputs');
+
+final PerformanceTag workOrderMoveNextPerformanceTag =
new PerformanceTag('WorkOrder.moveNext');
/**
@@ -42,11 +44,11 @@ class AnalysisDriver {
final InternalAnalysisContext context;
/**
- * The map of [ComputedResult] controllers.
+ * The map of [ResultChangedEvent] controllers.
*/
- final Map<ResultDescriptor,
- StreamController<ComputedResult>> resultComputedControllers =
- <ResultDescriptor, StreamController<ComputedResult>>{};
+ final Map<ResultDescriptor, StreamController<ResultChangedEvent>>
+ resultComputedControllers =
+ <ResultDescriptor, StreamController<ResultChangedEvent>>{};
/**
* The work order that was previously computed but that has not yet been
@@ -99,15 +101,18 @@ class AnalysisDriver {
try {
isTaskRunning = true;
AnalysisTask task;
- WorkOrder workOrder = createWorkOrderForResult(target, result);
- if (workOrder != null) {
- while (workOrder.moveNext()) {
-// AnalysisTask previousTask = task;
-// String message = workOrder.current.toString();
- task = performWorkItem(workOrder.current);
-// if (task == null) {
-// throw new AnalysisException(message, previousTask.caughtException);
-// }
+ while (true) {
+ try {
+ WorkOrder workOrder = createWorkOrderForResult(target, result);
+ if (workOrder != null) {
+ while (workOrder.moveNext()) {
+ task = performWorkItem(workOrder.current);
+ }
+ }
+ break;
+ } on ModificationTimeMismatchError {
+ // Cache inconsistency was detected and fixed by invalidating
+ // corresponding results in cache. Computation must be restarted.
}
}
return task;
@@ -152,7 +157,7 @@ class AnalysisDriver {
/**
* Create a work order that will produce the given [result] for the given
* [target]. Return the work order that was created, or `null` if the result
- * has already been computed.
+ * has either already been computed or cannot be computed.
*/
WorkOrder createWorkOrderForResult(
AnalysisTarget target, ResultDescriptor result) {
@@ -163,9 +168,16 @@ class AnalysisDriver {
state == CacheState.IN_PROCESS) {
return null;
}
+ if (context.aboutToComputeResult(entry, result)) {
+ return null;
+ }
TaskDescriptor taskDescriptor = taskManager.findTask(target, result);
+ if (taskDescriptor == null) {
+ return null;
+ }
try {
- WorkItem workItem = new WorkItem(context, target, taskDescriptor, result);
+ WorkItem workItem =
+ new WorkItem(context, target, taskDescriptor, result, 0, null);
return new WorkOrder(taskManager, workItem);
} catch (exception, stackTrace) {
throw new AnalysisException(
@@ -202,11 +214,10 @@ class AnalysisDriver {
* Return the stream that is notified when a new value for the given
* [descriptor] is computed.
*/
- Stream<ComputedResult> onResultComputed(ResultDescriptor descriptor) {
- return resultComputedControllers
- .putIfAbsent(descriptor,
- () => new StreamController<ComputedResult>.broadcast(sync: true))
- .stream;
+ Stream<ResultChangedEvent> onResultComputed(ResultDescriptor descriptor) {
+ return resultComputedControllers.putIfAbsent(descriptor, () {
+ return new StreamController<ResultChangedEvent>.broadcast(sync: true);
+ }).stream;
}
/**
@@ -240,7 +251,12 @@ class AnalysisDriver {
if (currentWorkOrder == null) {
currentWorkOrder = createNextWorkOrder();
} else if (currentWorkOrder.moveNext()) {
- performWorkItem(currentWorkOrder.current);
+ try {
+ performWorkItem(currentWorkOrder.current);
+ } on ModificationTimeMismatchError {
+ reset();
+ return true;
+ }
} else {
currentWorkOrder = createNextWorkOrder();
}
@@ -266,31 +282,39 @@ class AnalysisDriver {
AnalysisTask task = item.buildTask();
_onTaskStartedController.add(task);
task.perform();
- AnalysisTarget target = task.target;
- CacheEntry entry = context.getCacheEntry(target);
- if (task.caughtException == null) {
- List<TargetedResult> dependedOn = item.inputTargetedResults.toList();
- Map<ResultDescriptor, dynamic> outputs = task.outputs;
- for (ResultDescriptor result in task.descriptor.results) {
- // TODO(brianwilkerson) We could check here that a value was produced
- // and throw an exception if not (unless we want to allow null values).
- entry.setValue(result, outputs[result], dependedOn);
- }
- outputs.forEach((ResultDescriptor descriptor, value) {
- StreamController<ComputedResult> controller =
- resultComputedControllers[descriptor];
- if (controller != null) {
- ComputedResult event =
- new ComputedResult(context, descriptor, target, value);
- controller.add(event);
+ analysisDriverProcessOutputs.makeCurrentWhile(() {
+ AnalysisTarget target = task.target;
+ CacheEntry entry = context.getCacheEntry(target);
+ if (task.caughtException == null) {
+ List<TargetedResult> dependedOn =
+ context.analysisOptions.trackCacheDependencies
+ ? item.inputTargetedResults.toList()
+ : const <TargetedResult>[];
+ Map<ResultDescriptor, dynamic> outputs = task.outputs;
+ List<ResultDescriptor> results = task.descriptor.results;
+ int resultLength = results.length;
+ for (int i = 0; i < resultLength; i++) {
+ ResultDescriptor result = results[i];
+ // TODO(brianwilkerson) We could check here that a value was produced
+ // and throw an exception if not (unless we want to allow null values).
+ entry.setValue(result, outputs[result], dependedOn);
}
- });
- for (WorkManager manager in workManagers) {
- manager.resultsComputed(target, outputs);
+ outputs.forEach((ResultDescriptor descriptor, value) {
+ StreamController<ResultChangedEvent> controller =
+ resultComputedControllers[descriptor];
+ if (controller != null) {
+ ResultChangedEvent event = new ResultChangedEvent(
+ context, target, descriptor, value, true);
+ controller.add(event);
+ }
+ });
+ for (WorkManager manager in workManagers) {
+ manager.resultsComputed(target, outputs);
+ }
+ } else {
+ entry.setErrorState(task.caughtException, item.descriptor.results);
}
- } else {
- entry.setErrorState(task.caughtException, item.descriptor.results);
- }
+ });
_onTaskCompletedController.add(task);
return task;
}
@@ -389,6 +413,8 @@ abstract class CycleAwareDependencyWalker<Node> {
while (_currentIndices.isNotEmpty) {
Node nextUnevaluatedInput = getNextInput(_path[_currentIndices.last],
_provisionalDependencies[_currentIndices.last]);
+ // If the assertion below fails, it indicates that [getNextInput] did not
+ // skip an input that we asked it to skip.
assert(!_provisionalDependencies[_currentIndices.last]
.contains(nextUnevaluatedInput));
if (nextUnevaluatedInput != null) {
@@ -472,6 +498,14 @@ abstract class ExtendedAnalysisContext implements InternalAnalysisContext {
* target.
*/
class InfiniteTaskLoopException extends AnalysisException {
+ /**
+ * A concrete cyclic path of [TargetedResults] within the [dependencyCycle],
+ * `null` if no such path exists. All nodes in the path are in the
+ * dependencyCycle, but the path is not guaranteed to cover the
+ * entire cycle.
+ */
+ final List<TargetedResult> cyclicPath;
+
/**
* If a dependency cycle was found while computing the inputs for the task,
* the set of [WorkItem]s contained in the cycle (if there are overlapping
@@ -484,13 +518,40 @@ class InfiniteTaskLoopException extends AnalysisException {
* Initialize a newly created exception to represent a failed attempt to
* perform the given [task] due to the given [dependencyCycle].
*/
- InfiniteTaskLoopException(AnalysisTask task, this.dependencyCycle)
- : super(
- 'Infinite loop while performing task ${task.descriptor.name} for ${task.target}');
+ InfiniteTaskLoopException(AnalysisTask task, List<WorkItem> dependencyCycle,
+ [List<TargetedResult> cyclicPath])
+ : this.dependencyCycle = dependencyCycle,
+ this.cyclicPath = cyclicPath,
+ super(_composeMessage(task, dependencyCycle, cyclicPath));
+
+ /**
+ * Compose an error message based on the data we have available.
+ */
+ static String _composeMessage(AnalysisTask task,
+ List<WorkItem> dependencyCycle, List<TargetedResult> cyclicPath) {
+ StringBuffer buffer = new StringBuffer();
+ buffer.write('Infinite loop while performing task ');
+ buffer.write(task.descriptor.name);
+ buffer.write(' for ');
+ buffer.writeln(task.target);
+ buffer.writeln(' Dependency Cycle:');
+ for (WorkItem item in dependencyCycle) {
+ buffer.write(' ');
+ buffer.writeln(item);
+ }
+ if (cyclicPath != null) {
+ buffer.writeln(' Cyclic Path:');
+ for (TargetedResult result in cyclicPath) {
+ buffer.write(' ');
+ buffer.writeln(result);
+ }
+ }
+ return buffer.toString();
+ }
}
/**
- * Object used by CycleAwareDependencyWalker to report a single strongly
+ * Object used by [CycleAwareDependencyWalker] to report a single strongly
* connected component of nodes.
*/
class StronglyConnectedComponent<Node> {
@@ -511,7 +572,7 @@ class StronglyConnectedComponent<Node> {
}
/**
- * A description of a single anaysis task that can be performed to advance
+ * A description of a single analysis task that can be performed to advance
* analysis.
*/
class WorkItem {
@@ -535,12 +596,22 @@ class WorkItem {
*/
final ResultDescriptor spawningResult;
+ /**
+ * The level of this item in its [WorkOrder].
+ */
+ final int level;
+
+ /**
+ * The work order that this item is part of, may be `null`.
+ */
+ WorkOrder workOrder;
+
/**
* An iterator used to iterate over the descriptors of the inputs to the task,
* or `null` if all of the inputs have been collected and the task can be
* created.
*/
- TaskInputBuilder builder;
+ TopLevelTaskInputBuilder builder;
/**
* The [TargetedResult]s outputs of this task depends on.
@@ -551,7 +622,7 @@ class WorkItem {
/**
* The inputs to the task that have been computed.
*/
- Map<String, dynamic> inputs;
+ Map<String, dynamic> inputs = const <String, dynamic>{};
/**
* The exception that was found while trying to populate the inputs. If this
@@ -573,18 +644,19 @@ class WorkItem {
* Initialize a newly created work item to compute the inputs for the task
* described by the given descriptor.
*/
- WorkItem(this.context, this.target, this.descriptor, this.spawningResult) {
+ WorkItem(this.context, this.target, this.descriptor, this.spawningResult,
+ this.level, this.workOrder) {
AnalysisTarget actualTarget =
identical(target, AnalysisContextTarget.request)
? new AnalysisContextTarget(context)
: target;
+// print('${'\t' * level}$spawningResult of $actualTarget');
Map<String, TaskInput> inputDescriptors =
descriptor.createTaskInputs(actualTarget);
builder = new TopLevelTaskInputBuilder(inputDescriptors);
if (!builder.moveNext()) {
builder = null;
}
- inputs = new HashMap<String, dynamic>();
}
@override
@@ -632,15 +704,29 @@ class WorkItem {
while (builder != null) {
AnalysisTarget inputTarget = builder.currentTarget;
ResultDescriptor inputResult = builder.currentResult;
+
+ // TODO(scheglov) record information to debug
+ // https://github.com/dart-lang/sdk/issues/24939
+ if (inputTarget == null || inputResult == null) {
+ try {
+ String message =
+ 'Invalid input descriptor ($inputTarget, $inputResult) for $this';
+ if (workOrder != null) {
+ message += '\nPath:\n' + workOrder.workItems.join('|\n');
+ }
+ throw new AnalysisException(message);
+ } catch (exception, stackTrace) {
+ this.exception = new CaughtException(exception, stackTrace);
+ AnalysisEngine.instance.logger
+ .logError('Task failed: $this', this.exception);
+ }
+ return null;
+ }
+
inputTargetedResults.add(new TargetedResult(inputTarget, inputResult));
CacheEntry inputEntry = context.getCacheEntry(inputTarget);
CacheState inputState = inputEntry.getState(inputResult);
- if (skipInputs.any((WorkItem item) =>
- item.target == inputTarget && item.spawningResult == inputResult)) {
- // This input is being skipped due to a circular dependency. Tell the
- // builder that it's not available so we can move on to other inputs.
- builder.currentValueNotAvailable();
- } else if (inputState == CacheState.ERROR) {
+ if (inputState == CacheState.ERROR) {
exception = inputEntry.exception;
return null;
} else if (inputState == CacheState.IN_PROCESS) {
@@ -657,13 +743,36 @@ class WorkItem {
//
throw new UnimplementedError();
} else if (inputState != CacheState.VALID) {
- try {
- TaskDescriptor descriptor =
- taskManager.findTask(inputTarget, inputResult);
- return new WorkItem(context, inputTarget, descriptor, inputResult);
- } on AnalysisException catch (exception, stackTrace) {
- this.exception = new CaughtException(exception, stackTrace);
- return null;
+ if (context.aboutToComputeResult(inputEntry, inputResult)) {
+ inputState = CacheState.VALID;
+ builder.currentValue = inputEntry.getValue(inputResult);
+ } else {
+ try {
+ TaskDescriptor descriptor =
+ taskManager.findTask(inputTarget, inputResult);
+ if (descriptor == null) {
+ throw new AnalysisException(
+ 'Cannot find task to build $inputResult for $inputTarget');
+ }
+ if (skipInputs.any((WorkItem item) =>
+ item.target == inputTarget && item.descriptor == descriptor)) {
+ // This input is being skipped due to a circular dependency. Tell
+ // the builder that it's not available so we can move on to other
+ // inputs.
+ builder.currentValueNotAvailable();
+ } else {
+ return new WorkItem(context, inputTarget, descriptor, inputResult,
+ level + 1, workOrder);
+ }
+ } on AnalysisException catch (exception, stackTrace) {
+ this.exception = new CaughtException(exception, stackTrace);
+ return null;
+ } catch (exception, stackTrace) {
+ this.exception = new CaughtException(exception, stackTrace);
+ throw new AnalysisException(
+ 'Cannot create work order to build $inputResult for $inputTarget',
+ this.exception);
+ }
}
} else {
builder.currentValue = inputEntry.getValue(inputResult);
@@ -708,7 +817,9 @@ class WorkOrder implements Iterator<WorkItem> {
* the given work item.
*/
WorkOrder(TaskManager taskManager, WorkItem item)
- : _dependencyWalker = new _WorkOrderDependencyWalker(taskManager, item);
+ : _dependencyWalker = new _WorkOrderDependencyWalker(taskManager, item) {
+ item.workOrder = this;
+ }
@override
WorkItem get current {
@@ -719,9 +830,11 @@ class WorkOrder implements Iterator<WorkItem> {
}
}
+ List<WorkItem> get workItems => _dependencyWalker._path;
+
@override
bool moveNext() {
- return workOrderMoveNextPerfTag.makeCurrentWhile(() {
+ return workOrderMoveNextPerformanceTag.makeCurrentWhile(() {
if (currentItems != null && currentItems.length > 1) {
// Yield more items.
currentItems.removeLast();
@@ -750,7 +863,7 @@ class WorkOrder implements Iterator<WorkItem> {
}
/**
- * Specilaization of [CycleAwareDependencyWalker] for use by [WorkOrder].
+ * Specialization of [CycleAwareDependencyWalker] for use by [WorkOrder].
*/
class _WorkOrderDependencyWalker extends CycleAwareDependencyWalker<WorkItem> {
/**
« 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