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 |