Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(175)

Side by Side Diff: pkg/analyzer/lib/src/dart/analysis/driver.dart

Issue 2443083002: Delay invalidating linked hashes until after checking the API signature of the changed file. (Closed)
Patch Set: Extract _getCurrentUnlinked and _File.currentContentHash. Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
114 * [getResult] to the [Completer]s to report the result. 114 * [getResult] to the [Completer]s to report the result.
115 */ 115 */
116 final _requestedFiles = <String, List<Completer<AnalysisResult>>>{}; 116 final _requestedFiles = <String, List<Completer<AnalysisResult>>>{};
117 117
118 /** 118 /**
119 * The set of explicitly analyzed files. 119 * The set of explicitly analyzed files.
120 */ 120 */
121 final _explicitFiles = new LinkedHashSet<String>(); 121 final _explicitFiles = new LinkedHashSet<String>();
122 122
123 /** 123 /**
124 * The set of files were reported as changed through [changeFile] and for
125 * which API signatures should be recomputed and compared before performing
126 * any other analysis.
127 */
128 final _filesToVerifyUnlinkedSignature = new Set<String>();
129
130 /**
124 * The set of files that are currently scheduled for analysis. 131 * The set of files that are currently scheduled for analysis.
125 */ 132 */
126 final _filesToAnalyze = new LinkedHashSet<String>(); 133 final _filesToAnalyze = new LinkedHashSet<String>();
127 134
128 /** 135 /**
129 * Cache of URI resolution. The outer map key is the absolute URI of the 136 * Cache of URI resolution. The outer map key is the absolute URI of the
130 * containing file. The inner map key is the URI text of a directive 137 * containing file. The inner map key is the URI text of a directive
131 * contained in that file. The inner map value is the [Source] object which 138 * contained in that file. The inner map value is the [Source] object which
132 * that URI text resolves to. 139 * that URI text resolves to.
133 */ 140 */
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
185 * client does not change the state of the files. 192 * client does not change the state of the files.
186 * 193 *
187 * Results might be produced even for files that have never been added 194 * Results might be produced even for files that have never been added
188 * using [addFile], for example when [getResult] was called for a file. 195 * using [addFile], for example when [getResult] was called for a file.
189 */ 196 */
190 Stream<AnalysisResult> get results async* { 197 Stream<AnalysisResult> get results async* {
191 try { 198 try {
192 while (true) { 199 while (true) {
193 // TODO(scheglov) implement state transitioning 200 // TODO(scheglov) implement state transitioning
194 await for (String why in _hasWorkStreamController.stream) { 201 await for (String why in _hasWorkStreamController.stream) {
202 _verifyUnlinkedSignatureOfChangedFiles();
203
195 // Analyze the first file in the general queue. 204 // Analyze the first file in the general queue.
196 if (_filesToAnalyze.isNotEmpty) { 205 if (_filesToAnalyze.isNotEmpty) {
197 _logger.run('Analyze ${_filesToAnalyze.length} files', () { 206 _logger.run('Analyze ${_filesToAnalyze.length} files', () {
198 while (_filesToAnalyze.isNotEmpty) { 207 while (_filesToAnalyze.isNotEmpty) {
199 String path = _filesToAnalyze.first; 208 String path = _filesToAnalyze.first;
200 _filesToAnalyze.remove(path); 209 _filesToAnalyze.remove(path);
201 _File file = _fileForPath(path); 210 _File file = _fileForPath(path);
202 _computeAndPrintErrors(file); 211 _computeAndPrintErrors(file);
203 // TODO(scheglov) yield the result 212 // TODO(scheglov) yield the result
204 } 213 }
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
237 * that state already). Schedules the file contents for [path] to be read 246 * that state already). Schedules the file contents for [path] to be read
238 * into the current file state prior to the next time the analysis state 247 * into the current file state prior to the next time the analysis state
239 * transitions to "idle". 248 * transitions to "idle".
240 * 249 *
241 * Invocation of this method will not prevent a [Future] returned from 250 * Invocation of this method will not prevent a [Future] returned from
242 * [getResult] from completing with a result, but the result is not 251 * [getResult] from completing with a result, but the result is not
243 * guaranteed to be consistent with the new current file state after this 252 * guaranteed to be consistent with the new current file state after this
244 * [changeFile] invocation. 253 * [changeFile] invocation.
245 */ 254 */
246 void changeFile(String path) { 255 void changeFile(String path) {
247 // TODO(scheglov) Don't clear, schedule API signature validation. 256 _filesToVerifyUnlinkedSignature.add(path);
248 _fileContentHashMap.clear();
249 _dependencySignatureMap.clear();
250 _filesToAnalyze.add(path); 257 _filesToAnalyze.add(path);
251 _filesToAnalyze.addAll(_explicitFiles);
252 // TODO(scheglov) name?!
253 _hasWorkStreamController.add('do it!'); 258 _hasWorkStreamController.add('do it!');
254 } 259 }
255 260
256 /** 261 /**
257 * Return the [Future] that completes with a [AnalysisResult] for the file 262 * Return the [Future] that completes with a [AnalysisResult] for the file
258 * with the given [path]. 263 * with the given [path].
259 * 264 *
260 * The [path] must be absolute and normalized. 265 * The [path] must be absolute and normalized.
261 * 266 *
262 * The [path] can be any file - explicitly or implicitly analyzed, or neither. 267 * The [path] can be any file - explicitly or implicitly analyzed, or neither.
(...skipping 237 matching lines...) Expand 10 before | Expand all | Expand 10 after
500 * Return the [_File] for the given [path] in [_sourceFactory]. 505 * Return the [_File] for the given [path] in [_sourceFactory].
501 */ 506 */
502 _File _fileForPath(String path) { 507 _File _fileForPath(String path) {
503 Source fileSource = _resourceProvider.getFile(path).createSource(); 508 Source fileSource = _resourceProvider.getFile(path).createSource();
504 Uri uri = _sourceFactory.restoreUri(fileSource); 509 Uri uri = _sourceFactory.restoreUri(fileSource);
505 Source source = _resourceProvider.getFile(path).createSource(uri); 510 Source source = _resourceProvider.getFile(path).createSource(uri);
506 return new _File(this, source); 511 return new _File(this, source);
507 } 512 }
508 513
509 /** 514 /**
515 * Return the unlinked bundle of [file] for the current file state, or `null`.
516 */
517 PackageBundle _getCurrentUnlinked(_File file) {
518 String key = '${file.currentContentHash}.unlinked';
519 List<int> bytes = _byteStore.get(key);
520 return bytes != null ? new PackageBundle.fromBuffer(bytes) : null;
521 }
522
523 /**
510 * TODO(scheglov) It would be nice to get URIs of "parts" from unlinked. 524 * TODO(scheglov) It would be nice to get URIs of "parts" from unlinked.
511 */ 525 */
512 _ReferencedUris _getReferencedUris(_File file) { 526 _ReferencedUris _getReferencedUris(_File file) {
513 // Try to get from the store. 527 // Try to get from the store.
514 { 528 {
515 String key = '${file.contentHash}.uris'; 529 String key = '${file.contentHash}.uris';
516 List<int> bytes = _byteStore.get(key); 530 List<int> bytes = _byteStore.get(key);
517 if (bytes != null) { 531 if (bytes != null) {
518 fb.BufferContext bp = new fb.BufferContext.fromBytes(bytes); 532 fb.BufferContext bp = new fb.BufferContext.fromBytes(bytes);
519 int table = bp.derefObject(0); 533 int table = bp.derefObject(0);
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
576 // So, we need to update the key. 590 // So, we need to update the key.
577 String key = '${file.contentHash}.uris'; 591 String key = '${file.contentHash}.uris';
578 _byteStore.put(key, bytes); 592 _byteStore.put(key, bytes);
579 593
580 return referencedUris; 594 return referencedUris;
581 } 595 }
582 596
583 /** 597 /**
584 * Return the unlinked bundle of [file] for the current file state. 598 * Return the unlinked bundle of [file] for the current file state.
585 * 599 *
586 * That is, if there is an existing bundle for the current content hash 600 * Return [_getCurrentUnlinked] or read the [file] content is read, compute
587 * of the [file] in the [_byteStore], then it is returned. Otherwise, the 601 * the content hash and update the current file state accordingly. Parse the
588 * [file] content is read, the content hash is computed and the current file 602 * content into the [CompilationUnit] and serialize into a new unlinked
589 * state is updated accordingly. That the content is parsed into the 603 * bundle. The bundle is then put into the [_byteStore] and returned.
590 * [CompilationUnit] and serialized into a new unlinked bundle. The bundle
591 * is then put into the [_byteStore] and returned.
592 */ 604 */
593 PackageBundle _getUnlinked(_File file) { 605 PackageBundle _getUnlinked(_File file) {
594 // Try to get bytes for file's unlinked bundle. 606 return _getCurrentUnlinked(file) ??
595 List<int> bytes; 607 _logger.run('Create unlinked for $file', () {
596 { 608 String key = '${file.contentHash}.unlinked';
597 String key = '${file.contentHash}.unlinked'; 609 UnlinkedUnitBuilder unlinkedUnit = serializeAstUnlinked(file.unit);
598 bytes = _byteStore.get(key); 610 PackageBundleAssembler assembler = new PackageBundleAssembler();
611 assembler.addUnlinkedUnitWithHash(
612 file.uri.toString(), unlinkedUnit, key);
613 List<int> bytes = assembler.assemble().toBuffer();
614 _byteStore.put(key, bytes);
615 return new PackageBundle.fromBuffer(bytes);
616 });
617 }
618
619 /**
620 * Verify the API signatures for the changed files, and decide which linked
621 * libraries should be invalidated, and files reanalyzed.
622 */
623 void _verifyUnlinkedSignatureOfChangedFiles() {
624 if (_filesToVerifyUnlinkedSignature.isEmpty) {
625 return;
599 } 626 }
600 // If no cached unlinked bundle, compute it. 627 int numOfFiles = _filesToVerifyUnlinkedSignature.length;
601 if (bytes == null) { 628 _logger.run('Verify API signatures of $numOfFiles files', () {
602 _logger.run('Create unlinked for $file', () { 629 for (String path in _filesToVerifyUnlinkedSignature) {
603 // We read the content and recomputed the hash. 630 _File file = _fileForPath(path);
604 // So, we need to update the key. 631 // Get the existing old API signature, maybe null.
605 String key = '${file.contentHash}.unlinked'; 632 String oldSignature = _getCurrentUnlinked(file)?.apiSignature;
606 UnlinkedUnitBuilder unlinkedUnit = serializeAstUnlinked(file.unit); 633 // Clear the content hash cache, so force the file reading.
607 PackageBundleAssembler assembler = new PackageBundleAssembler(); 634 _fileContentHashMap.remove(path);
608 assembler.addUnlinkedUnitWithHash( 635 // Compute the new API signature.
609 file.uri.toString(), unlinkedUnit, key); 636 String newSignature = _getUnlinked(file).apiSignature;
610 bytes = assembler.assemble().toBuffer(); 637 // If the signatures are not the same, then potentially every linked
611 _byteStore.put(key, bytes); 638 // library is inconsistent and should be recomputed, and every explicit
612 }); 639 // file has inconsistent analysis results which also should be recompute d.
613 } 640 if (oldSignature != newSignature) {
614 return new PackageBundle.fromBuffer(bytes); 641 _logger.writeln('API signature mismatch found for $file.');
642 _dependencySignatureMap.clear();
643 _filesToAnalyze.addAll(_explicitFiles);
644 // Stop the verification, and restart analysis.
645 break;
646 }
647 }
648 _filesToVerifyUnlinkedSignature.clear();
649 });
615 } 650 }
616 } 651 }
617 652
618 /** 653 /**
619 * The result of analyzing of a single file. 654 * The result of analyzing of a single file.
620 * 655 *
621 * These results are self-consistent, i.e. [content], [contentHash], the 656 * These results are self-consistent, i.e. [content], [contentHash], the
622 * resolved [unit] correspond to each other. All referenced elements, even 657 * resolved [unit] correspond to each other. All referenced elements, even
623 * external ones, are also self-consistent. But none of the results is 658 * external ones, are also self-consistent. But none of the results is
624 * guaranteed to be consistent with the state of the files. 659 * guaranteed to be consistent with the state of the files.
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after
740 * current file state is updated. 775 * current file state is updated.
741 */ 776 */
742 String get content { 777 String get content {
743 if (_content == null) { 778 if (_content == null) {
744 _readContentAndComputeHash(); 779 _readContentAndComputeHash();
745 } 780 }
746 return _content; 781 return _content;
747 } 782 }
748 783
749 /** 784 /**
750 * Ensure that the [contentHash] is filled. 785 * Ensure that the content hash is set for this [_File] instance, return it.
751 * 786 *
752 * If the hash is already in the current file state, return the current 787 * If the content hash has already been set for this [_File] instance, it is
753 * value. Otherwise, read the [content], compute the hash, put it into 788 * not updated here. But the hash value might be updated on [content] access.
754 * the current file state, and update the [contentHash] field. 789 *
790 * If the content hash is known in the current file state, use it.
791 *
792 * Otherwise, read the [content], compute the hash, put it into the current
793 * file state, and update the [contentHash] field.
755 * 794 *
756 * The client should not remember values of this property, because its value 795 * The client should not remember values of this property, because its value
757 * might change when [content] is read and the hash is recomputed. 796 * might change when [content] is read and the hash is recomputed.
758 */ 797 */
759 String get contentHash { 798 String get contentHash {
760 _contentHash ??= driver._fileContentHashMap[path]; 799 _contentHash ??= currentContentHash;
761 if (_contentHash == null) { 800 if (_contentHash == null) {
762 _readContentAndComputeHash(); 801 _readContentAndComputeHash();
763 } 802 }
764 return _contentHash; 803 return _contentHash;
765 } 804 }
766 805
806 /**
807 * Return the hash of the file content in the current file state, or `null`
808 * if the current file state does not know the current file content hash.
809 */
810 String get currentContentHash {
811 return driver._fileContentHashMap[path];
812 }
813
767 String get path => source.fullName; 814 String get path => source.fullName;
768 815
769 /** 816 /**
770 * Return the unresolved [CompilationUnit] of the file. 817 * Return the unresolved [CompilationUnit] of the file.
771 * 818 *
772 * Performing resolution and computing errors is done in a separate analysis 819 * Performing resolution and computing errors is done in a separate analysis
773 * context. In the future we might push the existing unresolved unit into the 820 * context. In the future we might push the existing unresolved unit into the
774 * analysis context, so at some point the unit might become resolved. 821 * analysis context, so at some point the unit might become resolved.
775 */ 822 */
776 CompilationUnit get unit { 823 CompilationUnit get unit {
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after
952 999
953 /** 1000 /**
954 * TODO(scheglov) document 1001 * TODO(scheglov) document
955 */ 1002 */
956 class _ReferencedUris { 1003 class _ReferencedUris {
957 bool isLibrary = true; 1004 bool isLibrary = true;
958 final List<String> imported = <String>[]; 1005 final List<String> imported = <String>[];
959 final List<String> exported = <String>[]; 1006 final List<String> exported = <String>[];
960 final List<String> parted = <String>[]; 1007 final List<String> parted = <String>[];
961 } 1008 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698