 Chromium Code Reviews
 Chromium Code Reviews Issue 2450053003:
  Replace Stream with monitor.  (Closed)
    
  
    Issue 2450053003:
  Replace Stream with monitor.  (Closed) 
  | 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 |