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

Unified Diff: pkg/analyzer/lib/src/context/cache.dart

Issue 913483002: First cut at analysis driver (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | pkg/analyzer/lib/src/task/driver.dart » ('j') | pkg/analyzer/lib/src/task/driver.dart » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/analyzer/lib/src/context/cache.dart
diff --git a/pkg/analyzer/lib/src/context/cache.dart b/pkg/analyzer/lib/src/context/cache.dart
new file mode 100644
index 0000000000000000000000000000000000000000..72e92a6e00e52b4fdb4aa26e8cd2ffa9621ba46e
--- /dev/null
+++ b/pkg/analyzer/lib/src/context/cache.dart
@@ -0,0 +1,779 @@
+// 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: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/utilities_collection.dart';
+import 'package:analyzer/task/model.dart';
+
+/**
+ * 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;
+
+ /**
+ * 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);
+
+ /**
+ * Return the number of entries in this cache that have an AST associated with
+ * them.
+ */
+ int get astSize => _partitions[_partitions.length - 1].astSize;
Paul Berry 2015/03/03 17:21:31 Why not sum .astSize over all partitions?
Brian Wilkerson 2015/03/03 21:21:17 This is kind of a hold-over from an earlier implem
+
+// /**
Paul Berry 2015/03/03 17:21:31 Add a TODO comment here?
Brian Wilkerson 2015/03/03 21:21:16 Done
+// * 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;
+// }
+
+ /**
+ * Record that the AST associated with the given [target] was just read from
+ * the cache.
+ */
+ void accessedAst(AnalysisTarget target) {
+ int count = _partitions.length;
+ for (int i = 0; i < count; i++) {
+ if (_partitions[i].contains(target)) {
Paul Berry 2015/03/03 17:21:31 It seems like we do this loop over partitions in a
Brian Wilkerson 2015/03/03 21:21:16 Sounds reasonable. Left for a later CL.
+ _partitions[i].accessedAst(target);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Return the entry associated with the given [target].
+ */
+ CacheEntry get(AnalysisTarget target) {
+ int count = _partitions.length;
+ for (int i = 0; i < count; i++) {
+ if (_partitions[i].contains(target)) {
+ return _partitions[i].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 context that owns the given [target].
Paul Berry 2015/03/03 17:21:31 I'm not sure I understand what you mean by "owns".
Brian Wilkerson 2015/03/03 21:21:16 Done
+ */
+ InternalAnalysisContext getContextFor(AnalysisTarget target) {
+ int count = _partitions.length;
+ for (int i = 0; i < count; i++) {
+ if (_partitions[i].contains(target)) {
+ return _partitions[i].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.
+ //
+ AnalysisEngine.instance.logger.logInformation(
Paul Berry 2015/03/03 17:21:32 I'm concerned that if we just log the error, it wo
Brian Wilkerson 2015/03/03 21:21:16 Saved for a follow-on CL.
+ 'Could not find context for $target',
+ new CaughtException(new AnalysisException(), null));
+ return null;
+ }
+
+ /**
+ * 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>(count);
+ for (int i = 0; i < count; i++) {
+ maps[i] = _partitions[i].map;
+ }
+ return new MultipleMapIterator<AnalysisTarget, CacheEntry>(maps);
+ }
+
+ /**
+ * Associate the given [entry] with the given [target].
+ */
+ void put(AnalysisTarget target, CacheEntry entry) {
+ entry.fixExceptionState();
+ int count = _partitions.length;
+ for (int i = 0; i < count; i++) {
+ if (_partitions[i].contains(target)) {
+ if (_TRACE_CHANGES) {
+ try {
+ CacheEntry oldEntry = _partitions[i].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)}');
+ }
+ } catch (exception) {
Paul Berry 2015/03/03 17:21:31 Why would an exception ever occur here?
Brian Wilkerson 2015/03/03 21:21:17 My guess is that it was debugging code added to th
+ // Ignored
+ }
+ }
+ _partitions[i].put(target, entry);
+ return;
+ }
+ }
+ }
Paul Berry 2015/03/03 17:21:32 If we get to the end of the function without findi
Brian Wilkerson 2015/03/03 21:21:16 Added TODO.
+
+ /**
+ * 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++) {
+ if (_partitions[i].contains(target)) {
+ if (_TRACE_CHANGES) {
+ try {
+ AnalysisEngine.instance.logger.logInformation(
+ 'Removed the cache entry for $target.');
+ } catch (exception) {
Paul Berry 2015/03/03 17:21:32 Why would an exception ever occur here?
Brian Wilkerson 2015/03/03 21:21:16 Removed
+ // Ignored
+ }
+ }
+ _partitions[i].remove(target);
+ return;
+ }
+ }
+ }
+
+ /**
+ * 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.
+ */
+ int size() {
+ int size = 0;
+ int count = _partitions.length;
+ for (int i = 0; i < count; i++) {
+ size += _partitions[i].size();
+ }
+ 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;
+ }
+ }
+ }
+}
+
+/**
+ * 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 most recent time at which the state of the target matched the state
+ * represented by this entry.
+ */
+ int modificationTime = 0;
+
+ /**
+ * The exception that caused one or more values to have a state of
+ * [CacheState.ERROR].
+ */
Paul Berry 2015/03/03 17:21:32 I'm assuming that an intended invariant is that wh
Brian Wilkerson 2015/03/03 21:21:16 Yes.
+ 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>();
+
+ /**
+ * 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 `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.
+ */
+ void fixExceptionState() {
+ if (hasErrorState()) {
+ if (exception == null) {
+ //
+ // This code should never be reached, but is a fail-safe in case an
+ // exception is not recorded when it should be.
+ //
Paul Berry 2015/03/03 17:21:31 Since this should never happen, should we also add
Brian Wilkerson 2015/03/03 21:21:17 That would potentially let us know that it happene
+ String message = 'State set to ERROR without setting an exception';
+ exception = new CaughtException(new AnalysisException(message), null);
+ }
+ } else {
+ exception = null;
+ }
+ }
+
+ /**
+ * 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;
+ }
+ });
Paul Berry 2015/03/03 17:21:32 To restore the invariant, we might need to set thi
Brian Wilkerson 2015/03/03 21:21:16 If the state were ERROR, then the value would be n
+ }
+
+ /**
+ * 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;
+ }
+ 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();
Paul Berry 2015/03/03 17:21:31 To restore the invariant, we should set this.excep
Brian Wilkerson 2015/03/03 21:21:17 Done
+ }
+
+ /**
+ * Set the state of the result represented by the given [descriptor] to the
+ * given [state].
+ */
+ void setState(ResultDescriptor descriptor, CacheState state) {
+ if (state == CacheState.VALID) {
+ throw new ArgumentError('use setValue() to set the state to VALID');
+ }
+ _validateStateChange(descriptor, state);
+ if (state == CacheState.INVALID) {
+ _resultMap.remove(descriptor);
+ } else {
+ ResultData data =
+ _resultMap.putIfAbsent(descriptor, () => new ResultData(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;
+ }
+ }
+ }
Paul Berry 2015/03/03 17:21:31 I'm surprised this function doesn't set this.excep
Brian Wilkerson 2015/03/03 21:21:17 Then the client is expected to set the exception a
+
+ /**
+ * Set the value of the result represented by the given [descriptor] to the
+ * given [value].
+ */
+ /*<V>*/ void setValue(ResultDescriptor /*<V>*/ descriptor, dynamic /*V*/
+ value) {
+ _validateStateChange(descriptor, CacheState.VALID);
+ ResultData data =
+ _resultMap.putIfAbsent(descriptor, () => new ResultData(descriptor));
+ data.state = CacheState.VALID;
+ data.value = value == null ? descriptor.defaultValue : value;
+ }
Paul Berry 2015/03/03 17:21:31 Similar question about this.exception for this fun
Brian Wilkerson 2015/03/03 21:21:17 The current assumption is that values won't be cre
+
+ @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);
+
+ /**
+ * 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);
+ }
+ }
+}
+
+/**
+ * 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;
+
+ /**
+ * The maximum number of sources for which AST structures should be kept in
+ * the cache.
+ */
+ int _maxCacheSize = 0;
+
+ /**
+ * The policy used to determine which results to remove from the cache.
+ */
+ final CacheRetentionPolicy _retentionPolicy;
+
+ /**
+ * 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 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>[];
Paul Berry 2015/03/03 17:21:32 How big is this list expected to get in typical us
Brian Wilkerson 2015/03/03 21:21:16 It's a valid concern, but we should discuss off-li
+
+ /**
+ * 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.
+ */
+ 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;
+ }
+
+ /**
+ * 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
Paul Berry 2015/03/03 17:21:32 Since AnalysisCache is in the same library, how ab
Brian Wilkerson 2015/03/03 21:21:16 A hold-over from Java. It's currently also being u
+ * should not be used for any other purpose.
+ */
+ 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;
Paul Berry 2015/03/03 17:21:32 Here's a case where I'm concerned about the invari
Brian Wilkerson 2015/03/03 21:21:15 Part of a larger discussion.
+ }
+ }
+ _recentlyUsed.add(target);
+ }
+
+ /**
+ * Return `true` if the given [target] is contained in this partition.
+ */
+ bool contains(AnalysisTarget target);
Paul Berry 2015/03/03 17:21:32 For a long time I was confused by code calling con
Brian Wilkerson 2015/03/03 21:21:16 Yes, in a future CL.
+
+ /**
+ * Return the entry associated with the given [target].
+ */
+ CacheEntry get(AnalysisTarget target) => _targetMap[target];
+
+ /**
+ * Return an iterator returning all of the map entries mapping targets to
+ * cache entries.
+ */
+ MapIterator<AnalysisTarget, CacheEntry> iterator() =>
+ new SingleMapIterator<AnalysisTarget, CacheEntry>(_targetMap);
+
+ /**
+ * Associate the given [entry] with the given [target].
+ */
+ void put(AnalysisTarget target, CacheEntry entry) {
+ entry.fixExceptionState();
+ _targetMap[target] = entry;
+ }
+
+ /**
+ * Remove all information related to the given [target] from this cache.
+ */
+ void remove(AnalysisTarget target) {
+ _recentlyUsed.remove(target);
+ _targetMap.remove(target);
+ }
+
+ /**
+ * Record that the AST associated with the given [target] was just removed
+ * from the cache.
+ */
+ void removedAst(AnalysisTarget target) {
+ _recentlyUsed.remove(target);
+ }
+
+ /**
+ * Return the number of targets that are mapped to cache entries.
+ */
+ 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);
+ }
+
+ /**
+ * Attempt to flush one AST structure from the cache. Return `true` if a
+ * structure was flushed.
+ */
+ bool _flushAstFromCache() {
+ AnalysisTarget removedTarget = _removeAstToFlush();
+ if (removedTarget == null) {
+ return false;
+ }
+ CacheEntry entry = _targetMap[removedTarget];
+ entry.flushAstStructures();
+ return true;
+ }
+
+ /**
+ * Remove and return one target from the list of recently used targets whose
+ * AST structure can be flushed from the cache. 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.
+ */
Paul Berry 2015/03/03 17:21:30 The doc comments should also explain when this met
Brian Wilkerson 2015/03/03 21:21:17 Done
+ 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();
+
+// /**
+// * 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) {
+// if (sourceEntry is DartEntry) {
Paul Berry 2015/03/03 17:21:31 Add a TODO comment here?
Brian Wilkerson 2015/03/03 21:21:17 Done
+// DartEntry dartEntry = sourceEntry;
+// if (astIsNeeded(dartEntry)) {
+// return RetentionPriority.MEDIUM;
+// }
+// }
+// return RetentionPriority.LOW;
+ return RetentionPriority.MEDIUM;
+ }
+}
+
+/**
+ * The data about a single analysis result that is stored in a [CacheEntry].
+ */
+class ResultData {
Paul Berry 2015/03/03 17:21:31 Would it be beneficial to make this a generic clas
Brian Wilkerson 2015/03/03 21:21:16 Possibly. I'll look into it for a follow-on CL.
+ /**
+ * 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;
+
+ /**
+ * Initialize a newly created result holder to represent the value of data
+ * described by the given [descriptor].
+ */
+ ResultData(ResultDescriptor descriptor) {
+ state = CacheState.INVALID;
+ 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]. The partition will maintain at most [maxCacheSize] AST
+ * structures in the cache.
+ */
+ SdkCachePartition(InternalAnalysisContext context, int maxCacheSize)
+ : super(context, maxCacheSize, DefaultRetentionPolicy.POLICY);
+
+ @override
+ bool contains(AnalysisTarget target) => target.source.isInSystemLibrary;
+}
+
+/**
+ * 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]. The partition will maintain at most [maxCacheSize] AST
+ * structures in the cache, using the [retentionPolicy] to determine which
+ * AST structures to flush.
+ */
+ UniversalCachePartition(InternalAnalysisContext context, int maxCacheSize,
+ CacheRetentionPolicy retentionPolicy)
+ : super(context, maxCacheSize, retentionPolicy);
+
+ @override
+ bool contains(AnalysisTarget target) => true;
+}
« no previous file with comments | « no previous file | pkg/analyzer/lib/src/task/driver.dart » ('j') | pkg/analyzer/lib/src/task/driver.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698