| 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:convert'; | 7 import 'dart:convert'; |
| 8 | 8 |
| 9 import 'package:analyzer/dart/ast/ast.dart'; | 9 import 'package:analyzer/dart/ast/ast.dart'; |
| 10 import 'package:analyzer/dart/ast/token.dart'; | 10 import 'package:analyzer/dart/ast/token.dart'; |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 112 * [getResult] to the [Completer]s to report the result. | 112 * [getResult] to the [Completer]s to report the result. |
| 113 */ | 113 */ |
| 114 final _requestedFiles = <String, List<Completer<AnalysisResult>>>{}; | 114 final _requestedFiles = <String, List<Completer<AnalysisResult>>>{}; |
| 115 | 115 |
| 116 /** | 116 /** |
| 117 * The set of explicitly analyzed files. | 117 * The set of explicitly analyzed files. |
| 118 */ | 118 */ |
| 119 final _explicitFiles = new LinkedHashSet<String>(); | 119 final _explicitFiles = new LinkedHashSet<String>(); |
| 120 | 120 |
| 121 /** | 121 /** |
| 122 * The set of files were reported as changed through [changeFile] and for | 122 * The set of files were reported as changed through [changeFile] and not |
| 123 * which API signatures should be recomputed and compared before performing | 123 * checked for actual changes yet. |
| 124 * any other analysis. | |
| 125 */ | 124 */ |
| 126 final _filesToVerifyUnlinkedSignature = new Set<String>(); | 125 final _changedFiles = new LinkedHashSet<String>(); |
| 127 | 126 |
| 128 /** | 127 /** |
| 129 * The set of files that are currently scheduled for analysis. | 128 * The set of files that are currently scheduled for analysis. |
| 130 */ | 129 */ |
| 131 final _filesToAnalyze = new LinkedHashSet<String>(); | 130 final _filesToAnalyze = new LinkedHashSet<String>(); |
| 132 | 131 |
| 133 /** | 132 /** |
| 134 * Cache of URI resolution. The outer map key is the absolute URI of the | 133 * Cache of URI resolution. The outer map key is the absolute URI of the |
| 135 * containing file. The inner map key is the URI text of a directive | 134 * containing file. The inner map key is the URI text of a directive |
| 136 * contained in that file. The inner map value is the [Source] object which | 135 * contained in that file. The inner map value is the [Source] object which |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 198 * More than one result might be produced for the same file, even if the | 197 * More than one result might be produced for the same file, even if the |
| 199 * client does not change the state of the files. | 198 * client does not change the state of the files. |
| 200 * | 199 * |
| 201 * Results might be produced even for files that have never been added | 200 * Results might be produced even for files that have never been added |
| 202 * using [addFile], for example when [getResult] was called for a file. | 201 * using [addFile], for example when [getResult] was called for a file. |
| 203 */ | 202 */ |
| 204 Stream<AnalysisResult> get results async* { | 203 Stream<AnalysisResult> get results async* { |
| 205 try { | 204 try { |
| 206 PerformanceLogSection analysisSection = null; | 205 PerformanceLogSection analysisSection = null; |
| 207 while (true) { | 206 while (true) { |
| 208 // TODO(scheglov) implement state transitioning | |
| 209 await _hasWork.signal; | 207 await _hasWork.signal; |
| 210 | 208 |
| 209 // TODO(scheglov) implement state transitioning |
| 211 if (analysisSection == null) { | 210 if (analysisSection == null) { |
| 212 analysisSection = _logger.enter('Analyzing'); | 211 analysisSection = _logger.enter('Analyzing'); |
| 213 } | 212 } |
| 214 | 213 |
| 215 // TODO(scheglov) verify one file at a time | 214 // Verify all changed files one at a time. |
| 216 _verifyUnlinkedSignatureOfChangedFiles(); | 215 if (_changedFiles.isNotEmpty) { |
| 216 String path = _removeFirst(_changedFiles); |
| 217 _verifyApiSignatureOfChangedFile(path); |
| 218 // Repeat the processing loop. |
| 219 _hasWork.notify(); |
| 220 continue; |
| 221 } |
| 217 | 222 |
| 218 // Analyze the first file in the general queue. | 223 // Analyze the first file in the general queue. |
| 219 if (_filesToAnalyze.isNotEmpty) { | 224 if (_filesToAnalyze.isNotEmpty) { |
| 220 String path = _filesToAnalyze.first; | 225 String path = _removeFirst(_filesToAnalyze); |
| 221 _filesToAnalyze.remove(path); | |
| 222 _File file = _fileForPath(path); | 226 _File file = _fileForPath(path); |
| 223 AnalysisResult result = _computeAnalysisResult(file); | 227 AnalysisResult result = _computeAnalysisResult(file); |
| 224 yield result; | 228 yield result; |
| 229 // Repeat the processing loop. |
| 230 _hasWork.notify(); |
| 231 continue; |
| 225 } | 232 } |
| 226 | 233 |
| 227 // If there is work to do, notify the monitor. | 234 // There is nothing to do. |
| 228 if (_filesToAnalyze.isNotEmpty) { | 235 analysisSection.exit(); |
| 229 _hasWork.notify(); | 236 analysisSection = null; |
| 230 } else { | |
| 231 analysisSection.exit(); | |
| 232 analysisSection = null; | |
| 233 } | |
| 234 } | 237 } |
| 235 // TODO(scheglov) implement | 238 // TODO(scheglov) implement |
| 236 } finally { | 239 } finally { |
| 237 print('The stream was cancelled.'); | 240 print('The stream was cancelled.'); |
| 238 } | 241 } |
| 239 } | 242 } |
| 240 | 243 |
| 241 /** | 244 /** |
| 242 * Add the file with the given [path] to the set of files to analyze. | 245 * Add the file with the given [path] to the set of files to analyze. |
| 243 * | 246 * |
| (...skipping 19 matching lines...) Expand all Loading... |
| 263 * that state already). Schedules the file contents for [path] to be read | 266 * that state already). Schedules the file contents for [path] to be read |
| 264 * into the current file state prior to the next time the analysis state | 267 * into the current file state prior to the next time the analysis state |
| 265 * transitions to "idle". | 268 * transitions to "idle". |
| 266 * | 269 * |
| 267 * Invocation of this method will not prevent a [Future] returned from | 270 * Invocation of this method will not prevent a [Future] returned from |
| 268 * [getResult] from completing with a result, but the result is not | 271 * [getResult] from completing with a result, but the result is not |
| 269 * guaranteed to be consistent with the new current file state after this | 272 * guaranteed to be consistent with the new current file state after this |
| 270 * [changeFile] invocation. | 273 * [changeFile] invocation. |
| 271 */ | 274 */ |
| 272 void changeFile(String path) { | 275 void changeFile(String path) { |
| 273 _filesToVerifyUnlinkedSignature.add(path); | 276 _changedFiles.add(path); |
| 274 _filesToAnalyze.add(path); | 277 _filesToAnalyze.add(path); |
| 275 _hasWork.notify(); | 278 _hasWork.notify(); |
| 276 } | 279 } |
| 277 | 280 |
| 278 /** | 281 /** |
| 279 * Return the [Future] that completes with a [AnalysisResult] for the file | 282 * Return the [Future] that completes with a [AnalysisResult] for the file |
| 280 * with the given [path]. | 283 * with the given [path]. |
| 281 * | 284 * |
| 282 * The [path] must be absolute and normalized. | 285 * The [path] must be absolute and normalized. |
| 283 * | 286 * |
| (...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 510 * Return the [_File] for the given [path] in [_sourceFactory]. | 513 * Return the [_File] for the given [path] in [_sourceFactory]. |
| 511 */ | 514 */ |
| 512 _File _fileForPath(String path) { | 515 _File _fileForPath(String path) { |
| 513 Source fileSource = _resourceProvider.getFile(path).createSource(); | 516 Source fileSource = _resourceProvider.getFile(path).createSource(); |
| 514 Uri uri = _sourceFactory.restoreUri(fileSource); | 517 Uri uri = _sourceFactory.restoreUri(fileSource); |
| 515 Source source = _resourceProvider.getFile(path).createSource(uri); | 518 Source source = _resourceProvider.getFile(path).createSource(uri); |
| 516 return new _File.forResolution(this, source); | 519 return new _File.forResolution(this, source); |
| 517 } | 520 } |
| 518 | 521 |
| 519 /** | 522 /** |
| 520 * Verify the API signatures for the changed files, and decide which linked | 523 * Verify the API signature for the file with the given [path], and decide |
| 521 * libraries should be invalidated, and files reanalyzed. | 524 * which linked libraries should be invalidated, and files reanalyzed. |
| 522 * | 525 * |
| 523 * TODO(scheglov) I see that adding a local var changes (full) API signature. | 526 * TODO(scheglov) I see that adding a local var changes (full) API signature. |
| 524 */ | 527 */ |
| 525 void _verifyUnlinkedSignatureOfChangedFiles() { | 528 void _verifyApiSignatureOfChangedFile(String path) { |
| 526 if (_filesToVerifyUnlinkedSignature.isEmpty) { | 529 _logger.run('Verify API signature of $path', () { |
| 527 return; | 530 String oldSignature = _fileApiSignatureMap[path]; |
| 528 } | 531 // Compute the new API signature. |
| 529 int numOfFiles = _filesToVerifyUnlinkedSignature.length; | 532 // _File.forResolution() also updates the content hash in the cache. |
| 530 _logger.run('Verify API signatures of $numOfFiles files', () { | 533 _File newFile = _fileForPath(path); |
| 531 bool hasMismatch = false; | 534 String newSignature = newFile.unlinked.apiSignature; |
| 532 for (String path in _filesToVerifyUnlinkedSignature) { | 535 // If the old API signature is not null, then the file was used to |
| 533 String oldSignature = _fileApiSignatureMap[path]; | 536 // compute at least one dependency signature. If the new API signature |
| 534 // Compute the new API signature. | 537 // is different, then potentially all dependency signatures and |
| 535 // _File.forResolution() also updates the content hash in the cache. | 538 // resolution results are invalid. |
| 536 _File newFile = _fileForPath(path); | 539 if (oldSignature != null && oldSignature != newSignature) { |
| 537 String newSignature = newFile.unlinked.apiSignature; | 540 _logger.writeln('API signatures mismatch found for $newFile'); |
| 538 // If the old API signature is not null, then the file was used to | |
| 539 // compute at least one dependency signature. If the new API signature | |
| 540 // is different, then potentially all dependency signatures and | |
| 541 // resolution results are invalid. | |
| 542 if (oldSignature != null && oldSignature != newSignature) { | |
| 543 _logger.writeln('API signature mismatch found for $newFile.'); | |
| 544 hasMismatch = true; | |
| 545 } | |
| 546 } | |
| 547 if (hasMismatch) { | |
| 548 _dependencySignatureMap.clear(); | 541 _dependencySignatureMap.clear(); |
| 549 _filesToAnalyze.addAll(_explicitFiles); | 542 _filesToAnalyze.addAll(_explicitFiles); |
| 550 } else { | |
| 551 _logger.writeln('All API signatures match.'); | |
| 552 } | 543 } |
| 553 _filesToVerifyUnlinkedSignature.clear(); | |
| 554 }); | 544 }); |
| 555 } | 545 } |
| 546 |
| 547 /** |
| 548 * Remove and return the first item in the given [set]. |
| 549 */ |
| 550 static Object/*=T*/ _removeFirst/*<T>*/(LinkedHashSet<Object/*=T*/ > set) { |
| 551 Object/*=T*/ element = set.first; |
| 552 set.remove(element); |
| 553 return element; |
| 554 } |
| 556 } | 555 } |
| 557 | 556 |
| 558 /** | 557 /** |
| 559 * The result of analyzing of a single file. | 558 * The result of analyzing of a single file. |
| 560 * | 559 * |
| 561 * These results are self-consistent, i.e. [content], [contentHash], the | 560 * These results are self-consistent, i.e. [content], [contentHash], the |
| 562 * resolved [unit] correspond to each other. All referenced elements, even | 561 * resolved [unit] correspond to each other. All referenced elements, even |
| 563 * external ones, are also self-consistent. But none of the results is | 562 * external ones, are also self-consistent. But none of the results is |
| 564 * guaranteed to be consistent with the state of the files. | 563 * guaranteed to be consistent with the state of the files. |
| 565 * | 564 * |
| (...skipping 427 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 993 } | 992 } |
| 994 } | 993 } |
| 995 for (UnlinkedExportPublic export in unit.publicNamespace.exports) { | 994 for (UnlinkedExportPublic export in unit.publicNamespace.exports) { |
| 996 referenced.exported.add(export.uri); | 995 referenced.exported.add(export.uri); |
| 997 } | 996 } |
| 998 return referenced; | 997 return referenced; |
| 999 } | 998 } |
| 1000 | 999 |
| 1001 _ReferencedUris._(); | 1000 _ReferencedUris._(); |
| 1002 } | 1001 } |
| OLD | NEW |