Index: analyzer/lib/src/context/cache.dart |
diff --git a/analyzer/lib/src/context/cache.dart b/analyzer/lib/src/context/cache.dart |
deleted file mode 100644 |
index 0d67817f80ab56763b9737666b361f1640b8b793..0000000000000000000000000000000000000000 |
--- a/analyzer/lib/src/context/cache.dart |
+++ /dev/null |
@@ -1,1188 +0,0 @@ |
-// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-library analyzer.src.context.cache; |
- |
-import 'dart:async'; |
-import 'dart:collection'; |
- |
-import 'package:analyzer/src/generated/engine.dart' |
- show AnalysisEngine, CacheState, InternalAnalysisContext, RetentionPriority; |
-import 'package:analyzer/src/generated/java_engine.dart'; |
-import 'package:analyzer/src/generated/source.dart'; |
-import 'package:analyzer/src/generated/utilities_collection.dart'; |
-import 'package:analyzer/src/generated/utilities_general.dart'; |
-import 'package:analyzer/src/task/model.dart'; |
-import 'package:analyzer/task/model.dart'; |
- |
-/** |
- * Return `true` if the given [target] is a priority one. |
- */ |
-typedef bool IsPriorityAnalysisTarget(AnalysisTarget target); |
- |
-/** |
- * An LRU cache of results produced by analysis. |
- */ |
-class AnalysisCache { |
- /** |
- * A flag used to control whether trace information should be produced when |
- * the content of the cache is modified. |
- */ |
- static bool _TRACE_CHANGES = false; |
- |
- /** |
- * An array containing the partitions of which this cache is comprised. |
- */ |
- final List<CachePartition> _partitions; |
- |
- /** |
- * The [StreamController] reporting [InvalidatedResult]s. |
- */ |
- final StreamController<InvalidatedResult> _onResultInvalidated = |
- new StreamController<InvalidatedResult>.broadcast(sync: true); |
- |
- /** |
- * Initialize a newly created cache to have the given [partitions]. The |
- * partitions will be searched in the order in which they appear in the array, |
- * so the most specific partition (usually an [SdkCachePartition]) should be |
- * first and the most general (usually a [UniversalCachePartition]) last. |
- */ |
- AnalysisCache(this._partitions) { |
- for (CachePartition partition in _partitions) { |
- partition.onResultInvalidated.listen((InvalidatedResult event) { |
- _onResultInvalidated.add(event); |
- }); |
- } |
- } |
- |
- /** |
- * Return the stream that is notified when a value is invalidated. |
- */ |
- Stream<InvalidatedResult> get onResultInvalidated => |
- _onResultInvalidated.stream; |
- |
- // TODO(brianwilkerson) Implement or delete this. |
-// /** |
-// * Return information about each of the partitions in this cache. |
-// */ |
-// List<AnalysisContextStatistics_PartitionData> get partitionData { |
-// int count = _partitions.length; |
-// List<AnalysisContextStatistics_PartitionData> data = |
-// new List<AnalysisContextStatistics_PartitionData>(count); |
-// for (int i = 0; i < count; i++) { |
-// CachePartition partition = _partitions[i]; |
-// data[i] = new AnalysisContextStatisticsImpl_PartitionDataImpl( |
-// partition.astSize, |
-// partition.map.length); |
-// } |
-// return data; |
-// } |
- |
- /** |
- * Return an iterator returning all of the [Source] targets. |
- */ |
- Iterable<Source> get sources { |
- return _partitions |
- .map((CachePartition partition) => partition._sources) |
- .expand((Iterable<Source> sources) => sources); |
- } |
- |
- /** |
- * Return the entry associated with the given [target]. |
- */ |
- CacheEntry get(AnalysisTarget target) { |
- int count = _partitions.length; |
- for (int i = 0; i < count; i++) { |
- CachePartition partition = _partitions[i]; |
- if (partition.isResponsibleFor(target)) { |
- return partition.get(target); |
- } |
- } |
- // |
- // We should never get to this point because the last partition should |
- // always be a universal partition, except in the case of the SDK context, |
- // in which case the target should always be part of the SDK. |
- // |
- return null; |
- } |
- |
- /** |
- * Return the context to which the given [target] was explicitly added. |
- */ |
- InternalAnalysisContext getContextFor(AnalysisTarget target) { |
- int count = _partitions.length; |
- for (int i = 0; i < count; i++) { |
- CachePartition partition = _partitions[i]; |
- if (partition.isResponsibleFor(target)) { |
- return partition.context; |
- } |
- } |
- // |
- // We should never get to this point because the last partition should |
- // always be a universal partition, except in the case of the SDK context, |
- // in which case the target should always be part of the SDK. |
- // |
- // TODO(brianwilkerson) Throw an exception here. |
- AnalysisEngine.instance.logger.logInformation( |
- 'Could not find context for $target', |
- new CaughtException(new AnalysisException(), null)); |
- return null; |
- } |
- |
- /** |
- * Return [Source]s whose full path is equal to the given [path]. |
- * Maybe empty, but not `null`. |
- */ |
- List<Source> getSourcesWithFullName(String path) { |
- List<Source> sources = <Source>[]; |
- for (CachePartition partition in _partitions) { |
- List<Source> partitionSources = partition.getSourcesWithFullName(path); |
- sources.addAll(partitionSources); |
- } |
- return sources; |
- } |
- |
- /** |
- * Return the state of the given [result] for the given [target]. |
- * |
- * It does not update the cache, if the corresponding [CacheEntry] does not |
- * exist, then [CacheState.INVALID] is returned. |
- */ |
- CacheState getState(AnalysisTarget target, ResultDescriptor result) { |
- CacheEntry entry = get(target); |
- if (entry == null) { |
- return CacheState.INVALID; |
- } |
- return entry.getState(result); |
- } |
- |
- /** |
- * Return the value of the given [result] for the given [target]. |
- * |
- * It does not update the cache, if the corresponding [CacheEntry] does not |
- * exist, then the default value is returned. |
- */ |
- Object getValue(AnalysisTarget target, ResultDescriptor result) { |
- CacheEntry entry = get(target); |
- if (entry == null) { |
- return result.defaultValue; |
- } |
- return entry.getValue(result); |
- } |
- |
- /** |
- * Return an iterator returning all of the map entries mapping targets to |
- * cache entries. |
- */ |
- MapIterator<AnalysisTarget, CacheEntry> iterator() { |
- int count = _partitions.length; |
- List<Map<AnalysisTarget, CacheEntry>> maps = |
- new List<Map<AnalysisTarget, CacheEntry>>(count); |
- for (int i = 0; i < count; i++) { |
- maps[i] = _partitions[i].map; |
- } |
- return new MultipleMapIterator<AnalysisTarget, CacheEntry>(maps); |
- } |
- |
- /** |
- * Puts the given [entry] into the cache. |
- */ |
- void put(CacheEntry entry) { |
- AnalysisTarget target = entry.target; |
- entry.fixExceptionState(); |
- int count = _partitions.length; |
- for (int i = 0; i < count; i++) { |
- CachePartition partition = _partitions[i]; |
- if (partition.isResponsibleFor(target)) { |
- if (_TRACE_CHANGES) { |
- CacheEntry oldEntry = partition.get(target); |
- if (oldEntry == null) { |
- AnalysisEngine.instance.logger |
- .logInformation('Added a cache entry for $target.'); |
- } else { |
- AnalysisEngine.instance.logger |
- .logInformation('Modified the cache entry for $target.'); |
-// 'Diff = ${entry.getDiff(oldEntry)}'); |
- } |
- } |
- partition.put(entry); |
- return; |
- } |
- } |
- // TODO(brianwilkerson) Handle the case where no partition was found, |
- // possibly by throwing an exception. |
- } |
- |
- /** |
- * Remove all information related to the given [target] from this cache. |
- */ |
- void remove(AnalysisTarget target) { |
- int count = _partitions.length; |
- for (int i = 0; i < count; i++) { |
- CachePartition partition = _partitions[i]; |
- if (partition.isResponsibleFor(target)) { |
- if (_TRACE_CHANGES) { |
- AnalysisEngine.instance.logger |
- .logInformation('Removed the cache entry for $target.'); |
- } |
- partition.remove(target); |
- return; |
- } |
- } |
- } |
- |
- /** |
- * Return the number of targets that are mapped to cache entries. |
- */ |
- int size() { |
- int size = 0; |
- int count = _partitions.length; |
- for (int i = 0; i < count; i++) { |
- size += _partitions[i].size(); |
- } |
- return size; |
- } |
-} |
- |
-/** |
- * The information cached by an analysis context about an individual target. |
- */ |
-class CacheEntry { |
- /** |
- * The index of the flag indicating whether the source was explicitly added to |
- * the context or whether the source was implicitly added because it was |
- * referenced by another source. |
- */ |
- static int _EXPLICITLY_ADDED_FLAG = 0; |
- |
- /** |
- * The target this entry is about. |
- */ |
- final AnalysisTarget target; |
- |
- /** |
- * The partition that is responsible for this entry. |
- */ |
- CachePartition _partition; |
- |
- /** |
- * The most recent time at which the state of the target matched the state |
- * represented by this entry, `-1` if the target does not exist. |
- */ |
- int modificationTime = -1; |
- |
- /** |
- * The exception that caused one or more values to have a state of |
- * [CacheState.ERROR]. |
- */ |
- CaughtException _exception; |
- |
- /** |
- * A bit-encoding of boolean flags associated with this entry's target. |
- */ |
- int _flags = 0; |
- |
- /** |
- * A table mapping result descriptors to the cached values of those results. |
- */ |
- Map<ResultDescriptor, ResultData> _resultMap = |
- new HashMap<ResultDescriptor, ResultData>(); |
- |
- CacheEntry(this.target); |
- |
- /** |
- * The exception that caused one or more values to have a state of |
- * [CacheState.ERROR]. |
- */ |
- CaughtException get exception => _exception; |
- |
- /** |
- * Return `true` if the source was explicitly added to the context or `false` |
- * if the source was implicitly added because it was referenced by another |
- * source. |
- */ |
- bool get explicitlyAdded => _getFlag(_EXPLICITLY_ADDED_FLAG); |
- |
- /** |
- * Set whether the source was explicitly added to the context to match the |
- * [explicitlyAdded] flag. |
- */ |
- void set explicitlyAdded(bool explicitlyAdded) { |
- _setFlag(_EXPLICITLY_ADDED_FLAG, explicitlyAdded); |
- } |
- |
- /** |
- * Return a list of result descriptors for results whose state is not |
- * [CacheState.INVALID]. |
- */ |
- List<ResultDescriptor> get nonInvalidResults => _resultMap.keys.toList(); |
- |
- /** |
- * Notifies the entry that the client is going to stop using it. |
- */ |
- void dispose() { |
- _resultMap.forEach((descriptor, data) { |
- TargetedResult result = new TargetedResult(target, descriptor); |
- for (TargetedResult dependedOnResult in data.dependedOnResults) { |
- ResultData dependedOnData = _partition._getDataFor(dependedOnResult); |
- if (dependedOnData != null) { |
- dependedOnData.dependentResults.remove(result); |
- } |
- } |
- }); |
- _resultMap.clear(); |
- } |
- |
- /** |
- * Fix the state of the [exception] to match the current state of the entry. |
- */ |
- void fixExceptionState() { |
- if (!hasErrorState()) { |
- _exception = null; |
- } |
- } |
- |
- /** |
- * Look up the [ResultData] of [descriptor], or add a new one if it isn't |
- * there. |
- */ |
- ResultData getResultData(ResultDescriptor descriptor) { |
- return _resultMap.putIfAbsent(descriptor, () => new ResultData(descriptor)); |
- } |
- |
- /** |
- * Return the state of the result represented by the given [descriptor]. |
- */ |
- CacheState getState(ResultDescriptor descriptor) { |
- ResultData data = _resultMap[descriptor]; |
- if (data == null) { |
- return CacheState.INVALID; |
- } |
- return data.state; |
- } |
- |
- /** |
- * Return the value of the result represented by the given [descriptor], or |
- * the default value for the result if this entry does not have a valid value. |
- */ |
- /*<V>*/ dynamic /*V*/ getValue(ResultDescriptor /*<V>*/ descriptor) { |
- ResultData data = _resultMap[descriptor]; |
- if (data == null) { |
- return descriptor.defaultValue; |
- } |
- if (_partition != null) { |
- _partition.resultAccessed(target, descriptor); |
- } |
- return data.value; |
- } |
- |
- /** |
- * Return `true` if the state of any data value is [CacheState.ERROR]. |
- */ |
- bool hasErrorState() { |
- for (ResultData data in _resultMap.values) { |
- if (data.state == CacheState.ERROR) { |
- return true; |
- } |
- } |
- return false; |
- } |
- |
- /** |
- * Invalidate all of the information associated with this entry's target. |
- */ |
- void invalidateAllInformation() { |
- _resultMap.clear(); |
- _exception = null; |
- } |
- |
- /** |
- * Return `true` if the state of the result represented by the given |
- * [descriptor] is [CacheState.INVALID]. |
- */ |
- bool isInvalid(ResultDescriptor descriptor) => |
- getState(descriptor) == CacheState.INVALID; |
- |
- /** |
- * Return `true` if the state of the result represented by the given |
- * [descriptor] is [CacheState.VALID]. |
- */ |
- bool isValid(ResultDescriptor descriptor) => |
- getState(descriptor) == CacheState.VALID; |
- |
- /** |
- * For each of the given [descriptors], set their states to |
- * [CacheState.ERROR], their values to the corresponding default values, and |
- * remember the [exception] that caused this state. |
- */ |
- void setErrorState( |
- CaughtException exception, List<ResultDescriptor> descriptors) { |
- if (descriptors == null || descriptors.isEmpty) { |
- throw new ArgumentError('at least one descriptor is expected'); |
- } |
- if (exception == null) { |
- throw new ArgumentError('an exception is expected'); |
- } |
- this._exception = exception; |
- for (ResultDescriptor descriptor in descriptors) { |
- _setErrorState(descriptor, exception); |
- } |
- } |
- |
- /** |
- * Set the state of the result represented by the given [descriptor] to the |
- * given [state]. |
- */ |
- void setState(ResultDescriptor descriptor, CacheState state, {Delta delta}) { |
- if (state == CacheState.ERROR) { |
- throw new ArgumentError('use setErrorState() to set the state to ERROR'); |
- } |
- if (state == CacheState.VALID) { |
- throw new ArgumentError('use setValue() to set the state to VALID'); |
- } |
- _validateStateChange(descriptor, state); |
- if (state == CacheState.INVALID) { |
- ResultData data = _resultMap[descriptor]; |
- if (data != null) { |
- _invalidate(descriptor, delta); |
- } |
- } else { |
- ResultData data = getResultData(descriptor); |
- data.state = state; |
- if (state != CacheState.IN_PROCESS) { |
- // |
- // If the state is in-process, we can leave the current value in the |
- // cache for any 'get' methods to access. |
- // |
- data.value = descriptor.defaultValue; |
- } |
- } |
- } |
- |
- /** |
- * Set the value of the result represented by the given [descriptor] to the |
- * given [value]. |
- */ |
- /*<V>*/ void setValue(ResultDescriptor /*<V>*/ descriptor, dynamic /*V*/ |
- value, List<TargetedResult> dependedOn) { |
-// { |
-// String valueStr = '$value'; |
-// if (valueStr.length > 20) { |
-// valueStr = valueStr.substring(0, 20) + '...'; |
-// } |
-// valueStr = valueStr.replaceAll('\n', '\\n'); |
-// print( |
-// 'setValue $descriptor for $target value=$valueStr deps=$dependedOn'); |
-// } |
- _validateStateChange(descriptor, CacheState.VALID); |
- TargetedResult thisResult = new TargetedResult(target, descriptor); |
- if (_partition != null) { |
- _partition.resultStored(thisResult, value); |
- } |
- ResultData data = getResultData(descriptor); |
- _setDependedOnResults(data, thisResult, dependedOn); |
- data.state = CacheState.VALID; |
- data.value = value == null ? descriptor.defaultValue : value; |
- } |
- |
- /** |
- * Set the value of the result represented by the given [descriptor] to the |
- * given [value], keep its dependency, invalidate all the dependent result. |
- */ |
- void setValueIncremental(ResultDescriptor descriptor, dynamic value) { |
- ResultData data = getResultData(descriptor); |
- List<TargetedResult> dependedOn = data.dependedOnResults; |
- _invalidate(descriptor, null); |
- setValue(descriptor, value, dependedOn); |
- } |
- |
- @override |
- String toString() { |
- StringBuffer buffer = new StringBuffer(); |
- _writeOn(buffer); |
- return buffer.toString(); |
- } |
- |
- /** |
- * Return the value of the flag with the given [index]. |
- */ |
- bool _getFlag(int index) => BooleanArray.get(_flags, index); |
- |
- /** |
- * Invalidate the result represented by the given [descriptor] and propagate |
- * invalidation to other results that depend on it. |
- */ |
- void _invalidate(ResultDescriptor descriptor, Delta delta) { |
- DeltaResult deltaResult = null; |
- if (delta != null) { |
- deltaResult = delta.validate(_partition.context, target, descriptor); |
- if (deltaResult == DeltaResult.STOP) { |
-// print('not-invalidate $descriptor for $target'); |
- return; |
- } |
- } |
-// print('invalidate $descriptor for $target'); |
- ResultData thisData; |
- if (deltaResult == null || deltaResult == DeltaResult.INVALIDATE) { |
- thisData = _resultMap.remove(descriptor); |
- } |
- if (deltaResult == DeltaResult.KEEP_CONTINUE) { |
- thisData = _resultMap[descriptor]; |
- } |
- if (thisData == null) { |
- return; |
- } |
- // Stop depending on other results. |
- TargetedResult thisResult = new TargetedResult(target, descriptor); |
- for (TargetedResult dependedOnResult in thisData.dependedOnResults) { |
- ResultData data = _partition._getDataFor(dependedOnResult); |
- if (data != null) { |
- data.dependentResults.remove(thisResult); |
- } |
- } |
- // Invalidate results that depend on this result. |
- List<TargetedResult> dependentResults = thisData.dependentResults.toList(); |
- for (TargetedResult dependentResult in dependentResults) { |
- CacheEntry entry = _partition.get(dependentResult.target); |
- if (entry != null) { |
- entry._invalidate(dependentResult.result, delta); |
- } |
- } |
- // If empty, remove the entry altogether. |
- if (_resultMap.isEmpty) { |
- _partition._targetMap.remove(target); |
- _partition._removeIfSource(target); |
- } |
- // Notify controller. |
- _partition._onResultInvalidated |
- .add(new InvalidatedResult(this, descriptor)); |
- } |
- |
- /** |
- * Invalidates all the results of this entry, with propagation. |
- */ |
- void _invalidateAll() { |
- List<ResultDescriptor> results = _resultMap.keys.toList(); |
- for (ResultDescriptor result in results) { |
- _invalidate(result, null); |
- } |
- } |
- |
- /** |
- * Set the [dependedOn] on which this result depends. |
- */ |
- void _setDependedOnResults(ResultData thisData, TargetedResult thisResult, |
- List<TargetedResult> dependedOn) { |
- thisData.dependedOnResults.forEach((TargetedResult dependedOnResult) { |
- ResultData data = _partition._getDataFor(dependedOnResult); |
- if (data != null) { |
- data.dependentResults.remove(thisResult); |
- } |
- }); |
- thisData.dependedOnResults = dependedOn; |
- thisData.dependedOnResults.forEach((TargetedResult dependedOnResult) { |
- ResultData data = _partition._getDataFor(dependedOnResult); |
- if (data != null) { |
- data.dependentResults.add(thisResult); |
- } |
- }); |
- } |
- |
- /** |
- * Set states of the given and dependent results to [CacheState.ERROR] and |
- * their values to the corresponding default values |
- */ |
- void _setErrorState(ResultDescriptor descriptor, CaughtException exception) { |
- ResultData thisData = getResultData(descriptor); |
- // Set the error state. |
- _exception = exception; |
- thisData.state = CacheState.ERROR; |
- thisData.value = descriptor.defaultValue; |
- // Propagate the error state. |
- thisData.dependentResults.forEach((TargetedResult dependentResult) { |
- CacheEntry entry = _partition.get(dependentResult.target); |
- entry._setErrorState(dependentResult.result, exception); |
- }); |
- } |
- |
- /** |
- * Set the value of the flag with the given [index] to the given [value]. |
- */ |
- void _setFlag(int index, bool value) { |
- _flags = BooleanArray.set(_flags, index, value); |
- } |
- |
- /** |
- * If the state of the value described by the given [descriptor] is changing |
- * from ERROR to anything else, capture the information. This is an attempt to |
- * discover the underlying cause of a long-standing bug. |
- */ |
- void _validateStateChange(ResultDescriptor descriptor, CacheState newState) { |
- // TODO(brianwilkerson) Decide whether we still want to capture this data. |
-// if (descriptor != CONTENT) { |
-// return; |
-// } |
-// ResultData data = resultMap[CONTENT]; |
-// if (data != null && data.state == CacheState.ERROR) { |
-// String message = |
-// 'contentState changing from ${data.state} to $newState'; |
-// InstrumentationBuilder builder = |
-// Instrumentation.builder2('CacheEntry-validateStateChange'); |
-// builder.data3('message', message); |
-// //builder.data('source', source.getFullName()); |
-// builder.record(new CaughtException(new AnalysisException(message), null)); |
-// builder.log(); |
-// } |
- } |
- |
- /** |
- * Write a textual representation of this entry to the given [buffer]. The |
- * result should only be used for debugging purposes. |
- */ |
- void _writeOn(StringBuffer buffer) { |
- buffer.write('time = '); |
- buffer.write(modificationTime); |
- List<ResultDescriptor> results = _resultMap.keys.toList(); |
- results.sort((ResultDescriptor first, ResultDescriptor second) => |
- first.toString().compareTo(second.toString())); |
- for (ResultDescriptor result in results) { |
- ResultData data = _resultMap[result]; |
- buffer.write('; '); |
- buffer.write(result.toString()); |
- buffer.write(' = '); |
- buffer.write(data.state); |
- } |
- } |
-} |
- |
-/** |
- * An object that controls flushing of analysis results from the cache. |
- */ |
-class CacheFlushManager<T> { |
- final IsPriorityAnalysisTarget isPriorityAnalysisTarget; |
- final ResultCachingPolicy<T> policy; |
- final int maxActiveSize; |
- final int maxIdleSize; |
- |
- /** |
- * A map of the stored [TargetedResult] to their sizes. |
- */ |
- final HashMap<TargetedResult, int> resultSizeMap = |
- new HashMap<TargetedResult, int>(); |
- |
- /** |
- * A linked set containing the most recently accessed results with the most |
- * recently used at the end of the list. When more results are added than the |
- * maximum size allowed then the least recently used results will be flushed |
- * from the cache. |
- */ |
- final LinkedHashSet<TargetedResult> recentlyUsed = |
- new LinkedHashSet<TargetedResult>(); |
- |
- /** |
- * The current size of stored results. |
- */ |
- int currentSize = 0; |
- |
- /** |
- * The current maximum cache size. |
- */ |
- int maxSize; |
- |
- CacheFlushManager( |
- ResultCachingPolicy<T> policy, this.isPriorityAnalysisTarget) |
- : policy = policy, |
- maxActiveSize = policy.maxActiveSize, |
- maxIdleSize = policy.maxIdleSize, |
- maxSize = policy.maxIdleSize; |
- |
- /** |
- * If [currentSize] is already less than [maxSize], returns an empty list. |
- * Otherwise returns [TargetedResult]s to flush from the cache to make |
- * [currentSize] less or equal to [maxSize]. |
- * |
- * Results for priority files are never flushed, so this method might leave |
- * [currentSize] greater than [maxSize]. |
- */ |
- List<TargetedResult> flushToSize() { |
- // If still under the cap, done. |
- if (currentSize <= maxSize) { |
- return TargetedResult.EMPTY_LIST; |
- } |
- // Flush results until we are under the cap. |
- List<TargetedResult> resultsToFlush = <TargetedResult>[]; |
- for (TargetedResult result in recentlyUsed) { |
- if (isPriorityAnalysisTarget(result.target)) { |
- continue; |
- } |
- resultsToFlush.add(result); |
- int size = resultSizeMap.remove(result); |
- assert(size != null); |
- currentSize -= size; |
- if (currentSize <= maxSize) { |
- break; |
- } |
- } |
- recentlyUsed.removeAll(resultsToFlush); |
- return resultsToFlush; |
- } |
- |
- /** |
- * Notifies this manager that the corresponding analysis context is active. |
- */ |
- void madeActive() { |
- maxSize = maxActiveSize; |
- } |
- |
- /** |
- * Notifies this manager that the corresponding analysis context is idle. |
- * Returns [TargetedResult]s that should be flushed from the cache. |
- */ |
- List<TargetedResult> madeIdle() { |
- maxSize = maxIdleSize; |
- return flushToSize(); |
- } |
- |
- /** |
- * Records that the given [result] was just read from the cache. |
- */ |
- void resultAccessed(TargetedResult result) { |
- if (recentlyUsed.remove(result)) { |
- recentlyUsed.add(result); |
- } |
- } |
- |
- /** |
- * Records that the given [newResult] and [newValue] were stored to the cache. |
- * Returns [TargetedResult]s that should be flushed from the cache. |
- */ |
- List<TargetedResult> resultStored(TargetedResult newResult, T newValue) { |
- if (!recentlyUsed.remove(newResult)) { |
- int size = policy.measure(newValue); |
- resultSizeMap[newResult] = size; |
- currentSize += size; |
- } |
- recentlyUsed.add(newResult); |
- return flushToSize(); |
- } |
- |
- /** |
- * Records that the given [target] was just removed from to the cache. |
- */ |
- void targetRemoved(AnalysisTarget target) { |
- List<TargetedResult> resultsToRemove = <TargetedResult>[]; |
- for (TargetedResult result in recentlyUsed) { |
- if (result.target == target) { |
- resultsToRemove.add(result); |
- int size = resultSizeMap.remove(result); |
- assert(size != null); |
- currentSize -= size; |
- } |
- } |
- recentlyUsed.removeAll(resultsToRemove); |
- } |
-} |
- |
-/** |
- * A single partition in an LRU cache of information related to analysis. |
- */ |
-abstract class CachePartition { |
- /** |
- * The context that owns this partition. Multiple contexts can reference a |
- * partition, but only one context can own it. |
- */ |
- final InternalAnalysisContext context; |
- |
- /** |
- * A table mapping caching policies to the cache flush managers. |
- */ |
- final HashMap<ResultCachingPolicy, CacheFlushManager> _flushManagerMap = |
- new HashMap<ResultCachingPolicy, CacheFlushManager>(); |
- |
- /** |
- * The [StreamController] reporting [InvalidatedResult]s. |
- */ |
- final StreamController<InvalidatedResult> _onResultInvalidated = |
- new StreamController<InvalidatedResult>.broadcast(sync: true); |
- |
- /** |
- * A table mapping the targets belonging to this partition to the information |
- * known about those targets. |
- */ |
- HashMap<AnalysisTarget, CacheEntry> _targetMap = |
- new HashMap<AnalysisTarget, CacheEntry>(); |
- |
- /** |
- * A set of the [Source] targets. |
- */ |
- final HashSet<Source> _sources = new HashSet<Source>(); |
- |
- /** |
- * A table mapping full paths to lists of [Source]s with these full paths. |
- */ |
- final Map<String, List<Source>> _pathToSources = <String, List<Source>>{}; |
- |
- /** |
- * Initialize a newly created cache partition, belonging to the given |
- * [context]. |
- */ |
- CachePartition(this.context); |
- |
- /** |
- * Return a table mapping the targets known to the context to the information |
- * known about the target. |
- * |
- * <b>Note:</b> This method is only visible for use by [AnalysisCache] and |
- * should not be used for any other purpose. |
- */ |
- Map<AnalysisTarget, CacheEntry> get map => _targetMap; |
- |
- /** |
- * Return the stream that is notified when a value is invalidated. |
- */ |
- Stream<InvalidatedResult> get onResultInvalidated => |
- _onResultInvalidated.stream; |
- |
- /** |
- * Notifies the partition that the client is going to stop using it. |
- */ |
- void dispose() { |
- for (CacheEntry entry in _targetMap.values) { |
- entry.dispose(); |
- } |
- _targetMap.clear(); |
- } |
- |
- /** |
- * Return the entry associated with the given [target]. |
- */ |
- CacheEntry get(AnalysisTarget target) => _targetMap[target]; |
- |
- /** |
- * Return [Source]s whose full path is equal to the given [path]. |
- * Maybe empty, but not `null`. |
- */ |
- List<Source> getSourcesWithFullName(String path) { |
- List<Source> sources = _pathToSources[path]; |
- return sources != null ? sources : Source.EMPTY_LIST; |
- } |
- |
- /** |
- * Return `true` if this partition is responsible for the given [target]. |
- */ |
- bool isResponsibleFor(AnalysisTarget target); |
- |
- /** |
- * Return an iterator returning all of the map entries mapping targets to |
- * cache entries. |
- */ |
- MapIterator<AnalysisTarget, CacheEntry> iterator() => |
- new SingleMapIterator<AnalysisTarget, CacheEntry>(_targetMap); |
- |
- /** |
- * Puts the given [entry] into the partition. |
- */ |
- void put(CacheEntry entry) { |
- AnalysisTarget target = entry.target; |
- if (entry._partition != null) { |
- throw new StateError( |
- 'The entry for $target is already in ${entry._partition}'); |
- } |
- entry._partition = this; |
- entry.fixExceptionState(); |
- _targetMap[target] = entry; |
- _addIfSource(target); |
- } |
- |
- /** |
- * Remove all information related to the given [target] from this cache. |
- */ |
- void remove(AnalysisTarget target) { |
- for (CacheFlushManager flushManager in _flushManagerMap.values) { |
- flushManager.targetRemoved(target); |
- } |
- CacheEntry entry = _targetMap.remove(target); |
- if (entry != null) { |
- entry._invalidateAll(); |
- } |
- _removeIfSource(target); |
- } |
- |
- /** |
- * Records that a value of the result described by the given [descriptor] |
- * for the given [target] was just read from the cache. |
- */ |
- void resultAccessed(AnalysisTarget target, ResultDescriptor descriptor) { |
- CacheFlushManager flushManager = _getFlushManager(descriptor); |
- TargetedResult result = new TargetedResult(target, descriptor); |
- flushManager.resultAccessed(result); |
- } |
- |
- /** |
- * Records that the given [result] was just stored into the cache. |
- */ |
- void resultStored(TargetedResult result, Object value) { |
- CacheFlushManager flushManager = _getFlushManager(result.result); |
- List<TargetedResult> resultsToFlush = |
- flushManager.resultStored(result, value); |
- for (TargetedResult result in resultsToFlush) { |
- CacheEntry entry = get(result.target); |
- if (entry != null) { |
- ResultData data = entry._resultMap[result.result]; |
- if (data != null) { |
- data.flush(); |
- } |
- } |
- } |
- } |
- |
- /** |
- * Return the number of targets that are mapped to cache entries. |
- */ |
- int size() => _targetMap.length; |
- |
- /** |
- * If the given [target] is a [Source], adds it to [_sources]. |
- */ |
- void _addIfSource(AnalysisTarget target) { |
- if (target is Source) { |
- _sources.add(target); |
- { |
- String fullName = target.fullName; |
- _pathToSources.putIfAbsent(fullName, () => <Source>[]).add(target); |
- } |
- } |
- } |
- |
- ResultData _getDataFor(TargetedResult result) { |
- CacheEntry entry = context.analysisCache.get(result.target); |
- return entry != null ? entry._resultMap[result.result] : null; |
- } |
- |
- /** |
- * Return the [CacheFlushManager] for the given [descriptor], not `null`. |
- */ |
- CacheFlushManager _getFlushManager(ResultDescriptor descriptor) { |
- ResultCachingPolicy policy = descriptor.cachingPolicy; |
- if (identical(policy, DEFAULT_CACHING_POLICY)) { |
- return UnlimitedCacheFlushManager.INSTANCE; |
- } |
- CacheFlushManager manager = _flushManagerMap[policy]; |
- if (manager == null) { |
- manager = new CacheFlushManager(policy, _isPriorityAnalysisTarget); |
- _flushManagerMap[policy] = manager; |
- } |
- return manager; |
- } |
- |
- bool _isPriorityAnalysisTarget(AnalysisTarget target) { |
- return context.priorityTargets.contains(target); |
- } |
- |
- /** |
- * If the given [target] is a [Source], removes it from [_sources]. |
- */ |
- void _removeIfSource(AnalysisTarget target) { |
- if (target is Source) { |
- _sources.remove(target); |
- { |
- String fullName = target.fullName; |
- List<Source> sources = _pathToSources[fullName]; |
- if (sources != null) { |
- sources.remove(target); |
- if (sources.isEmpty) { |
- _pathToSources.remove(fullName); |
- } |
- } |
- } |
- } |
- } |
-} |
- |
-/** |
- * The description for a change. |
- */ |
-class Delta { |
- final Source source; |
- |
- Delta(this.source); |
- |
- /** |
- * Check whether this delta affects the result described by the given |
- * [descriptor] and [target]. |
- */ |
- DeltaResult validate(InternalAnalysisContext context, AnalysisTarget target, |
- ResultDescriptor descriptor) { |
- return DeltaResult.INVALIDATE; |
- } |
-} |
- |
-/** |
- * The possible results of validating analysis results againt a [Delta]. |
- */ |
-enum DeltaResult { INVALIDATE, KEEP_CONTINUE, STOP } |
- |
-/** |
- * [InvalidatedResult] describes an invalidated result. |
- */ |
-class InvalidatedResult { |
- /** |
- * The target in which the result was invalidated. |
- */ |
- final CacheEntry entry; |
- |
- /** |
- * The descriptor of the result which was invalidated. |
- */ |
- final ResultDescriptor descriptor; |
- |
- InvalidatedResult(this.entry, this.descriptor); |
- |
- @override |
- String toString() => '$descriptor of ${entry.target}'; |
-} |
- |
-/** |
- * The data about a single analysis result that is stored in a [CacheEntry]. |
- */ |
-// TODO(brianwilkerson) Consider making this a generic class so that the value |
-// can be typed. |
-class ResultData { |
- /** |
- * The [ResultDescriptor] this result is for. |
- */ |
- final ResultDescriptor descriptor; |
- |
- /** |
- * The state of the cached value. |
- */ |
- CacheState state; |
- |
- /** |
- * The value being cached, or the default value for the result if there is no |
- * value (for example, when the [state] is [CacheState.INVALID]). |
- */ |
- Object value; |
- |
- /** |
- * A list of the results on which this result depends. |
- */ |
- List<TargetedResult> dependedOnResults = <TargetedResult>[]; |
- |
- /** |
- * A list of the results that depend on this result. |
- */ |
- Set<TargetedResult> dependentResults = new Set<TargetedResult>(); |
- |
- /** |
- * Initialize a newly created result holder to represent the value of data |
- * described by the given [descriptor]. |
- */ |
- ResultData(this.descriptor) { |
- state = CacheState.INVALID; |
- value = descriptor.defaultValue; |
- } |
- |
- /** |
- * Flush this value. |
- */ |
- void flush() { |
- state = CacheState.FLUSHED; |
- value = descriptor.defaultValue; |
- } |
-} |
- |
-/** |
- * A cache partition that contains all of the targets in the SDK. |
- */ |
-class SdkCachePartition extends CachePartition { |
- /** |
- * Initialize a newly created cache partition, belonging to the given |
- * [context]. |
- */ |
- SdkCachePartition(InternalAnalysisContext context) : super(context); |
- |
- @override |
- bool isResponsibleFor(AnalysisTarget target) { |
- if (target is AnalysisContextTarget) { |
- return true; |
- } |
- Source source = target.source; |
- return source != null && source.isInSystemLibrary; |
- } |
-} |
- |
-/** |
- * A specification of a specific result computed for a specific target. |
- */ |
-class TargetedResult { |
- /** |
- * An empty list of results. |
- */ |
- static final List<TargetedResult> EMPTY_LIST = const <TargetedResult>[]; |
- |
- /** |
- * The target with which the result is associated. |
- */ |
- final AnalysisTarget target; |
- |
- /** |
- * The result associated with the target. |
- */ |
- final ResultDescriptor result; |
- |
- /** |
- * Initialize a new targeted result. |
- */ |
- TargetedResult(this.target, this.result); |
- |
- @override |
- int get hashCode { |
- return JenkinsSmiHash.combine(target.hashCode, result.hashCode); |
- } |
- |
- @override |
- bool operator ==(other) { |
- return other is TargetedResult && |
- other.target == target && |
- other.result == result; |
- } |
- |
- @override |
- String toString() => '$result for $target'; |
-} |
- |
-/** |
- * A cache partition that contains all targets not contained in other partitions. |
- */ |
-class UniversalCachePartition extends CachePartition { |
- /** |
- * Initialize a newly created cache partition, belonging to the given |
- * [context]. |
- */ |
- UniversalCachePartition(InternalAnalysisContext context) : super(context); |
- |
- @override |
- bool isResponsibleFor(AnalysisTarget target) => true; |
-} |
- |
-/** |
- * [CacheFlushManager] that does nothing, results are never flushed. |
- */ |
-class UnlimitedCacheFlushManager extends CacheFlushManager { |
- static final CacheFlushManager INSTANCE = new UnlimitedCacheFlushManager(); |
- |
- UnlimitedCacheFlushManager() : super(DEFAULT_CACHING_POLICY, (_) => false); |
- |
- @override |
- void resultAccessed(TargetedResult result) {} |
- |
- @override |
- List<TargetedResult> resultStored(TargetedResult newResult, newValue) { |
- return TargetedResult.EMPTY_LIST; |
- } |
- |
- @override |
- void targetRemoved(AnalysisTarget target) {} |
-} |