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 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
228 continue; | 228 continue; |
229 } | 229 } |
230 | 230 |
231 // TODO(scheglov) analyze requested files | 231 // TODO(scheglov) analyze requested files |
232 | 232 |
233 // Analyze a priority file. | 233 // Analyze a priority file. |
234 if (_priorityFiles.isNotEmpty) { | 234 if (_priorityFiles.isNotEmpty) { |
235 bool analyzed = false; | 235 bool analyzed = false; |
236 for (String path in _priorityFiles) { | 236 for (String path in _priorityFiles) { |
237 if (_filesToAnalyze.remove(path)) { | 237 if (_filesToAnalyze.remove(path)) { |
238 _File file = _fileForPath(path); | |
239 AnalysisResult result = | 238 AnalysisResult result = |
240 _computeAnalysisResult(file, withUnit: true); | 239 _computeAnalysisResult(path, withUnit: true); |
241 yield result; | 240 yield result; |
242 break; | 241 break; |
243 } | 242 } |
244 } | 243 } |
245 // Repeat the processing loop. | 244 // Repeat the processing loop. |
246 if (analyzed) { | 245 if (analyzed) { |
247 _hasWork.notify(); | 246 _hasWork.notify(); |
248 continue; | 247 continue; |
249 } | 248 } |
250 } | 249 } |
251 | 250 |
252 // Analyze a general file. | 251 // Analyze a general file. |
253 if (_filesToAnalyze.isNotEmpty) { | 252 if (_filesToAnalyze.isNotEmpty) { |
254 String path = _removeFirst(_filesToAnalyze); | 253 String path = _removeFirst(_filesToAnalyze); |
255 _File file = _fileForPath(path); | 254 AnalysisResult result = _computeAnalysisResult(path, withUnit: false); |
256 AnalysisResult result = _computeAnalysisResult(file, withUnit: false); | |
257 yield result; | 255 yield result; |
258 // Repeat the processing loop. | 256 // Repeat the processing loop. |
259 _hasWork.notify(); | 257 _hasWork.notify(); |
260 continue; | 258 continue; |
261 } | 259 } |
262 | 260 |
263 // There is nothing to do. | 261 // There is nothing to do. |
264 analysisSection.exit(); | 262 analysisSection.exit(); |
265 analysisSection = null; | 263 analysisSection = null; |
266 } | 264 } |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
359 * add [UnlinkedUnit] with wrong URI. | 357 * add [UnlinkedUnit] with wrong URI. |
360 * | 358 * |
361 * We need to clean this up. | 359 * We need to clean this up. |
362 */ | 360 */ |
363 void _addToStoreUnlinked( | 361 void _addToStoreUnlinked( |
364 SummaryDataStore store, String uri, UnlinkedUnit unlinked) { | 362 SummaryDataStore store, String uri, UnlinkedUnit unlinked) { |
365 store.unlinkedMap[uri] = unlinked; | 363 store.unlinkedMap[uri] = unlinked; |
366 } | 364 } |
367 | 365 |
368 /** | 366 /** |
369 * Compute the [AnalysisResult] for the [file]. | 367 * Return the cached or newly computed analysis result of the file with the |
| 368 * given [path]. |
370 * | 369 * |
371 * The result will have the fully resolved unit only if [withUnit] is `true`. | 370 * The result will have the fully resolved unit and will always be newly |
| 371 * compute only if [withUnit] is `true`. |
372 */ | 372 */ |
373 AnalysisResult _computeAnalysisResult(_File file, {bool withUnit: false}) { | 373 AnalysisResult _computeAnalysisResult(String path, {bool withUnit: false}) { |
374 // If we don't need to the fully resolved unit, check for a cached result. | 374 Source source = _sourceForPath(path); |
| 375 |
| 376 // If we don't need the fully resolved unit, check for the cached result. |
375 if (!withUnit) { | 377 if (!withUnit) { |
376 AnalysisResult result = _getCachedAnalysisResult(file); | 378 _File file = new _File.forLinking(this, source); |
| 379 // Prepare the key for the cached result. |
| 380 String key = _getResolvedUnitKey(file); |
| 381 if (key == null) { |
| 382 _logger.run('Compute the dependency hash for $source', () { |
| 383 _createLibraryContext(file); |
| 384 key = _getResolvedUnitKey(file); |
| 385 }); |
| 386 } |
| 387 // Check for the cached result. |
| 388 AnalysisResult result = _getCachedAnalysisResult(file, key); |
377 if (result != null) { | 389 if (result != null) { |
378 return result; | 390 return result; |
379 } | 391 } |
380 } | 392 } |
381 | 393 |
382 // We need the fully resolved unit, or the result is not cached. | 394 // We need the fully resolved unit, or the result is not cached. |
383 return _logger.run('Compute analysis result for $file', () { | 395 return _logger.run('Compute analysis result for $source', () { |
| 396 // Still no result, compute and store it. |
| 397 _File file = new _File.forResolution(this, source); |
384 _LibraryContext libraryContext = _createLibraryContext(file); | 398 _LibraryContext libraryContext = _createLibraryContext(file); |
385 | |
386 // We recomputed the dependency hash, and we might have a cached result. | |
387 if (!withUnit) { | |
388 AnalysisResult result = _getCachedAnalysisResult(file); | |
389 if (result != null) { | |
390 _logger.writeln('Return the cached analysis result.'); | |
391 return result; | |
392 } | |
393 } | |
394 | |
395 // Still no result, compute and store it. | |
396 AnalysisContext analysisContext = _createAnalysisContext(libraryContext); | 399 AnalysisContext analysisContext = _createAnalysisContext(libraryContext); |
397 try { | 400 try { |
398 analysisContext.setContents(file.source, file.content); | 401 analysisContext.setContents(file.source, file.content); |
399 // TODO(scheglov) Add support for parts. | 402 // TODO(scheglov) Add support for parts. |
400 CompilationUnit resolvedUnit = | 403 CompilationUnit resolvedUnit = |
401 analysisContext.resolveCompilationUnit2(file.source, file.source); | 404 analysisContext.resolveCompilationUnit2(file.source, file.source); |
402 List<AnalysisError> errors = analysisContext.computeErrors(file.source); | 405 List<AnalysisError> errors = analysisContext.computeErrors(file.source); |
403 | 406 |
404 // Store the result into the cache. | 407 // Store the result into the cache. |
405 { | 408 { |
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
552 PackageBundle linked = new PackageBundle.fromBuffer(bytes); | 555 PackageBundle linked = new PackageBundle.fromBuffer(bytes); |
553 _addToStoreLinked(store, uri, linked.linkedLibraries.single); | 556 _addToStoreLinked(store, uri, linked.linkedLibraries.single); |
554 _byteStore.put(key, bytes); | 557 _byteStore.put(key, bytes); |
555 }); | 558 }); |
556 | 559 |
557 return new _LibraryContext(libraryFile, libraryNode, store); | 560 return new _LibraryContext(libraryFile, libraryNode, store); |
558 }); | 561 }); |
559 } | 562 } |
560 | 563 |
561 /** | 564 /** |
562 * Return the [_File] for the given [path] in [_sourceFactory]. | 565 * If we know the result [key] for the [file], try to load the analysis |
| 566 * result from the cache. Return `null` if not found. |
563 */ | 567 */ |
564 _File _fileForPath(String path) { | 568 AnalysisResult _getCachedAnalysisResult(_File file, String key) { |
565 Source fileSource = _resourceProvider.getFile(path).createSource(); | 569 List<int> bytes = _byteStore.get(key); |
566 Uri uri = _sourceFactory.restoreUri(fileSource); | 570 if (bytes != null) { |
567 Source source = _resourceProvider.getFile(path).createSource(uri); | 571 var unit = new AnalysisDriverResolvedUnit.fromBuffer(bytes); |
568 return new _File.forResolution(this, source); | 572 List<AnalysisError> errors = unit.errors |
569 } | 573 .map((error) => new AnalysisError.forValues( |
570 | 574 file.source, |
571 /** | 575 error.offset, |
572 * If we know the dependency signature for the [file], try to load the | 576 error.length, |
573 * analysis result from the cache. Return `null` if not found. | 577 ErrorCode.byUniqueName(error.uniqueName), |
574 */ | 578 error.message, |
575 AnalysisResult _getCachedAnalysisResult(_File file) { | 579 error.correction)) |
576 String key = _getResolvedUnitKey(file); | 580 .toList(); |
577 if (key != null) { | 581 return new AnalysisResult( |
578 List<int> bytes = _byteStore.get(key); | 582 file.path, file.uri, null, file.contentHash, null, errors); |
579 if (bytes != null) { | |
580 var unit = new AnalysisDriverResolvedUnit.fromBuffer(bytes); | |
581 List<AnalysisError> errors = unit.errors | |
582 .map((error) => new AnalysisError.forValues( | |
583 file.source, | |
584 error.offset, | |
585 error.length, | |
586 ErrorCode.byUniqueName(error.uniqueName), | |
587 error.message, | |
588 error.correction)) | |
589 .toList(); | |
590 return new AnalysisResult( | |
591 file.path, file.uri, null, file.contentHash, null, errors); | |
592 } | |
593 } | 583 } |
594 return null; | 584 return null; |
595 } | 585 } |
596 | 586 |
597 /** | 587 /** |
598 * Return the key to store fully resolved results for the [file] into the | 588 * Return the key to store fully resolved results for the [file] into the |
599 * cache. Return `null` if the dependency signature is not known yet. | 589 * cache. Return `null` if the dependency signature is not known yet. |
600 */ | 590 */ |
601 String _getResolvedUnitKey(_File file) { | 591 String _getResolvedUnitKey(_File file) { |
602 String dependencyHash = _dependencySignatureMap[file.uri]; | 592 String dependencyHash = _dependencySignatureMap[file.uri]; |
603 if (dependencyHash != null) { | 593 if (dependencyHash != null) { |
604 ApiSignature signature = new ApiSignature(); | 594 ApiSignature signature = new ApiSignature(); |
605 signature.addString(dependencyHash); | 595 signature.addString(dependencyHash); |
606 signature.addString(file.contentHash); | 596 signature.addString(file.contentHash); |
607 return '${signature.toHex()}.resolved'; | 597 return '${signature.toHex()}.resolved'; |
608 } | 598 } |
609 return null; | 599 return null; |
610 } | 600 } |
611 | 601 |
612 /** | 602 /** |
| 603 * Return the [Source] for the given [path] in [_sourceFactory]. |
| 604 */ |
| 605 Source _sourceForPath(String path) { |
| 606 Source fileSource = _resourceProvider.getFile(path).createSource(); |
| 607 Uri uri = _sourceFactory.restoreUri(fileSource); |
| 608 return _resourceProvider.getFile(path).createSource(uri); |
| 609 } |
| 610 |
| 611 /** |
613 * Verify the API signature for the file with the given [path], and decide | 612 * Verify the API signature for the file with the given [path], and decide |
614 * which linked libraries should be invalidated, and files reanalyzed. | 613 * which linked libraries should be invalidated, and files reanalyzed. |
615 * | 614 * |
616 * TODO(scheglov) I see that adding a local var changes (full) API signature. | 615 * TODO(scheglov) I see that adding a local var changes (full) API signature. |
617 */ | 616 */ |
618 void _verifyApiSignatureOfChangedFile(String path) { | 617 void _verifyApiSignatureOfChangedFile(String path) { |
619 _logger.run('Verify API signature of $path', () { | 618 _logger.run('Verify API signature of $path', () { |
620 String oldSignature = _fileApiSignatureMap[path]; | 619 String oldSignature = _fileApiSignatureMap[path]; |
621 // Compute the new API signature. | 620 // Compute the new API signature. |
622 // _File.forResolution() also updates the content hash in the cache. | 621 // _File.forResolution() also updates the content hash in the cache. |
623 _File newFile = _fileForPath(path); | 622 Source source = _sourceForPath(path); |
| 623 _File newFile = new _File.forResolution(this, source); |
624 String newSignature = newFile.unlinked.apiSignature; | 624 String newSignature = newFile.unlinked.apiSignature; |
625 // If the old API signature is not null, then the file was used to | 625 // If the old API signature is not null, then the file was used to |
626 // compute at least one dependency signature. If the new API signature | 626 // compute at least one dependency signature. If the new API signature |
627 // is different, then potentially all dependency signatures and | 627 // is different, then potentially all dependency signatures and |
628 // resolution results are invalid. | 628 // resolution results are invalid. |
629 if (oldSignature != null && oldSignature != newSignature) { | 629 if (oldSignature != null && oldSignature != newSignature) { |
630 _logger.writeln('API signatures mismatch found for $newFile'); | 630 _logger.writeln('API signatures mismatch found for $newFile'); |
631 _dependencySignatureMap.clear(); | 631 _dependencySignatureMap.clear(); |
632 _filesToAnalyze.addAll(_explicitFiles); | 632 _filesToAnalyze.addAll(_explicitFiles); |
633 } | 633 } |
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
809 /** | 809 /** |
810 * The unlinked bundle, not `null`. | 810 * The unlinked bundle, not `null`. |
811 */ | 811 */ |
812 final PackageBundle unlinked; | 812 final PackageBundle unlinked; |
813 | 813 |
814 /** | 814 /** |
815 * The unresolved unit, not `null` if this file is for resolution. | 815 * The unresolved unit, not `null` if this file is for resolution. |
816 */ | 816 */ |
817 final CompilationUnit unit; | 817 final CompilationUnit unit; |
818 | 818 |
819 factory _File.forLinking(AnalysisDriver driver, Source source) { | 819 /** |
820 // If we have enough cached information, use it. | 820 * Return the file with consistent [content] and [contentHash]. |
821 String contentHash = driver._fileContentHashMap[source.fullName]; | 821 */ |
822 if (contentHash != null) { | 822 factory _File.forContent(AnalysisDriver driver, Source source) { |
823 String key = '$contentHash.unlinked'; | |
824 List<int> bytes = driver._byteStore.get(key); | |
825 if (bytes != null) { | |
826 PackageBundle unlinked = new PackageBundle.fromBuffer(bytes); | |
827 return new _File._(driver, source, null, contentHash, unlinked, null); | |
828 } | |
829 } | |
830 // Otherwise, read the source, parse and build a new unlinked bundle. | |
831 return new _File.forResolution(driver, source); | |
832 } | |
833 | |
834 factory _File.forResolution(AnalysisDriver driver, Source source) { | |
835 String path = source.fullName; | 823 String path = source.fullName; |
836 // Read the content. | 824 // Read the content. |
837 String content; | 825 String content; |
838 try { | 826 try { |
839 content = driver._contentCache.getContents(source); | 827 content = driver._contentCache.getContents(source); |
840 content ??= source.contents.data; | 828 content ??= source.contents.data; |
841 } catch (_) { | 829 } catch (_) { |
842 content = ''; | 830 content = ''; |
843 // TODO(scheglov) We fail to report URI_DOES_NOT_EXIST. | 831 // TODO(scheglov) We fail to report URI_DOES_NOT_EXIST. |
844 // On one hand we need to provide an unlinked bundle to prevent | 832 // On one hand we need to provide an unlinked bundle to prevent |
845 // analysis context from reading the file (we want it to work | 833 // analysis context from reading the file (we want it to work |
846 // hermetically and handle one one file at a time). OTOH, | 834 // hermetically and handle one one file at a time). OTOH, |
847 // ResynthesizerResultProvider happily reports that any source in the | 835 // ResynthesizerResultProvider happily reports that any source in the |
848 // SummaryDataStore has MODIFICATION_TIME `0`. We need to return `-1` | 836 // SummaryDataStore has MODIFICATION_TIME `0`. We need to return `-1` |
849 // for missing files. Maybe add this feature to SummaryDataStore? | 837 // for missing files. Maybe add this feature to SummaryDataStore? |
850 } | 838 } |
851 // Compute the content hash. | 839 // Compute the content hash. |
852 List<int> textBytes = UTF8.encode(content); | 840 List<int> textBytes = UTF8.encode(content); |
853 List<int> hashBytes = md5.convert(textBytes).bytes; | 841 List<int> hashBytes = md5.convert(textBytes).bytes; |
854 String contentHash = hex.encode(hashBytes); | 842 String contentHash = hex.encode(hashBytes); |
855 driver._fileContentHashMap[path] = contentHash; | 843 driver._fileContentHashMap[path] = contentHash; |
| 844 // Return information about the file content. |
| 845 return new _File._(driver, source, content, contentHash, null, null); |
| 846 } |
| 847 |
| 848 factory _File.forLinking(AnalysisDriver driver, Source source) { |
| 849 String path = source.fullName; |
| 850 String contentHash = driver._fileContentHashMap[path]; |
| 851 // If we don't have the file content hash, compute it. |
| 852 if (contentHash == null) { |
| 853 _File file = new _File.forContent(driver, source); |
| 854 contentHash = file.contentHash; |
| 855 } |
| 856 // If we have the cached unlinked bundle, use it. |
| 857 { |
| 858 String key = '$contentHash.unlinked'; |
| 859 List<int> bytes = driver._byteStore.get(key); |
| 860 if (bytes != null) { |
| 861 PackageBundle unlinked = new PackageBundle.fromBuffer(bytes); |
| 862 driver._fileApiSignatureMap[path] = unlinked.apiSignature; |
| 863 return new _File._(driver, source, null, contentHash, unlinked, null); |
| 864 } |
| 865 } |
| 866 // Otherwise, read the source, parse and build a new unlinked bundle. |
| 867 return new _File.forResolution(driver, source); |
| 868 } |
| 869 |
| 870 factory _File.forResolution(AnalysisDriver driver, Source source) { |
| 871 _File file = new _File.forContent(driver, source); |
| 872 String path = file.path; |
| 873 String content = file.content; |
| 874 String contentHash = file.contentHash; |
856 // Parse the unit. | 875 // Parse the unit. |
857 CompilationUnit unit = _parse(driver, source, content); | 876 CompilationUnit unit = _parse(driver, source, content); |
858 // Prepare the unlinked bundle. | 877 // Prepare the unlinked bundle. |
859 PackageBundle unlinked; | 878 PackageBundle unlinked; |
860 { | 879 { |
861 String key = '$contentHash.unlinked'; | 880 String key = '$contentHash.unlinked'; |
862 List<int> bytes = driver._byteStore.get(key); | 881 List<int> bytes = driver._byteStore.get(key); |
863 if (bytes == null) { | 882 if (bytes == null) { |
864 driver._logger.run('Create unlinked for $path', () { | 883 driver._logger.run('Create unlinked for $path', () { |
865 UnlinkedUnitBuilder unlinkedUnit = serializeAstUnlinked(unit); | 884 UnlinkedUnitBuilder unlinkedUnit = serializeAstUnlinked(unit); |
866 PackageBundleAssembler assembler = new PackageBundleAssembler(); | 885 PackageBundleAssembler assembler = new PackageBundleAssembler(); |
867 assembler.addUnlinkedUnitWithHash( | 886 assembler.addUnlinkedUnitWithHash( |
868 source.uri.toString(), unlinkedUnit, contentHash); | 887 source.uri.toString(), unlinkedUnit, contentHash); |
869 bytes = assembler.assemble().toBuffer(); | 888 bytes = assembler.assemble().toBuffer(); |
870 driver._byteStore.put(key, bytes); | 889 driver._byteStore.put(key, bytes); |
871 }); | 890 }); |
872 } | 891 } |
873 unlinked = new PackageBundle.fromBuffer(bytes); | 892 unlinked = new PackageBundle.fromBuffer(bytes); |
874 driver._fileApiSignatureMap[path] = unlinked.apiSignature; | 893 driver._fileApiSignatureMap[path] = unlinked.apiSignature; |
875 } | 894 } |
876 // Update the current file state. | 895 // Return the full file. |
877 return new _File._(driver, source, content, contentHash, unlinked, unit); | 896 return new _File._(driver, source, content, contentHash, unlinked, unit); |
878 } | 897 } |
879 | 898 |
880 _File._(this.driver, this.source, this.content, this.contentHash, | 899 _File._(this.driver, this.source, this.content, this.contentHash, |
881 this.unlinked, this.unit); | 900 this.unlinked, this.unit); |
882 | 901 |
883 String get path => source.fullName; | 902 String get path => source.fullName; |
884 | 903 |
885 Uri get uri => source.uri; | 904 Uri get uri => source.uri; |
886 | 905 |
(...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1082 } | 1101 } |
1083 } | 1102 } |
1084 for (UnlinkedExportPublic export in unit.publicNamespace.exports) { | 1103 for (UnlinkedExportPublic export in unit.publicNamespace.exports) { |
1085 referenced.exported.add(export.uri); | 1104 referenced.exported.add(export.uri); |
1086 } | 1105 } |
1087 return referenced; | 1106 return referenced; |
1088 } | 1107 } |
1089 | 1108 |
1090 _ReferencedUris._(); | 1109 _ReferencedUris._(); |
1091 } | 1110 } |
OLD | NEW |