| 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 6114222030f774ece1d4216d8b883f428c4fe444..fafc9ae64bba4b0611aca5677364b912f6f94c76 100644
|
| --- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
|
| +++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
|
| @@ -113,7 +113,7 @@ class AnalysisDriver {
|
| * The mapping from the files for which analysis was requested using
|
| * [getResult] to the [Completer]s to report the result.
|
| */
|
| - final _requestedFiles = <String, Completer<AnalysisResult>>{};
|
| + final _requestedFiles = <String, List<Completer<AnalysisResult>>>{};
|
|
|
| /**
|
| * The set of explicitly analyzed files.
|
| @@ -139,9 +139,9 @@ class AnalysisDriver {
|
| final _fileContentHashMap = <String, String>{};
|
|
|
| /**
|
| - * Mapping from library URIs to the linked hash of the library.
|
| + * Mapping from library URIs to the dependency signature of the library.
|
| */
|
| - final _linkedHashMap = <Uri, String>{};
|
| + final _dependencySignatureMap = <Uri, String>{};
|
|
|
| /**
|
| * TODO(scheglov) document and improve
|
| @@ -192,7 +192,7 @@ class AnalysisDriver {
|
| await for (String why in _hasWorkStreamController.stream) {
|
| // Analyze the first file in the general queue.
|
| if (_filesToAnalyze.isNotEmpty) {
|
| - _logger.runTimed('Analyzed ${_filesToAnalyze.length} files', () {
|
| + _logger.run('Analyze ${_filesToAnalyze.length} files', () {
|
| while (_filesToAnalyze.isNotEmpty) {
|
| String path = _filesToAnalyze.first;
|
| _filesToAnalyze.remove(path);
|
| @@ -244,7 +244,7 @@ class AnalysisDriver {
|
| void changeFile(String path) {
|
| // TODO(scheglov) Don't clear, schedule API signature validation.
|
| _fileContentHashMap.clear();
|
| - _linkedHashMap.clear();
|
| + _dependencySignatureMap.clear();
|
| _filesToAnalyze.add(path);
|
| _filesToAnalyze.addAll(_explicitFiles);
|
| // TODO(scheglov) name?!
|
| @@ -267,7 +267,10 @@ class AnalysisDriver {
|
| */
|
| Future<AnalysisResult> getResult(String path) {
|
| var completer = new Completer<AnalysisResult>();
|
| - _requestedFiles[path] = completer;
|
| + _requestedFiles
|
| + .putIfAbsent(path, () => <Completer<AnalysisResult>>[])
|
| + .add(completer);
|
| + _hasWorkStreamController.add(path);
|
| return completer.future;
|
| }
|
|
|
| @@ -309,12 +312,12 @@ class AnalysisDriver {
|
| }
|
|
|
| List<String> errorStrings = _logger.run('Compute errors $file', () {
|
| - LibraryContext libraryContext = _createLibraryContext(file);
|
| + _LibraryContext libraryContext = _createLibraryContext(file);
|
|
|
| String errorsKey;
|
| {
|
| ApiSignature signature = new ApiSignature();
|
| - signature.addString(libraryContext.node.linkedHash);
|
| + signature.addString(libraryContext.node.dependencySignature);
|
| signature.addString(file.contentHash);
|
| errorsKey = '${signature.toHex()}.errors';
|
| }
|
| @@ -340,7 +343,7 @@ class AnalysisDriver {
|
| // Compute errors.
|
| List<AnalysisError> errors;
|
| try {
|
| - errors = _logger.runTimed('Computed errors', () {
|
| + errors = _logger.run('Compute errors', () {
|
| return analysisContext.computeErrors(file.source);
|
| });
|
| } catch (e, st) {
|
| @@ -378,7 +381,7 @@ class AnalysisDriver {
|
| return errorStrings;
|
| }
|
|
|
| - AnalysisContext _createAnalysisContext(LibraryContext libraryContext) {
|
| + AnalysisContext _createAnalysisContext(_LibraryContext libraryContext) {
|
| AnalysisContextImpl analysisContext =
|
| AnalysisEngine.instance.createAnalysisContext();
|
|
|
| @@ -392,28 +395,32 @@ class AnalysisDriver {
|
| }
|
|
|
| /**
|
| - * Return the content in which the library represented by the given
|
| + * Return the context in which the library represented by the given
|
| * [libraryFile] should be analyzed it.
|
| *
|
| - * TODO(scheglov) We often don't need [SummaryDataStore], only linked hash.
|
| + * TODO(scheglov) We often don't need [SummaryDataStore], only dependency
|
| + * signature.
|
| */
|
| - LibraryContext _createLibraryContext(_File libraryFile) {
|
| - Map<String, _LibraryNode> nodes = <String, _LibraryNode>{};
|
| -
|
| + _LibraryContext _createLibraryContext(_File libraryFile) {
|
| return _logger.run('Create library context', () {
|
| + Map<String, _LibraryNode> nodes = <String, _LibraryNode>{};
|
| SummaryDataStore store = new SummaryDataStore(const <String>[]);
|
| store.addBundle(null, _sdkBundle);
|
|
|
| - void createLibraryNodes(_File libraryFile) {
|
| + _LibraryNode createLibraryNodes(_File libraryFile) {
|
| Uri libraryUri = libraryFile.uri;
|
| +
|
| + // URIs with the 'dart:' scheme are served from the SDK bundle.
|
| if (libraryUri.scheme == 'dart') {
|
| - return;
|
| + return null;
|
| }
|
| - String uriStr = libraryUri.toString();
|
| - if (!nodes.containsKey(uriStr)) {
|
| - _LibraryNode node = new _LibraryNode(this, nodes, libraryUri);
|
| - nodes[uriStr] = node;
|
| - ReferencedUris referenced = _getReferencedUris(libraryFile);
|
| +
|
| + String libraryUriStr = libraryUri.toString();
|
| + _LibraryNode node = nodes[libraryUriStr];
|
| + if (node == null) {
|
| + node = new _LibraryNode(this, nodes, libraryUri);
|
| + nodes[libraryUriStr] = node;
|
| + _ReferencedUris referenced = _getReferencedUris(libraryFile);
|
|
|
| // Append unlinked bundles.
|
| for (String uri in referenced.parted) {
|
| @@ -433,31 +440,33 @@ class AnalysisDriver {
|
| createLibraryNodes(file);
|
| }
|
| }
|
| +
|
| + // Done with this node.
|
| + return node;
|
| }
|
|
|
| - _logger.runTimed2(() {
|
| - createLibraryNodes(libraryFile);
|
| - }, () => 'Computed ${nodes.length} nodes');
|
| - _LibraryNode libraryNode = nodes[libraryFile.uri.toString()];
|
| + _LibraryNode libraryNode = _logger.run('Compute library nodes', () {
|
| + return createLibraryNodes(libraryFile);
|
| + });
|
|
|
| Set<String> libraryUrisToLink = new Set<String>();
|
| - int numberOfNodesWithLinked = 0;
|
| - _logger.runTimed2(() {
|
| + _logger.run('Load linked bundles', () {
|
| for (_LibraryNode node in nodes.values) {
|
| - String key = '${node.linkedHash}.linked';
|
| + String key = '${node.dependencySignature}.linked';
|
| List<int> bytes = _byteStore.get(key);
|
| if (bytes != null) {
|
| PackageBundle linked = new PackageBundle.fromBuffer(bytes);
|
| store.addBundle(null, linked);
|
| - numberOfNodesWithLinked++;
|
| } else {
|
| libraryUrisToLink.add(node.uri.toString());
|
| }
|
| }
|
| - }, () => 'Loaded $numberOfNodesWithLinked linked bundles');
|
| + int numOfLoaded = nodes.length - libraryUrisToLink.length;
|
| + _logger.writeln('Loaded $numOfLoaded linked bundles.');
|
| + });
|
|
|
| Map<String, LinkedLibraryBuilder> linkedLibraries = {};
|
| - _logger.runTimed2(() {
|
| + _logger.run('Link bundles', () {
|
| linkedLibraries = link(libraryUrisToLink, (String uri) {
|
| LinkedLibrary linkedLibrary = store.linkedMap[uri];
|
| if (linkedLibrary == null) {
|
| @@ -471,11 +480,12 @@ class AnalysisDriver {
|
| }
|
| return unlinkedUnit;
|
| }, (_) => null, _analysisOptions.strongMode);
|
| - }, () => 'Linked ${linkedLibraries.length} bundles');
|
| + _logger.writeln('Linked ${linkedLibraries.length} bundles.');
|
| + });
|
|
|
| linkedLibraries.forEach((uri, linkedBuilder) {
|
| _LibraryNode node = nodes[uri];
|
| - String key = '${node.linkedHash}.linked';
|
| + String key = '${node.dependencySignature}.linked';
|
| List<int> bytes;
|
| {
|
| PackageBundleAssembler assembler = new PackageBundleAssembler();
|
| @@ -487,7 +497,7 @@ class AnalysisDriver {
|
| _byteStore.put(key, bytes);
|
| });
|
|
|
| - return new LibraryContext(libraryFile, libraryNode, store);
|
| + return new _LibraryContext(libraryFile, libraryNode, store);
|
| });
|
| }
|
|
|
| @@ -504,7 +514,7 @@ class AnalysisDriver {
|
| /**
|
| * TODO(scheglov) It would be nice to get URIs of "parts" from unlinked.
|
| */
|
| - ReferencedUris _getReferencedUris(_File file) {
|
| + _ReferencedUris _getReferencedUris(_File file) {
|
| // Try to get from the store.
|
| {
|
| String key = '${file.contentHash}.uris';
|
| @@ -518,7 +528,7 @@ class AnalysisDriver {
|
| List<String> imported = stringListReader.vTableGet(bp, table, 1);
|
| List<String> exported = stringListReader.vTableGet(bp, table, 2);
|
| List<String> parted = stringListReader.vTableGet(bp, table, 3);
|
| - ReferencedUris referencedUris = new ReferencedUris();
|
| + _ReferencedUris referencedUris = new _ReferencedUris();
|
| referencedUris.isLibrary = isLibrary;
|
| referencedUris.imported.addAll(imported);
|
| referencedUris.exported.addAll(exported);
|
| @@ -528,7 +538,7 @@ class AnalysisDriver {
|
| }
|
|
|
| // Compute URIs.
|
| - ReferencedUris referencedUris = new ReferencedUris();
|
| + _ReferencedUris referencedUris = new _ReferencedUris();
|
| referencedUris.parted.add(file.uri.toString());
|
| for (Directive directive in file.unit.directives) {
|
| if (directive is PartOfDirective) {
|
| @@ -594,7 +604,7 @@ class AnalysisDriver {
|
| }
|
| // If no cached unlinked bundle, compute it.
|
| if (bytes == null) {
|
| - _logger.runTimed('Create unlinked for $file', () {
|
| + _logger.run('Create unlinked for $file', () {
|
| // We read the content and recomputed the hash.
|
| // So, we need to update the key.
|
| String key = '${file.contentHash}.unlinked';
|
| @@ -658,19 +668,22 @@ class AnalysisResult {
|
| this.errors);
|
| }
|
|
|
| -class LibraryContext {
|
| - final _File file;
|
| - final _LibraryNode node;
|
| - final SummaryDataStore store;
|
| - LibraryContext(this.file, this.node, this.store);
|
| -}
|
| -
|
| +/**
|
| + * This class is used to gather and print performance information.
|
| + */
|
| class PerformanceLog {
|
| final StringSink sink;
|
| int _level = 0;
|
|
|
| PerformanceLog(this.sink);
|
|
|
| + /**
|
| + * Return the result of the function [f] invocation and log the elapsed time.
|
| + *
|
| + * Each invocation of [run] creates a new enclosed section in the log,
|
| + * which begins with printing [msg], then any log output produced during
|
| + * [f] invocation, and ends with printing [msg] with the elapsed time.
|
| + */
|
| /*=T*/ run/*<T>*/(String msg, /*=T*/ f()) {
|
| Stopwatch timer = new Stopwatch()..start();
|
| try {
|
| @@ -684,48 +697,25 @@ class PerformanceLog {
|
| }
|
| }
|
|
|
| - /*=T*/ runTimed/*<T>*/(String msg, /*=T*/ f()) {
|
| - _level++;
|
| - Stopwatch timer = new Stopwatch()..start();
|
| - try {
|
| - return f();
|
| - } finally {
|
| - _level--;
|
| - int ms = timer.elapsedMilliseconds;
|
| - writeln('$msg in $ms ms.');
|
| - }
|
| - }
|
| -
|
| - runTimed2(f(), String getMsg()) {
|
| - _level++;
|
| - Stopwatch timer = new Stopwatch()..start();
|
| - try {
|
| - return f();
|
| - } finally {
|
| - _level--;
|
| - int ms = timer.elapsedMilliseconds;
|
| - String msg = getMsg();
|
| - writeln('$msg in $ms ms.');
|
| - }
|
| - }
|
| -
|
| + /**
|
| + * Write a new line into the log
|
| + */
|
| void writeln(String msg) {
|
| String indent = '\t' * _level;
|
| sink.writeln('$indent$msg');
|
| }
|
| }
|
|
|
| -class ReferencedUris {
|
| - bool isLibrary = true;
|
| - final List<String> imported = <String>[];
|
| - final List<String> exported = <String>[];
|
| - final List<String> parted = <String>[];
|
| -}
|
| -
|
| /**
|
| * Information about a file being analyzed, explicitly or implicitly.
|
| *
|
| * It keeps a consistent view on its [content], [contentHash] and [unit].
|
| + *
|
| + * Instances of this class may only be used during computing a single analysis
|
| + * result and should not be cached anywhere. We need this limitation to prevent
|
| + * references from caches to the resolved [unit], so to element models, etc.
|
| + * The data structures should be short lived - computed, returned to the client,
|
| + * processed there and quickly thrown away.
|
| */
|
| class _File {
|
| /**
|
| @@ -768,7 +758,7 @@ class _File {
|
| * value. Otherwise, read the [content], compute the hash, put it into
|
| * the current file state, and update the [contentHash] field.
|
| *
|
| - * The client cannot remember values of this property, because its value
|
| + * The client should not remember values of this property, because its value
|
| * might change when [content] is read and the hash is recomputed.
|
| */
|
| String get contentHash {
|
| @@ -782,11 +772,11 @@ class _File {
|
| String get path => source.fullName;
|
|
|
| /**
|
| - * Return the [CompilationUnit] of the file.
|
| + * Return the unresolved [CompilationUnit] of the file.
|
| *
|
| - * Current this unit is resolved, it is used to compute unlinked summaries
|
| - * and and URIs. We use a separate analysis context to perform resolution
|
| - * and computing errors. But this might change in the future.
|
| + * Performing resolution and computing errors is done in a separate analysis
|
| + * context. In the future we might push the existing unresolved unit into the
|
| + * analysis context, so at some point the unit might become resolved.
|
| */
|
| CompilationUnit get unit {
|
| AnalysisErrorListener errorListener = AnalysisErrorListener.NULL_LISTENER;
|
| @@ -823,7 +813,7 @@ class _File {
|
| /**
|
| * Fill the [_content] and [_contentHash] fields.
|
| *
|
| - * If the [_content] field if it is still `null`, get the content from the
|
| + * If the [_content] field is still `null`, get the content from the
|
| * content cache or from the [source]. If the content cannot be accessed
|
| * because of an exception, it considers to be an empty string.
|
| *
|
| @@ -835,6 +825,10 @@ class _File {
|
| _content = driver._contentCache.getContents(source);
|
| _content ??= source.contents.data;
|
| } catch (_) {
|
| + // TODO(scheglov) Fix the bug with not existing sources.
|
| + // We should not put "self URI" into cached _ReferencedUris.
|
| + // Otherwise such not-existing/empty sources all have the same hash,
|
| + // but their "self URIs" must be all different.
|
| _content = '';
|
| }
|
| // Compute the content hash.
|
| @@ -846,6 +840,16 @@ class _File {
|
| }
|
| }
|
|
|
| +/**
|
| + * TODO(scheglov) document
|
| + */
|
| +class _LibraryContext {
|
| + final _File file;
|
| + final _LibraryNode node;
|
| + final SummaryDataStore store;
|
| + _LibraryContext(this.file, this.node, this.store);
|
| +}
|
| +
|
| class _LibraryNode {
|
| final AnalysisDriver driver;
|
| final Map<String, _LibraryNode> nodes;
|
| @@ -854,7 +858,7 @@ class _LibraryNode {
|
|
|
| Set<_LibraryNode> transitiveDependencies;
|
| List<_LibraryNode> _dependencies;
|
| - String _linkedHash;
|
| + String _dependencySignature;
|
|
|
| _LibraryNode(this.driver, this.nodes, this.uri);
|
|
|
| @@ -901,11 +905,9 @@ class _LibraryNode {
|
| return _dependencies;
|
| }
|
|
|
| - @override
|
| - int get hashCode => uri.hashCode;
|
| -
|
| - String get linkedHash {
|
| - _linkedHash ??= driver._linkedHashMap.putIfAbsent(uri, () {
|
| + String get dependencySignature {
|
| + return _dependencySignature ??=
|
| + driver._dependencySignatureMap.putIfAbsent(uri, () {
|
| computeTransitiveDependencies();
|
|
|
| // Add all unlinked API signatures.
|
| @@ -924,9 +926,11 @@ class _LibraryNode {
|
| signatures.forEach(signature.addString);
|
| return signature.toHex();
|
| });
|
| - return _linkedHash;
|
| }
|
|
|
| + @override
|
| + int get hashCode => uri.hashCode;
|
| +
|
| bool operator ==(other) {
|
| return other is _LibraryNode && other.uri == uri;
|
| }
|
| @@ -948,3 +952,13 @@ class _LibraryNode {
|
| @override
|
| String toString() => uri.toString();
|
| }
|
| +
|
| +/**
|
| + * TODO(scheglov) document
|
| + */
|
| +class _ReferencedUris {
|
| + bool isLibrary = true;
|
| + final List<String> imported = <String>[];
|
| + final List<String> exported = <String>[];
|
| + final List<String> parted = <String>[];
|
| +}
|
|
|