| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library analyzer.src.context.cache; | 5 library analyzer.src.context.cache; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:collection'; | 8 import 'dart:collection'; |
| 9 | 9 |
| 10 import 'package:analyzer/src/generated/engine.dart' | 10 import 'package:analyzer/src/generated/engine.dart' |
| 11 show AnalysisEngine, CacheState, InternalAnalysisContext, RetentionPriority; | 11 show AnalysisEngine, CacheState, InternalAnalysisContext, RetentionPriority; |
| 12 import 'package:analyzer/src/generated/java_engine.dart'; | 12 import 'package:analyzer/src/generated/java_engine.dart'; |
| 13 import 'package:analyzer/src/generated/source.dart'; | 13 import 'package:analyzer/src/generated/source.dart'; |
| 14 import 'package:analyzer/src/generated/utilities_collection.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'; | 15 import 'package:analyzer/src/task/model.dart'; |
| 17 import 'package:analyzer/task/model.dart'; | 16 import 'package:analyzer/task/model.dart'; |
| 18 | 17 |
| 19 /** | 18 /** |
| 20 * Return `true` if the given [target] is a priority one. | 19 * Return `true` if the given [target] is a priority one. |
| 21 */ | 20 */ |
| 22 typedef bool IsPriorityAnalysisTarget(AnalysisTarget target); | 21 typedef bool IsPriorityAnalysisTarget(AnalysisTarget target); |
| 23 | 22 |
| 24 /** | 23 /** |
| 25 * An LRU cache of results produced by analysis. | 24 * An LRU cache of results produced by analysis. |
| 26 */ | 25 */ |
| 27 class AnalysisCache { | 26 class AnalysisCache { |
| 28 /** | 27 /** |
| 29 * A flag used to control whether trace information should be produced when | 28 * A flag used to control whether trace information should be produced when |
| 30 * the content of the cache is modified. | 29 * the content of the cache is modified. |
| 31 */ | 30 */ |
| 32 static bool _TRACE_CHANGES = false; | 31 static bool _TRACE_CHANGES = false; |
| 33 | 32 |
| 34 /** | 33 /** |
| 35 * An array containing the partitions of which this cache is comprised. | 34 * An array containing the partitions of which this cache is comprised. |
| 36 */ | 35 */ |
| 37 final List<CachePartition> _partitions; | 36 final List<CachePartition> _partitions; |
| 38 | 37 |
| 39 /** | 38 /** |
| 40 * The [StreamController] reporting [InvalidatedResult]s. | 39 * The [StreamController] reporting [InvalidatedResult]s. |
| 41 */ | 40 */ |
| 42 final StreamController<InvalidatedResult> _onResultInvalidated = | 41 final ReentrantSynchronousStream<InvalidatedResult> onResultInvalidated = |
| 43 new StreamController<InvalidatedResult>.broadcast(sync: true); | 42 new ReentrantSynchronousStream<InvalidatedResult>(); |
| 44 | 43 |
| 45 /** | 44 /** |
| 46 * Initialize a newly created cache to have the given [partitions]. The | 45 * 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, | 46 * 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 | 47 * so the most specific partition (usually an [SdkCachePartition]) should be |
| 49 * first and the most general (usually a [UniversalCachePartition]) last. | 48 * first and the most general (usually a [UniversalCachePartition]) last. |
| 50 */ | 49 */ |
| 51 AnalysisCache(this._partitions) { | 50 AnalysisCache(this._partitions) { |
| 52 for (CachePartition partition in _partitions) { | 51 for (CachePartition partition in _partitions) { |
| 53 partition.onResultInvalidated.listen((InvalidatedResult event) { | 52 partition.onResultInvalidated.listen((InvalidatedResult event) { |
| 54 _onResultInvalidated.add(event); | 53 onResultInvalidated.add(event); |
| 55 }); | 54 }); |
| 56 } | 55 } |
| 57 } | 56 } |
| 58 | 57 |
| 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. | 58 // TODO(brianwilkerson) Implement or delete this. |
| 66 // /** | 59 // /** |
| 67 // * Return information about each of the partitions in this cache. | 60 // * Return information about each of the partitions in this cache. |
| 68 // */ | 61 // */ |
| 69 // List<AnalysisContextStatistics_PartitionData> get partitionData { | 62 // List<AnalysisContextStatistics_PartitionData> get partitionData { |
| 70 // int count = _partitions.length; | 63 // int count = _partitions.length; |
| 71 // List<AnalysisContextStatistics_PartitionData> data = | 64 // List<AnalysisContextStatistics_PartitionData> data = |
| 72 // new List<AnalysisContextStatistics_PartitionData>(count); | 65 // new List<AnalysisContextStatistics_PartitionData>(count); |
| 73 // for (int i = 0; i < count; i++) { | 66 // for (int i = 0; i < count; i++) { |
| 74 // CachePartition partition = _partitions[i]; | 67 // CachePartition partition = _partitions[i]; |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 166 Object getValue(AnalysisTarget target, ResultDescriptor result) { | 159 Object getValue(AnalysisTarget target, ResultDescriptor result) { |
| 167 CacheEntry entry = get(target); | 160 CacheEntry entry = get(target); |
| 168 if (entry == null) { | 161 if (entry == null) { |
| 169 return result.defaultValue; | 162 return result.defaultValue; |
| 170 } | 163 } |
| 171 return entry.getValue(result); | 164 return entry.getValue(result); |
| 172 } | 165 } |
| 173 | 166 |
| 174 /** | 167 /** |
| 175 * Return an iterator returning all of the map entries mapping targets to | 168 * Return an iterator returning all of the map entries mapping targets to |
| 176 * cache entries. | 169 * cache entries. If the [context] is not `null`, then only entries that are |
| 170 * owned by the given context will be returned. |
| 177 */ | 171 */ |
| 178 MapIterator<AnalysisTarget, CacheEntry> iterator() { | 172 MapIterator<AnalysisTarget, CacheEntry> iterator( |
| 179 int count = _partitions.length; | 173 {InternalAnalysisContext context: null}) { |
| 180 List<Map<AnalysisTarget, CacheEntry>> maps = | 174 List<Map<AnalysisTarget, CacheEntry>> maps = |
| 181 new List<Map<AnalysisTarget, CacheEntry>>(count); | 175 <Map<AnalysisTarget, CacheEntry>>[]; |
| 182 for (int i = 0; i < count; i++) { | 176 for (CachePartition partition in _partitions) { |
| 183 maps[i] = _partitions[i].map; | 177 if (context == null || partition.context == context) { |
| 178 maps.add(partition.map); |
| 179 } |
| 184 } | 180 } |
| 185 return new MultipleMapIterator<AnalysisTarget, CacheEntry>(maps); | 181 return new MultipleMapIterator<AnalysisTarget, CacheEntry>(maps); |
| 186 } | 182 } |
| 187 | 183 |
| 188 /** | 184 /** |
| 189 * Puts the given [entry] into the cache. | 185 * Puts the given [entry] into the cache. |
| 190 */ | 186 */ |
| 191 void put(CacheEntry entry) { | 187 void put(CacheEntry entry) { |
| 192 AnalysisTarget target = entry.target; | 188 AnalysisTarget target = entry.target; |
| 193 entry.fixExceptionState(); | 189 entry.fixExceptionState(); |
| (...skipping 15 matching lines...) Expand all Loading... |
| 209 partition.put(entry); | 205 partition.put(entry); |
| 210 return; | 206 return; |
| 211 } | 207 } |
| 212 } | 208 } |
| 213 // TODO(brianwilkerson) Handle the case where no partition was found, | 209 // TODO(brianwilkerson) Handle the case where no partition was found, |
| 214 // possibly by throwing an exception. | 210 // possibly by throwing an exception. |
| 215 } | 211 } |
| 216 | 212 |
| 217 /** | 213 /** |
| 218 * Remove all information related to the given [target] from this cache. | 214 * Remove all information related to the given [target] from this cache. |
| 215 * Return the entry associated with the target, or `null` if there was cache |
| 216 * entry for the target. |
| 219 */ | 217 */ |
| 220 void remove(AnalysisTarget target) { | 218 CacheEntry remove(AnalysisTarget target) { |
| 221 int count = _partitions.length; | 219 int count = _partitions.length; |
| 222 for (int i = 0; i < count; i++) { | 220 for (int i = 0; i < count; i++) { |
| 223 CachePartition partition = _partitions[i]; | 221 CachePartition partition = _partitions[i]; |
| 224 if (partition.isResponsibleFor(target)) { | 222 if (partition.isResponsibleFor(target)) { |
| 225 if (_TRACE_CHANGES) { | 223 if (_TRACE_CHANGES) { |
| 226 AnalysisEngine.instance.logger | 224 AnalysisEngine.instance.logger |
| 227 .logInformation('Removed the cache entry for $target.'); | 225 .logInformation('Removed the cache entry for $target.'); |
| 228 } | 226 } |
| 229 partition.remove(target); | 227 return partition.remove(target); |
| 230 return; | |
| 231 } | 228 } |
| 232 } | 229 } |
| 230 return null; |
| 233 } | 231 } |
| 234 | 232 |
| 235 /** | 233 /** |
| 236 * Return the number of targets that are mapped to cache entries. | 234 * Return the number of targets that are mapped to cache entries. |
| 237 */ | 235 */ |
| 238 int size() { | 236 int size() { |
| 239 int size = 0; | 237 int size = 0; |
| 240 int count = _partitions.length; | 238 int count = _partitions.length; |
| 241 for (int i = 0; i < count; i++) { | 239 for (int i = 0; i < count; i++) { |
| 242 size += _partitions[i].size(); | 240 size += _partitions[i].size(); |
| 243 } | 241 } |
| 244 return size; | 242 return size; |
| 245 } | 243 } |
| 246 } | 244 } |
| 247 | 245 |
| 248 /** | 246 /** |
| 249 * The information cached by an analysis context about an individual target. | 247 * The information cached by an analysis context about an individual target. |
| 250 */ | 248 */ |
| 251 class CacheEntry { | 249 class CacheEntry { |
| 252 /** | 250 /** |
| 253 * The index of the flag indicating whether the source was explicitly added to | 251 * 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 | 252 * the context or whether the source was implicitly added because it was |
| 255 * referenced by another source. | 253 * referenced by another source. |
| 256 */ | 254 */ |
| 257 static int _EXPLICITLY_ADDED_FLAG = 0; | 255 static int _EXPLICITLY_ADDED_FLAG = 0; |
| 258 | 256 |
| 259 /** | 257 /** |
| 258 * The next invalidation process identifier. |
| 259 */ |
| 260 static int nextInvalidateId = 0; |
| 261 |
| 262 /** |
| 260 * The target this entry is about. | 263 * The target this entry is about. |
| 261 */ | 264 */ |
| 262 final AnalysisTarget target; | 265 final AnalysisTarget target; |
| 263 | 266 |
| 264 /** | 267 /** |
| 265 * The partition that is responsible for this entry. | 268 * The partition that is responsible for this entry. |
| 266 */ | 269 */ |
| 267 CachePartition _partition; | 270 CachePartition _partition; |
| 268 | 271 |
| 269 /** | 272 /** |
| (...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 438 if (state == CacheState.ERROR) { | 441 if (state == CacheState.ERROR) { |
| 439 throw new ArgumentError('use setErrorState() to set the state to ERROR'); | 442 throw new ArgumentError('use setErrorState() to set the state to ERROR'); |
| 440 } | 443 } |
| 441 if (state == CacheState.VALID) { | 444 if (state == CacheState.VALID) { |
| 442 throw new ArgumentError('use setValue() to set the state to VALID'); | 445 throw new ArgumentError('use setValue() to set the state to VALID'); |
| 443 } | 446 } |
| 444 _validateStateChange(descriptor, state); | 447 _validateStateChange(descriptor, state); |
| 445 if (state == CacheState.INVALID) { | 448 if (state == CacheState.INVALID) { |
| 446 ResultData data = _resultMap[descriptor]; | 449 ResultData data = _resultMap[descriptor]; |
| 447 if (data != null) { | 450 if (data != null) { |
| 448 _invalidate(descriptor, delta); | 451 _invalidate(nextInvalidateId++, descriptor, delta, 0); |
| 449 } | 452 } |
| 450 } else { | 453 } else { |
| 451 ResultData data = getResultData(descriptor); | 454 ResultData data = getResultData(descriptor); |
| 452 data.state = state; | 455 data.state = state; |
| 453 if (state != CacheState.IN_PROCESS) { | 456 if (state != CacheState.IN_PROCESS) { |
| 454 // | 457 // |
| 455 // If the state is in-process, we can leave the current value in the | 458 // If the state is in-process, we can leave the current value in the |
| 456 // cache for any 'get' methods to access. | 459 // cache for any 'get' methods to access. |
| 457 // | 460 // |
| 458 data.value = descriptor.defaultValue; | 461 data.value = descriptor.defaultValue; |
| 459 } | 462 } |
| 460 } | 463 } |
| 461 } | 464 } |
| 462 | 465 |
| 463 /** | 466 /** |
| 464 * Set the value of the result represented by the given [descriptor] to the | 467 * Set the value of the result represented by the given [descriptor] to the |
| 465 * given [value]. | 468 * given [value]. |
| 466 */ | 469 */ |
| 467 /*<V>*/ void setValue(ResultDescriptor /*<V>*/ descriptor, dynamic /*V*/ | 470 /*<V>*/ void setValue( |
| 468 value, List<TargetedResult> dependedOn) { | 471 ResultDescriptor /*<V>*/ descriptor, |
| 472 dynamic /*V*/ |
| 473 value, |
| 474 List<TargetedResult> dependedOn) { |
| 469 // { | 475 // { |
| 470 // String valueStr = '$value'; | 476 // String valueStr = '$value'; |
| 471 // if (valueStr.length > 20) { | 477 // if (valueStr.length > 20) { |
| 472 // valueStr = valueStr.substring(0, 20) + '...'; | 478 // valueStr = valueStr.substring(0, 20) + '...'; |
| 473 // } | 479 // } |
| 474 // valueStr = valueStr.replaceAll('\n', '\\n'); | 480 // valueStr = valueStr.replaceAll('\n', '\\n'); |
| 475 // print( | 481 // print( |
| 476 // 'setValue $descriptor for $target value=$valueStr deps=$dependedOn')
; | 482 // 'setValue $descriptor for $target value=$valueStr deps=$dependedOn')
; |
| 477 // } | 483 // } |
| 478 _validateStateChange(descriptor, CacheState.VALID); | 484 _validateStateChange(descriptor, CacheState.VALID); |
| 479 TargetedResult thisResult = new TargetedResult(target, descriptor); | 485 TargetedResult thisResult = new TargetedResult(target, descriptor); |
| 480 if (_partition != null) { | 486 if (_partition != null) { |
| 481 _partition.resultStored(thisResult, value); | 487 _partition.resultStored(thisResult, value); |
| 482 } | 488 } |
| 483 ResultData data = getResultData(descriptor); | 489 ResultData data = getResultData(descriptor); |
| 484 _setDependedOnResults(data, thisResult, dependedOn); | 490 _setDependedOnResults(data, thisResult, dependedOn); |
| 485 data.state = CacheState.VALID; | 491 data.state = CacheState.VALID; |
| 486 data.value = value == null ? descriptor.defaultValue : value; | 492 data.value = value == null ? descriptor.defaultValue : value; |
| 487 } | 493 } |
| 488 | 494 |
| 489 /** | 495 /** |
| 490 * Set the value of the result represented by the given [descriptor] to the | 496 * Set the value of the result represented by the given [descriptor] to the |
| 491 * given [value], keep its dependency, invalidate all the dependent result. | 497 * given [value], keep its dependency, invalidate all the dependent result. |
| 492 */ | 498 */ |
| 493 void setValueIncremental(ResultDescriptor descriptor, dynamic value) { | 499 void setValueIncremental( |
| 500 ResultDescriptor descriptor, dynamic value, bool invalidateDependent) { |
| 494 ResultData data = getResultData(descriptor); | 501 ResultData data = getResultData(descriptor); |
| 495 List<TargetedResult> dependedOn = data.dependedOnResults; | 502 data.state = CacheState.VALID; |
| 496 _invalidate(descriptor, null); | 503 data.value = value; |
| 497 setValue(descriptor, value, dependedOn); | 504 if (invalidateDependent) { |
| 505 _invalidateDependentResults(nextInvalidateId++, data, null, 0); |
| 506 } |
| 498 } | 507 } |
| 499 | 508 |
| 500 @override | 509 @override |
| 501 String toString() { | 510 String toString() { |
| 502 StringBuffer buffer = new StringBuffer(); | 511 StringBuffer buffer = new StringBuffer(); |
| 503 _writeOn(buffer); | 512 _writeOn(buffer); |
| 504 return buffer.toString(); | 513 return buffer.toString(); |
| 505 } | 514 } |
| 506 | 515 |
| 507 /** | 516 /** |
| 508 * Return the value of the flag with the given [index]. | 517 * Return the value of the flag with the given [index]. |
| 509 */ | 518 */ |
| 510 bool _getFlag(int index) => BooleanArray.get(_flags, index); | 519 bool _getFlag(int index) => BooleanArray.get(_flags, index); |
| 511 | 520 |
| 512 /** | 521 /** |
| 513 * Invalidate the result represented by the given [descriptor] and propagate | 522 * Invalidate the result represented by the given [descriptor] and propagate |
| 514 * invalidation to other results that depend on it. | 523 * invalidation to other results that depend on it. |
| 515 */ | 524 */ |
| 516 void _invalidate(ResultDescriptor descriptor, Delta delta) { | 525 void _invalidate( |
| 526 int id, ResultDescriptor descriptor, Delta delta, int level) { |
| 527 ResultData thisData = _resultMap[descriptor]; |
| 528 if (thisData == null) { |
| 529 return; |
| 530 } |
| 531 // Stop if already validated. |
| 532 if (delta != null) { |
| 533 if (thisData.invalidateId == id) { |
| 534 return; |
| 535 } |
| 536 thisData.invalidateId = id; |
| 537 } |
| 538 // Ask the delta to validate. |
| 517 DeltaResult deltaResult = null; | 539 DeltaResult deltaResult = null; |
| 518 if (delta != null) { | 540 if (delta != null) { |
| 519 deltaResult = delta.validate(_partition.context, target, descriptor); | 541 deltaResult = delta.validate(_partition.context, target, descriptor); |
| 520 if (deltaResult == DeltaResult.STOP) { | 542 if (deltaResult == DeltaResult.STOP) { |
| 521 // print('not-invalidate $descriptor for $target'); | |
| 522 return; | 543 return; |
| 523 } | 544 } |
| 524 } | 545 } |
| 525 // print('invalidate $descriptor for $target'); | 546 if (deltaResult == DeltaResult.INVALIDATE_NO_DELTA) { |
| 526 ResultData thisData; | 547 delta = null; |
| 527 if (deltaResult == null || deltaResult == DeltaResult.INVALIDATE) { | |
| 528 thisData = _resultMap.remove(descriptor); | |
| 529 } | 548 } |
| 530 if (deltaResult == DeltaResult.KEEP_CONTINUE) { | 549 if (deltaResult == null || |
| 531 thisData = _resultMap[descriptor]; | 550 deltaResult == DeltaResult.INVALIDATE || |
| 532 } | 551 deltaResult == DeltaResult.INVALIDATE_NO_DELTA) { |
| 533 if (thisData == null) { | 552 _resultMap.remove(descriptor); |
| 534 return; | 553 // { |
| 554 // String indent = ' ' * level; |
| 555 // print('[$id]$indent invalidate $descriptor for $target'); |
| 556 // } |
| 535 } | 557 } |
| 536 // Stop depending on other results. | 558 // Stop depending on other results. |
| 537 TargetedResult thisResult = new TargetedResult(target, descriptor); | 559 TargetedResult thisResult = new TargetedResult(target, descriptor); |
| 538 for (TargetedResult dependedOnResult in thisData.dependedOnResults) { | 560 for (TargetedResult dependedOnResult in thisData.dependedOnResults) { |
| 539 ResultData data = _partition._getDataFor(dependedOnResult); | 561 ResultData data = _partition._getDataFor(dependedOnResult); |
| 540 if (data != null) { | 562 if (data != null && deltaResult != DeltaResult.KEEP_CONTINUE) { |
| 541 data.dependentResults.remove(thisResult); | 563 data.dependentResults.remove(thisResult); |
| 542 } | 564 } |
| 543 } | 565 } |
| 544 // Invalidate results that depend on this result. | 566 // Invalidate results that depend on this result. |
| 545 List<TargetedResult> dependentResults = thisData.dependentResults.toList(); | 567 _invalidateDependentResults(id, thisData, delta, level + 1); |
| 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. | 568 // If empty, remove the entry altogether. |
| 553 if (_resultMap.isEmpty) { | 569 if (_resultMap.isEmpty) { |
| 554 _partition._targetMap.remove(target); | 570 _partition._targetMap.remove(target); |
| 555 _partition._removeIfSource(target); | 571 _partition._removeIfSource(target); |
| 556 } | 572 } |
| 557 // Notify controller. | 573 // Notify controller. |
| 558 _partition._onResultInvalidated | 574 _partition.onResultInvalidated |
| 559 .add(new InvalidatedResult(this, descriptor)); | 575 .add(new InvalidatedResult(this, descriptor, thisData.value)); |
| 560 } | 576 } |
| 561 | 577 |
| 562 /** | 578 /** |
| 563 * Invalidates all the results of this entry, with propagation. | 579 * Invalidates all the results of this entry, with propagation. |
| 564 */ | 580 */ |
| 565 void _invalidateAll() { | 581 void _invalidateAll() { |
| 566 List<ResultDescriptor> results = _resultMap.keys.toList(); | 582 List<ResultDescriptor> results = _resultMap.keys.toList(); |
| 567 for (ResultDescriptor result in results) { | 583 for (ResultDescriptor result in results) { |
| 568 _invalidate(result, null); | 584 _invalidate(nextInvalidateId++, result, null, 0); |
| 569 } | 585 } |
| 570 } | 586 } |
| 571 | 587 |
| 588 /** |
| 589 * Invalidate results that depend on [thisData]. |
| 590 */ |
| 591 void _invalidateDependentResults( |
| 592 int id, ResultData thisData, Delta delta, int level) { |
| 593 List<TargetedResult> dependentResults = thisData.dependentResults.toList(); |
| 594 for (TargetedResult dependentResult in dependentResults) { |
| 595 CacheEntry entry = _partition.get(dependentResult.target); |
| 596 if (entry != null) { |
| 597 entry._invalidate(id, dependentResult.result, delta, level); |
| 598 } |
| 599 } |
| 600 } |
| 601 |
| 572 /** | 602 /** |
| 573 * Set the [dependedOn] on which this result depends. | 603 * Set the [dependedOn] on which this result depends. |
| 574 */ | 604 */ |
| 575 void _setDependedOnResults(ResultData thisData, TargetedResult thisResult, | 605 void _setDependedOnResults(ResultData thisData, TargetedResult thisResult, |
| 576 List<TargetedResult> dependedOn) { | 606 List<TargetedResult> dependedOn) { |
| 577 thisData.dependedOnResults.forEach((TargetedResult dependedOnResult) { | 607 thisData.dependedOnResults.forEach((TargetedResult dependedOnResult) { |
| 578 ResultData data = _partition._getDataFor(dependedOnResult); | 608 ResultData data = _partition._getDataFor(dependedOnResult); |
| 579 if (data != null) { | 609 if (data != null) { |
| 580 data.dependentResults.remove(thisResult); | 610 data.dependentResults.remove(thisResult); |
| 581 } | 611 } |
| (...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 796 | 826 |
| 797 /** | 827 /** |
| 798 * A table mapping caching policies to the cache flush managers. | 828 * A table mapping caching policies to the cache flush managers. |
| 799 */ | 829 */ |
| 800 final HashMap<ResultCachingPolicy, CacheFlushManager> _flushManagerMap = | 830 final HashMap<ResultCachingPolicy, CacheFlushManager> _flushManagerMap = |
| 801 new HashMap<ResultCachingPolicy, CacheFlushManager>(); | 831 new HashMap<ResultCachingPolicy, CacheFlushManager>(); |
| 802 | 832 |
| 803 /** | 833 /** |
| 804 * The [StreamController] reporting [InvalidatedResult]s. | 834 * The [StreamController] reporting [InvalidatedResult]s. |
| 805 */ | 835 */ |
| 806 final StreamController<InvalidatedResult> _onResultInvalidated = | 836 final ReentrantSynchronousStream<InvalidatedResult> onResultInvalidated = |
| 807 new StreamController<InvalidatedResult>.broadcast(sync: true); | 837 new ReentrantSynchronousStream<InvalidatedResult>(); |
| 808 | 838 |
| 809 /** | 839 /** |
| 810 * A table mapping the targets belonging to this partition to the information | 840 * A table mapping the targets belonging to this partition to the information |
| 811 * known about those targets. | 841 * known about those targets. |
| 812 */ | 842 */ |
| 813 HashMap<AnalysisTarget, CacheEntry> _targetMap = | 843 HashMap<AnalysisTarget, CacheEntry> _targetMap = |
| 814 new HashMap<AnalysisTarget, CacheEntry>(); | 844 new HashMap<AnalysisTarget, CacheEntry>(); |
| 815 | 845 |
| 816 /** | 846 /** |
| 817 * A set of the [Source] targets. | 847 * A set of the [Source] targets. |
| (...skipping 14 matching lines...) Expand all Loading... |
| 832 /** | 862 /** |
| 833 * Return a table mapping the targets known to the context to the information | 863 * Return a table mapping the targets known to the context to the information |
| 834 * known about the target. | 864 * known about the target. |
| 835 * | 865 * |
| 836 * <b>Note:</b> This method is only visible for use by [AnalysisCache] and | 866 * <b>Note:</b> This method is only visible for use by [AnalysisCache] and |
| 837 * should not be used for any other purpose. | 867 * should not be used for any other purpose. |
| 838 */ | 868 */ |
| 839 Map<AnalysisTarget, CacheEntry> get map => _targetMap; | 869 Map<AnalysisTarget, CacheEntry> get map => _targetMap; |
| 840 | 870 |
| 841 /** | 871 /** |
| 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. | 872 * Notifies the partition that the client is going to stop using it. |
| 849 */ | 873 */ |
| 850 void dispose() { | 874 void dispose() { |
| 851 for (CacheEntry entry in _targetMap.values) { | 875 for (CacheEntry entry in _targetMap.values) { |
| 852 entry.dispose(); | 876 entry.dispose(); |
| 853 } | 877 } |
| 854 _targetMap.clear(); | 878 _targetMap.clear(); |
| 855 } | 879 } |
| 856 | 880 |
| 857 /** | 881 /** |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 889 throw new StateError( | 913 throw new StateError( |
| 890 'The entry for $target is already in ${entry._partition}'); | 914 'The entry for $target is already in ${entry._partition}'); |
| 891 } | 915 } |
| 892 entry._partition = this; | 916 entry._partition = this; |
| 893 entry.fixExceptionState(); | 917 entry.fixExceptionState(); |
| 894 _targetMap[target] = entry; | 918 _targetMap[target] = entry; |
| 895 _addIfSource(target); | 919 _addIfSource(target); |
| 896 } | 920 } |
| 897 | 921 |
| 898 /** | 922 /** |
| 899 * Remove all information related to the given [target] from this cache. | 923 * Remove all information related to the given [target] from this partition. |
| 924 * Return the entry associated with the target, or `null` if there was cache |
| 925 * entry for the target. |
| 900 */ | 926 */ |
| 901 void remove(AnalysisTarget target) { | 927 CacheEntry remove(AnalysisTarget target) { |
| 902 for (CacheFlushManager flushManager in _flushManagerMap.values) { | 928 for (CacheFlushManager flushManager in _flushManagerMap.values) { |
| 903 flushManager.targetRemoved(target); | 929 flushManager.targetRemoved(target); |
| 904 } | 930 } |
| 905 CacheEntry entry = _targetMap.remove(target); | 931 CacheEntry entry = _targetMap.remove(target); |
| 906 if (entry != null) { | 932 if (entry != null) { |
| 907 entry._invalidateAll(); | 933 entry._invalidateAll(); |
| 908 } | 934 } |
| 909 _removeIfSource(target); | 935 _removeIfSource(target); |
| 936 return entry; |
| 910 } | 937 } |
| 911 | 938 |
| 912 /** | 939 /** |
| 913 * Records that a value of the result described by the given [descriptor] | 940 * Records that a value of the result described by the given [descriptor] |
| 914 * for the given [target] was just read from the cache. | 941 * for the given [target] was just read from the cache. |
| 915 */ | 942 */ |
| 916 void resultAccessed(AnalysisTarget target, ResultDescriptor descriptor) { | 943 void resultAccessed(AnalysisTarget target, ResultDescriptor descriptor) { |
| 917 CacheFlushManager flushManager = _getFlushManager(descriptor); | 944 CacheFlushManager flushManager = _getFlushManager(descriptor); |
| 918 TargetedResult result = new TargetedResult(target, descriptor); | 945 TargetedResult result = new TargetedResult(target, descriptor); |
| 919 flushManager.resultAccessed(result); | 946 flushManager.resultAccessed(result); |
| (...skipping 21 matching lines...) Expand all Loading... |
| 941 * Return the number of targets that are mapped to cache entries. | 968 * Return the number of targets that are mapped to cache entries. |
| 942 */ | 969 */ |
| 943 int size() => _targetMap.length; | 970 int size() => _targetMap.length; |
| 944 | 971 |
| 945 /** | 972 /** |
| 946 * If the given [target] is a [Source], adds it to [_sources]. | 973 * If the given [target] is a [Source], adds it to [_sources]. |
| 947 */ | 974 */ |
| 948 void _addIfSource(AnalysisTarget target) { | 975 void _addIfSource(AnalysisTarget target) { |
| 949 if (target is Source) { | 976 if (target is Source) { |
| 950 _sources.add(target); | 977 _sources.add(target); |
| 951 { | 978 String fullName = target.fullName; |
| 952 String fullName = target.fullName; | 979 _pathToSources.putIfAbsent(fullName, () => <Source>[]).add(target); |
| 953 _pathToSources.putIfAbsent(fullName, () => <Source>[]).add(target); | |
| 954 } | |
| 955 } | 980 } |
| 956 } | 981 } |
| 957 | 982 |
| 958 ResultData _getDataFor(TargetedResult result) { | 983 ResultData _getDataFor(TargetedResult result) { |
| 959 CacheEntry entry = context.analysisCache.get(result.target); | 984 CacheEntry entry = context.analysisCache.get(result.target); |
| 960 return entry != null ? entry._resultMap[result.result] : null; | 985 return entry != null ? entry._resultMap[result.result] : null; |
| 961 } | 986 } |
| 962 | 987 |
| 963 /** | 988 /** |
| 964 * Return the [CacheFlushManager] for the given [descriptor], not `null`. | 989 * Return the [CacheFlushManager] for the given [descriptor], not `null`. |
| 965 */ | 990 */ |
| 966 CacheFlushManager _getFlushManager(ResultDescriptor descriptor) { | 991 CacheFlushManager _getFlushManager(ResultDescriptor descriptor) { |
| 967 ResultCachingPolicy policy = descriptor.cachingPolicy; | 992 ResultCachingPolicy policy = descriptor.cachingPolicy; |
| 968 if (identical(policy, DEFAULT_CACHING_POLICY)) { | 993 if (identical(policy, DEFAULT_CACHING_POLICY)) { |
| 969 return UnlimitedCacheFlushManager.INSTANCE; | 994 return UnlimitedCacheFlushManager.INSTANCE; |
| 970 } | 995 } |
| 971 CacheFlushManager manager = _flushManagerMap[policy]; | 996 CacheFlushManager manager = _flushManagerMap[policy]; |
| 972 if (manager == null) { | 997 if (manager == null) { |
| 973 manager = new CacheFlushManager(policy, _isPriorityAnalysisTarget); | 998 manager = new CacheFlushManager(policy, _isPriorityAnalysisTarget); |
| 974 _flushManagerMap[policy] = manager; | 999 _flushManagerMap[policy] = manager; |
| 975 } | 1000 } |
| 976 return manager; | 1001 return manager; |
| 977 } | 1002 } |
| 978 | 1003 |
| 979 bool _isPriorityAnalysisTarget(AnalysisTarget target) { | 1004 bool _isPriorityAnalysisTarget(AnalysisTarget target) { |
| 980 return context.priorityTargets.contains(target); | 1005 return context.priorityTargets.contains(target); |
| 981 } | 1006 } |
| 982 | 1007 |
| 983 /** | 1008 /** |
| 984 * If the given [target] is a [Source], removes it from [_sources]. | 1009 * If the given [target] is a [Source], remove it from the list of [_sources]. |
| 985 */ | 1010 */ |
| 986 void _removeIfSource(AnalysisTarget target) { | 1011 void _removeIfSource(AnalysisTarget target) { |
| 987 if (target is Source) { | 1012 if (target is Source) { |
| 988 _sources.remove(target); | 1013 _sources.remove(target); |
| 989 { | 1014 String fullName = target.fullName; |
| 990 String fullName = target.fullName; | 1015 List<Source> sources = _pathToSources[fullName]; |
| 991 List<Source> sources = _pathToSources[fullName]; | 1016 if (sources != null) { |
| 992 if (sources != null) { | 1017 sources.remove(target); |
| 993 sources.remove(target); | 1018 if (sources.isEmpty) { |
| 994 if (sources.isEmpty) { | 1019 _pathToSources.remove(fullName); |
| 995 _pathToSources.remove(fullName); | |
| 996 } | |
| 997 } | 1020 } |
| 998 } | 1021 } |
| 999 } | 1022 } |
| 1000 } | 1023 } |
| 1001 } | 1024 } |
| 1002 | 1025 |
| 1003 /** | 1026 /** |
| 1004 * The description for a change. | 1027 * The description for a change. |
| 1005 */ | 1028 */ |
| 1006 class Delta { | 1029 class Delta { |
| 1007 final Source source; | 1030 final Source source; |
| 1008 | 1031 |
| 1009 Delta(this.source); | 1032 Delta(this.source); |
| 1010 | 1033 |
| 1011 /** | 1034 /** |
| 1012 * Check whether this delta affects the result described by the given | 1035 * Check whether this delta affects the result described by the given |
| 1013 * [descriptor] and [target]. | 1036 * [descriptor] and [target]. |
| 1014 */ | 1037 */ |
| 1015 DeltaResult validate(InternalAnalysisContext context, AnalysisTarget target, | 1038 DeltaResult validate(InternalAnalysisContext context, AnalysisTarget target, |
| 1016 ResultDescriptor descriptor) { | 1039 ResultDescriptor descriptor) { |
| 1017 return DeltaResult.INVALIDATE; | 1040 return DeltaResult.INVALIDATE; |
| 1018 } | 1041 } |
| 1019 } | 1042 } |
| 1020 | 1043 |
| 1021 /** | 1044 /** |
| 1022 * The possible results of validating analysis results againt a [Delta]. | 1045 * The possible results of validating analysis results againt a [Delta]. |
| 1023 */ | 1046 */ |
| 1024 enum DeltaResult { INVALIDATE, KEEP_CONTINUE, STOP } | 1047 enum DeltaResult { |
| 1048 /** |
| 1049 * Invalidate this result and continue visiting dependent results |
| 1050 * with this [Delta]. |
| 1051 */ |
| 1052 INVALIDATE, |
| 1053 |
| 1054 /** |
| 1055 * Invalidate this result and stop using this [Delta], so unconditionally |
| 1056 * invalidate all the dependent results. |
| 1057 */ |
| 1058 INVALIDATE_NO_DELTA, |
| 1059 |
| 1060 /** |
| 1061 * Keep this result and continue validating dependent results |
| 1062 * with this [Delta]. |
| 1063 */ |
| 1064 KEEP_CONTINUE, |
| 1065 |
| 1066 /** |
| 1067 * Keep this result and stop visiting results that depend on this one. |
| 1068 */ |
| 1069 STOP |
| 1070 } |
| 1025 | 1071 |
| 1026 /** | 1072 /** |
| 1027 * [InvalidatedResult] describes an invalidated result. | 1073 * [InvalidatedResult] describes an invalidated result. |
| 1028 */ | 1074 */ |
| 1029 class InvalidatedResult { | 1075 class InvalidatedResult { |
| 1030 /** | 1076 /** |
| 1031 * The target in which the result was invalidated. | 1077 * The target in which the result was invalidated. |
| 1032 */ | 1078 */ |
| 1033 final CacheEntry entry; | 1079 final CacheEntry entry; |
| 1034 | 1080 |
| 1035 /** | 1081 /** |
| 1036 * The descriptor of the result which was invalidated. | 1082 * The descriptor of the result which was invalidated. |
| 1037 */ | 1083 */ |
| 1038 final ResultDescriptor descriptor; | 1084 final ResultDescriptor descriptor; |
| 1039 | 1085 |
| 1040 InvalidatedResult(this.entry, this.descriptor); | 1086 /** |
| 1087 * The value of the result which was invalidated. |
| 1088 */ |
| 1089 final Object value; |
| 1090 |
| 1091 InvalidatedResult(this.entry, this.descriptor, this.value); |
| 1041 | 1092 |
| 1042 @override | 1093 @override |
| 1043 String toString() => '$descriptor of ${entry.target}'; | 1094 String toString() => '$descriptor of ${entry.target}'; |
| 1044 } | 1095 } |
| 1045 | 1096 |
| 1046 /** | 1097 /** |
| 1098 * A Stream-like interface, which broadcasts events synchronously. |
| 1099 * If a second event is fired while delivering a first event, then the second |
| 1100 * event will be delivered first, and then delivering of the first will be |
| 1101 * continued. |
| 1102 */ |
| 1103 class ReentrantSynchronousStream<T> { |
| 1104 final List<Function> listeners = <Function>[]; |
| 1105 |
| 1106 /** |
| 1107 * Send the given [event] to the stream. |
| 1108 */ |
| 1109 void add(T event) { |
| 1110 List<Function> listeners = this.listeners.toList(); |
| 1111 for (Function listener in listeners) { |
| 1112 listener(event); |
| 1113 } |
| 1114 } |
| 1115 |
| 1116 /** |
| 1117 * Listen for the events in this stream. |
| 1118 * Note that if the [listener] fires a new event, then the [listener] will be |
| 1119 * invoked again before returning from the [add] invocation. |
| 1120 */ |
| 1121 void listen(void listener(T event)) { |
| 1122 listeners.add(listener); |
| 1123 } |
| 1124 } |
| 1125 |
| 1126 /** |
| 1047 * The data about a single analysis result that is stored in a [CacheEntry]. | 1127 * The data about a single analysis result that is stored in a [CacheEntry]. |
| 1048 */ | 1128 */ |
| 1049 // TODO(brianwilkerson) Consider making this a generic class so that the value | 1129 // TODO(brianwilkerson) Consider making this a generic class so that the value |
| 1050 // can be typed. | 1130 // can be typed. |
| 1051 class ResultData { | 1131 class ResultData { |
| 1052 /** | 1132 /** |
| 1053 * The [ResultDescriptor] this result is for. | 1133 * The [ResultDescriptor] this result is for. |
| 1054 */ | 1134 */ |
| 1055 final ResultDescriptor descriptor; | 1135 final ResultDescriptor descriptor; |
| 1056 | 1136 |
| 1057 /** | 1137 /** |
| 1058 * The state of the cached value. | 1138 * The state of the cached value. |
| 1059 */ | 1139 */ |
| 1060 CacheState state; | 1140 CacheState state; |
| 1061 | 1141 |
| 1062 /** | 1142 /** |
| 1063 * The value being cached, or the default value for the result if there is no | 1143 * 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]). | 1144 * value (for example, when the [state] is [CacheState.INVALID]). |
| 1065 */ | 1145 */ |
| 1066 Object value; | 1146 Object value; |
| 1067 | 1147 |
| 1068 /** | 1148 /** |
| 1149 * The identifier of the invalidation process that most recently checked |
| 1150 * this value. If it is the same as the current invalidation identifier, |
| 1151 * then there is no reason to check it (and its subtree again). |
| 1152 */ |
| 1153 int invalidateId = -1; |
| 1154 |
| 1155 /** |
| 1069 * A list of the results on which this result depends. | 1156 * A list of the results on which this result depends. |
| 1070 */ | 1157 */ |
| 1071 List<TargetedResult> dependedOnResults = <TargetedResult>[]; | 1158 List<TargetedResult> dependedOnResults = <TargetedResult>[]; |
| 1072 | 1159 |
| 1073 /** | 1160 /** |
| 1074 * A list of the results that depend on this result. | 1161 * A list of the results that depend on this result. |
| 1075 */ | 1162 */ |
| 1076 Set<TargetedResult> dependentResults = new Set<TargetedResult>(); | 1163 Set<TargetedResult> dependentResults = new Set<TargetedResult>(); |
| 1077 | 1164 |
| 1078 /** | 1165 /** |
| (...skipping 28 matching lines...) Expand all Loading... |
| 1107 bool isResponsibleFor(AnalysisTarget target) { | 1194 bool isResponsibleFor(AnalysisTarget target) { |
| 1108 if (target is AnalysisContextTarget) { | 1195 if (target is AnalysisContextTarget) { |
| 1109 return true; | 1196 return true; |
| 1110 } | 1197 } |
| 1111 Source source = target.source; | 1198 Source source = target.source; |
| 1112 return source != null && source.isInSystemLibrary; | 1199 return source != null && source.isInSystemLibrary; |
| 1113 } | 1200 } |
| 1114 } | 1201 } |
| 1115 | 1202 |
| 1116 /** | 1203 /** |
| 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
. | 1204 * A cache partition that contains all targets not contained in other partitions
. |
| 1158 */ | 1205 */ |
| 1159 class UniversalCachePartition extends CachePartition { | 1206 class UniversalCachePartition extends CachePartition { |
| 1160 /** | 1207 /** |
| 1161 * Initialize a newly created cache partition, belonging to the given | 1208 * Initialize a newly created cache partition, belonging to the given |
| 1162 * [context]. | 1209 * [context]. |
| 1163 */ | 1210 */ |
| 1164 UniversalCachePartition(InternalAnalysisContext context) : super(context); | 1211 UniversalCachePartition(InternalAnalysisContext context) : super(context); |
| 1165 | 1212 |
| 1166 @override | 1213 @override |
| (...skipping 12 matching lines...) Expand all Loading... |
| 1179 void resultAccessed(TargetedResult result) {} | 1226 void resultAccessed(TargetedResult result) {} |
| 1180 | 1227 |
| 1181 @override | 1228 @override |
| 1182 List<TargetedResult> resultStored(TargetedResult newResult, newValue) { | 1229 List<TargetedResult> resultStored(TargetedResult newResult, newValue) { |
| 1183 return TargetedResult.EMPTY_LIST; | 1230 return TargetedResult.EMPTY_LIST; |
| 1184 } | 1231 } |
| 1185 | 1232 |
| 1186 @override | 1233 @override |
| 1187 void targetRemoved(AnalysisTarget target) {} | 1234 void targetRemoved(AnalysisTarget target) {} |
| 1188 } | 1235 } |
| OLD | NEW |