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 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
151 * It maps file paths to the unlinked API signatures. | 151 * It maps file paths to the unlinked API signatures. |
152 */ | 152 */ |
153 final _fileApiSignatureMap = <String, String>{}; | 153 final _fileApiSignatureMap = <String, String>{}; |
154 | 154 |
155 /** | 155 /** |
156 * Mapping from library URIs to the dependency signature of the library. | 156 * Mapping from library URIs to the dependency signature of the library. |
157 */ | 157 */ |
158 final _dependencySignatureMap = <Uri, String>{}; | 158 final _dependencySignatureMap = <Uri, String>{}; |
159 | 159 |
160 /** | 160 /** |
161 * TODO(scheglov) document and improve | 161 * The monitor that is signalled when there is work to do. |
162 */ | 162 */ |
163 final _hasWorkStreamController = new StreamController<String>(); | 163 final _Monitor _hasWork = new _Monitor(); |
164 | 164 |
165 AnalysisDriver(this._logger, this._resourceProvider, this._byteStore, | 165 AnalysisDriver(this._logger, this._resourceProvider, this._byteStore, |
166 this._contentCache, this._sourceFactory, this._analysisOptions) { | 166 this._contentCache, this._sourceFactory, this._analysisOptions) { |
167 _sdkBundle = _sourceFactory.dartSdk.getLinkedBundle(); | 167 _sdkBundle = _sourceFactory.dartSdk.getLinkedBundle(); |
168 } | 168 } |
169 | 169 |
170 /** | 170 /** |
171 * Set the list of files that the driver should try to analyze sooner. | 171 * Set the list of files that the driver should try to analyze sooner. |
172 * | 172 * |
173 * Every path in the list must be absolute and normalized. | 173 * Every path in the list must be absolute and normalized. |
174 * | 174 * |
175 * The driver will produce the results through the [results] stream. The | 175 * The driver will produce the results through the [results] stream. The |
176 * exact order in which results are produced is not defined, neither | 176 * exact order in which results are produced is not defined, neither |
177 * between priority files, nor between priority and non-priority files. | 177 * between priority files, nor between priority and non-priority files. |
178 */ | 178 */ |
179 void set priorityFiles(List<String> priorityPaths) { | 179 void set priorityFiles(List<String> priorityPaths) { |
180 // TODO(scheglov) implement | 180 // TODO(scheglov) implement |
181 } | 181 } |
182 | 182 |
183 /** | 183 /** |
184 * Return the [Stream] that produces [AnalysisResult]s for added files. | 184 * Return the [Stream] that produces [AnalysisResult]s for added files. |
185 * | 185 * |
186 * Analysis starts when the client starts listening to the stream, and stops | 186 * Analysis starts when the client starts listening to the stream, and stops |
187 * when the client cancels the subscription. | 187 * when the client cancels the subscription. |
Paul Berry
2016/10/31 16:40:47
It looks to me like it's not safe to call `.result
scheglov
2016/10/31 17:45:43
Yes, this is a normal Stream, not broadcast.
So, o
| |
188 * | 188 * |
189 * When the client starts listening, the analysis state transitions to | 189 * When the client starts listening, the analysis state transitions to |
190 * "analyzing" and an analysis result is produced for every added file prior | 190 * "analyzing" and an analysis result is produced for every added file prior |
191 * to the next time the analysis state transitions to "idle". | 191 * to the next time the analysis state transitions to "idle". |
192 * | 192 * |
193 * At least one analysis result is produced for every file passed to | 193 * At least one analysis result is produced for every file passed to |
194 * [addFile] or [changeFile] prior to the next time the analysis state | 194 * [addFile] or [changeFile] prior to the next time the analysis state |
195 * transitions to "idle". Analysis results for other files are produced | 195 * transitions to "idle". Analysis results for other files are produced |
196 * only if the changes affect analysis results of other files. | 196 * only if the changes affect analysis results of other files. |
197 * | 197 * |
198 * More than one result might be produced for the same file, even if the | 198 * More than one result might be produced for the same file, even if the |
199 * client does not change the state of the files. | 199 * client does not change the state of the files. |
200 * | 200 * |
201 * Results might be produced even for files that have never been added | 201 * Results might be produced even for files that have never been added |
202 * using [addFile], for example when [getResult] was called for a file. | 202 * using [addFile], for example when [getResult] was called for a file. |
203 */ | 203 */ |
204 Stream<AnalysisResult> get results async* { | 204 Stream<AnalysisResult> get results async* { |
205 try { | 205 try { |
206 PerformanceLogSection analysisSection = null; | |
206 while (true) { | 207 while (true) { |
207 // TODO(scheglov) implement state transitioning | 208 // TODO(scheglov) implement state transitioning |
208 await for (String why in _hasWorkStreamController.stream) { | 209 await _hasWork.signal; |
209 _verifyUnlinkedSignatureOfChangedFiles(); | |
210 | 210 |
211 // Analyze the first file in the general queue. | 211 if (analysisSection == null) { |
212 if (_filesToAnalyze.isNotEmpty) { | 212 analysisSection = _logger.enter('Analyzing'); |
213 PerformanceLogSection analysisSection = | |
214 _logger.enter('Analyze ${_filesToAnalyze.length} files'); | |
215 try { | |
216 // TODO(scheglov) The loop is strange for now. | |
217 while (_filesToAnalyze.isNotEmpty) { | |
218 String path = _filesToAnalyze.first; | |
219 _filesToAnalyze.remove(path); | |
220 _File file = _fileForPath(path); | |
221 AnalysisResult result = _computeAnalysisResult(file); | |
222 yield result; | |
223 } | |
224 } finally { | |
225 analysisSection.exit(); | |
226 } | |
227 } | |
228 } | 213 } |
229 // TODO(scheglov) implement | 214 |
215 // TODO(scheglov) verify one file at a time | |
216 _verifyUnlinkedSignatureOfChangedFiles(); | |
217 | |
218 // Analyze the first file in the general queue. | |
219 if (_filesToAnalyze.isNotEmpty) { | |
220 String path = _filesToAnalyze.first; | |
221 _filesToAnalyze.remove(path); | |
222 _File file = _fileForPath(path); | |
223 AnalysisResult result = _computeAnalysisResult(file); | |
224 yield result; | |
225 } | |
226 | |
227 // If there is work to do, notify the monitor. | |
228 if (_filesToAnalyze.isNotEmpty) { | |
229 _hasWork.notify(); | |
230 } else { | |
231 analysisSection.exit(); | |
232 analysisSection = null; | |
233 } | |
230 } | 234 } |
235 // TODO(scheglov) implement | |
231 } finally { | 236 } finally { |
232 print('The stream was cancelled.'); | 237 print('The stream was cancelled.'); |
233 } | 238 } |
234 } | 239 } |
235 | 240 |
236 /** | 241 /** |
237 * Add the file with the given [path] to the set of files to analyze. | 242 * Add the file with the given [path] to the set of files to analyze. |
238 * | 243 * |
239 * The [path] must be absolute and normalized. | 244 * The [path] must be absolute and normalized. |
240 * | 245 * |
241 * The results of analysis are eventually produced by the [results] stream. | 246 * The results of analysis are eventually produced by the [results] stream. |
242 */ | 247 */ |
243 void addFile(String path) { | 248 void addFile(String path) { |
244 _explicitFiles.add(path); | 249 _explicitFiles.add(path); |
245 _filesToAnalyze.add(path); | 250 _filesToAnalyze.add(path); |
246 _hasWorkStreamController.add('do it!'); | 251 _hasWork.notify(); |
247 } | 252 } |
248 | 253 |
249 /** | 254 /** |
250 * The file with the given [path] might have changed - updated, added or | 255 * The file with the given [path] might have changed - updated, added or |
251 * removed. Or not, we don't know. Or it might have, but then changed back. | 256 * removed. Or not, we don't know. Or it might have, but then changed back. |
252 * | 257 * |
253 * The [path] must be absolute and normalized. | 258 * The [path] must be absolute and normalized. |
254 * | 259 * |
255 * The [path] can be any file - explicitly or implicitly analyzed, or neither. | 260 * The [path] can be any file - explicitly or implicitly analyzed, or neither. |
256 * | 261 * |
257 * Causes the analysis state to transition to "analyzing" (if it is not in | 262 * Causes the analysis state to transition to "analyzing" (if it is not in |
258 * that state already). Schedules the file contents for [path] to be read | 263 * that state already). Schedules the file contents for [path] to be read |
259 * into the current file state prior to the next time the analysis state | 264 * into the current file state prior to the next time the analysis state |
260 * transitions to "idle". | 265 * transitions to "idle". |
261 * | 266 * |
262 * Invocation of this method will not prevent a [Future] returned from | 267 * Invocation of this method will not prevent a [Future] returned from |
263 * [getResult] from completing with a result, but the result is not | 268 * [getResult] from completing with a result, but the result is not |
264 * guaranteed to be consistent with the new current file state after this | 269 * guaranteed to be consistent with the new current file state after this |
265 * [changeFile] invocation. | 270 * [changeFile] invocation. |
266 */ | 271 */ |
267 void changeFile(String path) { | 272 void changeFile(String path) { |
268 _filesToVerifyUnlinkedSignature.add(path); | 273 _filesToVerifyUnlinkedSignature.add(path); |
269 _filesToAnalyze.add(path); | 274 _filesToAnalyze.add(path); |
270 _hasWorkStreamController.add('do it!'); | 275 _hasWork.notify(); |
271 } | 276 } |
272 | 277 |
273 /** | 278 /** |
274 * Return the [Future] that completes with a [AnalysisResult] for the file | 279 * Return the [Future] that completes with a [AnalysisResult] for the file |
275 * with the given [path]. | 280 * with the given [path]. |
276 * | 281 * |
277 * The [path] must be absolute and normalized. | 282 * The [path] must be absolute and normalized. |
278 * | 283 * |
279 * The [path] can be any file - explicitly or implicitly analyzed, or neither. | 284 * The [path] can be any file - explicitly or implicitly analyzed, or neither. |
280 * | 285 * |
281 * Causes the analysis state to transition to "analyzing" (if it is not in | 286 * Causes the analysis state to transition to "analyzing" (if it is not in |
282 * that state already), the driver will read the file and produce the analysis | 287 * that state already), the driver will read the file and produce the analysis |
283 * result for it, which is consistent with the current file state (including | 288 * result for it, which is consistent with the current file state (including |
284 * the new state of the file), prior to the next time the analysis state | 289 * the new state of the file), prior to the next time the analysis state |
285 * transitions to "idle". | 290 * transitions to "idle". |
286 */ | 291 */ |
287 Future<AnalysisResult> getResult(String path) { | 292 Future<AnalysisResult> getResult(String path) { |
288 var completer = new Completer<AnalysisResult>(); | 293 var completer = new Completer<AnalysisResult>(); |
289 _requestedFiles | 294 _requestedFiles |
290 .putIfAbsent(path, () => <Completer<AnalysisResult>>[]) | 295 .putIfAbsent(path, () => <Completer<AnalysisResult>>[]) |
291 .add(completer); | 296 .add(completer); |
292 _hasWorkStreamController.add(path); | 297 _hasWork.notify(); |
293 return completer.future; | 298 return completer.future; |
294 } | 299 } |
295 | 300 |
296 /** | 301 /** |
297 * Remove the file with the given [path] from the list of files to analyze. | 302 * Remove the file with the given [path] from the list of files to analyze. |
298 * | 303 * |
299 * The [path] must be absolute and normalized. | 304 * The [path] must be absolute and normalized. |
300 * | 305 * |
301 * The results of analysis of the file might still be produced by the | 306 * The results of analysis of the file might still be produced by the |
302 * [results] stream. The driver will try to stop producing these results, | 307 * [results] stream. The driver will try to stop producing these results, |
(...skipping 632 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
935 | 940 |
936 appendDependencies(this); | 941 appendDependencies(this); |
937 } | 942 } |
938 } | 943 } |
939 | 944 |
940 @override | 945 @override |
941 String toString() => uri.toString(); | 946 String toString() => uri.toString(); |
942 } | 947 } |
943 | 948 |
944 /** | 949 /** |
950 * [_Monitor] can be used to wait for a signal. | |
951 * | |
952 * Signals are not queued, the client will receive exactly one signal | |
953 * regardless of the number of [notify] invocations. The [signal] is reset | |
954 * after completion and will not complete until [notify] is called next time. | |
955 */ | |
956 class _Monitor { | |
957 Completer<Null> _completer = new Completer<Null>(); | |
958 | |
959 /** | |
960 * Return a [Future] that completes when [notify] is called at least once. | |
961 */ | |
962 Future<Null> get signal async { | |
963 await _completer.future; | |
964 _completer = new Completer<Null>(); | |
965 } | |
966 | |
967 /** | |
968 * Complete the [signal] future if it is not completed yet. It is safe to | |
969 * call this method multiple times, but the [signal] will complete only once. | |
970 */ | |
971 void notify() { | |
972 if (!_completer.isCompleted) { | |
973 _completer.complete(true); | |
974 } | |
975 } | |
976 } | |
977 | |
978 /** | |
945 * TODO(scheglov) document | 979 * TODO(scheglov) document |
946 */ | 980 */ |
947 class _ReferencedUris { | 981 class _ReferencedUris { |
948 bool isLibrary = true; | 982 bool isLibrary = true; |
949 final List<String> imported = <String>[]; | 983 final List<String> imported = <String>[]; |
950 final List<String> exported = <String>[]; | 984 final List<String> exported = <String>[]; |
951 final List<String> parted = <String>[]; | 985 final List<String> parted = <String>[]; |
952 | 986 |
953 factory _ReferencedUris(UnlinkedUnit unit) { | 987 factory _ReferencedUris(UnlinkedUnit unit) { |
954 _ReferencedUris referenced = new _ReferencedUris._(); | 988 _ReferencedUris referenced = new _ReferencedUris._(); |
955 referenced.parted.addAll(unit.publicNamespace.parts); | 989 referenced.parted.addAll(unit.publicNamespace.parts); |
956 for (UnlinkedImport import in unit.imports) { | 990 for (UnlinkedImport import in unit.imports) { |
957 if (!import.isImplicit) { | 991 if (!import.isImplicit) { |
958 referenced.imported.add(import.uri); | 992 referenced.imported.add(import.uri); |
959 } | 993 } |
960 } | 994 } |
961 for (UnlinkedExportPublic export in unit.publicNamespace.exports) { | 995 for (UnlinkedExportPublic export in unit.publicNamespace.exports) { |
962 referenced.exported.add(export.uri); | 996 referenced.exported.add(export.uri); |
963 } | 997 } |
964 return referenced; | 998 return referenced; |
965 } | 999 } |
966 | 1000 |
967 _ReferencedUris._(); | 1001 _ReferencedUris._(); |
968 } | 1002 } |
OLD | NEW |