Cache flushing implementation for the task model.

R=brianwilkerson@google.com
BUG=

Review URL: https://codereview.chromium.org//1133513003

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@45597 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
scheglov@google.com 2015-05-07 15:50:13 +00:00
parent 2a1657d467
commit a3366361de
10 changed files with 685 additions and 578 deletions

View file

@ -6,16 +6,19 @@ library analyzer.src.context.cache;
import 'dart:collection';
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/engine.dart'
show AnalysisEngine, CacheState, InternalAnalysisContext, RetentionPriority;
import 'package:analyzer/src/generated/html.dart';
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/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.
*/
@ -37,13 +40,11 @@ class AnalysisCache {
* so the most specific partition (usually an [SdkCachePartition]) should be
* first and the most general (usually a [UniversalCachePartition]) last.
*/
AnalysisCache(this._partitions);
/**
* Return the number of entries in this cache that have an AST associated with
* them.
*/
int get astSize => _partitions[_partitions.length - 1].astSize;
AnalysisCache(this._partitions) {
for (CachePartition partition in _partitions) {
partition._cache = this;
}
}
// TODO(brianwilkerson) Implement or delete this.
// /**
@ -62,22 +63,6 @@ class AnalysisCache {
// return data;
// }
/**
* Record that the AST associated with the given [target] was just read from
* the cache.
*/
void accessedAst(AnalysisTarget target) {
// TODO(brianwilkerson) Extract this logic to a helper method (here and
// elsewhere)
int count = _partitions.length;
for (int i = 0; i < count; i++) {
if (_partitions[i].contains(target)) {
_partitions[i].accessedAst(target);
return;
}
}
}
/**
* Return the entry associated with the given [target].
*/
@ -135,8 +120,6 @@ class AnalysisCache {
* Associate the given [entry] with the given [target].
*/
void put(AnalysisTarget target, CacheEntry entry) {
entry._cache = this;
entry._target = target;
entry.fixExceptionState();
int count = _partitions.length;
for (int i = 0; i < count; i++) {
@ -177,20 +160,6 @@ class AnalysisCache {
}
}
/**
* Record that the AST associated with the given [target] was just removed
* from the cache.
*/
void removedAst(AnalysisTarget target) {
int count = _partitions.length;
for (int i = 0; i < count; i++) {
if (_partitions[i].contains(target)) {
_partitions[i].removedAst(target);
return;
}
}
}
/**
* Return the number of targets that are mapped to cache entries.
*/
@ -203,20 +172,6 @@ class AnalysisCache {
return size;
}
/**
* Record that the AST associated with the given [target] was just stored to
* the cache.
*/
void storedAst(AnalysisTarget target) {
int count = _partitions.length;
for (int i = 0; i < count; i++) {
if (_partitions[i].contains(target)) {
_partitions[i].storedAst(target);
return;
}
}
}
ResultData _getDataFor(TargetedResult result) {
AnalysisTarget target = result.target;
int count = _partitions.length;
@ -242,9 +197,9 @@ class CacheEntry {
static int _EXPLICITLY_ADDED_FLAG = 0;
/**
* The cache that contains this entry.
* The partition that is responsible for this entry.
*/
AnalysisCache _cache;
CachePartition _partition;
/**
* The target this entry is about.
@ -295,19 +250,6 @@ class CacheEntry {
_setFlag(_EXPLICITLY_ADDED_FLAG, explicitlyAdded);
}
/**
* Return `true` if this entry contains at least one result whose value is an
* AST structure.
*/
bool get hasAstStructure {
for (ResultData data in _resultMap.values) {
if (data.value is AstNode || data.value is XmlNode) {
return true;
}
}
return false;
}
/**
* Fix the state of the [exception] to match the current state of the entry.
*/
@ -317,19 +259,6 @@ class CacheEntry {
}
}
/**
* Mark any AST structures associated with this cache entry as being flushed.
*/
void flushAstStructures() {
_resultMap.forEach((ResultDescriptor descriptor, ResultData data) {
if (data.value is AstNode || data.value is XmlNode) {
_validateStateChange(descriptor, CacheState.FLUSHED);
data.state = CacheState.FLUSHED;
data.value = descriptor.defaultValue;
}
});
}
/**
* Return the memento of the result represented by the given [descriptor].
*/
@ -361,6 +290,9 @@ class CacheEntry {
if (data == null) {
return descriptor.defaultValue;
}
if (_partition != null) {
_partition.resultAccessed(_target, descriptor);
}
return data.value;
}
@ -415,7 +347,7 @@ class CacheEntry {
for (ResultDescriptor descriptor in descriptors) {
ResultData data = _getResultData(descriptor);
TargetedResult thisResult = new TargetedResult(_target, descriptor);
data.invalidate(_cache, thisResult, CacheState.ERROR);
data.invalidate(_partition, thisResult, CacheState.ERROR);
}
}
@ -435,7 +367,7 @@ class CacheEntry {
ResultData data = _resultMap[descriptor];
if (data != null) {
TargetedResult thisResult = new TargetedResult(_target, descriptor);
data.invalidate(_cache, thisResult, CacheState.INVALID);
data.invalidate(_partition, thisResult, CacheState.INVALID);
}
} else {
ResultData data = _getResultData(descriptor);
@ -458,12 +390,13 @@ class CacheEntry {
/*<V>*/ void setValue(ResultDescriptor /*<V>*/ descriptor, dynamic /*V*/
value, List<TargetedResult> dependedOn, Object memento) {
_validateStateChange(descriptor, CacheState.VALID);
ResultData data = _getResultData(descriptor);
{
TargetedResult thisResult = new TargetedResult(_target, descriptor);
data.invalidate(_cache, thisResult, CacheState.INVALID);
data.setDependedOnResults(_cache, thisResult, dependedOn);
TargetedResult thisResult = new TargetedResult(_target, descriptor);
if (_partition != null) {
_partition.resultStored(thisResult, value);
}
ResultData data = _getResultData(descriptor);
data.invalidate(_partition, thisResult, CacheState.INVALID);
data.setDependedOnResults(_partition, thisResult, dependedOn);
data.state = CacheState.VALID;
data.value = value == null ? descriptor.defaultValue : value;
data.memento = memento;
@ -539,10 +472,147 @@ class CacheEntry {
}
}
/**
* 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 (maxSize == -1 || 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 [AnalysisCache] that owns this partition.
*
* TODO(scheglov) It seems wrong. Partitions may be shared between caches.
* But we need a way to go from every "enclosing" partition into "enclosed"
* ones.
*/
AnalysisCache _cache;
/**
* The context that owns this partition. Multiple contexts can reference a
* partition, but only one context can own it.
@ -550,15 +620,10 @@ abstract class CachePartition {
final InternalAnalysisContext context;
/**
* The maximum number of sources for which AST structures should be kept in
* the cache.
* A table mapping caching policies to the cache flush managers.
*/
int _maxCacheSize = 0;
/**
* The policy used to determine which results to remove from the cache.
*/
final CacheRetentionPolicy _retentionPolicy;
final HashMap<ResultCachingPolicy, CacheFlushManager> _flushManagerMap =
new HashMap<ResultCachingPolicy, CacheFlushManager>();
/**
* A table mapping the targets belonging to this partition to the information
@ -567,38 +632,11 @@ abstract class CachePartition {
HashMap<AnalysisTarget, CacheEntry> _targetMap =
new HashMap<AnalysisTarget, CacheEntry>();
/**
* A list containing the most recently accessed targets with the most recently
* used at the end of the list. When more targets are added than the maximum
* allowed then the least recently used target will be removed and will have
* it's cached AST structure flushed.
*/
List<AnalysisTarget> _recentlyUsed = <AnalysisTarget>[];
/**
* Initialize a newly created cache partition, belonging to the given
* [context]. The partition will maintain at most [_maxCacheSize] AST
* structures in the cache, using the [_retentionPolicy] to determine which
* AST structures to flush.
* [context].
*/
CachePartition(this.context, this._maxCacheSize, this._retentionPolicy);
/**
* Return the number of entries in this partition that have an AST associated
* with them.
*/
int get astSize {
int astSize = 0;
int count = _recentlyUsed.length;
for (int i = 0; i < count; i++) {
AnalysisTarget target = _recentlyUsed[i];
CacheEntry entry = _targetMap[target];
if (entry.hasAstStructure) {
astSize++;
}
}
return astSize;
}
CachePartition(this.context);
/**
* Return a table mapping the targets known to the context to the information
@ -609,40 +647,6 @@ abstract class CachePartition {
*/
Map<AnalysisTarget, CacheEntry> get map => _targetMap;
/**
* Return the maximum size of the cache.
*/
int get maxCacheSize => _maxCacheSize;
/**
* Set the maximum size of the cache to the given [size].
*/
void set maxCacheSize(int size) {
_maxCacheSize = size;
while (_recentlyUsed.length > _maxCacheSize) {
if (!_flushAstFromCache()) {
break;
}
}
}
/**
* Record that the AST associated with the given [target] was just read from
* the cache.
*/
void accessedAst(AnalysisTarget target) {
if (_recentlyUsed.remove(target)) {
_recentlyUsed.add(target);
return;
}
while (_recentlyUsed.length >= _maxCacheSize) {
if (!_flushAstFromCache()) {
break;
}
}
_recentlyUsed.add(target);
}
/**
* Return `true` if the given [target] is contained in this partition.
*/
@ -666,6 +670,12 @@ abstract class CachePartition {
* Associate the given [entry] with the given [target].
*/
void put(AnalysisTarget target, CacheEntry entry) {
if (entry._partition != null) {
throw new StateError(
'The entry for $target is already in ${entry._partition}');
}
entry._partition = this;
entry._target = target;
entry.fixExceptionState();
_targetMap[target] = entry;
}
@ -674,16 +684,34 @@ abstract class CachePartition {
* Remove all information related to the given [target] from this cache.
*/
void remove(AnalysisTarget target) {
_recentlyUsed.remove(target);
for (CacheFlushManager flushManager in _flushManagerMap.values) {
flushManager.targetRemoved(target);
}
_targetMap.remove(target);
}
/**
* Record that the AST associated with the given [target] was just removed
* from the cache.
* Records that a value of the result described by the given [descriptor]
* for the given [target] was just read from the cache.
*/
void removedAst(AnalysisTarget target) {
_recentlyUsed.remove(target);
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);
ResultData data = entry._resultMap[result.result];
data.flush();
}
}
/**
@ -691,123 +719,21 @@ abstract class CachePartition {
*/
int size() => _targetMap.length;
/**
* Record that the AST associated with the given [target] was just stored to
* the cache.
*/
void storedAst(AnalysisTarget target) {
if (_recentlyUsed.contains(target)) {
return;
}
while (_recentlyUsed.length >= _maxCacheSize) {
if (!_flushAstFromCache()) {
break;
}
}
_recentlyUsed.add(target);
ResultData _getDataFor(TargetedResult result) {
return _cache._getDataFor(result);
}
/**
* Attempt to flush one AST structure from the cache. Return `true` if a
* structure was flushed.
* Return the [CacheFlushManager] for the given [descriptor], not `null`.
*/
bool _flushAstFromCache() {
AnalysisTarget removedTarget = _removeAstToFlush();
if (removedTarget == null) {
return false;
}
CacheEntry entry = _targetMap[removedTarget];
entry.flushAstStructures();
return true;
CacheFlushManager _getFlushManager(ResultDescriptor descriptor) {
ResultCachingPolicy policy = descriptor.cachingPolicy;
return _flushManagerMap.putIfAbsent(
policy, () => new CacheFlushManager(policy, _isPriorityAnalysisTarget));
}
/**
* Remove and return one target from the list of recently used targets whose
* AST structure can be flushed from the cache, or `null` if none of the
* targets can be removed. The target that will be returned will be the target
* that has been unreferenced for the longest period of time but that is not a
* priority for analysis.
*/
AnalysisTarget _removeAstToFlush() {
int targetToRemove = -1;
for (int i = 0; i < _recentlyUsed.length; i++) {
AnalysisTarget target = _recentlyUsed[i];
RetentionPriority priority =
_retentionPolicy.getAstPriority(target, _targetMap[target]);
if (priority == RetentionPriority.LOW) {
return _recentlyUsed.removeAt(i);
} else if (priority == RetentionPriority.MEDIUM && targetToRemove < 0) {
targetToRemove = i;
}
}
if (targetToRemove < 0) {
// This happens if the retention policy returns a priority of HIGH for all
// of the targets that have been recently used. This is the case, for
// example, when the list of priority sources is bigger than the current
// cache size.
return null;
}
return _recentlyUsed.removeAt(targetToRemove);
}
}
/**
* A policy objecy that determines how important it is for data to be retained
* in the analysis cache.
*/
abstract class CacheRetentionPolicy {
/**
* Return the priority of retaining the AST structure for the given [target]
* in the given [entry].
*/
// TODO(brianwilkerson) Find a more general mechanism, probably based on task
// descriptors, to determine which data is still needed for analysis and which
// can be removed from the cache. Ideally we could (a) remove the need for
// this class and (b) be able to flush all result data (not just AST's).
RetentionPriority getAstPriority(AnalysisTarget target, CacheEntry entry);
}
/**
* A retention policy that will keep AST's in the cache if there is analysis
* information that needs to be computed for a source, where the computation is
* dependent on having the AST.
*/
class DefaultRetentionPolicy implements CacheRetentionPolicy {
/**
* An instance of this class that can be shared.
*/
static const DefaultRetentionPolicy POLICY = const DefaultRetentionPolicy();
/**
* Initialize a newly created instance of this class.
*/
const DefaultRetentionPolicy();
// TODO(brianwilkerson) Implement or delete this.
// /**
// * Return `true` if there is analysis information in the given entry that needs to be
// * computed, where the computation is dependent on having the AST.
// *
// * @param dartEntry the entry being tested
// * @return `true` if there is analysis information that needs to be computed from the AST
// */
// bool astIsNeeded(DartEntry dartEntry) =>
// dartEntry.hasInvalidData(DartEntry.HINTS) ||
// dartEntry.hasInvalidData(DartEntry.LINTS) ||
// dartEntry.hasInvalidData(DartEntry.VERIFICATION_ERRORS) ||
// dartEntry.hasInvalidData(DartEntry.RESOLUTION_ERRORS);
@override
RetentionPriority getAstPriority(AnalysisTarget target, CacheEntry entry) {
// TODO(brianwilkerson) Implement or replace this.
// if (sourceEntry is DartEntry) {
// DartEntry dartEntry = sourceEntry;
// if (astIsNeeded(dartEntry)) {
// return RetentionPriority.MEDIUM;
// }
// }
// return RetentionPriority.LOW;
return RetentionPriority.MEDIUM;
bool _isPriorityAnalysisTarget(AnalysisTarget target) {
return context.priorityTargets.contains(target);
}
}
@ -865,12 +791,20 @@ class ResultData {
dependentResults.add(result);
}
/**
* Flush this value.
*/
void flush() {
state = CacheState.FLUSHED;
value = descriptor.defaultValue;
}
/**
* Invalidate this [ResultData] that corresponds to [thisResult] and
* propagate invalidation to the results that depend on this one.
*/
void invalidate(
AnalysisCache cache, TargetedResult thisResult, CacheState newState) {
void invalidate(CachePartition partition, TargetedResult thisResult,
CacheState newState) {
// Invalidate this result.
state = newState;
value = descriptor.defaultValue;
@ -878,15 +812,15 @@ class ResultData {
List<TargetedResult> dependedOnResults = this.dependedOnResults;
this.dependedOnResults = <TargetedResult>[];
dependedOnResults.forEach((TargetedResult dependedOnResult) {
ResultData data = cache._getDataFor(dependedOnResult);
ResultData data = partition._getDataFor(dependedOnResult);
data.removeDependentResult(thisResult);
});
// Invalidate results that depend on this result.
List<TargetedResult> dependentResults = this.dependentResults;
this.dependentResults = <TargetedResult>[];
dependentResults.forEach((TargetedResult dependentResult) {
ResultData data = cache._getDataFor(dependentResult);
data.invalidate(cache, dependentResult, newState);
ResultData data = partition._getDataFor(dependentResult);
data.invalidate(partition, dependentResult, newState);
});
}
@ -900,15 +834,15 @@ class ResultData {
/**
* Set the [dependedOn] on which this result depends.
*/
void setDependedOnResults(AnalysisCache cache, TargetedResult thisResult,
void setDependedOnResults(CachePartition partition, TargetedResult thisResult,
List<TargetedResult> dependedOn) {
dependedOnResults.forEach((TargetedResult dependedOnResult) {
ResultData data = cache._getDataFor(dependedOnResult);
ResultData data = partition._getDataFor(dependedOnResult);
data.removeDependentResult(thisResult);
});
dependedOnResults = dependedOn;
dependedOnResults.forEach((TargetedResult dependentResult) {
ResultData data = cache._getDataFor(dependentResult);
ResultData data = partition._getDataFor(dependentResult);
data.addDependentResult(thisResult);
});
}
@ -920,11 +854,9 @@ class ResultData {
class SdkCachePartition extends CachePartition {
/**
* Initialize a newly created cache partition, belonging to the given
* [context]. The partition will maintain at most [maxCacheSize] AST
* structures in the cache.
* [context].
*/
SdkCachePartition(InternalAnalysisContext context, int maxCacheSize)
: super(context, maxCacheSize, DefaultRetentionPolicy.POLICY);
SdkCachePartition(InternalAnalysisContext context) : super(context);
@override
bool contains(AnalysisTarget target) {
@ -979,13 +911,9 @@ class TargetedResult {
class UniversalCachePartition extends CachePartition {
/**
* Initialize a newly created cache partition, belonging to the given
* [context]. The partition will maintain at most [maxCacheSize] AST
* structures in the cache, using the [retentionPolicy] to determine which
* AST structures to flush.
* [context].
*/
UniversalCachePartition(InternalAnalysisContext context, int maxCacheSize,
CacheRetentionPolicy retentionPolicy)
: super(context, maxCacheSize, retentionPolicy);
UniversalCachePartition(InternalAnalysisContext context) : super(context);
@override
bool contains(AnalysisTarget target) => true;

View file

@ -181,9 +181,7 @@ class AnalysisContextImpl implements InternalAnalysisContext {
* Initialize a newly created analysis context.
*/
AnalysisContextImpl() {
_privatePartition = new cache.UniversalCachePartition(this,
AnalysisOptionsImpl.DEFAULT_CACHE_SIZE,
new ContextRetentionPolicy(this));
_privatePartition = new cache.UniversalCachePartition(this);
_cache = createCacheFromSourceFactory(null);
_taskManager = AnalysisEngine.instance.taskManager;
_driver = new AnalysisDriver(_taskManager, this);
@ -210,7 +208,6 @@ class AnalysisContextImpl implements InternalAnalysisContext {
int cacheSize = options.cacheSize;
if (this._options.cacheSize != cacheSize) {
this._options.cacheSize = cacheSize;
_privatePartition.maxCacheSize = cacheSize;
}
this._options.analyzeFunctionBodiesPredicate =
options.analyzeFunctionBodiesPredicate;
@ -369,7 +366,8 @@ class AnalysisContextImpl implements InternalAnalysisContext {
bool hintsEnabled = _options.hint;
bool lintsEnabled = _options.lint;
MapIterator<AnalysisTarget, cache.CacheEntry> iterator = _privatePartition.iterator();
MapIterator<AnalysisTarget, cache.CacheEntry> iterator =
_privatePartition.iterator();
while (iterator.moveNext()) {
AnalysisTarget target = iterator.key;
if (target is Source) {
@ -841,7 +839,8 @@ class AnalysisContextImpl implements InternalAnalysisContext {
return <Source>[source];
} else if (kind == SourceKind.PART) {
List<Source> libraries = <Source>[];
MapIterator<AnalysisTarget, cache.CacheEntry> iterator = _cache.iterator();
MapIterator<AnalysisTarget, cache.CacheEntry> iterator =
_cache.iterator();
while (iterator.moveNext()) {
AnalysisTarget target = iterator.key;
if (target is Source && getKindOf(target) == SourceKind.LIBRARY) {
@ -1964,57 +1963,11 @@ class AnalysisContextImpl implements InternalAnalysisContext {
}
}
/**
* A retention policy used by an analysis context.
*/
class ContextRetentionPolicy implements cache.CacheRetentionPolicy {
/**
* The context associated with this policy.
*/
final AnalysisContextImpl context;
/**
* Initialize a newly created policy to be associated with the given
* [context].
*/
ContextRetentionPolicy(this.context);
@override
RetentionPriority getAstPriority(
AnalysisTarget target, cache.CacheEntry entry) {
int priorityCount = context._priorityOrder.length;
for (int i = 0; i < priorityCount; i++) {
if (target == context._priorityOrder[i]) {
return RetentionPriority.HIGH;
}
}
if (_astIsNeeded(entry)) {
return RetentionPriority.MEDIUM;
}
return RetentionPriority.LOW;
}
bool _astIsNeeded(cache.CacheEntry entry) =>
entry.isInvalid(BUILD_FUNCTION_TYPE_ALIASES_ERRORS) ||
entry.isInvalid(BUILD_LIBRARY_ERRORS) ||
entry.isInvalid(CONSTRUCTORS_ERRORS) ||
entry.isInvalid(HINTS) ||
//entry.isInvalid(LINTS) ||
entry.isInvalid(RESOLVE_REFERENCES_ERRORS) ||
entry.isInvalid(RESOLVE_TYPE_NAMES_ERRORS) ||
entry.isInvalid(VERIFY_ERRORS);
}
/**
* An object that manages the partitions that can be shared between analysis
* contexts.
*/
class PartitionManager {
/**
* The default cache size for a Dart SDK partition.
*/
static int _DEFAULT_SDK_CACHE_SIZE = 256;
/**
* A table mapping SDK's to the partitions used for those SDK's.
*/
@ -2042,8 +1995,7 @@ class PartitionManager {
// Check cache for an existing partition.
cache.SdkCachePartition partition = _sdkPartitions[sdk];
if (partition == null) {
partition =
new cache.SdkCachePartition(sdkContext, _DEFAULT_SDK_CACHE_SIZE);
partition = new cache.SdkCachePartition(sdkContext);
_sdkPartitions[sdk] = partition;
}
return partition;

View file

@ -20,10 +20,17 @@ import 'package:analyzer/src/generated/sdk.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/task/general.dart';
import 'package:analyzer/src/task/incremental_element_builder.dart';
import 'package:analyzer/src/task/model.dart';
import 'package:analyzer/task/dart.dart';
import 'package:analyzer/task/general.dart';
import 'package:analyzer/task/model.dart';
/**
* The [ResultCachingPolicy] for ASTs.
*/
const ResultCachingPolicy AST_CACHING_POLICY =
const SimpleResultCachingPolicy(256, 64);
/**
* The errors produced while resolving a library directives.
*
@ -64,7 +71,8 @@ final ResultDescriptor<List<AnalysisError>> BUILD_LIBRARY_ERRORS =
* The [ClassElement]s of a [LibrarySpecificUnit].
*/
final ListResultDescriptor<ClassElement> CLASS_ELEMENTS =
new ListResultDescriptor<ClassElement>('CLASS_ELEMENTS', null);
new ListResultDescriptor<ClassElement>('CLASS_ELEMENTS', null,
cachingPolicy: ELEMENT_CACHING_POLICY);
/**
* The element model associated with a single compilation unit.
@ -73,7 +81,8 @@ final ListResultDescriptor<ClassElement> CLASS_ELEMENTS =
*/
final ResultDescriptor<CompilationUnitElement> COMPILATION_UNIT_ELEMENT =
new ResultDescriptor<CompilationUnitElement>(
'COMPILATION_UNIT_ELEMENT', null);
'COMPILATION_UNIT_ELEMENT', null,
cachingPolicy: ELEMENT_CACHING_POLICY);
/**
* The [ConstructorElement]s of a [ClassElement].
@ -92,6 +101,12 @@ final ResultDescriptor<List<AnalysisError>> CONSTRUCTORS_ERRORS =
new ResultDescriptor<List<AnalysisError>>(
'CONSTRUCTORS_ERRORS', AnalysisError.NO_ERRORS);
/**
* The [ResultCachingPolicy] for [Element]s.
*/
const ResultCachingPolicy ELEMENT_CACHING_POLICY =
const SimpleResultCachingPolicy(-1, -1);
/**
* The sources representing the export closure of a library.
* The [Source]s include only library sources, not their units.
@ -130,7 +145,8 @@ final ListResultDescriptor<Source> IMPORT_SOURCE_CLOSURE =
* The result is only available for targets representing a Dart library.
*/
final ResultDescriptor<LibraryElement> LIBRARY_ELEMENT1 =
new ResultDescriptor<LibraryElement>('LIBRARY_ELEMENT1', null);
new ResultDescriptor<LibraryElement>('LIBRARY_ELEMENT1', null,
cachingPolicy: ELEMENT_CACHING_POLICY);
/**
* The partial [LibraryElement] associated with a library.
@ -141,7 +157,8 @@ final ResultDescriptor<LibraryElement> LIBRARY_ELEMENT1 =
* The result is only available for targets representing a Dart library.
*/
final ResultDescriptor<LibraryElement> LIBRARY_ELEMENT2 =
new ResultDescriptor<LibraryElement>('LIBRARY_ELEMENT2', null);
new ResultDescriptor<LibraryElement>('LIBRARY_ELEMENT2', null,
cachingPolicy: ELEMENT_CACHING_POLICY);
/**
* The partial [LibraryElement] associated with a library.
@ -151,7 +168,8 @@ final ResultDescriptor<LibraryElement> LIBRARY_ELEMENT2 =
* The result is only available for targets representing a Dart library.
*/
final ResultDescriptor<LibraryElement> LIBRARY_ELEMENT3 =
new ResultDescriptor<LibraryElement>('LIBRARY_ELEMENT3', null);
new ResultDescriptor<LibraryElement>('LIBRARY_ELEMENT3', null,
cachingPolicy: ELEMENT_CACHING_POLICY);
/**
* The partial [LibraryElement] associated with a library.
@ -165,7 +183,8 @@ final ResultDescriptor<LibraryElement> LIBRARY_ELEMENT3 =
* The result is only available for targets representing a Dart library.
*/
final ResultDescriptor<LibraryElement> LIBRARY_ELEMENT4 =
new ResultDescriptor<LibraryElement>('LIBRARY_ELEMENT4', null);
new ResultDescriptor<LibraryElement>('LIBRARY_ELEMENT4', null,
cachingPolicy: ELEMENT_CACHING_POLICY);
/**
* The partial [LibraryElement] associated with a library.
@ -175,7 +194,8 @@ final ResultDescriptor<LibraryElement> LIBRARY_ELEMENT4 =
* The result is only available for targets representing a Dart library.
*/
final ResultDescriptor<LibraryElement> LIBRARY_ELEMENT5 =
new ResultDescriptor<LibraryElement>('LIBRARY_ELEMENT5', null);
new ResultDescriptor<LibraryElement>('LIBRARY_ELEMENT5', null,
cachingPolicy: ELEMENT_CACHING_POLICY);
/**
* The errors produced while parsing a compilation unit.
@ -220,7 +240,8 @@ final ResultDescriptor<List<AnalysisError>> RESOLVE_TYPE_NAMES_ERRORS =
* The result is only available for targets representing a unit.
*/
final ResultDescriptor<CompilationUnit> RESOLVED_UNIT1 =
new ResultDescriptor<CompilationUnit>('RESOLVED_UNIT1', null);
new ResultDescriptor<CompilationUnit>('RESOLVED_UNIT1', null,
cachingPolicy: AST_CACHING_POLICY);
/**
* The partially resolved [CompilationUnit] associated with a unit.
@ -230,7 +251,8 @@ final ResultDescriptor<CompilationUnit> RESOLVED_UNIT1 =
* The result is only available for targets representing a unit.
*/
final ResultDescriptor<CompilationUnit> RESOLVED_UNIT2 =
new ResultDescriptor<CompilationUnit>('RESOLVED_UNIT2', null);
new ResultDescriptor<CompilationUnit>('RESOLVED_UNIT2', null,
cachingPolicy: AST_CACHING_POLICY);
/**
* The partially resolved [CompilationUnit] associated with a unit.
@ -240,7 +262,8 @@ final ResultDescriptor<CompilationUnit> RESOLVED_UNIT2 =
* The result is only available for targets representing a unit.
*/
final ResultDescriptor<CompilationUnit> RESOLVED_UNIT3 =
new ResultDescriptor<CompilationUnit>('RESOLVED_UNIT3', null);
new ResultDescriptor<CompilationUnit>('RESOLVED_UNIT3', null,
cachingPolicy: AST_CACHING_POLICY);
/**
* The partially resolved [CompilationUnit] associated with a unit.
@ -250,7 +273,8 @@ final ResultDescriptor<CompilationUnit> RESOLVED_UNIT3 =
* The result is only available for targets representing a unit.
*/
final ResultDescriptor<CompilationUnit> RESOLVED_UNIT4 =
new ResultDescriptor<CompilationUnit>('RESOLVED_UNIT4', null);
new ResultDescriptor<CompilationUnit>('RESOLVED_UNIT4', null,
cachingPolicy: AST_CACHING_POLICY);
/**
* The partially resolved [CompilationUnit] associated with a unit.
@ -260,7 +284,8 @@ final ResultDescriptor<CompilationUnit> RESOLVED_UNIT4 =
* The result is only available for targets representing a unit.
*/
final ResultDescriptor<CompilationUnit> RESOLVED_UNIT5 =
new ResultDescriptor<CompilationUnit>('RESOLVED_UNIT5', null);
new ResultDescriptor<CompilationUnit>('RESOLVED_UNIT5', null,
cachingPolicy: AST_CACHING_POLICY);
/**
* The errors produced while scanning a compilation unit.
@ -273,6 +298,12 @@ final ResultDescriptor<List<AnalysisError>> SCAN_ERRORS =
new ResultDescriptor<List<AnalysisError>>(
'SCAN_ERRORS', AnalysisError.NO_ERRORS, contributesTo: DART_ERRORS);
/**
* The [ResultCachingPolicy] for [TOKEN_STREAM].
*/
const ResultCachingPolicy TOKEN_STREAM_CACHING_POLICY =
const SimpleResultCachingPolicy(1, 1);
/**
* The [TypeProvider] of the context.
*/
@ -283,13 +314,15 @@ final ResultDescriptor<TypeProvider> TYPE_PROVIDER =
* The [UsedImportedElements] of a [LibrarySpecificUnit].
*/
final ResultDescriptor<UsedImportedElements> USED_IMPORTED_ELEMENTS =
new ResultDescriptor<UsedImportedElements>('USED_IMPORTED_ELEMENTS', null);
new ResultDescriptor<UsedImportedElements>('USED_IMPORTED_ELEMENTS', null,
cachingPolicy: ELEMENT_CACHING_POLICY);
/**
* The [UsedLocalElements] of a [LibrarySpecificUnit].
*/
final ResultDescriptor<UsedLocalElements> USED_LOCAL_ELEMENTS =
new ResultDescriptor<UsedLocalElements>('USED_LOCAL_ELEMENTS', null);
new ResultDescriptor<UsedLocalElements>('USED_LOCAL_ELEMENTS', null,
cachingPolicy: ELEMENT_CACHING_POLICY);
/**
* The errors produced while verifying a compilation unit.

View file

@ -8,6 +8,12 @@ import 'package:analyzer/src/generated/engine.dart' hide AnalysisTask;
import 'package:analyzer/src/task/inputs.dart';
import 'package:analyzer/task/model.dart';
/**
* The default [ResultCachingPolicy], results are never flushed.
*/
const ResultCachingPolicy DEFAULT_CACHING_POLICY =
const SimpleResultCachingPolicy(-1, -1);
/**
* A concrete implementation of a [CompositeResultDescriptor].
*/
@ -42,8 +48,10 @@ class ListResultDescriptorImpl<E> extends ResultDescriptorImpl<List<E>>
* contribute to it.
*/
ListResultDescriptorImpl(String name, List<E> defaultValue,
{CompositeResultDescriptor contributesTo})
: super(name, defaultValue, contributesTo: contributesTo);
{CompositeResultDescriptor contributesTo,
ResultCachingPolicy<List<E>> cachingPolicy: DEFAULT_CACHING_POLICY})
: super(name, defaultValue,
contributesTo: contributesTo, cachingPolicy: cachingPolicy);
@override
ListTaskInput<E> of(AnalysisTarget target) =>
@ -64,13 +72,19 @@ class ResultDescriptorImpl<V> implements ResultDescriptor<V> {
*/
final V defaultValue;
/**
* The caching policy for results described by this descriptor.
*/
final ResultCachingPolicy<V> cachingPolicy;
/**
* Initialize a newly created analysis result to have the given [name] and
* [defaultValue]. If a composite result is specified, then this result will
* contribute to it.
*/
ResultDescriptorImpl(this.name, this.defaultValue,
{CompositeResultDescriptor contributesTo}) {
{CompositeResultDescriptor contributesTo,
this.cachingPolicy: DEFAULT_CACHING_POLICY}) {
if (contributesTo is CompositeResultDescriptorImpl) {
contributesTo.recordContributor(this);
}
@ -84,6 +98,23 @@ class ResultDescriptorImpl<V> implements ResultDescriptor<V> {
String toString() => name;
}
/**
* A simple [ResultCachingPolicy] implementation that consider all the objects
* to be of the size `1`.
*/
class SimpleResultCachingPolicy<T> implements ResultCachingPolicy<T> {
@override
final int maxActiveSize;
@override
final int maxIdleSize;
const SimpleResultCachingPolicy(this.maxActiveSize, this.maxIdleSize);
@override
int measure(T object) => 1;
}
/**
* A concrete implementation of a [TaskDescriptor].
*/

View file

@ -10,6 +10,7 @@ import 'package:analyzer/src/generated/error.dart';
import 'package:analyzer/src/generated/scanner.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/utilities_general.dart';
import 'package:analyzer/src/task/dart.dart';
import 'package:analyzer/task/model.dart';
/**
@ -102,7 +103,8 @@ final ResultDescriptor<LibraryElement> LIBRARY_ELEMENT =
* The result is only available for targets representing a Dart compilation unit.
*/
final ResultDescriptor<CompilationUnit> PARSED_UNIT =
new ResultDescriptor<CompilationUnit>('PARSED_UNIT', null);
new ResultDescriptor<CompilationUnit>('PARSED_UNIT', null,
cachingPolicy: AST_CACHING_POLICY);
/**
* The resolved [CompilationUnit] associated with a unit.
@ -110,7 +112,8 @@ final ResultDescriptor<CompilationUnit> PARSED_UNIT =
* The result is only available for targets representing a unit.
*/
final ResultDescriptor<CompilationUnit> RESOLVED_UNIT =
new ResultDescriptor<CompilationUnit>('RESOLVED_UNIT', null);
new ResultDescriptor<CompilationUnit>('RESOLVED_UNIT', null,
cachingPolicy: AST_CACHING_POLICY);
/**
* The token stream produced while scanning a compilation unit.
@ -120,8 +123,8 @@ final ResultDescriptor<CompilationUnit> RESOLVED_UNIT =
*
* The result is only available for targets representing a Dart compilation unit.
*/
final ResultDescriptor<Token> TOKEN_STREAM =
new ResultDescriptor<Token>('TOKEN_STREAM', null);
final ResultDescriptor<Token> TOKEN_STREAM = new ResultDescriptor<Token>(
'TOKEN_STREAM', null, cachingPolicy: TOKEN_STREAM_CACHING_POLICY);
/**
* The sources of the Dart files that a library consists of.

View file

@ -279,7 +279,8 @@ abstract class ListResultDescriptor<E> implements ResultDescriptor<List<E>> {
* composite result is specified, then this result will contribute to it.
*/
factory ListResultDescriptor(String name, List<E> defaultValue,
{CompositeResultDescriptor<List<E>> contributesTo}) = ListResultDescriptorImpl<E>;
{CompositeResultDescriptor<List<E>> contributesTo,
ResultCachingPolicy<List<E>> cachingPolicy}) = ListResultDescriptorImpl<E>;
@override
ListTaskInput<E> of(AnalysisTarget target);
@ -337,6 +338,32 @@ abstract class MapTaskInput<K, V> extends TaskInput<Map<K, V>> {
BinaryFunction<K, dynamic /*element of V*/, dynamic /*<E>*/ > mapper);
}
/**
* A policy object that can compute sizes of results and provide the maximum
* active and idle sizes that can be kept in the cache.
*
* All the [ResultDescriptor]s with the same [ResultCachingPolicy] instance
* share the same total size in a cache.
*/
abstract class ResultCachingPolicy<T> {
/**
* Return the maximum total size of results that can be kept in the cache
* while analysis is in progress.
*/
int get maxActiveSize;
/**
* Return the maximum total size of results that can be kept in the cache
* while analysis is idle.
*/
int get maxIdleSize;
/**
* Return the size of the given [object].
*/
int measure(T object);
}
/**
* A description of an analysis result that can be computed by an [AnalysisTask].
*
@ -346,9 +373,19 @@ abstract class ResultDescriptor<V> {
/**
* Initialize a newly created analysis result to have the given [name]. If a
* composite result is specified, then this result will contribute to it.
*
* The given [cachingPolicy] is used to limit the total size of results
* described by this descriptor. If no policy is specified, the results are
* never evicted from the cache, and removed only when they are invalidated.
*/
factory ResultDescriptor(String name, V defaultValue,
{CompositeResultDescriptor<V> contributesTo}) = ResultDescriptorImpl;
{CompositeResultDescriptor<V> contributesTo,
ResultCachingPolicy<V> cachingPolicy}) = ResultDescriptorImpl;
/**
* Return the caching policy for results described by this descriptor.
*/
ResultCachingPolicy<V> get cachingPolicy;
/**
* Return the default value for results described by this descriptor.

View file

@ -5485,6 +5485,7 @@ class TestAnalysisContext implements InternalAnalysisContext {
fail("Unexpected invocation of getPrioritySources");
return null;
}
@override
List<AnalysisTarget> get priorityTargets {
fail("Unexpected invocation of visitCacheItems");
@ -5502,7 +5503,6 @@ class TestAnalysisContext implements InternalAnalysisContext {
fail("Unexpected invocation of getSourceFactory");
return null;
}
@override
void set sourceFactory(SourceFactory factory) {
fail("Unexpected invocation of setSourceFactory");
@ -5750,6 +5750,7 @@ class TestAnalysisContext implements InternalAnalysisContext {
void recordLibraryElements(Map<Source, LibraryElement> elementMap) {
fail("Unexpected invocation of recordLibraryElements");
}
@override
void removeListener(AnalysisListener listener) {
fail("Unexpected invocation of removeListener");

View file

@ -5,14 +5,19 @@
library test.src.task.driver_test;
import 'package:analyzer/src/context/cache.dart';
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/engine.dart'
show AnalysisContext, CacheState, RetentionPriority;
show
AnalysisContext,
CacheState,
InternalAnalysisContext,
RetentionPriority;
import 'package:analyzer/src/generated/java_engine.dart';
import 'package:analyzer/src/generated/sdk_io.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/utilities_collection.dart';
import 'package:analyzer/src/task/model.dart';
import 'package:analyzer/task/model.dart';
import 'package:typed_mock/typed_mock.dart';
import 'package:unittest/unittest.dart';
import '../../generated/engine_test.dart';
@ -23,6 +28,7 @@ main() {
groupSep = ' | ';
runReflectiveTests(AnalysisCacheTest);
runReflectiveTests(CacheEntryTest);
runReflectiveTests(CacheFlushManagerTest);
runReflectiveTests(SdkCachePartitionTest);
runReflectiveTests(UniversalCachePartitionTest);
runReflectiveTests(ResultDataTest);
@ -30,35 +36,12 @@ main() {
AnalysisCache createCache({AnalysisContext context,
RetentionPriority policy: RetentionPriority.LOW}) {
CachePartition partition = new UniversalCachePartition(
context, 8, new TestCacheRetentionPolicy(policy));
CachePartition partition = new UniversalCachePartition(context);
return new AnalysisCache(<CachePartition>[partition]);
}
@reflectiveTest
class AnalysisCacheTest extends EngineTestCase {
void test_astSize_empty() {
AnalysisCache cache = createCache();
expect(cache.astSize, 0);
}
void test_astSize_nonEmpty() {
ResultDescriptor result = new ResultDescriptor('test', null);
AstNode node = new NullLiteral(null);
AnalysisCache cache = createCache();
AnalysisTarget target1 = new TestSource('/test1.dart');
CacheEntry entry1 = new CacheEntry();
entry1.setValue(result, node, TargetedResult.EMPTY_LIST, null);
AnalysisTarget target2 = new TestSource('/test2.dart');
CacheEntry entry2 = new CacheEntry();
entry2.setValue(result, node, TargetedResult.EMPTY_LIST, null);
cache.put(target1, entry1);
cache.accessedAst(target1);
cache.put(target2, entry2);
cache.accessedAst(target2);
expect(cache.astSize, 2);
}
void test_creation() {
expect(createCache(), isNotNull);
}
@ -103,38 +86,6 @@ class AnalysisCacheTest extends EngineTestCase {
cache.remove(target);
}
void test_setMaxCacheSize() {
CachePartition partition = new UniversalCachePartition(
null, 8, new TestCacheRetentionPolicy(RetentionPriority.MEDIUM));
AnalysisCache cache = new AnalysisCache(<CachePartition>[partition]);
ResultDescriptor result = new ResultDescriptor('test', null);
AstNode node = new NullLiteral(null);
int size = 6;
for (int i = 0; i < size; i++) {
AnalysisTarget target = new TestSource("/test$i.dart");
CacheEntry entry = new CacheEntry();
entry.setValue(result, node, TargetedResult.EMPTY_LIST, null);
cache.put(target, entry);
cache.accessedAst(target);
}
void _assertNonFlushedCount(int expectedCount, AnalysisCache cache) {
int nonFlushedCount = 0;
MapIterator<AnalysisTarget, CacheEntry> iterator = cache.iterator();
while (iterator.moveNext()) {
if (iterator.value.getState(result) != CacheState.FLUSHED) {
nonFlushedCount++;
}
}
expect(nonFlushedCount, expectedCount);
}
_assertNonFlushedCount(size, cache);
int newSize = size - 2;
partition.maxCacheSize = newSize;
_assertNonFlushedCount(newSize, cache);
}
void test_size() {
AnalysisCache cache = createCache();
int size = 4;
@ -148,6 +99,16 @@ class AnalysisCacheTest extends EngineTestCase {
@reflectiveTest
class CacheEntryTest extends EngineTestCase {
InternalAnalysisContext context;
AnalysisCache cache;
void setUp() {
context = new _InternalAnalysisContextMock();
when(context.priorityTargets).thenReturn([]);
cache = createCache(context: context);
// when(context.analysisCache).thenReturn(cache);
}
test_explicitlyAdded() {
CacheEntry entry = new CacheEntry();
expect(entry.explicitlyAdded, false);
@ -186,14 +147,11 @@ class CacheEntryTest extends EngineTestCase {
expect(entry.exception, isNull);
}
test_flushAstStructures() {
ResultDescriptor result = new ResultDescriptor('test', null);
test_getMemento_noResult() {
String defaultValue = 'value';
ResultDescriptor result = new ResultDescriptor('test', defaultValue);
CacheEntry entry = new CacheEntry();
entry.setValue(
result, new NullLiteral(null), TargetedResult.EMPTY_LIST, null);
expect(entry.hasAstStructure, true);
entry.flushAstStructures();
expect(entry.hasAstStructure, false);
expect(entry.getMemento(result), null);
}
test_getState() {
@ -209,24 +167,34 @@ class CacheEntryTest extends EngineTestCase {
expect(entry.getValue(result), defaultValue);
}
test_getMemento_noResult() {
String defaultValue = 'value';
ResultDescriptor result = new ResultDescriptor('test', defaultValue);
test_getValue_flushResults() {
ResultCachingPolicy cachingPolicy = new SimpleResultCachingPolicy(2, 2);
ResultDescriptor descriptor1 =
new ResultDescriptor('result1', null, cachingPolicy: cachingPolicy);
ResultDescriptor descriptor2 =
new ResultDescriptor('result2', null, cachingPolicy: cachingPolicy);
ResultDescriptor descriptor3 =
new ResultDescriptor('result3', null, cachingPolicy: cachingPolicy);
AnalysisTarget target = new TestSource();
CacheEntry entry = new CacheEntry();
expect(entry.getMemento(result), null);
}
test_hasAstStructure_false() {
CacheEntry entry = new CacheEntry();
expect(entry.hasAstStructure, false);
}
test_hasAstStructure_true() {
ResultDescriptor result = new ResultDescriptor('test', null);
CacheEntry entry = new CacheEntry();
entry.setValue(
result, new NullLiteral(null), TargetedResult.EMPTY_LIST, null);
expect(entry.hasAstStructure, true);
cache.put(target, entry);
{
entry.setValue(descriptor1, 1, TargetedResult.EMPTY_LIST, null);
expect(entry.getState(descriptor1), CacheState.VALID);
}
{
entry.setValue(descriptor2, 2, TargetedResult.EMPTY_LIST, null);
expect(entry.getState(descriptor1), CacheState.VALID);
expect(entry.getState(descriptor2), CacheState.VALID);
}
// get descriptor1, so that descriptor2 will be flushed
entry.getValue(descriptor1);
{
entry.setValue(descriptor3, 3, TargetedResult.EMPTY_LIST, null);
expect(entry.getState(descriptor1), CacheState.VALID);
expect(entry.getState(descriptor2), CacheState.FLUSHED);
expect(entry.getState(descriptor3), CacheState.VALID);
}
}
test_hasErrorState_false() {
@ -274,7 +242,6 @@ class CacheEntryTest extends EngineTestCase {
}
test_setErrorState_invalidateDependent() {
AnalysisCache cache = createCache();
AnalysisTarget target = new TestSource();
CacheEntry entry = new CacheEntry();
cache.put(target, entry);
@ -388,7 +355,6 @@ class CacheEntryTest extends EngineTestCase {
}
test_setState_invalid_invalidateDependent() {
AnalysisCache cache = createCache();
AnalysisTarget target = new TestSource();
CacheEntry entry = new CacheEntry();
cache.put(target, entry);
@ -438,8 +404,35 @@ class CacheEntryTest extends EngineTestCase {
expect(entry.getMemento(result), memento);
}
test_setValue_flushResults() {
ResultCachingPolicy cachingPolicy = new SimpleResultCachingPolicy(2, 2);
ResultDescriptor descriptor1 =
new ResultDescriptor('result1', null, cachingPolicy: cachingPolicy);
ResultDescriptor descriptor2 =
new ResultDescriptor('result2', null, cachingPolicy: cachingPolicy);
ResultDescriptor descriptor3 =
new ResultDescriptor('result3', null, cachingPolicy: cachingPolicy);
AnalysisTarget target = new TestSource();
CacheEntry entry = new CacheEntry();
cache.put(target, entry);
{
entry.setValue(descriptor1, 1, TargetedResult.EMPTY_LIST, null);
expect(entry.getState(descriptor1), CacheState.VALID);
}
{
entry.setValue(descriptor2, 2, TargetedResult.EMPTY_LIST, null);
expect(entry.getState(descriptor1), CacheState.VALID);
expect(entry.getState(descriptor2), CacheState.VALID);
}
{
entry.setValue(descriptor3, 3, TargetedResult.EMPTY_LIST, null);
expect(entry.getState(descriptor1), CacheState.FLUSHED);
expect(entry.getState(descriptor2), CacheState.VALID);
expect(entry.getState(descriptor3), CacheState.VALID);
}
}
test_setValue_invalidateDependent() {
AnalysisCache cache = createCache();
AnalysisTarget target = new TestSource();
CacheEntry entry = new CacheEntry();
cache.put(target, entry);
@ -473,7 +466,6 @@ class CacheEntryTest extends EngineTestCase {
}
test_setValue_invalidateDependent2() {
AnalysisCache cache = createCache();
AnalysisTarget target1 = new TestSource('a');
AnalysisTarget target2 = new TestSource('b');
CacheEntry entry1 = new CacheEntry();
@ -517,8 +509,149 @@ class CacheEntryTest extends EngineTestCase {
}
}
@reflectiveTest
class CacheFlushManagerTest {
CacheFlushManager manager = new CacheFlushManager(
new SimpleResultCachingPolicy(15, 3), (AnalysisTarget target) => false);
test_madeActive() {
manager.madeActive();
expect(manager.maxSize, 15);
}
test_madeIdle() {
manager.madeActive();
AnalysisTarget target = new TestSource();
// prepare TargetedResult(s)
List<TargetedResult> results = <TargetedResult>[];
for (int i = 0; i < 15; i++) {
ResultDescriptor descriptor = new ResultDescriptor('result$i', null);
results.add(new TargetedResult(target, descriptor));
}
// notify about storing TargetedResult(s)
for (TargetedResult result in results) {
manager.resultStored(result, null);
}
expect(manager.recentlyUsed, results);
expect(manager.currentSize, 15);
// make idle
List<TargetedResult> resultsToFlush = manager.madeIdle();
expect(manager.maxSize, 3);
expect(manager.recentlyUsed, results.skip(15 - 3));
expect(resultsToFlush, results.take(15 - 3));
}
test_new() {
expect(manager.maxActiveSize, 15);
expect(manager.maxIdleSize, 3);
expect(manager.maxSize, 3);
expect(manager.currentSize, 0);
expect(manager.recentlyUsed, isEmpty);
}
test_resultAccessed() {
ResultDescriptor descriptor1 = new ResultDescriptor('result1', null);
ResultDescriptor descriptor2 = new ResultDescriptor('result2', null);
ResultDescriptor descriptor3 = new ResultDescriptor('result3', null);
AnalysisTarget target = new TestSource();
TargetedResult result1 = new TargetedResult(target, descriptor1);
TargetedResult result2 = new TargetedResult(target, descriptor2);
TargetedResult result3 = new TargetedResult(target, descriptor3);
manager.resultStored(result1, null);
manager.resultStored(result2, null);
manager.resultStored(result3, null);
expect(manager.currentSize, 3);
expect(manager.recentlyUsed, orderedEquals([result1, result2, result3]));
// access result2
manager.resultAccessed(result2);
expect(manager.currentSize, 3);
expect(manager.recentlyUsed, orderedEquals([result1, result3, result2]));
}
test_resultAccessed_noSuchResult() {
ResultDescriptor descriptor1 = new ResultDescriptor('result1', null);
ResultDescriptor descriptor2 = new ResultDescriptor('result2', null);
ResultDescriptor descriptor3 = new ResultDescriptor('result3', null);
AnalysisTarget target = new TestSource();
TargetedResult result1 = new TargetedResult(target, descriptor1);
TargetedResult result2 = new TargetedResult(target, descriptor2);
TargetedResult result3 = new TargetedResult(target, descriptor3);
manager.resultStored(result1, null);
manager.resultStored(result2, null);
expect(manager.currentSize, 2);
expect(manager.recentlyUsed, orderedEquals([result1, result2]));
// access result3, no-op
manager.resultAccessed(result3);
expect(manager.currentSize, 2);
expect(manager.recentlyUsed, orderedEquals([result1, result2]));
}
test_resultStored() {
ResultDescriptor descriptor1 = new ResultDescriptor('result1', null);
ResultDescriptor descriptor2 = new ResultDescriptor('result2', null);
ResultDescriptor descriptor3 = new ResultDescriptor('result3', null);
ResultDescriptor descriptor4 = new ResultDescriptor('result4', null);
AnalysisTarget target = new TestSource();
TargetedResult result1 = new TargetedResult(target, descriptor1);
TargetedResult result2 = new TargetedResult(target, descriptor2);
TargetedResult result3 = new TargetedResult(target, descriptor3);
TargetedResult result4 = new TargetedResult(target, descriptor4);
manager.resultStored(result1, null);
manager.resultStored(result2, null);
manager.resultStored(result3, null);
expect(manager.currentSize, 3);
expect(manager.recentlyUsed, orderedEquals([result1, result2, result3]));
// store result2 again
{
List<TargetedResult> resultsToFlush = manager.resultStored(result2, null);
expect(resultsToFlush, isEmpty);
expect(manager.currentSize, 3);
expect(manager.recentlyUsed, orderedEquals([result1, result3, result2]));
}
// store result4
{
List<TargetedResult> resultsToFlush = manager.resultStored(result4, null);
expect(resultsToFlush, [result1]);
expect(manager.currentSize, 3);
expect(manager.recentlyUsed, orderedEquals([result3, result2, result4]));
expect(manager.resultSizeMap, {result3: 1, result2: 1, result4: 1});
}
}
test_targetRemoved() {
ResultDescriptor descriptor1 = new ResultDescriptor('result1', null);
ResultDescriptor descriptor2 = new ResultDescriptor('result2', null);
ResultDescriptor descriptor3 = new ResultDescriptor('result3', null);
AnalysisTarget target1 = new TestSource('a.dart');
AnalysisTarget target2 = new TestSource('b.dart');
TargetedResult result1 = new TargetedResult(target1, descriptor1);
TargetedResult result2 = new TargetedResult(target2, descriptor2);
TargetedResult result3 = new TargetedResult(target1, descriptor3);
manager.resultStored(result1, null);
manager.resultStored(result2, null);
manager.resultStored(result3, null);
expect(manager.currentSize, 3);
expect(manager.recentlyUsed, orderedEquals([result1, result2, result3]));
expect(manager.resultSizeMap, {result1: 1, result2: 1, result3: 1});
// remove target1
{
manager.targetRemoved(target1);
expect(manager.currentSize, 1);
expect(manager.recentlyUsed, orderedEquals([result2]));
expect(manager.resultSizeMap, {result2: 1});
}
// remove target2
{
manager.targetRemoved(target2);
expect(manager.currentSize, 0);
expect(manager.recentlyUsed, isEmpty);
expect(manager.resultSizeMap, isEmpty);
}
}
}
abstract class CachePartitionTest extends EngineTestCase {
CachePartition createPartition([CacheRetentionPolicy policy = null]);
CachePartition createPartition();
void test_creation() {
expect(createPartition(), isNotNull);
@ -542,6 +675,15 @@ abstract class CachePartitionTest extends EngineTestCase {
expect(partition.get(target), isNull);
}
void test_put_alreadyInPartition() {
CachePartition partition1 = createPartition();
CachePartition partition2 = createPartition();
AnalysisTarget target = new TestSource();
CacheEntry entry = new CacheEntry();
partition1.put(target, entry);
expect(() => partition2.put(target, entry), throwsStateError);
}
void test_put_noFlush() {
CachePartition partition = createPartition();
AnalysisTarget target = new TestSource();
@ -559,48 +701,6 @@ abstract class CachePartitionTest extends EngineTestCase {
partition.remove(target);
expect(partition.get(target), isNull);
}
void test_setMaxCacheSize() {
CachePartition partition =
createPartition(new TestCacheRetentionPolicy(RetentionPriority.LOW));
ResultDescriptor result = new ResultDescriptor('result', null);
NullLiteral node = new NullLiteral(null);
int size = 6; // Must be <= partition.maxCacheSize
for (int i = 0; i < size; i++) {
AnalysisTarget target = new TestSource("/test$i.dart");
CacheEntry entry = new CacheEntry();
entry.setValue(result, node, TargetedResult.EMPTY_LIST, null);
partition.put(target, entry);
partition.accessedAst(target);
}
void assertNonFlushedCount(int expectedCount, CachePartition partition) {
int nonFlushedCount = 0;
Map<AnalysisTarget, CacheEntry> entryMap = partition.map;
entryMap.values.forEach((CacheEntry entry) {
if (entry.getState(result) != CacheState.FLUSHED) {
nonFlushedCount++;
}
});
expect(nonFlushedCount, expectedCount);
}
assertNonFlushedCount(size, partition);
int newSize = size - 2;
partition.maxCacheSize = newSize;
assertNonFlushedCount(newSize, partition);
}
void test_size() {
CachePartition partition = createPartition();
int size = 4;
for (int i = 0; i < size; i++) {
AnalysisTarget target = new TestSource("/test$i.dart");
partition.put(target, new CacheEntry());
partition.accessedAst(target);
}
expect(partition.size(), size);
}
}
@reflectiveTest
@ -616,8 +716,8 @@ class ResultDataTest extends EngineTestCase {
@reflectiveTest
class SdkCachePartitionTest extends CachePartitionTest {
CachePartition createPartition([CacheRetentionPolicy policy = null]) {
return new SdkCachePartition(null, 8);
CachePartition createPartition() {
return new SdkCachePartition(null);
}
void test_contains_false() {
@ -627,7 +727,7 @@ class SdkCachePartitionTest extends CachePartitionTest {
}
void test_contains_true() {
SdkCachePartition partition = new SdkCachePartition(null, 8);
SdkCachePartition partition = new SdkCachePartition(null);
SourceFactory factory = new SourceFactory(
[new DartUriResolver(DirectoryBasedDartSdk.defaultSdk)]);
AnalysisTarget target = factory.forUri("dart:core");
@ -635,27 +735,20 @@ class SdkCachePartitionTest extends CachePartitionTest {
}
}
@reflectiveTest
class TestCacheRetentionPolicy extends CacheRetentionPolicy {
final RetentionPriority policy;
TestCacheRetentionPolicy([this.policy = RetentionPriority.MEDIUM]);
@override
RetentionPriority getAstPriority(AnalysisTarget target, CacheEntry entry) =>
policy;
}
@reflectiveTest
class UniversalCachePartitionTest extends CachePartitionTest {
CachePartition createPartition([CacheRetentionPolicy policy = null]) {
return new UniversalCachePartition(null, 8, policy);
CachePartition createPartition() {
return new UniversalCachePartition(null);
}
void test_contains() {
UniversalCachePartition partition =
new UniversalCachePartition(null, 8, null);
UniversalCachePartition partition = new UniversalCachePartition(null);
TestSource source = new TestSource();
expect(partition.contains(source), isTrue);
}
}
class _InternalAnalysisContextMock extends TypedMock
implements InternalAnalysisContext {
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}

View file

@ -23,6 +23,7 @@ import 'package:analyzer/src/generated/engine.dart'
AnalysisOptions,
AnalysisOptionsImpl,
AnalysisResult,
CacheState,
ChangeNotice,
ChangeSet,
IncrementalAnalysisCache,
@ -38,6 +39,7 @@ import 'package:analyzer/src/generated/sdk_io.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/source_io.dart';
import 'package:analyzer/src/plugin/engine_plugin.dart';
import 'package:analyzer/task/dart.dart';
import 'package:plugin/manager.dart';
import 'package:unittest/unittest.dart';
import 'package:watcher/src/utils.dart';
@ -282,11 +284,6 @@ import 'libB.dart';''';
expect(_context.computeImportedLibraries(source), hasLength(2));
}
void test_computeKindOf_html() {
Source source = _addSource("/test.html", "");
expect(_context.computeKindOf(source), same(SourceKind.HTML));
}
void fail_computeResolvableCompilationUnit_dart_exception() {
TestSource source = _addSourceWithException("/test.dart");
try {
@ -324,8 +321,7 @@ import 'libB.dart';''';
// Complete all pending analysis tasks and flush the AST so that it won't
// be available immediately.
_performPendingAnalysisTasks();
CacheEntry entry = _context.getReadableSourceEntryOrNull(source);
entry.flushAstStructures();
_flushAst(source);
CancelableFuture<CompilationUnit> future =
_context.computeResolvedCompilationUnitAsync(source, source);
bool completed = false;
@ -512,27 +508,6 @@ class A {
expect(sources[0], source);
}
void test_getKindOf_html() {
Source source = _addSource("/test.html", "");
expect(_context.getKindOf(source), same(SourceKind.HTML));
}
void test_getLibrariesContaining() {
_context = contextWithCore();
_sourceFactory = _context.sourceFactory;
Source librarySource = _addSource("/lib.dart", r'''
library lib;
part 'part.dart';''');
Source partSource = _addSource("/part.dart", "part of lib;");
_context.computeLibraryElement(librarySource);
List<Source> result = _context.getLibrariesContaining(librarySource);
expect(result, hasLength(1));
expect(result[0], librarySource);
result = _context.getLibrariesContaining(partSource);
expect(result, hasLength(1));
expect(result[0], librarySource);
}
void fail_getLibrariesReferencedFromHtml() {
_context = contextWithCore();
_sourceFactory = _context.sourceFactory;
@ -1117,6 +1092,19 @@ library test2;''');
_context.analysisOptions = options;
}
void test_applyChanges_change_flush_element() {
_context = contextWithCore();
_sourceFactory = _context.sourceFactory;
Source librarySource = _addSource("/lib.dart", r'''
library lib;
int a = 0;''');
expect(_context.computeLibraryElement(librarySource), isNotNull);
_context.setContents(librarySource, r'''
library lib;
int aa = 0;''');
expect(_context.getLibraryElement(librarySource), isNull);
}
@override
void tearDown() {
_context = null;
@ -1186,19 +1174,6 @@ library test2;''');
});
}
void test_applyChanges_change_flush_element() {
_context = contextWithCore();
_sourceFactory = _context.sourceFactory;
Source librarySource = _addSource("/lib.dart", r'''
library lib;
int a = 0;''');
expect(_context.computeLibraryElement(librarySource), isNotNull);
_context.setContents(librarySource, r'''
library lib;
int aa = 0;''');
expect(_context.getLibraryElement(librarySource), isNull);
}
Future test_applyChanges_change_multiple() {
_context = contextWithCore();
SourcesChangedListener listener = new SourcesChangedListener();
@ -1346,6 +1321,11 @@ class A {}""");
expect(_context.computeHtmlElement(source), isNull);
}
void test_computeKindOf_html() {
Source source = _addSource("/test.html", "");
expect(_context.computeKindOf(source), same(SourceKind.HTML));
}
void test_computeKindOf_library() {
Source source = _addSource("/test.dart", "library lib;");
expect(_context.computeKindOf(source), same(SourceKind.LIBRARY));
@ -1396,8 +1376,7 @@ main() {}''');
// Complete all pending analysis tasks and flush the AST so that it won't
// be available immediately.
_performPendingAnalysisTasks();
CacheEntry entry = _context.getReadableSourceEntryOrNull(source);
entry.flushAstStructures();
_flushAst(source);
// Dispose of the context.
_context.dispose();
// Any attempt to start an asynchronous computation should return a future
@ -1515,6 +1494,11 @@ main() {}''');
expect(result, hasLength(0));
}
void test_getKindOf_html() {
Source source = _addSource("/test.html", "");
expect(_context.getKindOf(source), same(SourceKind.HTML));
}
void test_getKindOf_library() {
Source source = _addSource("/test.dart", "library lib;");
expect(_context.getKindOf(source), same(SourceKind.UNKNOWN));
@ -1630,6 +1614,22 @@ main() {}''');
expect(_context.launchableServerLibrarySources, isEmpty);
}
void test_getLibrariesContaining() {
_context = contextWithCore();
_sourceFactory = _context.sourceFactory;
Source librarySource = _addSource("/lib.dart", r'''
library lib;
part 'part.dart';''');
Source partSource = _addSource("/part.dart", "part of lib;");
_context.computeLibraryElement(librarySource);
List<Source> result = _context.getLibrariesContaining(librarySource);
expect(result, hasLength(1));
expect(result[0], librarySource);
result = _context.getLibrariesContaining(partSource);
expect(result, hasLength(1));
expect(result[0], librarySource);
}
void test_getLibrariesDependingOn() {
_context = contextWithCore();
_sourceFactory = _context.sourceFactory;
@ -1860,17 +1860,6 @@ main() {}''');
expect(errorInfo.errors, hasLength(0));
}
// void test_resolveCompilationUnit_sourceChangeDuringResolution() {
// _context = new _AnalysisContext_sourceChangeDuringResolution();
// AnalysisContextFactory.initContextWithCore(_context);
// _sourceFactory = _context.sourceFactory;
// Source source = _addSource("/lib.dart", "library lib;");
// CompilationUnit compilationUnit =
// _context.resolveCompilationUnit2(source, source);
// expect(compilationUnit, isNotNull);
// expect(_context.getLineInfo(source), isNotNull);
// }
void test_parseCompilationUnit_nonExistentSource() {
Source source =
new FileBasedSource.con1(FileUtilities2.createFile("/test.dart"));
@ -1882,6 +1871,17 @@ main() {}''');
}
}
// void test_resolveCompilationUnit_sourceChangeDuringResolution() {
// _context = new _AnalysisContext_sourceChangeDuringResolution();
// AnalysisContextFactory.initContextWithCore(_context);
// _sourceFactory = _context.sourceFactory;
// Source source = _addSource("/lib.dart", "library lib;");
// CompilationUnit compilationUnit =
// _context.resolveCompilationUnit2(source, source);
// expect(compilationUnit, isNotNull);
// expect(_context.getLineInfo(source), isNotNull);
// }
void test_performAnalysisTask_modifiedAfterParse() {
// TODO(scheglov) no threads in Dart
// Source source = _addSource("/test.dart", "library lib;");
@ -2044,8 +2044,7 @@ int a = 0;''');
// Complete all pending analysis tasks and flush the AST so that it won't
// be available immediately.
_performPendingAnalysisTasks();
CacheEntry entry = _context.getReadableSourceEntryOrNull(source);
entry.flushAstStructures();
_flushAst(source);
bool completed = false;
_context
.computeResolvedCompilationUnitAsync(source, source)
@ -2068,8 +2067,7 @@ int a = 0;''');
// Complete all pending analysis tasks and flush the AST so that it won't
// be available immediately.
_performPendingAnalysisTasks();
CacheEntry entry = _context.getReadableSourceEntryOrNull(source);
entry.flushAstStructures();
_flushAst(source);
CancelableFuture<CompilationUnit> future =
_context.computeResolvedCompilationUnitAsync(source, source);
bool completed = false;
@ -2188,6 +2186,11 @@ int a = 0;''');
return null;
}
void _flushAst(Source source) {
CacheEntry entry = _context.getReadableSourceEntryOrNull(source);
entry.setState(RESOLVED_UNIT, CacheState.FLUSHED);
}
IncrementalAnalysisCache _getIncrementalAnalysisCache(
AnalysisContextImpl context2) {
return context2.test_incrementalAnalysisCache;

View file

@ -19,6 +19,7 @@ main() {
runReflectiveTests(AnalysisTaskTest);
runReflectiveTests(ContributionPointImplTest);
runReflectiveTests(ResultDescriptorImplTest);
runReflectiveTests(SimpleResultCachingPolicyTest);
runReflectiveTests(TaskDescriptorImplTest);
}
@ -94,6 +95,13 @@ class ContributionPointImplTest extends EngineTestCase {
@reflectiveTest
class ResultDescriptorImplTest extends EngineTestCase {
test_create_withCachingPolicy() {
ResultCachingPolicy policy = new SimpleResultCachingPolicy(128, 16);
ResultDescriptorImpl result =
new ResultDescriptorImpl('result', null, cachingPolicy: policy);
expect(result.cachingPolicy, same(policy));
}
test_create_withContribution() {
CompositeResultDescriptorImpl point =
new CompositeResultDescriptorImpl('point');
@ -104,6 +112,14 @@ class ResultDescriptorImplTest extends EngineTestCase {
expect(contributors, unorderedEquals([result]));
}
test_create_withoutCachingPolicy() {
ResultDescriptorImpl result = new ResultDescriptorImpl('result', null);
ResultCachingPolicy cachingPolicy = result.cachingPolicy;
expect(cachingPolicy, isNotNull);
expect(cachingPolicy.maxActiveSize, -1);
expect(cachingPolicy.maxIdleSize, -1);
}
test_create_withoutContribution() {
expect(new ResultDescriptorImpl('name', null), isNotNull);
}
@ -122,6 +138,16 @@ class ResultDescriptorImplTest extends EngineTestCase {
}
}
@reflectiveTest
class SimpleResultCachingPolicyTest extends EngineTestCase {
test_create() {
ResultCachingPolicy policy = new SimpleResultCachingPolicy(256, 32);
expect(policy.maxActiveSize, 256);
expect(policy.maxIdleSize, 32);
expect(policy.measure(null), 1);
}
}
@reflectiveTest
class TaskDescriptorImplTest extends EngineTestCase {
test_create() {