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

Side by Side Diff: analyzer/lib/src/context/cache.dart

Issue 1400473008: Roll Observatory packages and add a roll script (Closed) Base URL: git@github.com:dart-lang/observatory_pub_packages.git@master
Patch Set: Created 5 years, 2 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 unified diff | Download patch
« no previous file with comments | « analyzer/lib/src/cancelable_future.dart ('k') | analyzer/lib/src/context/context.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 library analyzer.src.context.cache;
6
7 import 'dart:async';
8 import 'dart:collection';
9
10 import 'package:analyzer/src/generated/engine.dart'
11 show AnalysisEngine, CacheState, InternalAnalysisContext, RetentionPriority;
12 import 'package:analyzer/src/generated/java_engine.dart';
13 import 'package:analyzer/src/generated/source.dart';
14 import 'package:analyzer/src/generated/utilities_collection.dart';
15 import 'package:analyzer/src/generated/utilities_general.dart';
16 import 'package:analyzer/src/task/model.dart';
17 import 'package:analyzer/task/model.dart';
18
19 /**
20 * Return `true` if the given [target] is a priority one.
21 */
22 typedef bool IsPriorityAnalysisTarget(AnalysisTarget target);
23
24 /**
25 * An LRU cache of results produced by analysis.
26 */
27 class AnalysisCache {
28 /**
29 * A flag used to control whether trace information should be produced when
30 * the content of the cache is modified.
31 */
32 static bool _TRACE_CHANGES = false;
33
34 /**
35 * An array containing the partitions of which this cache is comprised.
36 */
37 final List<CachePartition> _partitions;
38
39 /**
40 * The [StreamController] reporting [InvalidatedResult]s.
41 */
42 final StreamController<InvalidatedResult> _onResultInvalidated =
43 new StreamController<InvalidatedResult>.broadcast(sync: true);
44
45 /**
46 * Initialize a newly created cache to have the given [partitions]. The
47 * partitions will be searched in the order in which they appear in the array,
48 * so the most specific partition (usually an [SdkCachePartition]) should be
49 * first and the most general (usually a [UniversalCachePartition]) last.
50 */
51 AnalysisCache(this._partitions) {
52 for (CachePartition partition in _partitions) {
53 partition.onResultInvalidated.listen((InvalidatedResult event) {
54 _onResultInvalidated.add(event);
55 });
56 }
57 }
58
59 /**
60 * Return the stream that is notified when a value is invalidated.
61 */
62 Stream<InvalidatedResult> get onResultInvalidated =>
63 _onResultInvalidated.stream;
64
65 // TODO(brianwilkerson) Implement or delete this.
66 // /**
67 // * Return information about each of the partitions in this cache.
68 // */
69 // List<AnalysisContextStatistics_PartitionData> get partitionData {
70 // int count = _partitions.length;
71 // List<AnalysisContextStatistics_PartitionData> data =
72 // new List<AnalysisContextStatistics_PartitionData>(count);
73 // for (int i = 0; i < count; i++) {
74 // CachePartition partition = _partitions[i];
75 // data[i] = new AnalysisContextStatisticsImpl_PartitionDataImpl(
76 // partition.astSize,
77 // partition.map.length);
78 // }
79 // return data;
80 // }
81
82 /**
83 * Return an iterator returning all of the [Source] targets.
84 */
85 Iterable<Source> get sources {
86 return _partitions
87 .map((CachePartition partition) => partition._sources)
88 .expand((Iterable<Source> sources) => sources);
89 }
90
91 /**
92 * Return the entry associated with the given [target].
93 */
94 CacheEntry get(AnalysisTarget target) {
95 int count = _partitions.length;
96 for (int i = 0; i < count; i++) {
97 CachePartition partition = _partitions[i];
98 if (partition.isResponsibleFor(target)) {
99 return partition.get(target);
100 }
101 }
102 //
103 // We should never get to this point because the last partition should
104 // always be a universal partition, except in the case of the SDK context,
105 // in which case the target should always be part of the SDK.
106 //
107 return null;
108 }
109
110 /**
111 * Return the context to which the given [target] was explicitly added.
112 */
113 InternalAnalysisContext getContextFor(AnalysisTarget target) {
114 int count = _partitions.length;
115 for (int i = 0; i < count; i++) {
116 CachePartition partition = _partitions[i];
117 if (partition.isResponsibleFor(target)) {
118 return partition.context;
119 }
120 }
121 //
122 // We should never get to this point because the last partition should
123 // always be a universal partition, except in the case of the SDK context,
124 // in which case the target should always be part of the SDK.
125 //
126 // TODO(brianwilkerson) Throw an exception here.
127 AnalysisEngine.instance.logger.logInformation(
128 'Could not find context for $target',
129 new CaughtException(new AnalysisException(), null));
130 return null;
131 }
132
133 /**
134 * Return [Source]s whose full path is equal to the given [path].
135 * Maybe empty, but not `null`.
136 */
137 List<Source> getSourcesWithFullName(String path) {
138 List<Source> sources = <Source>[];
139 for (CachePartition partition in _partitions) {
140 List<Source> partitionSources = partition.getSourcesWithFullName(path);
141 sources.addAll(partitionSources);
142 }
143 return sources;
144 }
145
146 /**
147 * Return the state of the given [result] for the given [target].
148 *
149 * It does not update the cache, if the corresponding [CacheEntry] does not
150 * exist, then [CacheState.INVALID] is returned.
151 */
152 CacheState getState(AnalysisTarget target, ResultDescriptor result) {
153 CacheEntry entry = get(target);
154 if (entry == null) {
155 return CacheState.INVALID;
156 }
157 return entry.getState(result);
158 }
159
160 /**
161 * Return the value of the given [result] for the given [target].
162 *
163 * It does not update the cache, if the corresponding [CacheEntry] does not
164 * exist, then the default value is returned.
165 */
166 Object getValue(AnalysisTarget target, ResultDescriptor result) {
167 CacheEntry entry = get(target);
168 if (entry == null) {
169 return result.defaultValue;
170 }
171 return entry.getValue(result);
172 }
173
174 /**
175 * Return an iterator returning all of the map entries mapping targets to
176 * cache entries.
177 */
178 MapIterator<AnalysisTarget, CacheEntry> iterator() {
179 int count = _partitions.length;
180 List<Map<AnalysisTarget, CacheEntry>> maps =
181 new List<Map<AnalysisTarget, CacheEntry>>(count);
182 for (int i = 0; i < count; i++) {
183 maps[i] = _partitions[i].map;
184 }
185 return new MultipleMapIterator<AnalysisTarget, CacheEntry>(maps);
186 }
187
188 /**
189 * Puts the given [entry] into the cache.
190 */
191 void put(CacheEntry entry) {
192 AnalysisTarget target = entry.target;
193 entry.fixExceptionState();
194 int count = _partitions.length;
195 for (int i = 0; i < count; i++) {
196 CachePartition partition = _partitions[i];
197 if (partition.isResponsibleFor(target)) {
198 if (_TRACE_CHANGES) {
199 CacheEntry oldEntry = partition.get(target);
200 if (oldEntry == null) {
201 AnalysisEngine.instance.logger
202 .logInformation('Added a cache entry for $target.');
203 } else {
204 AnalysisEngine.instance.logger
205 .logInformation('Modified the cache entry for $target.');
206 // 'Diff = ${entry.getDiff(oldEntry)}');
207 }
208 }
209 partition.put(entry);
210 return;
211 }
212 }
213 // TODO(brianwilkerson) Handle the case where no partition was found,
214 // possibly by throwing an exception.
215 }
216
217 /**
218 * Remove all information related to the given [target] from this cache.
219 */
220 void remove(AnalysisTarget target) {
221 int count = _partitions.length;
222 for (int i = 0; i < count; i++) {
223 CachePartition partition = _partitions[i];
224 if (partition.isResponsibleFor(target)) {
225 if (_TRACE_CHANGES) {
226 AnalysisEngine.instance.logger
227 .logInformation('Removed the cache entry for $target.');
228 }
229 partition.remove(target);
230 return;
231 }
232 }
233 }
234
235 /**
236 * Return the number of targets that are mapped to cache entries.
237 */
238 int size() {
239 int size = 0;
240 int count = _partitions.length;
241 for (int i = 0; i < count; i++) {
242 size += _partitions[i].size();
243 }
244 return size;
245 }
246 }
247
248 /**
249 * The information cached by an analysis context about an individual target.
250 */
251 class CacheEntry {
252 /**
253 * The index of the flag indicating whether the source was explicitly added to
254 * the context or whether the source was implicitly added because it was
255 * referenced by another source.
256 */
257 static int _EXPLICITLY_ADDED_FLAG = 0;
258
259 /**
260 * The target this entry is about.
261 */
262 final AnalysisTarget target;
263
264 /**
265 * The partition that is responsible for this entry.
266 */
267 CachePartition _partition;
268
269 /**
270 * The most recent time at which the state of the target matched the state
271 * represented by this entry, `-1` if the target does not exist.
272 */
273 int modificationTime = -1;
274
275 /**
276 * The exception that caused one or more values to have a state of
277 * [CacheState.ERROR].
278 */
279 CaughtException _exception;
280
281 /**
282 * A bit-encoding of boolean flags associated with this entry's target.
283 */
284 int _flags = 0;
285
286 /**
287 * A table mapping result descriptors to the cached values of those results.
288 */
289 Map<ResultDescriptor, ResultData> _resultMap =
290 new HashMap<ResultDescriptor, ResultData>();
291
292 CacheEntry(this.target);
293
294 /**
295 * The exception that caused one or more values to have a state of
296 * [CacheState.ERROR].
297 */
298 CaughtException get exception => _exception;
299
300 /**
301 * Return `true` if the source was explicitly added to the context or `false`
302 * if the source was implicitly added because it was referenced by another
303 * source.
304 */
305 bool get explicitlyAdded => _getFlag(_EXPLICITLY_ADDED_FLAG);
306
307 /**
308 * Set whether the source was explicitly added to the context to match the
309 * [explicitlyAdded] flag.
310 */
311 void set explicitlyAdded(bool explicitlyAdded) {
312 _setFlag(_EXPLICITLY_ADDED_FLAG, explicitlyAdded);
313 }
314
315 /**
316 * Return a list of result descriptors for results whose state is not
317 * [CacheState.INVALID].
318 */
319 List<ResultDescriptor> get nonInvalidResults => _resultMap.keys.toList();
320
321 /**
322 * Notifies the entry that the client is going to stop using it.
323 */
324 void dispose() {
325 _resultMap.forEach((descriptor, data) {
326 TargetedResult result = new TargetedResult(target, descriptor);
327 for (TargetedResult dependedOnResult in data.dependedOnResults) {
328 ResultData dependedOnData = _partition._getDataFor(dependedOnResult);
329 if (dependedOnData != null) {
330 dependedOnData.dependentResults.remove(result);
331 }
332 }
333 });
334 _resultMap.clear();
335 }
336
337 /**
338 * Fix the state of the [exception] to match the current state of the entry.
339 */
340 void fixExceptionState() {
341 if (!hasErrorState()) {
342 _exception = null;
343 }
344 }
345
346 /**
347 * Look up the [ResultData] of [descriptor], or add a new one if it isn't
348 * there.
349 */
350 ResultData getResultData(ResultDescriptor descriptor) {
351 return _resultMap.putIfAbsent(descriptor, () => new ResultData(descriptor));
352 }
353
354 /**
355 * Return the state of the result represented by the given [descriptor].
356 */
357 CacheState getState(ResultDescriptor descriptor) {
358 ResultData data = _resultMap[descriptor];
359 if (data == null) {
360 return CacheState.INVALID;
361 }
362 return data.state;
363 }
364
365 /**
366 * Return the value of the result represented by the given [descriptor], or
367 * the default value for the result if this entry does not have a valid value.
368 */
369 /*<V>*/ dynamic /*V*/ getValue(ResultDescriptor /*<V>*/ descriptor) {
370 ResultData data = _resultMap[descriptor];
371 if (data == null) {
372 return descriptor.defaultValue;
373 }
374 if (_partition != null) {
375 _partition.resultAccessed(target, descriptor);
376 }
377 return data.value;
378 }
379
380 /**
381 * Return `true` if the state of any data value is [CacheState.ERROR].
382 */
383 bool hasErrorState() {
384 for (ResultData data in _resultMap.values) {
385 if (data.state == CacheState.ERROR) {
386 return true;
387 }
388 }
389 return false;
390 }
391
392 /**
393 * Invalidate all of the information associated with this entry's target.
394 */
395 void invalidateAllInformation() {
396 _resultMap.clear();
397 _exception = null;
398 }
399
400 /**
401 * Return `true` if the state of the result represented by the given
402 * [descriptor] is [CacheState.INVALID].
403 */
404 bool isInvalid(ResultDescriptor descriptor) =>
405 getState(descriptor) == CacheState.INVALID;
406
407 /**
408 * Return `true` if the state of the result represented by the given
409 * [descriptor] is [CacheState.VALID].
410 */
411 bool isValid(ResultDescriptor descriptor) =>
412 getState(descriptor) == CacheState.VALID;
413
414 /**
415 * For each of the given [descriptors], set their states to
416 * [CacheState.ERROR], their values to the corresponding default values, and
417 * remember the [exception] that caused this state.
418 */
419 void setErrorState(
420 CaughtException exception, List<ResultDescriptor> descriptors) {
421 if (descriptors == null || descriptors.isEmpty) {
422 throw new ArgumentError('at least one descriptor is expected');
423 }
424 if (exception == null) {
425 throw new ArgumentError('an exception is expected');
426 }
427 this._exception = exception;
428 for (ResultDescriptor descriptor in descriptors) {
429 _setErrorState(descriptor, exception);
430 }
431 }
432
433 /**
434 * Set the state of the result represented by the given [descriptor] to the
435 * given [state].
436 */
437 void setState(ResultDescriptor descriptor, CacheState state, {Delta delta}) {
438 if (state == CacheState.ERROR) {
439 throw new ArgumentError('use setErrorState() to set the state to ERROR');
440 }
441 if (state == CacheState.VALID) {
442 throw new ArgumentError('use setValue() to set the state to VALID');
443 }
444 _validateStateChange(descriptor, state);
445 if (state == CacheState.INVALID) {
446 ResultData data = _resultMap[descriptor];
447 if (data != null) {
448 _invalidate(descriptor, delta);
449 }
450 } else {
451 ResultData data = getResultData(descriptor);
452 data.state = state;
453 if (state != CacheState.IN_PROCESS) {
454 //
455 // If the state is in-process, we can leave the current value in the
456 // cache for any 'get' methods to access.
457 //
458 data.value = descriptor.defaultValue;
459 }
460 }
461 }
462
463 /**
464 * Set the value of the result represented by the given [descriptor] to the
465 * given [value].
466 */
467 /*<V>*/ void setValue(ResultDescriptor /*<V>*/ descriptor, dynamic /*V*/
468 value, List<TargetedResult> dependedOn) {
469 // {
470 // String valueStr = '$value';
471 // if (valueStr.length > 20) {
472 // valueStr = valueStr.substring(0, 20) + '...';
473 // }
474 // valueStr = valueStr.replaceAll('\n', '\\n');
475 // print(
476 // 'setValue $descriptor for $target value=$valueStr deps=$dependedOn') ;
477 // }
478 _validateStateChange(descriptor, CacheState.VALID);
479 TargetedResult thisResult = new TargetedResult(target, descriptor);
480 if (_partition != null) {
481 _partition.resultStored(thisResult, value);
482 }
483 ResultData data = getResultData(descriptor);
484 _setDependedOnResults(data, thisResult, dependedOn);
485 data.state = CacheState.VALID;
486 data.value = value == null ? descriptor.defaultValue : value;
487 }
488
489 /**
490 * Set the value of the result represented by the given [descriptor] to the
491 * given [value], keep its dependency, invalidate all the dependent result.
492 */
493 void setValueIncremental(ResultDescriptor descriptor, dynamic value) {
494 ResultData data = getResultData(descriptor);
495 List<TargetedResult> dependedOn = data.dependedOnResults;
496 _invalidate(descriptor, null);
497 setValue(descriptor, value, dependedOn);
498 }
499
500 @override
501 String toString() {
502 StringBuffer buffer = new StringBuffer();
503 _writeOn(buffer);
504 return buffer.toString();
505 }
506
507 /**
508 * Return the value of the flag with the given [index].
509 */
510 bool _getFlag(int index) => BooleanArray.get(_flags, index);
511
512 /**
513 * Invalidate the result represented by the given [descriptor] and propagate
514 * invalidation to other results that depend on it.
515 */
516 void _invalidate(ResultDescriptor descriptor, Delta delta) {
517 DeltaResult deltaResult = null;
518 if (delta != null) {
519 deltaResult = delta.validate(_partition.context, target, descriptor);
520 if (deltaResult == DeltaResult.STOP) {
521 // print('not-invalidate $descriptor for $target');
522 return;
523 }
524 }
525 // print('invalidate $descriptor for $target');
526 ResultData thisData;
527 if (deltaResult == null || deltaResult == DeltaResult.INVALIDATE) {
528 thisData = _resultMap.remove(descriptor);
529 }
530 if (deltaResult == DeltaResult.KEEP_CONTINUE) {
531 thisData = _resultMap[descriptor];
532 }
533 if (thisData == null) {
534 return;
535 }
536 // Stop depending on other results.
537 TargetedResult thisResult = new TargetedResult(target, descriptor);
538 for (TargetedResult dependedOnResult in thisData.dependedOnResults) {
539 ResultData data = _partition._getDataFor(dependedOnResult);
540 if (data != null) {
541 data.dependentResults.remove(thisResult);
542 }
543 }
544 // Invalidate results that depend on this result.
545 List<TargetedResult> dependentResults = thisData.dependentResults.toList();
546 for (TargetedResult dependentResult in dependentResults) {
547 CacheEntry entry = _partition.get(dependentResult.target);
548 if (entry != null) {
549 entry._invalidate(dependentResult.result, delta);
550 }
551 }
552 // If empty, remove the entry altogether.
553 if (_resultMap.isEmpty) {
554 _partition._targetMap.remove(target);
555 _partition._removeIfSource(target);
556 }
557 // Notify controller.
558 _partition._onResultInvalidated
559 .add(new InvalidatedResult(this, descriptor));
560 }
561
562 /**
563 * Invalidates all the results of this entry, with propagation.
564 */
565 void _invalidateAll() {
566 List<ResultDescriptor> results = _resultMap.keys.toList();
567 for (ResultDescriptor result in results) {
568 _invalidate(result, null);
569 }
570 }
571
572 /**
573 * Set the [dependedOn] on which this result depends.
574 */
575 void _setDependedOnResults(ResultData thisData, TargetedResult thisResult,
576 List<TargetedResult> dependedOn) {
577 thisData.dependedOnResults.forEach((TargetedResult dependedOnResult) {
578 ResultData data = _partition._getDataFor(dependedOnResult);
579 if (data != null) {
580 data.dependentResults.remove(thisResult);
581 }
582 });
583 thisData.dependedOnResults = dependedOn;
584 thisData.dependedOnResults.forEach((TargetedResult dependedOnResult) {
585 ResultData data = _partition._getDataFor(dependedOnResult);
586 if (data != null) {
587 data.dependentResults.add(thisResult);
588 }
589 });
590 }
591
592 /**
593 * Set states of the given and dependent results to [CacheState.ERROR] and
594 * their values to the corresponding default values
595 */
596 void _setErrorState(ResultDescriptor descriptor, CaughtException exception) {
597 ResultData thisData = getResultData(descriptor);
598 // Set the error state.
599 _exception = exception;
600 thisData.state = CacheState.ERROR;
601 thisData.value = descriptor.defaultValue;
602 // Propagate the error state.
603 thisData.dependentResults.forEach((TargetedResult dependentResult) {
604 CacheEntry entry = _partition.get(dependentResult.target);
605 entry._setErrorState(dependentResult.result, exception);
606 });
607 }
608
609 /**
610 * Set the value of the flag with the given [index] to the given [value].
611 */
612 void _setFlag(int index, bool value) {
613 _flags = BooleanArray.set(_flags, index, value);
614 }
615
616 /**
617 * If the state of the value described by the given [descriptor] is changing
618 * from ERROR to anything else, capture the information. This is an attempt to
619 * discover the underlying cause of a long-standing bug.
620 */
621 void _validateStateChange(ResultDescriptor descriptor, CacheState newState) {
622 // TODO(brianwilkerson) Decide whether we still want to capture this data.
623 // if (descriptor != CONTENT) {
624 // return;
625 // }
626 // ResultData data = resultMap[CONTENT];
627 // if (data != null && data.state == CacheState.ERROR) {
628 // String message =
629 // 'contentState changing from ${data.state} to $newState';
630 // InstrumentationBuilder builder =
631 // Instrumentation.builder2('CacheEntry-validateStateChange');
632 // builder.data3('message', message);
633 // //builder.data('source', source.getFullName());
634 // builder.record(new CaughtException(new AnalysisException(message), null) );
635 // builder.log();
636 // }
637 }
638
639 /**
640 * Write a textual representation of this entry to the given [buffer]. The
641 * result should only be used for debugging purposes.
642 */
643 void _writeOn(StringBuffer buffer) {
644 buffer.write('time = ');
645 buffer.write(modificationTime);
646 List<ResultDescriptor> results = _resultMap.keys.toList();
647 results.sort((ResultDescriptor first, ResultDescriptor second) =>
648 first.toString().compareTo(second.toString()));
649 for (ResultDescriptor result in results) {
650 ResultData data = _resultMap[result];
651 buffer.write('; ');
652 buffer.write(result.toString());
653 buffer.write(' = ');
654 buffer.write(data.state);
655 }
656 }
657 }
658
659 /**
660 * An object that controls flushing of analysis results from the cache.
661 */
662 class CacheFlushManager<T> {
663 final IsPriorityAnalysisTarget isPriorityAnalysisTarget;
664 final ResultCachingPolicy<T> policy;
665 final int maxActiveSize;
666 final int maxIdleSize;
667
668 /**
669 * A map of the stored [TargetedResult] to their sizes.
670 */
671 final HashMap<TargetedResult, int> resultSizeMap =
672 new HashMap<TargetedResult, int>();
673
674 /**
675 * A linked set containing the most recently accessed results with the most
676 * recently used at the end of the list. When more results are added than the
677 * maximum size allowed then the least recently used results will be flushed
678 * from the cache.
679 */
680 final LinkedHashSet<TargetedResult> recentlyUsed =
681 new LinkedHashSet<TargetedResult>();
682
683 /**
684 * The current size of stored results.
685 */
686 int currentSize = 0;
687
688 /**
689 * The current maximum cache size.
690 */
691 int maxSize;
692
693 CacheFlushManager(
694 ResultCachingPolicy<T> policy, this.isPriorityAnalysisTarget)
695 : policy = policy,
696 maxActiveSize = policy.maxActiveSize,
697 maxIdleSize = policy.maxIdleSize,
698 maxSize = policy.maxIdleSize;
699
700 /**
701 * If [currentSize] is already less than [maxSize], returns an empty list.
702 * Otherwise returns [TargetedResult]s to flush from the cache to make
703 * [currentSize] less or equal to [maxSize].
704 *
705 * Results for priority files are never flushed, so this method might leave
706 * [currentSize] greater than [maxSize].
707 */
708 List<TargetedResult> flushToSize() {
709 // If still under the cap, done.
710 if (currentSize <= maxSize) {
711 return TargetedResult.EMPTY_LIST;
712 }
713 // Flush results until we are under the cap.
714 List<TargetedResult> resultsToFlush = <TargetedResult>[];
715 for (TargetedResult result in recentlyUsed) {
716 if (isPriorityAnalysisTarget(result.target)) {
717 continue;
718 }
719 resultsToFlush.add(result);
720 int size = resultSizeMap.remove(result);
721 assert(size != null);
722 currentSize -= size;
723 if (currentSize <= maxSize) {
724 break;
725 }
726 }
727 recentlyUsed.removeAll(resultsToFlush);
728 return resultsToFlush;
729 }
730
731 /**
732 * Notifies this manager that the corresponding analysis context is active.
733 */
734 void madeActive() {
735 maxSize = maxActiveSize;
736 }
737
738 /**
739 * Notifies this manager that the corresponding analysis context is idle.
740 * Returns [TargetedResult]s that should be flushed from the cache.
741 */
742 List<TargetedResult> madeIdle() {
743 maxSize = maxIdleSize;
744 return flushToSize();
745 }
746
747 /**
748 * Records that the given [result] was just read from the cache.
749 */
750 void resultAccessed(TargetedResult result) {
751 if (recentlyUsed.remove(result)) {
752 recentlyUsed.add(result);
753 }
754 }
755
756 /**
757 * Records that the given [newResult] and [newValue] were stored to the cache.
758 * Returns [TargetedResult]s that should be flushed from the cache.
759 */
760 List<TargetedResult> resultStored(TargetedResult newResult, T newValue) {
761 if (!recentlyUsed.remove(newResult)) {
762 int size = policy.measure(newValue);
763 resultSizeMap[newResult] = size;
764 currentSize += size;
765 }
766 recentlyUsed.add(newResult);
767 return flushToSize();
768 }
769
770 /**
771 * Records that the given [target] was just removed from to the cache.
772 */
773 void targetRemoved(AnalysisTarget target) {
774 List<TargetedResult> resultsToRemove = <TargetedResult>[];
775 for (TargetedResult result in recentlyUsed) {
776 if (result.target == target) {
777 resultsToRemove.add(result);
778 int size = resultSizeMap.remove(result);
779 assert(size != null);
780 currentSize -= size;
781 }
782 }
783 recentlyUsed.removeAll(resultsToRemove);
784 }
785 }
786
787 /**
788 * A single partition in an LRU cache of information related to analysis.
789 */
790 abstract class CachePartition {
791 /**
792 * The context that owns this partition. Multiple contexts can reference a
793 * partition, but only one context can own it.
794 */
795 final InternalAnalysisContext context;
796
797 /**
798 * A table mapping caching policies to the cache flush managers.
799 */
800 final HashMap<ResultCachingPolicy, CacheFlushManager> _flushManagerMap =
801 new HashMap<ResultCachingPolicy, CacheFlushManager>();
802
803 /**
804 * The [StreamController] reporting [InvalidatedResult]s.
805 */
806 final StreamController<InvalidatedResult> _onResultInvalidated =
807 new StreamController<InvalidatedResult>.broadcast(sync: true);
808
809 /**
810 * A table mapping the targets belonging to this partition to the information
811 * known about those targets.
812 */
813 HashMap<AnalysisTarget, CacheEntry> _targetMap =
814 new HashMap<AnalysisTarget, CacheEntry>();
815
816 /**
817 * A set of the [Source] targets.
818 */
819 final HashSet<Source> _sources = new HashSet<Source>();
820
821 /**
822 * A table mapping full paths to lists of [Source]s with these full paths.
823 */
824 final Map<String, List<Source>> _pathToSources = <String, List<Source>>{};
825
826 /**
827 * Initialize a newly created cache partition, belonging to the given
828 * [context].
829 */
830 CachePartition(this.context);
831
832 /**
833 * Return a table mapping the targets known to the context to the information
834 * known about the target.
835 *
836 * <b>Note:</b> This method is only visible for use by [AnalysisCache] and
837 * should not be used for any other purpose.
838 */
839 Map<AnalysisTarget, CacheEntry> get map => _targetMap;
840
841 /**
842 * Return the stream that is notified when a value is invalidated.
843 */
844 Stream<InvalidatedResult> get onResultInvalidated =>
845 _onResultInvalidated.stream;
846
847 /**
848 * Notifies the partition that the client is going to stop using it.
849 */
850 void dispose() {
851 for (CacheEntry entry in _targetMap.values) {
852 entry.dispose();
853 }
854 _targetMap.clear();
855 }
856
857 /**
858 * Return the entry associated with the given [target].
859 */
860 CacheEntry get(AnalysisTarget target) => _targetMap[target];
861
862 /**
863 * Return [Source]s whose full path is equal to the given [path].
864 * Maybe empty, but not `null`.
865 */
866 List<Source> getSourcesWithFullName(String path) {
867 List<Source> sources = _pathToSources[path];
868 return sources != null ? sources : Source.EMPTY_LIST;
869 }
870
871 /**
872 * Return `true` if this partition is responsible for the given [target].
873 */
874 bool isResponsibleFor(AnalysisTarget target);
875
876 /**
877 * Return an iterator returning all of the map entries mapping targets to
878 * cache entries.
879 */
880 MapIterator<AnalysisTarget, CacheEntry> iterator() =>
881 new SingleMapIterator<AnalysisTarget, CacheEntry>(_targetMap);
882
883 /**
884 * Puts the given [entry] into the partition.
885 */
886 void put(CacheEntry entry) {
887 AnalysisTarget target = entry.target;
888 if (entry._partition != null) {
889 throw new StateError(
890 'The entry for $target is already in ${entry._partition}');
891 }
892 entry._partition = this;
893 entry.fixExceptionState();
894 _targetMap[target] = entry;
895 _addIfSource(target);
896 }
897
898 /**
899 * Remove all information related to the given [target] from this cache.
900 */
901 void remove(AnalysisTarget target) {
902 for (CacheFlushManager flushManager in _flushManagerMap.values) {
903 flushManager.targetRemoved(target);
904 }
905 CacheEntry entry = _targetMap.remove(target);
906 if (entry != null) {
907 entry._invalidateAll();
908 }
909 _removeIfSource(target);
910 }
911
912 /**
913 * Records that a value of the result described by the given [descriptor]
914 * for the given [target] was just read from the cache.
915 */
916 void resultAccessed(AnalysisTarget target, ResultDescriptor descriptor) {
917 CacheFlushManager flushManager = _getFlushManager(descriptor);
918 TargetedResult result = new TargetedResult(target, descriptor);
919 flushManager.resultAccessed(result);
920 }
921
922 /**
923 * Records that the given [result] was just stored into the cache.
924 */
925 void resultStored(TargetedResult result, Object value) {
926 CacheFlushManager flushManager = _getFlushManager(result.result);
927 List<TargetedResult> resultsToFlush =
928 flushManager.resultStored(result, value);
929 for (TargetedResult result in resultsToFlush) {
930 CacheEntry entry = get(result.target);
931 if (entry != null) {
932 ResultData data = entry._resultMap[result.result];
933 if (data != null) {
934 data.flush();
935 }
936 }
937 }
938 }
939
940 /**
941 * Return the number of targets that are mapped to cache entries.
942 */
943 int size() => _targetMap.length;
944
945 /**
946 * If the given [target] is a [Source], adds it to [_sources].
947 */
948 void _addIfSource(AnalysisTarget target) {
949 if (target is Source) {
950 _sources.add(target);
951 {
952 String fullName = target.fullName;
953 _pathToSources.putIfAbsent(fullName, () => <Source>[]).add(target);
954 }
955 }
956 }
957
958 ResultData _getDataFor(TargetedResult result) {
959 CacheEntry entry = context.analysisCache.get(result.target);
960 return entry != null ? entry._resultMap[result.result] : null;
961 }
962
963 /**
964 * Return the [CacheFlushManager] for the given [descriptor], not `null`.
965 */
966 CacheFlushManager _getFlushManager(ResultDescriptor descriptor) {
967 ResultCachingPolicy policy = descriptor.cachingPolicy;
968 if (identical(policy, DEFAULT_CACHING_POLICY)) {
969 return UnlimitedCacheFlushManager.INSTANCE;
970 }
971 CacheFlushManager manager = _flushManagerMap[policy];
972 if (manager == null) {
973 manager = new CacheFlushManager(policy, _isPriorityAnalysisTarget);
974 _flushManagerMap[policy] = manager;
975 }
976 return manager;
977 }
978
979 bool _isPriorityAnalysisTarget(AnalysisTarget target) {
980 return context.priorityTargets.contains(target);
981 }
982
983 /**
984 * If the given [target] is a [Source], removes it from [_sources].
985 */
986 void _removeIfSource(AnalysisTarget target) {
987 if (target is Source) {
988 _sources.remove(target);
989 {
990 String fullName = target.fullName;
991 List<Source> sources = _pathToSources[fullName];
992 if (sources != null) {
993 sources.remove(target);
994 if (sources.isEmpty) {
995 _pathToSources.remove(fullName);
996 }
997 }
998 }
999 }
1000 }
1001 }
1002
1003 /**
1004 * The description for a change.
1005 */
1006 class Delta {
1007 final Source source;
1008
1009 Delta(this.source);
1010
1011 /**
1012 * Check whether this delta affects the result described by the given
1013 * [descriptor] and [target].
1014 */
1015 DeltaResult validate(InternalAnalysisContext context, AnalysisTarget target,
1016 ResultDescriptor descriptor) {
1017 return DeltaResult.INVALIDATE;
1018 }
1019 }
1020
1021 /**
1022 * The possible results of validating analysis results againt a [Delta].
1023 */
1024 enum DeltaResult { INVALIDATE, KEEP_CONTINUE, STOP }
1025
1026 /**
1027 * [InvalidatedResult] describes an invalidated result.
1028 */
1029 class InvalidatedResult {
1030 /**
1031 * The target in which the result was invalidated.
1032 */
1033 final CacheEntry entry;
1034
1035 /**
1036 * The descriptor of the result which was invalidated.
1037 */
1038 final ResultDescriptor descriptor;
1039
1040 InvalidatedResult(this.entry, this.descriptor);
1041
1042 @override
1043 String toString() => '$descriptor of ${entry.target}';
1044 }
1045
1046 /**
1047 * The data about a single analysis result that is stored in a [CacheEntry].
1048 */
1049 // TODO(brianwilkerson) Consider making this a generic class so that the value
1050 // can be typed.
1051 class ResultData {
1052 /**
1053 * The [ResultDescriptor] this result is for.
1054 */
1055 final ResultDescriptor descriptor;
1056
1057 /**
1058 * The state of the cached value.
1059 */
1060 CacheState state;
1061
1062 /**
1063 * The value being cached, or the default value for the result if there is no
1064 * value (for example, when the [state] is [CacheState.INVALID]).
1065 */
1066 Object value;
1067
1068 /**
1069 * A list of the results on which this result depends.
1070 */
1071 List<TargetedResult> dependedOnResults = <TargetedResult>[];
1072
1073 /**
1074 * A list of the results that depend on this result.
1075 */
1076 Set<TargetedResult> dependentResults = new Set<TargetedResult>();
1077
1078 /**
1079 * Initialize a newly created result holder to represent the value of data
1080 * described by the given [descriptor].
1081 */
1082 ResultData(this.descriptor) {
1083 state = CacheState.INVALID;
1084 value = descriptor.defaultValue;
1085 }
1086
1087 /**
1088 * Flush this value.
1089 */
1090 void flush() {
1091 state = CacheState.FLUSHED;
1092 value = descriptor.defaultValue;
1093 }
1094 }
1095
1096 /**
1097 * A cache partition that contains all of the targets in the SDK.
1098 */
1099 class SdkCachePartition extends CachePartition {
1100 /**
1101 * Initialize a newly created cache partition, belonging to the given
1102 * [context].
1103 */
1104 SdkCachePartition(InternalAnalysisContext context) : super(context);
1105
1106 @override
1107 bool isResponsibleFor(AnalysisTarget target) {
1108 if (target is AnalysisContextTarget) {
1109 return true;
1110 }
1111 Source source = target.source;
1112 return source != null && source.isInSystemLibrary;
1113 }
1114 }
1115
1116 /**
1117 * A specification of a specific result computed for a specific target.
1118 */
1119 class TargetedResult {
1120 /**
1121 * An empty list of results.
1122 */
1123 static final List<TargetedResult> EMPTY_LIST = const <TargetedResult>[];
1124
1125 /**
1126 * The target with which the result is associated.
1127 */
1128 final AnalysisTarget target;
1129
1130 /**
1131 * The result associated with the target.
1132 */
1133 final ResultDescriptor result;
1134
1135 /**
1136 * Initialize a new targeted result.
1137 */
1138 TargetedResult(this.target, this.result);
1139
1140 @override
1141 int get hashCode {
1142 return JenkinsSmiHash.combine(target.hashCode, result.hashCode);
1143 }
1144
1145 @override
1146 bool operator ==(other) {
1147 return other is TargetedResult &&
1148 other.target == target &&
1149 other.result == result;
1150 }
1151
1152 @override
1153 String toString() => '$result for $target';
1154 }
1155
1156 /**
1157 * A cache partition that contains all targets not contained in other partitions .
1158 */
1159 class UniversalCachePartition extends CachePartition {
1160 /**
1161 * Initialize a newly created cache partition, belonging to the given
1162 * [context].
1163 */
1164 UniversalCachePartition(InternalAnalysisContext context) : super(context);
1165
1166 @override
1167 bool isResponsibleFor(AnalysisTarget target) => true;
1168 }
1169
1170 /**
1171 * [CacheFlushManager] that does nothing, results are never flushed.
1172 */
1173 class UnlimitedCacheFlushManager extends CacheFlushManager {
1174 static final CacheFlushManager INSTANCE = new UnlimitedCacheFlushManager();
1175
1176 UnlimitedCacheFlushManager() : super(DEFAULT_CACHING_POLICY, (_) => false);
1177
1178 @override
1179 void resultAccessed(TargetedResult result) {}
1180
1181 @override
1182 List<TargetedResult> resultStored(TargetedResult newResult, newValue) {
1183 return TargetedResult.EMPTY_LIST;
1184 }
1185
1186 @override
1187 void targetRemoved(AnalysisTarget target) {}
1188 }
OLDNEW
« no previous file with comments | « analyzer/lib/src/cancelable_future.dart ('k') | analyzer/lib/src/context/context.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698