| Index: pkg/analyzer/lib/src/context/cache.dart
|
| diff --git a/pkg/analyzer/lib/src/context/cache.dart b/pkg/analyzer/lib/src/context/cache.dart
|
| index fd65089cc2e791a7760b8a1f4131caf1b9e70bf2..8e9ca2bd3d3d668b77ce673efad3413c744da90c 100644
|
| --- a/pkg/analyzer/lib/src/context/cache.dart
|
| +++ b/pkg/analyzer/lib/src/context/cache.dart
|
| @@ -18,6 +18,11 @@ import 'package:analyzer/src/task/model.dart';
|
| import 'package:analyzer/task/model.dart';
|
|
|
| /**
|
| + * The cache results visiting function type.
|
| + */
|
| +typedef void CacheResultVisitor(AnalysisTarget target, ResultData data);
|
| +
|
| +/**
|
| * Return `true` if the [result] of the [target] should be flushed.
|
| */
|
| typedef bool FlushResultFilter<V>(
|
| @@ -297,9 +302,9 @@ class CacheEntry {
|
| static int _EXPLICITLY_ADDED_FLAG = 0;
|
|
|
| /**
|
| - * The next invalidation process identifier.
|
| + * The next visit process identifier.
|
| */
|
| - static int nextInvalidateId = 0;
|
| + static int nextVisitId = 0;
|
|
|
| /**
|
| * A table containing the number of times the value of a result descriptor was
|
| @@ -527,7 +532,12 @@ class CacheEntry {
|
| if (state == CacheState.INVALID) {
|
| ResultData data = _resultMap[descriptor];
|
| if (data != null) {
|
| - _invalidate(nextInvalidateId++, descriptor, delta, 0);
|
| + bool canUseDelta =
|
| + _gatherResultsInvalidatedByDelta(descriptor, delta, 0);
|
| + if (!canUseDelta) {
|
| + delta = null;
|
| + }
|
| + _invalidate(nextVisitId++, descriptor, delta, 0);
|
| }
|
| } else {
|
| ResultData data = getResultData(descriptor);
|
| @@ -584,7 +594,7 @@ class CacheEntry {
|
| data.value = value;
|
| }
|
| if (invalidateDependent) {
|
| - _invalidateDependentResults(nextInvalidateId++, data, null, 0);
|
| + _invalidateDependentResults(nextVisitId++, data, null, 0);
|
| }
|
| }
|
|
|
| @@ -596,6 +606,35 @@ class CacheEntry {
|
| }
|
|
|
| /**
|
| + * Visit the given [result] and all results that depend on it, and
|
| + * ask [delta] to gather changes. Return `true` if the [delta] can be used
|
| + * to perform limited invalidation, or `false` if the changes collection
|
| + * process does not stop (should not happen).
|
| + */
|
| + bool _gatherResultsInvalidatedByDelta(
|
| + ResultDescriptor result, Delta delta, int level) {
|
| + if (delta == null) {
|
| + return false;
|
| + }
|
| + for (int i = 0; i < 64; i++) {
|
| + bool hasVisitChanges = false;
|
| + _visitResults(nextVisitId++, result,
|
| + (AnalysisTarget target, ResultData data) {
|
| + bool hasDeltaChanges = delta.gatherChanges(
|
| + _partition.context, target, data.descriptor, data.value);
|
| + if (hasDeltaChanges) {
|
| + hasVisitChanges = true;
|
| + }
|
| + });
|
| + delta.gatherEnd();
|
| + if (!hasVisitChanges) {
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + /**
|
| * Return the value of the flag with the given [index].
|
| */
|
| bool _getFlag(int index) => BooleanArray.get(_flags, index);
|
| @@ -612,10 +651,10 @@ class CacheEntry {
|
| }
|
| // Stop if already validated.
|
| if (delta != null) {
|
| - if (thisData.invalidateId == id) {
|
| + if (thisData.visitId == id) {
|
| return;
|
| }
|
| - thisData.invalidateId = id;
|
| + thisData.visitId = id;
|
| }
|
| // Ask the delta to validate.
|
| DeltaResult deltaResult = null;
|
| @@ -688,7 +727,7 @@ class CacheEntry {
|
| int length = results.length;
|
| for (int i = 0; i < length; i++) {
|
| ResultDescriptor result = results[i];
|
| - _invalidate(nextInvalidateId++, result, null, 0);
|
| + _invalidate(nextVisitId++, result, null, 0);
|
| }
|
| }
|
|
|
| @@ -819,6 +858,41 @@ class CacheEntry {
|
| }
|
|
|
| /**
|
| + * Call [visitor] for the result described by the given [descriptor] and all
|
| + * results that depend on directly or indirectly. Each result is visited
|
| + * only once.
|
| + */
|
| + void _visitResults(
|
| + int id, ResultDescriptor descriptor, CacheResultVisitor visitor) {
|
| + ResultData thisData = _resultMap[descriptor];
|
| + if (thisData == null) {
|
| + return;
|
| + }
|
| + // Stop if already visited.
|
| + if (thisData.visitId == id) {
|
| + return;
|
| + }
|
| + thisData.visitId = id;
|
| + // Visit this result.
|
| + visitor(target, thisData);
|
| + // Visit results that depend on this result.
|
| + List<AnalysisCache> caches = _partition.containingCaches;
|
| + int cacheLength = caches.length;
|
| + List<TargetedResult> dependentResults = thisData.dependentResults.toList();
|
| + int resultLength = dependentResults.length;
|
| + for (int i = 0; i < resultLength; i++) {
|
| + TargetedResult dependentResult = dependentResults[i];
|
| + for (int j = 0; j < cacheLength; j++) {
|
| + AnalysisCache cache = caches[j];
|
| + CacheEntry entry = cache.get(dependentResult.target);
|
| + if (entry != null) {
|
| + entry._visitResults(id, dependentResult.result, visitor);
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| * Write a textual representation of this entry to the given [buffer]. The
|
| * result should only be used for debugging purposes.
|
| */
|
| @@ -1186,6 +1260,16 @@ class Delta {
|
|
|
| Delta(this.source);
|
|
|
| + bool gatherChanges(InternalAnalysisContext context, AnalysisTarget target,
|
| + ResultDescriptor descriptor, Object value) {
|
| + return false;
|
| + }
|
| +
|
| + /**
|
| + * The current cache results visit is done.
|
| + */
|
| + void gatherEnd() {}
|
| +
|
| /**
|
| * Check whether this delta affects the result described by the given
|
| * [descriptor] and [target]. The current [value] of the result is provided.
|
| @@ -1344,11 +1428,10 @@ class ResultData {
|
| Object value;
|
|
|
| /**
|
| - * The identifier of the invalidation process that most recently checked
|
| - * this value. If it is the same as the current invalidation identifier,
|
| - * then there is no reason to check it (and its subtree again).
|
| + * The identifier of the most recent visiting process. We use it to visit
|
| + * every result only once.
|
| */
|
| - int invalidateId = -1;
|
| + int visitId = -1;
|
|
|
| /**
|
| * A list of the results on which this result depends.
|
|
|