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

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

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

Powered by Google App Engine
This is Rietveld 408576698