Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | |
| 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. | |
| 4 | |
| 5 import 'dart:collection'; | |
| 6 import 'dart:typed_data'; | |
| 7 | |
| 8 import 'package:analyzer/file_system/file_system.dart'; | |
| 9 import 'package:analyzer/src/dart/analysis/byte_store.dart'; | |
| 10 import 'package:analyzer/src/dart/analysis/driver.dart'; | |
| 11 import 'package:analyzer/src/dart/analysis/file_state.dart'; | |
| 12 import 'package:analyzer/src/generated/engine.dart'; | |
| 13 import 'package:analyzer/src/generated/source.dart'; | |
| 14 | |
| 15 /** | |
| 16 * Callback used by [FileTracker] to report to its client that files have been | |
| 17 * added, changed, or removed, and therefore more analysis may be necessary. | |
| 18 */ | |
| 19 typedef void FileTrackerChangeHook(); | |
| 20 | |
| 21 /** | |
| 22 * Maintains the file system state needed by the analysis driver, as well as | |
| 23 * information about files that have changed and the impact of those changes. | |
| 24 * | |
| 25 * Three related sets of files are tracked: "added files" is the set of files | |
| 26 * for which the client would like analysis. "changed files" is the set of | |
| 27 * files which is known to have changed, but for which we have not yet measured | |
| 28 * the impact of the change. "pending files" is the subset of "added files" | |
| 29 * which have been impacted by a change, and thus need analysis. | |
|
scheglov
2017/02/02 21:52:17
It is more precise to say "might have been impacte
Paul Berry
2017/02/02 22:22:19
Done.
| |
| 30 * | |
| 31 * Provides methods for updating the file system state in response to changes. | |
| 32 */ | |
| 33 class FileTracker { | |
| 34 /** | |
| 35 * Callback invoked whenever a change occurs that may require the client to | |
| 36 * perform analysis. | |
| 37 */ | |
| 38 final FileTrackerChangeHook _changeHook; | |
| 39 | |
| 40 /** | |
| 41 * The logger to write performed operations and performance to. | |
| 42 */ | |
| 43 final PerformanceLog logger; | |
| 44 | |
| 45 /** | |
| 46 * The current file system state. | |
| 47 */ | |
| 48 final FileSystemState fsState; | |
| 49 | |
| 50 /** | |
| 51 * The set of added files. | |
| 52 */ | |
| 53 final addedFiles = new LinkedHashSet<String>(); | |
| 54 | |
| 55 /** | |
| 56 * The set of files were reported as changed through [changeFile] and not | |
| 57 * checked for actual changes yet. | |
| 58 */ | |
| 59 final _changedFiles = new LinkedHashSet<String>(); | |
| 60 | |
| 61 /** | |
| 62 * The set of files that are currently scheduled for analysis. | |
| 63 */ | |
| 64 final _pendingFiles = new LinkedHashSet<String>(); | |
| 65 | |
| 66 FileTracker( | |
| 67 this.logger, | |
| 68 ByteStore byteStore, | |
| 69 FileContentOverlay contentOverlay, | |
| 70 ResourceProvider resourceProvider, | |
| 71 SourceFactory sourceFactory, | |
| 72 AnalysisOptions analysisOptions, | |
| 73 Uint32List salt, | |
| 74 this._changeHook) | |
| 75 : fsState = new FileSystemState(logger, byteStore, contentOverlay, | |
| 76 resourceProvider, sourceFactory, analysisOptions, salt); | |
| 77 | |
| 78 /** | |
| 79 * Returns the path to exactly one that needs analysis. Throws a [StateError] | |
| 80 * if no files need analysis. | |
| 81 */ | |
| 82 String get anyPendingFile => _pendingFiles.first; | |
| 83 | |
| 84 /** | |
| 85 * Returns a boolean indicating whether there are any files that have changed, | |
| 86 * but for which the impact of the changes hasn't been measured. | |
| 87 */ | |
| 88 bool get hasChangedFiles => _changedFiles.isNotEmpty; | |
| 89 | |
| 90 /** | |
| 91 * Returns a boolean indicating whether there are any files that need | |
| 92 * analysis. | |
| 93 */ | |
| 94 bool get hasPendingFiles => _pendingFiles.isNotEmpty; | |
| 95 | |
| 96 /** | |
| 97 * Returns a count of how many files need analysis. | |
| 98 */ | |
| 99 int get numberOfPendingFiles => _pendingFiles.length; | |
| 100 | |
| 101 /** | |
| 102 * Adds the given [path] to the set of "added files". | |
| 103 */ | |
| 104 void addFile(String path) { | |
| 105 addedFiles.add(path); | |
| 106 _pendingFiles.add(path); | |
| 107 _changeHook(); | |
| 108 } | |
| 109 | |
| 110 /** | |
| 111 * Adds the given [paths] to the set of "added files". | |
| 112 */ | |
| 113 void addFiles(Iterable<String> paths) { | |
| 114 addedFiles.addAll(paths); | |
| 115 _pendingFiles.addAll(paths); | |
| 116 _changeHook(); | |
| 117 } | |
| 118 | |
| 119 /** | |
| 120 * Adds the given [path] to the set of "changed files". | |
| 121 */ | |
| 122 void changeFile(String path) { | |
| 123 _changedFiles.add(path); | |
| 124 if (addedFiles.contains(path)) { | |
| 125 _pendingFiles.add(path); | |
| 126 } | |
| 127 _changeHook(); | |
| 128 } | |
| 129 | |
| 130 /** | |
| 131 * Removes the given [path] from the set of "pending files". | |
| 132 * | |
| 133 * Should be called after the client has analyzed a file. | |
| 134 */ | |
| 135 void fileWasAnalyzed(String path) { | |
| 136 _pendingFiles.remove(path); | |
| 137 } | |
| 138 | |
| 139 /** | |
| 140 * Returns a boolean indicating whether the given [path] points to a file that | |
| 141 * requires analysis. | |
| 142 */ | |
| 143 bool isFilePending(String path) => _pendingFiles.contains(path); | |
| 144 | |
| 145 /** | |
| 146 * Removes the given [path] from the set of "added files". | |
| 147 */ | |
| 148 void removeFile(String path) { | |
| 149 addedFiles.remove(path); | |
| 150 _pendingFiles.remove(path); | |
| 151 // TODO(paulberry): removing the path from [fsState] and re-analyzing all | |
| 152 // files seems extreme. | |
| 153 fsState.removeFile(path); | |
| 154 _pendingFiles.addAll(addedFiles); | |
| 155 _changeHook(); | |
| 156 } | |
| 157 | |
| 158 /** | |
| 159 * Verify the API signature for the file with the given [path], and decide | |
| 160 * which linked libraries should be invalidated, and files reanalyzed. | |
| 161 */ | |
| 162 FileState verifyApiSignature(String path) { | |
| 163 return logger.run('Verify API signature of $path', () { | |
| 164 bool anyApiChanged = false; | |
| 165 List<FileState> files = fsState.getFilesForPath(path); | |
| 166 for (FileState file in files) { | |
| 167 bool apiChanged = file.refresh(); | |
| 168 if (apiChanged) { | |
| 169 anyApiChanged = true; | |
| 170 } | |
| 171 } | |
| 172 if (anyApiChanged) { | |
| 173 logger.writeln('API signatures mismatch found for $path'); | |
| 174 // TODO(scheglov) schedule analysis of only affected files | |
| 175 _pendingFiles.addAll(addedFiles); | |
| 176 } | |
| 177 return files[0]; | |
| 178 }); | |
| 179 } | |
| 180 | |
| 181 /** | |
| 182 * If at least one file is in the "changed files" set, determines the impact | |
| 183 * of the change, updates the set of pending files, and returns `true`. | |
| 184 * | |
| 185 * If no files are in the "changed files" set, returns `false`. | |
| 186 */ | |
| 187 bool verifyChangedFilesIfNeeded() { | |
| 188 // Verify all changed files one at a time. | |
| 189 if (_changedFiles.isNotEmpty) { | |
| 190 String path = _changedFiles.first; | |
| 191 _changedFiles.remove(path); | |
| 192 // If the file has not been accessed yet, we either will eventually read | |
| 193 // it later while analyzing one of the added files, or don't need it. | |
| 194 if (fsState.knownFilePaths.contains(path)) { | |
| 195 verifyApiSignature(path); | |
| 196 } | |
| 197 return true; | |
| 198 } | |
| 199 return false; | |
| 200 } | |
| 201 } | |
| OLD | NEW |