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