Chromium Code Reviews| Index: pkg/analyzer/lib/src/dart/analysis/driver.dart |
| diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart |
| index 4ea1d04f724271cbeafea9afc977bb30a6157cde..18149589a98050f3f2cf5e869b84caa33eebdd01 100644 |
| --- a/pkg/analyzer/lib/src/dart/analysis/driver.dart |
| +++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart |
| @@ -15,6 +15,7 @@ import 'package:analyzer/exception/exception.dart'; |
| import 'package:analyzer/file_system/file_system.dart'; |
| import 'package:analyzer/src/dart/analysis/byte_store.dart'; |
| import 'package:analyzer/src/dart/analysis/file_state.dart'; |
| +import 'package:analyzer/src/dart/analysis/file_tracker.dart'; |
| import 'package:analyzer/src/dart/analysis/index.dart'; |
| import 'package:analyzer/src/dart/analysis/library_context.dart'; |
| import 'package:analyzer/src/dart/analysis/search.dart'; |
| @@ -90,11 +91,6 @@ class AnalysisDriver { |
| final AnalysisDriverScheduler _scheduler; |
| /** |
| - * The logger to write performed operations and performance to. |
| - */ |
| - final PerformanceLog _logger; |
| - |
| - /** |
| * The resource provider for working with files. |
| */ |
| final ResourceProvider _resourceProvider; |
| @@ -139,16 +135,6 @@ class AnalysisDriver { |
| final Uint32List _salt = new Uint32List(1 + AnalysisOptions.signatureLength); |
| /** |
| - * The current file system state. |
| - */ |
| - FileSystemState _fsState; |
| - |
| - /** |
| - * The set of added files. |
| - */ |
| - final _addedFiles = new LinkedHashSet<String>(); |
| - |
| - /** |
| * The set of priority files, that should be analyzed sooner. |
| */ |
| final _priorityFiles = new LinkedHashSet<String>(); |
| @@ -189,17 +175,6 @@ class AnalysisDriver { |
| <String, List<Completer<CompilationUnitElement>>>{}; |
| /** |
| - * The set of files were reported as changed through [changeFile] and not |
| - * checked for actual changes yet. |
| - */ |
| - final _changedFiles = new LinkedHashSet<String>(); |
| - |
| - /** |
| - * The set of files that are currently scheduled for analysis. |
| - */ |
| - final _filesToAnalyze = new LinkedHashSet<String>(); |
| - |
| - /** |
| * The mapping from the files for which analysis was requested using |
| * [getResult], and which were found to be parts without known libraries, |
| * to the [Completer]s to report the result. |
| @@ -235,6 +210,11 @@ class AnalysisDriver { |
| AnalysisDriverTestView _testView; |
| /** |
| + * The [FileTracker] used by this driver. |
| + */ |
| + FileTracker _fileTracker; |
| + |
| + /** |
| * Create a new instance of [AnalysisDriver]. |
| * |
| * The given [SourceFactory] is cloned to ensure that it does not contain a |
| @@ -242,7 +222,7 @@ class AnalysisDriver { |
| */ |
| AnalysisDriver( |
| this._scheduler, |
| - this._logger, |
| + PerformanceLog logger, |
| this._resourceProvider, |
| this._byteStore, |
| this._contentOverlay, |
| @@ -253,9 +233,7 @@ class AnalysisDriver { |
| : _sourceFactory = sourceFactory.clone(), |
| _sdkBundle = sdkBundle { |
| _testView = new AnalysisDriverTestView(this); |
| - _fillSalt(); |
| - _fsState = new FileSystemState(_logger, _byteStore, _contentOverlay, |
| - _resourceProvider, sourceFactory, _analysisOptions, _salt); |
| + _createFileTracker(logger); |
| _scheduler._add(this); |
| _search = new Search(this); |
| } |
| @@ -263,7 +241,7 @@ class AnalysisDriver { |
| /** |
| * Return the set of files explicitly added to analysis using [addFile]. |
| */ |
| - Set<String> get addedFiles => _addedFiles; |
| + Set<String> get addedFiles => _fileTracker.addedFiles; |
| /** |
| * Return the analysis options used to control analysis. |
| @@ -278,16 +256,16 @@ class AnalysisDriver { |
| /** |
| * The current file system state. |
| */ |
| - FileSystemState get fsState => _fsState; |
| + FileSystemState get fsState => _fileTracker.fsState; |
| /** |
| * Return `true` if the driver has a file to analyze. |
| */ |
| bool get hasFilesToAnalyze { |
| - return _changedFiles.isNotEmpty || |
| + return _fileTracker.hasChangedFiles || |
| _requestedFiles.isNotEmpty || |
| _requestedParts.isNotEmpty || |
| - _filesToAnalyze.isNotEmpty || |
| + _fileTracker.hasPendingFiles || |
| _partsToAnalyze.isNotEmpty; |
| } |
| @@ -296,12 +274,12 @@ class AnalysisDriver { |
| * always include all added files or all implicitly used file. If a file has |
| * not been processed yet, it might be missing. |
| */ |
| - Set<String> get knownFiles => _fsState.knownFilePaths; |
| + Set<String> get knownFiles => _fileTracker.fsState.knownFilePaths; |
| /** |
| * Return the number of files scheduled for analysis. |
| */ |
| - int get numberOfFilesToAnalyze => _filesToAnalyze.length; |
| + int get numberOfFilesToAnalyze => _fileTracker.numberOfPendingFiles; |
| /** |
| * Return the list of files that the driver should try to analyze sooner. |
| @@ -324,7 +302,7 @@ class AnalysisDriver { |
| .forEach(_priorityResults.remove); |
| _priorityFiles.clear(); |
| _priorityFiles.addAll(priorityPaths); |
| - _scheduler._notify(this); |
| + _scheduler.notify(this); |
| } |
| /** |
| @@ -387,15 +365,15 @@ class AnalysisDriver { |
| } |
| if (_priorityFiles.isNotEmpty) { |
| for (String path in _priorityFiles) { |
| - if (_filesToAnalyze.contains(path)) { |
| + if (_fileTracker.isFilePending(path)) { |
| return AnalysisDriverPriority.priority; |
| } |
| } |
| } |
| - if (_filesToAnalyze.isNotEmpty) { |
| + if (_fileTracker.hasPendingFiles) { |
| return AnalysisDriverPriority.general; |
| } |
| - if (_changedFiles.isNotEmpty) { |
| + if (_fileTracker.hasChangedFiles) { |
| return AnalysisDriverPriority.general; |
| } |
| if (_requestedParts.isNotEmpty || _partsToAnalyze.isNotEmpty) { |
| @@ -412,15 +390,12 @@ class AnalysisDriver { |
| * The results of analysis are eventually produced by the [results] stream. |
| */ |
| void addFile(String path) { |
| - if (!_fsState.hasUri(path)) { |
| + if (!_fileTracker.fsState.hasUri(path)) { |
| return; |
| } |
| if (AnalysisEngine.isDartFileName(path)) { |
| - _addedFiles.add(path); |
| - _filesToAnalyze.add(path); |
| - _priorityResults.clear(); |
| + _fileTracker.addFile(path); |
| } |
| - _scheduler._notify(this); |
| } |
| /** |
| @@ -442,12 +417,8 @@ class AnalysisDriver { |
| * [changeFile] invocation. |
| */ |
| void changeFile(String path) { |
| - _changedFiles.add(path); |
| - if (_addedFiles.contains(path)) { |
| - _filesToAnalyze.add(path); |
| - } |
| + _fileTracker.changeFile(path); |
| _priorityResults.clear(); |
| - _scheduler._notify(this); |
| } |
| /** |
| @@ -465,11 +436,9 @@ class AnalysisDriver { |
| if (sourceFactory != null) { |
| _sourceFactory = sourceFactory; |
| } |
| - _fillSalt(); |
| - _fsState = new FileSystemState(_logger, _byteStore, _contentOverlay, |
| - _resourceProvider, _sourceFactory, _analysisOptions, _salt); |
| - _filesToAnalyze.addAll(_addedFiles); |
| - _scheduler._notify(this); |
| + var addedFiles = _fileTracker.addedFiles; |
|
scheglov
2017/02/02 21:52:17
Type?
Paul Berry
2017/02/02 22:22:19
Done.
|
| + _createFileTracker(_fileTracker.logger); |
| + _fileTracker.addFiles(addedFiles); |
| } |
| /** |
| @@ -516,7 +485,7 @@ class AnalysisDriver { |
| Future<List<String>> getFilesDefiningClassMemberName(String name) { |
| var task = new _FilesDefiningClassMemberNameTask(this, name); |
| _definingClassMemberNameTasks.add(task); |
| - _scheduler._notify(this); |
| + _scheduler.notify(this); |
| return task.completer.future; |
| } |
| @@ -527,7 +496,7 @@ class AnalysisDriver { |
| Future<List<String>> getFilesReferencingName(String name) { |
| var task = new _FilesReferencingNameTask(this, name); |
| _referencingNameTasks.add(task); |
| - _scheduler._notify(this); |
| + _scheduler.notify(this); |
| return task.completer.future; |
| } |
| @@ -537,14 +506,14 @@ class AnalysisDriver { |
| * analyzed. |
| */ |
| Future<AnalysisDriverUnitIndex> getIndex(String path) { |
| - if (!_fsState.hasUri(path)) { |
| + if (!_fileTracker.fsState.hasUri(path)) { |
| return new Future.value(); |
| } |
| var completer = new Completer<AnalysisDriverUnitIndex>(); |
| _indexRequestedFiles |
| .putIfAbsent(path, () => <Completer<AnalysisDriverUnitIndex>>[]) |
| .add(completer); |
| - _scheduler._notify(this); |
| + _scheduler.notify(this); |
| return completer.future; |
| } |
| @@ -566,7 +535,7 @@ class AnalysisDriver { |
| * state transitions to "idle". |
| */ |
| Future<AnalysisResult> getResult(String path) { |
| - if (!_fsState.hasUri(path)) { |
| + if (!_fileTracker.fsState.hasUri(path)) { |
| return new Future.value(); |
| } |
| @@ -583,7 +552,7 @@ class AnalysisDriver { |
| _requestedFiles |
| .putIfAbsent(path, () => <Completer<AnalysisResult>>[]) |
| .add(completer); |
| - _scheduler._notify(this); |
| + _scheduler.notify(this); |
| return completer.future; |
| } |
| @@ -596,7 +565,7 @@ class AnalysisDriver { |
| */ |
| Future<SourceKind> getSourceKind(String path) async { |
| if (AnalysisEngine.isDartFileName(path)) { |
| - FileState file = _fsState.getFileForPath(path); |
| + FileState file = _fileTracker.fsState.getFileForPath(path); |
| return file.isPart ? SourceKind.PART : SourceKind.LIBRARY; |
| } |
| return null; |
| @@ -610,7 +579,7 @@ class AnalysisDriver { |
| String name) { |
| var task = new _TopLevelNameDeclarationsTask(this, name); |
| _topLevelNameDeclarationsTasks.add(task); |
| - _scheduler._notify(this); |
| + _scheduler.notify(this); |
| return task.completer.future; |
| } |
| @@ -619,14 +588,14 @@ class AnalysisDriver { |
| * file with the given [path], or with `null` if the file cannot be analyzed. |
| */ |
| Future<CompilationUnitElement> getUnitElement(String path) { |
| - if (!_fsState.hasUri(path)) { |
| + if (!_fileTracker.fsState.hasUri(path)) { |
| return new Future.value(); |
| } |
| var completer = new Completer<CompilationUnitElement>(); |
| _unitElementRequestedFiles |
| .putIfAbsent(path, () => <Completer<CompilationUnitElement>>[]) |
| .add(completer); |
| - _scheduler._notify(this); |
| + _scheduler.notify(this); |
| return completer.future; |
| } |
| @@ -643,7 +612,7 @@ class AnalysisDriver { |
| * resolved unit). |
| */ |
| Future<ParseResult> parseFile(String path) async { |
| - FileState file = _verifyApiSignature(path); |
| + FileState file = _fileTracker.verifyApiSignature(path); |
| RecordingErrorListener listener = new RecordingErrorListener(); |
| CompilationUnit unit = file.parse(listener); |
| return new ParseResult(file.path, file.uri, file.content, file.contentHash, |
| @@ -660,12 +629,17 @@ class AnalysisDriver { |
| * but does not guarantee this. |
| */ |
| void removeFile(String path) { |
| - _addedFiles.remove(path); |
| - _filesToAnalyze.remove(path); |
| - _fsState.removeFile(path); |
| - _filesToAnalyze.addAll(_addedFiles); |
| + _fileTracker.removeFile(path); |
| + _priorityResults.clear(); |
| + } |
| + |
| + /** |
| + * Handles a notification from the [FileTracker] that there has been a change |
| + * of state. |
| + */ |
| + void _changeHook() { |
| _priorityResults.clear(); |
| - _scheduler._notify(this); |
| + _scheduler.notify(this); |
| } |
| /** |
| @@ -696,7 +670,7 @@ class AnalysisDriver { |
| // If we don't need the fully resolved unit, check for the cached result. |
| if (!withUnit) { |
| - FileState file = _fsState.getFileForPath(path); |
| + FileState file = _fileTracker.fsState.getFileForPath(path); |
| // Prepare the library file - the file itself, or the known library. |
| FileState libraryFile = getLibraryFile(file); |
| @@ -713,8 +687,8 @@ class AnalysisDriver { |
| } |
| // We need the fully resolved unit, or the result is not cached. |
| - return _logger.run('Compute analysis result for $path', () { |
| - FileState file = _verifyApiSignature(path); |
| + return _fileTracker.logger.run('Compute analysis result for $path', () { |
|
scheglov
2017/02/02 21:52:17
Accessing logger from _fileTracker seems a bit cum
Paul Berry
2017/02/02 22:22:19
Done.
|
| + FileState file = _fileTracker.verifyApiSignature(path); |
| // Prepare the library file - the file itself, or the known library. |
| FileState libraryFile = getLibraryFile(file); |
| @@ -750,10 +724,10 @@ class AnalysisDriver { |
| } |
| // Return the result, full or partial. |
| - _logger.writeln('Computed new analysis result.'); |
| + _fileTracker.logger.writeln('Computed new analysis result.'); |
| AnalysisResult result = _getAnalysisResultFromBytes(file, bytes, |
| content: withUnit ? file.content : null, |
| - withErrors: _addedFiles.contains(path), |
| + withErrors: _fileTracker.addedFiles.contains(path), |
| resolvedUnit: withUnit ? resolvedUnit : null); |
| if (withUnit && _priorityFiles.contains(path)) { |
| _priorityResults[path] = result; |
| @@ -777,7 +751,7 @@ class AnalysisDriver { |
| } |
| CompilationUnitElement _computeUnitElement(String path) { |
| - FileState file = _fsState.getFileForPath(path); |
| + FileState file = _fileTracker.fsState.getFileForPath(path); |
| FileState libraryFile = file.library ?? file; |
| // Create the AnalysisContext to resynthesize elements in. |
| @@ -792,18 +766,30 @@ class AnalysisDriver { |
| } |
| /** |
| + * Creates a new [FileTracker] object and stores it in [_fileTracker]. |
| + * |
| + * This is used both on initial construction and whenever the configuration |
| + * changes. |
| + */ |
| + void _createFileTracker(PerformanceLog logger) { |
| + _fillSalt(); |
| + _fileTracker = new FileTracker(logger, _byteStore, _contentOverlay, |
| + _resourceProvider, sourceFactory, _analysisOptions, _salt, _changeHook); |
| + } |
| + |
| + /** |
| * Return the context in which the [library] should be analyzed. |
| */ |
| LibraryContext _createLibraryContext(FileState library) => |
| new LibraryContext.forSingleLibrary( |
| library, |
| - _logger, |
| + _fileTracker.logger, |
| _sdkBundle, |
| _byteStore, |
| _analysisOptions, |
| declaredVariables, |
| _sourceFactory, |
| - _fsState); |
| + _fileTracker); |
| /** |
| * Fill [_salt] with data. |
| @@ -896,14 +882,7 @@ class AnalysisDriver { |
| * Perform a single chunk of work and produce [results]. |
| */ |
| Future<Null> _performWork() async { |
| - // Verify all changed files one at a time. |
| - if (_changedFiles.isNotEmpty) { |
| - String path = _removeFirst(_changedFiles); |
| - // If the file has not been accessed yet, we either will eventually read |
| - // it later while analyzing one of the added files, or don't need it. |
| - if (_fsState.knownFilePaths.contains(path)) { |
| - _verifyApiSignature(path); |
| - } |
| + if (_fileTracker.verifyChangedFilesIfNeeded()) { |
| return; |
| } |
| @@ -924,13 +903,13 @@ class AnalysisDriver { |
| completer.complete(result); |
| }); |
| // Remove from to be analyzed and produce it now. |
| - _filesToAnalyze.remove(path); |
| _resultController.add(result); |
| } catch (exception, stackTrace) { |
| - _filesToAnalyze.remove(path); |
| _requestedFiles.remove(path).forEach((completer) { |
| completer.completeError(exception, stackTrace); |
| }); |
| + } finally { |
| + _fileTracker.fileWasAnalyzed(path); |
|
scheglov
2017/02/02 21:52:17
This code will be executed even if result == null,
Paul Berry
2017/02/02 22:22:19
Ah, I see. Good catch. Fixed.
|
| } |
| return; |
| } |
| @@ -989,7 +968,7 @@ class AnalysisDriver { |
| // Analyze a priority file. |
| if (_priorityFiles.isNotEmpty) { |
| for (String path in _priorityFiles) { |
| - if (_filesToAnalyze.remove(path)) { |
| + if (_fileTracker.isFilePending(path)) { |
| try { |
| AnalysisResult result = |
| _computeAnalysisResult(path, withUnit: true); |
| @@ -1000,15 +979,17 @@ class AnalysisDriver { |
| } |
| } catch (exception, stackTrace) { |
| _reportException(path, exception, stackTrace); |
| + } finally { |
| + _fileTracker.fileWasAnalyzed(path); |
| } |
| return; |
| } |
| } |
| } |
| - // Analyze a general file. |
| - if (_filesToAnalyze.isNotEmpty) { |
| - String path = _removeFirst(_filesToAnalyze); |
| + if (_fileTracker.hasPendingFiles) { |
| + // Analyze a general file. |
| + String path = _fileTracker.anyPendingFile; |
| try { |
| AnalysisResult result = _computeAnalysisResult(path, withUnit: false); |
| if (result == null) { |
| @@ -1018,6 +999,8 @@ class AnalysisDriver { |
| } |
| } catch (exception, stackTrace) { |
| _reportException(path, exception, stackTrace); |
| + } finally { |
| + _fileTracker.fileWasAnalyzed(path); |
| } |
| return; |
| } |
| @@ -1046,7 +1029,8 @@ class AnalysisDriver { |
| // Analyze a general part. |
| if (_partsToAnalyze.isNotEmpty) { |
| - String path = _removeFirst(_partsToAnalyze); |
| + String path = _partsToAnalyze.first; |
| + _partsToAnalyze.remove(path); |
| try { |
| AnalysisResult result = _computeAnalysisResult(path, |
| withUnit: _priorityFiles.contains(path), |
| @@ -1119,38 +1103,6 @@ class AnalysisDriver { |
| return null; |
| } |
| } |
| - |
| - /** |
| - * Verify the API signature for the file with the given [path], and decide |
| - * which linked libraries should be invalidated, and files reanalyzed. |
| - */ |
| - FileState _verifyApiSignature(String path) { |
| - return _logger.run('Verify API signature of $path', () { |
| - bool anyApiChanged = false; |
| - List<FileState> files = _fsState.getFilesForPath(path); |
| - for (FileState file in files) { |
| - bool apiChanged = file.refresh(); |
| - if (apiChanged) { |
| - anyApiChanged = true; |
| - } |
| - } |
| - if (anyApiChanged) { |
| - _logger.writeln('API signatures mismatch found for $path'); |
| - // TODO(scheglov) schedule analysis of only affected files |
| - _filesToAnalyze.addAll(_addedFiles); |
| - } |
| - return files[0]; |
| - }); |
| - } |
| - |
| - /** |
| - * Remove and return the first item in the given [set]. |
| - */ |
| - static Object/*=T*/ _removeFirst/*<T>*/(LinkedHashSet<Object/*=T*/ > set) { |
| - Object/*=T*/ element = set.first; |
| - set.remove(element); |
| - return element; |
| - } |
| } |
| /** |
| @@ -1218,6 +1170,15 @@ class AnalysisDriverScheduler { |
| } |
| /** |
| + * Notify that there is a change to the [driver], it it might need to |
| + * perform some work. |
| + */ |
| + void notify(AnalysisDriver driver) { |
| + _hasWork.notify(); |
| + _statusSupport.preTransitionToAnalyzing(); |
| + } |
| + |
| + /** |
| * Start the scheduler, so that any [AnalysisDriver] created before or |
| * after will be asked to perform work. |
| */ |
| @@ -1246,15 +1207,6 @@ class AnalysisDriverScheduler { |
| } |
| /** |
| - * Notify that there is a change to the [driver], it it might need to |
| - * perform some work. |
| - */ |
| - void _notify(AnalysisDriver driver) { |
| - _hasWork.notify(); |
| - _statusSupport.preTransitionToAnalyzing(); |
| - } |
| - |
| - /** |
| * Remove the given [driver] from the scheduler, so that it will not be |
| * asked to perform any new work. |
| */ |
| @@ -1334,7 +1286,7 @@ class AnalysisDriverTestView { |
| AnalysisDriverTestView(this.driver); |
| - Set<String> get filesToAnalyze => driver._filesToAnalyze; |
| + FileTracker get fileTracker => driver._fileTracker; |
| Map<String, AnalysisResult> get priorityResults => driver._priorityResults; |
| } |
| @@ -1672,7 +1624,7 @@ class _FilesDefiningClassMemberNameTask { |
| // Check the next file. |
| String path = filesToCheck.removeLast(); |
| - FileState file = driver._fsState.getFileForPath(path); |
| + FileState file = driver._fileTracker.fsState.getFileForPath(path); |
| if (file.definedClassMemberNames.contains(name)) { |
| definingFiles.add(path); |
| } |
| @@ -1729,7 +1681,7 @@ class _FilesReferencingNameTask { |
| // Check the next file. |
| String path = filesToCheck.removeLast(); |
| - FileState file = driver._fsState.getFileForPath(path); |
| + FileState file = driver._fileTracker.fsState.getFileForPath(path); |
| if (file.referencedNames.contains(name)) { |
| referencingFiles.add(path); |
| } |
| @@ -1779,7 +1731,7 @@ class _TopLevelNameDeclarationsTask { |
| // Check the next file. |
| String path = filesToCheck.removeLast(); |
| if (checkedFiles.add(path)) { |
| - FileState file = driver._fsState.getFileForPath(path); |
| + FileState file = driver._fileTracker.fsState.getFileForPath(path); |
| if (!file.isPart) { |
| bool isExported = false; |
| TopLevelDeclaration declaration = file.topLevelDeclarations[name]; |