OLD | NEW |
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, 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 import 'dart:async'; | 5 import 'dart:async'; |
6 import 'dart:collection'; | 6 import 'dart:collection'; |
7 import 'dart:typed_data'; | 7 import 'dart:typed_data'; |
8 | 8 |
9 import 'package:analyzer/context/context_root.dart'; | 9 import 'package:analyzer/context/context_root.dart'; |
10 import 'package:analyzer/context/declared_variables.dart'; | 10 import 'package:analyzer/context/declared_variables.dart'; |
(...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
231 Search _search; | 231 Search _search; |
232 | 232 |
233 AnalysisDriverTestView _testView; | 233 AnalysisDriverTestView _testView; |
234 | 234 |
235 /** | 235 /** |
236 * The [FileTracker] used by this driver. | 236 * The [FileTracker] used by this driver. |
237 */ | 237 */ |
238 FileTracker _fileTracker; | 238 FileTracker _fileTracker; |
239 | 239 |
240 /** | 240 /** |
| 241 * When this flag is set to `true`, the set of analyzed files must not change, |
| 242 * and all [AnalysisResult]s are cached infinitely. |
| 243 * |
| 244 * The flag is intended to be used for non-interactive clients, like DDC, |
| 245 * which start a new analysis session, load a set of files, resolve all of |
| 246 * them, process the resolved units, and then throw away that whole session. |
| 247 * |
| 248 * The key problem that this flag is solving is that the driver analyzes the |
| 249 * whole library when the result for a unit of the library is requested. So, |
| 250 * when the client requests sequentially the defining unit, then the first |
| 251 * part, then the second part, the driver has to perform analysis of the |
| 252 * library three times and every time throw away all the units except the one |
| 253 * which was requested. With this flag set to `true`, the driver can analyze |
| 254 * once and cache all the resolved units. |
| 255 */ |
| 256 final bool disableChangesAndCacheAllResults; |
| 257 |
| 258 /** |
| 259 * The cache to use with [disableChangesAndCacheAllResults]. |
| 260 */ |
| 261 final Map<String, AnalysisResult> _allCachedResults = {}; |
| 262 |
| 263 /** |
241 * Create a new instance of [AnalysisDriver]. | 264 * Create a new instance of [AnalysisDriver]. |
242 * | 265 * |
243 * The given [SourceFactory] is cloned to ensure that it does not contain a | 266 * The given [SourceFactory] is cloned to ensure that it does not contain a |
244 * reference to a [AnalysisContext] in which it could have been used. | 267 * reference to a [AnalysisContext] in which it could have been used. |
245 */ | 268 */ |
246 AnalysisDriver( | 269 AnalysisDriver( |
247 this._scheduler, | 270 this._scheduler, |
248 PerformanceLog logger, | 271 PerformanceLog logger, |
249 this._resourceProvider, | 272 this._resourceProvider, |
250 this._byteStore, | 273 this._byteStore, |
251 this._contentOverlay, | 274 this._contentOverlay, |
252 this.contextRoot, | 275 this.contextRoot, |
253 SourceFactory sourceFactory, | 276 SourceFactory sourceFactory, |
254 this._analysisOptions, | 277 this._analysisOptions, |
255 {PackageBundle sdkBundle}) | 278 {PackageBundle sdkBundle, |
| 279 this.disableChangesAndCacheAllResults: false}) |
256 : _logger = logger, | 280 : _logger = logger, |
257 _sourceFactory = sourceFactory.clone(), | 281 _sourceFactory = sourceFactory.clone(), |
258 _sdkBundle = sdkBundle { | 282 _sdkBundle = sdkBundle { |
259 _onResults = _resultController.stream.asBroadcastStream(); | 283 _onResults = _resultController.stream.asBroadcastStream(); |
260 _testView = new AnalysisDriverTestView(this); | 284 _testView = new AnalysisDriverTestView(this); |
261 _createFileTracker(logger); | 285 _createFileTracker(logger); |
262 _scheduler.add(this); | 286 _scheduler.add(this); |
263 _search = new Search(this); | 287 _search = new Search(this); |
264 } | 288 } |
265 | 289 |
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
452 * that state already). Schedules the file contents for [path] to be read | 476 * that state already). Schedules the file contents for [path] to be read |
453 * into the current file state prior to the next time the analysis state | 477 * into the current file state prior to the next time the analysis state |
454 * transitions to "idle". | 478 * transitions to "idle". |
455 * | 479 * |
456 * Invocation of this method will not prevent a [Future] returned from | 480 * Invocation of this method will not prevent a [Future] returned from |
457 * [getResult] from completing with a result, but the result is not | 481 * [getResult] from completing with a result, but the result is not |
458 * guaranteed to be consistent with the new current file state after this | 482 * guaranteed to be consistent with the new current file state after this |
459 * [changeFile] invocation. | 483 * [changeFile] invocation. |
460 */ | 484 */ |
461 void changeFile(String path) { | 485 void changeFile(String path) { |
| 486 _throwIfChangesAreNotAllowed(); |
462 _fileTracker.changeFile(path); | 487 _fileTracker.changeFile(path); |
463 _priorityResults.clear(); | 488 _priorityResults.clear(); |
464 } | 489 } |
465 | 490 |
466 /** | 491 /** |
467 * Some state on which analysis depends has changed, so the driver needs to be | 492 * Some state on which analysis depends has changed, so the driver needs to be |
468 * re-configured with the new state. | 493 * re-configured with the new state. |
469 * | 494 * |
470 * At least one of the optional parameters should be provided, but only those | 495 * At least one of the optional parameters should be provided, but only those |
471 * that represent state that has actually changed need be provided. | 496 * that represent state that has actually changed need be provided. |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
584 * time the analysis state transitions to "idle". | 609 * time the analysis state transitions to "idle". |
585 */ | 610 */ |
586 Future<AnalysisResult> getResult(String path) { | 611 Future<AnalysisResult> getResult(String path) { |
587 if (!_fileTracker.fsState.hasUri(path)) { | 612 if (!_fileTracker.fsState.hasUri(path)) { |
588 return new Future.value(); | 613 return new Future.value(); |
589 } | 614 } |
590 | 615 |
591 // Return the cached result. | 616 // Return the cached result. |
592 { | 617 { |
593 AnalysisResult result = _priorityResults[path]; | 618 AnalysisResult result = _priorityResults[path]; |
| 619 if (disableChangesAndCacheAllResults) { |
| 620 result ??= _allCachedResults[path]; |
| 621 } |
594 if (result != null) { | 622 if (result != null) { |
595 return new Future.value(result); | 623 return new Future.value(result); |
596 } | 624 } |
597 } | 625 } |
598 | 626 |
599 // Schedule analysis. | 627 // Schedule analysis. |
600 var completer = new Completer<AnalysisResult>(); | 628 var completer = new Completer<AnalysisResult>(); |
601 _requestedFiles | 629 _requestedFiles |
602 .putIfAbsent(path, () => <Completer<AnalysisResult>>[]) | 630 .putIfAbsent(path, () => <Completer<AnalysisResult>>[]) |
603 .add(completer); | 631 .add(completer); |
(...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
880 /** | 908 /** |
881 * Remove the file with the given [path] from the list of files to analyze. | 909 * Remove the file with the given [path] from the list of files to analyze. |
882 * | 910 * |
883 * The [path] must be absolute and normalized. | 911 * The [path] must be absolute and normalized. |
884 * | 912 * |
885 * The results of analysis of the file might still be produced by the | 913 * The results of analysis of the file might still be produced by the |
886 * [results] stream. The driver will try to stop producing these results, | 914 * [results] stream. The driver will try to stop producing these results, |
887 * but does not guarantee this. | 915 * but does not guarantee this. |
888 */ | 916 */ |
889 void removeFile(String path) { | 917 void removeFile(String path) { |
| 918 _throwIfChangesAreNotAllowed(); |
890 _fileTracker.removeFile(path); | 919 _fileTracker.removeFile(path); |
891 _priorityResults.clear(); | 920 _priorityResults.clear(); |
892 } | 921 } |
893 | 922 |
894 /** | 923 /** |
895 * Handles a notification from the [FileTracker] that there has been a change | 924 * Handles a notification from the [FileTracker] that there has been a change |
896 * of state. | 925 * of state. |
897 */ | 926 */ |
898 void _changeHook() { | 927 void _changeHook() { |
899 _priorityResults.clear(); | 928 _priorityResults.clear(); |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
949 if (bytes != null) { | 978 if (bytes != null) { |
950 return _getAnalysisResultFromBytes(file, signature, bytes); | 979 return _getAnalysisResultFromBytes(file, signature, bytes); |
951 } | 980 } |
952 } | 981 } |
953 | 982 |
954 // We need the fully resolved unit, or the result is not cached. | 983 // We need the fully resolved unit, or the result is not cached. |
955 return _logger.run('Compute analysis result for $path', () { | 984 return _logger.run('Compute analysis result for $path', () { |
956 try { | 985 try { |
957 LibraryContext libraryContext = _createLibraryContext(library); | 986 LibraryContext libraryContext = _createLibraryContext(library); |
958 try { | 987 try { |
| 988 _testView.numOfAnalyzedLibraries++; |
959 LibraryAnalyzer analyzer = new LibraryAnalyzer( | 989 LibraryAnalyzer analyzer = new LibraryAnalyzer( |
960 analysisOptions, | 990 analysisOptions, |
961 declaredVariables, | 991 declaredVariables, |
962 sourceFactory, | 992 sourceFactory, |
963 _fileTracker.fsState, | 993 _fileTracker.fsState, |
964 libraryContext.store, | 994 libraryContext.store, |
965 library); | 995 library); |
966 Map<FileState, UnitAnalysisResult> results = analyzer.analyze(); | 996 Map<FileState, UnitAnalysisResult> results = analyzer.analyze(); |
967 | 997 |
968 List<int> bytes; | 998 List<int> bytes; |
969 CompilationUnit resolvedUnit; | 999 CompilationUnit resolvedUnit; |
970 for (FileState unitFile in results.keys) { | 1000 for (FileState unitFile in results.keys) { |
971 UnitAnalysisResult unitResult = results[unitFile]; | 1001 UnitAnalysisResult unitResult = results[unitFile]; |
972 List<int> unitBytes = | 1002 List<int> unitBytes = |
973 _serializeResolvedUnit(unitResult.unit, unitResult.errors); | 1003 _serializeResolvedUnit(unitResult.unit, unitResult.errors); |
974 String unitSignature = _getResolvedUnitSignature(library, unitFile); | 1004 String unitSignature = _getResolvedUnitSignature(library, unitFile); |
975 String unitKey = _getResolvedUnitKey(unitSignature); | 1005 String unitKey = _getResolvedUnitKey(unitSignature); |
976 _byteStore.put(unitKey, unitBytes); | 1006 _byteStore.put(unitKey, unitBytes); |
977 if (unitFile == file) { | 1007 if (unitFile == file) { |
978 bytes = unitBytes; | 1008 bytes = unitBytes; |
979 resolvedUnit = unitResult.unit; | 1009 resolvedUnit = unitResult.unit; |
980 } | 1010 } |
| 1011 if (disableChangesAndCacheAllResults) { |
| 1012 AnalysisResult result = _getAnalysisResultFromBytes( |
| 1013 unitFile, unitSignature, unitBytes, |
| 1014 content: unitFile.content, resolvedUnit: unitResult.unit); |
| 1015 _allCachedResults[unitFile.path] = result; |
| 1016 } |
981 } | 1017 } |
982 | 1018 |
983 // Return the result, full or partial. | 1019 // Return the result, full or partial. |
984 _logger.writeln('Computed new analysis result.'); | 1020 _logger.writeln('Computed new analysis result.'); |
985 AnalysisResult result = _getAnalysisResultFromBytes( | 1021 AnalysisResult result = _getAnalysisResultFromBytes( |
986 file, signature, bytes, | 1022 file, signature, bytes, |
987 content: withUnit ? file.content : null, | 1023 content: withUnit ? file.content : null, |
988 resolvedUnit: withUnit ? resolvedUnit : null); | 1024 resolvedUnit: withUnit ? resolvedUnit : null); |
989 if (withUnit && _priorityFiles.contains(path)) { | 1025 if (withUnit && _priorityFiles.contains(path)) { |
990 _priorityResults[path] = result; | 1026 _priorityResults[path] = result; |
(...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1230 String key = 'exception_${time.year}$m$d' '_$h$min$sec' + '_$ms'; | 1266 String key = 'exception_${time.year}$m$d' '_$h$min$sec' + '_$ms'; |
1231 | 1267 |
1232 _byteStore.put(key, bytes); | 1268 _byteStore.put(key, bytes); |
1233 return key; | 1269 return key; |
1234 } catch (_) { | 1270 } catch (_) { |
1235 return null; | 1271 return null; |
1236 } | 1272 } |
1237 } | 1273 } |
1238 | 1274 |
1239 /** | 1275 /** |
| 1276 * If the driver is used in the read-only mode with infinite cache, |
| 1277 * we should not allow invocations that change files. |
| 1278 */ |
| 1279 void _throwIfChangesAreNotAllowed() { |
| 1280 if (disableChangesAndCacheAllResults) { |
| 1281 throw new StateError('Changing files is not allowed for this driver.'); |
| 1282 } |
| 1283 } |
| 1284 |
| 1285 /** |
1240 * Given the list of [errors] for the [file], update the [file]'s | 1286 * Given the list of [errors] for the [file], update the [file]'s |
1241 * [FileState.hasErrorOrWarning] flag. | 1287 * [FileState.hasErrorOrWarning] flag. |
1242 */ | 1288 */ |
1243 void _updateHasErrorOrWarningFlag( | 1289 void _updateHasErrorOrWarningFlag( |
1244 FileState file, List<AnalysisError> errors) { | 1290 FileState file, List<AnalysisError> errors) { |
1245 for (AnalysisError error in errors) { | 1291 for (AnalysisError error in errors) { |
1246 ErrorSeverity severity = error.errorCode.errorSeverity; | 1292 ErrorSeverity severity = error.errorCode.errorSeverity; |
1247 if (severity == ErrorSeverity.ERROR || | 1293 if (severity == ErrorSeverity.ERROR || |
1248 severity == ErrorSeverity.WARNING) { | 1294 severity == ErrorSeverity.WARNING) { |
1249 file.hasErrorOrWarning = true; | 1295 file.hasErrorOrWarning = true; |
(...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1471 return new Future.value(); | 1517 return new Future.value(); |
1472 } | 1518 } |
1473 return new Future.delayed(Duration.ZERO, () => _pumpEventQueue(times - 1)); | 1519 return new Future.delayed(Duration.ZERO, () => _pumpEventQueue(times - 1)); |
1474 } | 1520 } |
1475 } | 1521 } |
1476 | 1522 |
1477 @visibleForTesting | 1523 @visibleForTesting |
1478 class AnalysisDriverTestView { | 1524 class AnalysisDriverTestView { |
1479 final AnalysisDriver driver; | 1525 final AnalysisDriver driver; |
1480 | 1526 |
| 1527 int numOfAnalyzedLibraries = 0; |
| 1528 |
1481 AnalysisDriverTestView(this.driver); | 1529 AnalysisDriverTestView(this.driver); |
1482 | 1530 |
1483 FileTracker get fileTracker => driver._fileTracker; | 1531 FileTracker get fileTracker => driver._fileTracker; |
1484 | 1532 |
1485 Map<String, AnalysisResult> get priorityResults => driver._priorityResults; | 1533 Map<String, AnalysisResult> get priorityResults => driver._priorityResults; |
1486 } | 1534 } |
1487 | 1535 |
1488 /** | 1536 /** |
1489 * The result of analyzing of a single file. | 1537 * The result of analyzing of a single file. |
1490 * | 1538 * |
(...skipping 515 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2006 libraryDeclarations.add(new TopLevelDeclarationInSource( | 2054 libraryDeclarations.add(new TopLevelDeclarationInSource( |
2007 file.source, declaration, isExported)); | 2055 file.source, declaration, isExported)); |
2008 } | 2056 } |
2009 } | 2057 } |
2010 } | 2058 } |
2011 | 2059 |
2012 // We're not done yet. | 2060 // We're not done yet. |
2013 return false; | 2061 return false; |
2014 } | 2062 } |
2015 } | 2063 } |
OLD | NEW |