Optimize peak heap usage (#1858)
* asynchronous PackageGraph construction and precaching
* checkpoint
* cleanup
* checkpoint
* Run tools more asynchronously
* Enhacements and dropping of Future.wait
* dartfmt
* Rate-limit the number of tools in flight
* checkpoint
* Workaround for duplicate ResovledLibraryResult objects
* Patch up merge problem and rebuild test package docs
* Use maps to avoid having to filter results
* dartfmt and review comments
* Update test package docs again (quotation marks different in stable branch)
* Clear pubspec overrides (this will only work in sdk-analyzer Travis)
* Change sdk test branch to master (analyzer-0.33 not integrated yet)
* Set correct analyzer version
* Eliminate initialization race where analyzer might not find all the files in time for empty packages
* Update analyzer requirement
* Fix merge error
* Optimize peak heap usage
* Fix bug in library count
* Fix embedder and refactor file generation
* Review comments
diff --git a/lib/dartdoc.dart b/lib/dartdoc.dart
index 1bdbb6c..8e6d186 100644
--- a/lib/dartdoc.dart
+++ b/lib/dartdoc.dart
@@ -115,7 +115,7 @@
seconds = _stopwatch.elapsedMilliseconds / 1000.0;
logInfo(
- "Documented ${packageGraph.publicLibraries.length} public librar${packageGraph.publicLibraries.length == 1 ? 'y' : 'ies'} "
+ "Documented ${packageGraph.localPublicLibraries.length} public librar${packageGraph.localPublicLibraries.length == 1 ? 'y' : 'ies'} "
"in ${seconds.toStringAsFixed(1)} seconds");
return new DartdocResults(
config.topLevelPackageMeta, packageGraph, outputDir);
@@ -123,7 +123,7 @@
Future<DartdocResults> generateDocs() async {
DartdocResults dartdocResults = await generateDocsBase();
- if (dartdocResults.packageGraph.publicLibraries.isEmpty) {
+ if (dartdocResults.packageGraph.localPublicLibraries.isEmpty) {
throw new DartdocFailure(
"dartdoc could not find any libraries to document");
}
diff --git a/lib/src/model.dart b/lib/src/model.dart
index 0232f3d..b9d7f61 100644
--- a/lib/src/model.dart
+++ b/lib/src/model.dart
@@ -17,10 +17,12 @@
AnnotatedNode,
AstNode,
CommentReference,
+ CompilationUnit,
Expression,
InstanceCreationExpression;
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/dart/element/visitor.dart';
import 'package:analyzer/file_system/file_system.dart' as fileSystem;
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:analyzer/src/source/package_map_resolver.dart';
@@ -439,7 +441,7 @@
if (_sourceCode == null) {
if (isSynthetic) {
_sourceCode = packageGraph
- ._getModelNodeFor((element as PropertyAccessorElement).variable, this.definingLibrary)
+ ._getModelNodeFor((element as PropertyAccessorElement).variable)
.sourceCode;
} else {
_sourceCode = super.sourceCode;
@@ -535,7 +537,7 @@
/// Implements the Dart 2.1 "mixin" style of mixin declarations.
class Mixin extends Class {
Mixin(ClassElement element, Library library, PackageGraph packageGraph)
- : super(element, library, packageGraph) {}
+ : super(element, library, packageGraph);
@override
bool get isAbstract => false;
@@ -1430,19 +1432,22 @@
final Element staticElement;
ModelCommentReference(CommentReference ref)
: name = ref.identifier.name,
- staticElement = ref.identifier.staticElement {}
+ staticElement = ref.identifier.staticElement;
}
/// Stripped down information derived from [AstNode] containing only information
/// needed for Dartdoc. Drops link to the [AstNode] after construction.
class ModelNode {
final List<ModelCommentReference> commentRefs;
- final String sourceCode;
final Element element;
+ final int _sourceOffset;
+ final int _sourceEnd;
+
ModelNode(AstNode sourceNode, this.element)
- : sourceCode = _sourceCodeFor(sourceNode, element),
- commentRefs = _commentRefsFor(sourceNode) {}
+ : _sourceOffset = sourceNode?.offset,
+ _sourceEnd = sourceNode?.end,
+ commentRefs = _commentRefsFor(sourceNode);
static List<ModelCommentReference> _commentRefsFor(AstNode node) {
if (node is AnnotatedNode &&
@@ -1454,11 +1459,11 @@
return null;
}
- static String _sourceCodeFor(AstNode node, Element element) {
+ String get sourceCode {
String contents = getFileContentsFor(element);
- if (node != null) {
+ if (_sourceOffset != null) {
// Find the start of the line, so that we can line up all the indents.
- int i = node.offset;
+ int i = _sourceOffset;
while (i > 0) {
i -= 1;
if (contents[i] == '\n' || contents[i] == '\r') {
@@ -1468,8 +1473,8 @@
}
// Trim the common indent from the source snippet.
- var start = node.offset - (node.offset - i);
- String source = contents.substring(start, node.end);
+ var start = _sourceOffset - (_sourceOffset - i);
+ String source = contents.substring(start, _sourceEnd);
source = const HtmlEscape().convert(source);
source = stripIndentFromSource(source);
@@ -2060,8 +2065,41 @@
Accessor get setter;
}
+/// Find all hashable children of a given element that are defined in the
+/// [LibraryElement] given at initialization.
+class _HashableChildLibraryElementVisitor
+ extends GeneralizingElementVisitor<void> {
+ final void Function(Element) libraryProcessor;
+ _HashableChildLibraryElementVisitor(this.libraryProcessor);
+
+ @override
+ void visitElement(Element element) {
+ libraryProcessor(element);
+ super.visitElement(element);
+ return null;
+ }
+
+ @override
+ void visitExportElement(ExportElement element) {
+ // [ExportElement]s are not always hashable; skip them.
+ return null;
+ }
+
+ @override
+ void visitImportElement(ImportElement element) {
+ // [ImportElement]s are not always hashable; skip them.
+ return null;
+ }
+
+ @override
+ void visitParameterElement(ParameterElement element) {
+ // [ParameterElement]s without names do not provide sufficiently distinct
+ // hashes / comparison, so just skip them all. (dart-lang/sdk#30146)
+ return null;
+ }
+}
+
class Library extends ModelElement with Categorization, TopLevelContainer {
- final ResolvedLibraryResult libraryResult;
List<TopLevelVariable> _variables;
Namespace _exportedNamespace;
String _name;
@@ -2070,9 +2108,19 @@
return packageGraph.findButDoNotCreateLibraryFor(element);
}
- Library._(this.libraryResult, PackageGraph packageGraph, this._package)
+ Library._(ResolvedLibraryResult libraryResult, PackageGraph packageGraph,
+ this._package)
: super(libraryResult.element, null, packageGraph, null) {
if (element == null) throw new ArgumentError.notNull('element');
+
+ // Initialize [packageGraph]'s cache of ModelNodes for relevant
+ // elements in this library.
+ Map<String, CompilationUnit> _compilationUnitMap = new Map();
+ _compilationUnitMap.addEntries(libraryResult.units
+ .map((ResolvedUnitResult u) => new MapEntry(u.path, u.unit)));
+ _HashableChildLibraryElementVisitor((Element e) =>
+ packageGraph._populateModelNodeFor(e, _compilationUnitMap))
+ .visitElement(element);
_exportedNamespace =
new NamespaceBuilder().createExportNamespaceForLibrary(element);
_package._allLibraries.add(this);
@@ -2897,7 +2945,7 @@
// TODO(jcollins-g): make _originalMember optional after dart-lang/sdk#15101
// is fixed.
ModelElement(
- this._element, this._library, this._packageGraph, this._originalMember) {}
+ this._element, this._library, this._packageGraph, this._originalMember);
factory ModelElement.fromElement(Element e, PackageGraph p) {
Library lib = p.findButDoNotCreateLibraryFor(e);
@@ -3105,7 +3153,7 @@
ModelNode _modelNode;
@override
ModelNode get modelNode =>
- _modelNode ??= packageGraph._getModelNodeFor(element, definingLibrary);
+ _modelNode ??= packageGraph._getModelNodeFor(element);
List<String> get annotations => annotationsFromMetadata(element.metadata);
@@ -4487,7 +4535,7 @@
class ModelFunctionAnonymous extends ModelFunctionTyped {
ModelFunctionAnonymous(
FunctionTypedElement element, PackageGraph packageGraph)
- : super(element, null, packageGraph) {}
+ : super(element, null, packageGraph);
@override
ModelElement get enclosingElement {
@@ -4653,73 +4701,73 @@
}
class PackageGraph {
- // TODO(jcollins-g): This constructor is convoluted. Clean this up by
- // building Libraries and adding them to Packages, then adding Packages
- // to this graph.
-
- PackageGraph._(this.config, this.packageMeta, this._packageWarningOptions,
- this.driver, this.sdk) : session = driver.currentSession {}
-
- static Future<PackageGraph> setUpPackageGraph(
- Iterable<ResolvedLibraryResult> libraryResults,
- Iterable<ResolvedLibraryResult> specialLibraryResults,
- DartdocOptionContext config,
- PackageMeta packageMeta,
- packageWarningOptions,
- driver,
- sdk,) async {
- PackageGraph newGraph =
- PackageGraph._(config, packageMeta, packageWarningOptions, driver, sdk);
- assert(newGraph._allConstructedModelElements.isEmpty);
- assert(newGraph.allLibraries.isEmpty);
- newGraph._packageWarningCounter =
- new PackageWarningCounter(newGraph._packageWarningOptions);
-
- // Build [Library] objects, and link them to [Package]s.
- libraryResults.forEach((result) {
- LibraryElement element = result.element;
- var packageMeta = new PackageMeta.fromElement(element, config);
- var lib = new Library._(result, newGraph,
- new Package.fromPackageMeta(packageMeta, newGraph));
- newGraph.packageMap[packageMeta.name]._libraries.add(lib);
- newGraph.allLibraries[element] = lib;
- });
-
+ PackageGraph.UninitializedPackageGraph(
+ this.config,
+ PackageWarningOptions packageWarningOptions,
+ this.driver,
+ this.sdk,
+ this.hasEmbedderSdk)
+ : packageMeta = config.topLevelPackageMeta,
+ session = driver.currentSession,
+ _packageWarningCounter =
+ new PackageWarningCounter(packageWarningOptions) {
// Make sure the default package exists, even if it has no libraries.
// This can happen for packages that only contain embedder SDKs.
- new Package.fromPackageMeta(packageMeta, newGraph);
- newGraph.allLibrariesAdded = true;
+ new Package.fromPackageMeta(packageMeta, this);
+ }
- // [findOrCreateLibraryFor] already adds to the proper structures.
- for (ResolvedLibraryResult result in specialLibraryResults) {
- await newGraph.findOrCreateLibraryFor(result.element);
- }
+ /// Call during initialization to add a library to this [PackageGraph].
+ ///
+ /// Libraries added in this manner are assumed to be part of documented
+ /// packages, even if includes or embedder.yaml files cause these to
+ /// span packages.
+ void addLibraryToGraph(ResolvedLibraryResult result) {
+ assert(!allLibrariesAdded);
+ LibraryElement element = result.element;
+ var packageMeta = new PackageMeta.fromElement(element, config);
+ var lib = new Library._(
+ result, this, new Package.fromPackageMeta(packageMeta, this));
+ packageMap[packageMeta.name]._libraries.add(lib);
+ allLibraries[element] = lib;
+ }
+ /// Call during initialization to add a library possibly containing
+ /// special/non-documented elements to this [PackageGraph]. Must be called
+ /// after any normal libraries.
+ void addSpecialLibraryToGraph(ResolvedLibraryResult result) {
+ allLibrariesAdded = true;
+ assert(!_localDocumentationBuilt);
+ findOrCreateLibraryFor(result);
+ }
+
+ /// Call after all libraries are added.
+ Future<void> initializePackageGraph() async {
+ allLibrariesAdded = true;
+ assert(!_localDocumentationBuilt);
// From here on in, we might find special objects. Initialize the
// specialClasses handler so when we find them, they get added.
- newGraph.specialClasses = new SpecialClasses();
+ specialClasses = new SpecialClasses();
// Go through docs of every ModelElement in package to pre-build the macros
// index.
- List<Future> precacheFutures = newGraph.precacheLocalDocs().toList();
+ List<Future> precacheFutures = precacheLocalDocs().toList();
for (Future f in precacheFutures) await f;
- newGraph._localDocumentationBuilt = true;
+ _localDocumentationBuilt = true;
// Scan all model elements to insure that interceptor and other special
// objects are found.
// After the allModelElements traversal to be sure that all packages
// are picked up.
- newGraph.documentedPackages.toList().forEach((package) {
+ documentedPackages.toList().forEach((package) {
package._libraries.sort((a, b) => compareNatural(a.name, b.name));
package._libraries.forEach((library) {
- library._allClasses.forEach(newGraph._addToImplementors);
+ library._allClasses.forEach(_addToImplementors);
});
});
- newGraph._implementors.values.forEach((l) => l.sort());
- newGraph.allImplementorsAdded = true;
+ _implementors.values.forEach((l) => l.sort());
+ allImplementorsAdded = true;
// We should have found all special classes by now.
- newGraph.specialClasses.assertSpecials();
- return newGraph;
+ specialClasses.assertSpecials();
}
/// Generate a list of futures for any docs that actually require precaching.
@@ -4735,12 +4783,14 @@
// Many ModelElements have the same ModelNode; don't build/cache this data more
// than once for them.
final Map<Element, ModelNode> _modelNodes = Map();
- ModelNode _getModelNodeFor(element, Library definingLibrary) {
- _modelNodes.putIfAbsent(
- element, () => ModelNode(definingLibrary.libraryResult.getElementDeclaration(element)?.node , element));
- return _modelNodes[element];
+ void _populateModelNodeFor(
+ Element element, Map<String, CompilationUnit> compilationUnitMap) {
+ _modelNodes.putIfAbsent(element,
+ () => ModelNode(getAstNode(element, compilationUnitMap), element));
}
+ ModelNode _getModelNodeFor(Element element) => _modelNodes[element];
+
SpecialClasses specialClasses;
/// It is safe to cache values derived from the _implementors table if this
@@ -4774,8 +4824,7 @@
// All library objects related to this package; a superset of _libraries.
final Map<LibraryElement, Library> allLibraries = new Map();
- /// Objects to keep track of warnings.
- final PackageWarningOptions _packageWarningOptions;
+ /// Keep track of warnings
PackageWarningCounter _packageWarningCounter;
/// All ModelElements constructed for this package; a superset of [allModelElements].
@@ -4806,6 +4855,8 @@
return _defaultPackage;
}
+ final bool hasEmbedderSdk;
+
PackageGraph get packageGraph => this;
/// Map of package name to Package.
@@ -5417,24 +5468,22 @@
/// This is used when we might need a Library object that isn't actually
/// a documentation entry point (for elements that have no Library within the
/// set of canonical Libraries).
- Future<Library> findOrCreateLibraryFor(Element e) async {
+ Library findOrCreateLibraryFor(ResolvedLibraryResult result) {
// This is just a cache to avoid creating lots of libraries over and over.
- if (allLibraries.containsKey(e.library)) {
- return allLibraries[e.library];
+ if (allLibraries.containsKey(result.element.library)) {
+ return allLibraries[result.element.library];
}
// can be null if e is for dynamic
- if (e.library == null) {
+ if (result.element.library == null) {
return null;
}
-
- ResolvedLibraryResult result =
- await session.getResolvedLibraryByElement(e.library);
Library foundLibrary = new Library._(
result,
this,
new Package.fromPackageMeta(
- new PackageMeta.fromElement(e.library, config), packageGraph));
- allLibraries[e.library] = foundLibrary;
+ new PackageMeta.fromElement(result.element.library, config),
+ packageGraph));
+ allLibraries[result.element.library] = foundLibrary;
return foundLibrary;
}
@@ -5829,7 +5878,6 @@
implements Privacy, Documentable {
String _name;
PackageGraph _packageGraph;
- final _isLocal;
final Map<String, Category> _nameToCategory = {};
@@ -5837,15 +5885,13 @@
factory Package.fromPackageMeta(
PackageMeta packageMeta, PackageGraph packageGraph) {
String packageName = packageMeta.name;
- bool isLocal = packageMeta == packageGraph.packageMeta ||
- packageGraph.config.autoIncludeDependencies;
- isLocal = isLocal && !packageGraph.config.isPackageExcluded(packageName);
+
bool expectNonLocal = false;
if (!packageGraph.packageMap.containsKey(packageName) &&
packageGraph.allLibrariesAdded) expectNonLocal = true;
packageGraph.packageMap.putIfAbsent(packageName,
- () => new Package._(packageName, packageGraph, packageMeta, isLocal));
+ () => new Package._(packageName, packageGraph, packageMeta));
// Verify that we don't somehow decide to document locally a package picked
// up after all documented libraries are added, because that breaks the
// assumption that we've picked up all documented libraries and packages
@@ -5858,7 +5904,7 @@
return packageGraph.packageMap[packageName];
}
- Package._(this._name, this._packageGraph, this._packageMeta, this._isLocal);
+ Package._(this._name, this._packageGraph, this._packageMeta);
@override
bool get isCanonical => true;
@override
@@ -5935,9 +5981,20 @@
return _isPublic;
}
- /// Returns true if this package is being documented locally. If it isn't
- /// documented locally, it still might be documented remotely; see documentedWhere.
- bool get isLocal => _isLocal;
+ bool _isLocal;
+
+ /// Return true if this is the default package, this is part of an embedder SDK,
+ /// or if [config.autoIncludeDependencies] is true -- but only if the package
+ /// was not excluded on the command line.
+ bool get isLocal {
+ if (_isLocal == null) {
+ _isLocal = (packageMeta == packageGraph.packageMeta ||
+ packageGraph.hasEmbedderSdk && packageMeta.isSdk ||
+ packageGraph.config.autoIncludeDependencies) &&
+ !packageGraph.config.isPackageExcluded(name);
+ }
+ return _isLocal;
+ }
DocumentLocation get documentedWhere {
if (isLocal) {
@@ -6180,7 +6237,8 @@
Library get library;
String _sourceCode;
- String get sourceCode => _sourceCode ??= modelNode.sourceCode;
+ String get sourceCode =>
+ _sourceCode ??= modelNode == null ? '' : modelNode.sourceCode;
}
abstract class TypeParameters implements ModelElement {
@@ -6407,23 +6465,16 @@
PackageBuilder(this.config);
- Future<void> logAnalysisErrors(Set<Source> sources) async {}
-
Future<PackageGraph> buildPackageGraph() async {
- PackageMeta packageMeta = config.topLevelPackageMeta;
- if (packageMeta.needsPubGet) {
- packageMeta.runPubGet();
+ if (config.topLevelPackageMeta.needsPubGet) {
+ config.topLevelPackageMeta.runPubGet();
}
- Map<LibraryElement, ResolvedLibraryResult> libraryResults = new Map();
- Map<LibraryElement, ResolvedLibraryResult> specialLibraryResults = new Map();
- DartSdk findSpecialsSdk = sdk;
- if (embedderSdk != null && embedderSdk.urlMappings.isNotEmpty) {
- findSpecialsSdk = embedderSdk;
- }
- await getLibraries(libraryResults, specialLibraryResults, getFiles,
- specialLibraryFiles(findSpecialsSdk).toSet());
- return await PackageGraph.setUpPackageGraph(libraryResults.values, specialLibraryResults.values,
- config, config.topLevelPackageMeta, getWarningOptions(), driver, sdk);
+
+ PackageGraph newGraph = new PackageGraph.UninitializedPackageGraph(
+ config, getWarningOptions(), driver, sdk, hasEmbedderSdkFiles);
+ await getLibraries(newGraph);
+ await newGraph.initializePackageGraph();
+ return newGraph;
}
DartSdk _sdk;
@@ -6590,7 +6641,6 @@
name = name.substring(directoryCurrentPath.length);
if (name.startsWith(Platform.pathSeparator)) name = name.substring(1);
}
- logInfo('parsing ${name}...');
JavaFile javaFile = new JavaFile(filePath).getAbsoluteFile();
Source source = new FileBasedSource(javaFile);
@@ -6623,14 +6673,34 @@
return metas;
}
- Future<Map<LibraryElement, ResolvedLibraryResult>> _parseLibraries(Set<String> files) async {
- Map<LibraryElement, ResolvedLibraryResult> libraries = new Map();
+ /// Parse libraries with the analyzer and invoke a callback with the
+ /// result.
+ ///
+ /// Uses the [libraries] parameter to prevent calling
+ /// the callback more than once with the same [LibraryElement].
+ /// Adds [LibraryElement]s found to that parameter.
+ Future<void> _parseLibraries(
+ void Function(ResolvedLibraryResult) libraryAdder,
+ Set<LibraryElement> libraries,
+ Set<String> files,
+ [bool Function(LibraryElement) isLibraryIncluded]) async {
+ isLibraryIncluded ??= (_) => true;
Set<PackageMeta> lastPass = new Set();
Set<PackageMeta> current;
do {
lastPass = _packageMetasForFiles(files);
- for (ResolvedLibraryResult r in (await Future.wait(files.map((f) => processLibrary(f)))).where((ResolvedLibraryResult l) => l != null)) {
- libraries[r.element] = r;
+
+ // Be careful here not to accidentally stack up multiple
+ // ResolvedLibraryResults, as those eat our heap.
+ for (String f in files) {
+ ResolvedLibraryResult r = await processLibrary(f);
+ if (r != null &&
+ !libraries.contains(r.element) &&
+ isLibraryIncluded(r.element)) {
+ logInfo('parsing ${f}...');
+ libraryAdder(r);
+ libraries.add(r.element);
+ }
}
// Be sure to give the analyzer enough time to find all the files.
@@ -6652,7 +6722,6 @@
}
}
} while (!lastPass.containsAll(current));
- return libraries;
}
/// Given a package name, explore the directory and pull out all top level
@@ -6709,48 +6778,65 @@
/// file might be part of a [DartdocOptionContext], and loads those
/// objects to find any [DartdocOptionContext.includeExternal] configurations
/// therein.
- Iterable<String> _includeExternalsFrom(Iterable<String> files) {
- Set<String> includeExternalsFound = new Set();
+ Iterable<String> _includeExternalsFrom(Iterable<String> files) sync* {
for (String file in files) {
DartdocOptionContext fileContext =
new DartdocOptionContext.fromContext(config, new File(file));
if (fileContext.includeExternal != null) {
- includeExternalsFound.addAll(fileContext.includeExternal);
+ yield* fileContext.includeExternal;
}
}
- return includeExternalsFound;
}
- Set<String> get getFiles {
- Set<String> files = new Set();
- files.addAll(config.topLevelPackageMeta.isSdk
- ? new Set()
- : findFilesToDocumentInPackage(
- config.inputDir, config.autoIncludeDependencies));
+ Set<String> getFiles() {
+ Iterable<String> files;
if (config.topLevelPackageMeta.isSdk) {
- files.addAll(getSdkFilesToDocument());
- } else if (embedderSdk.urlMappings.isNotEmpty &&
- !config.topLevelPackageMeta.isSdk) {
- embedderSdk.urlMappings.keys.forEach((String dartUri) {
- Source source = embedderSdk.mapDartUri(dartUri);
- files.add(source.fullName);
- });
+ files = getSdkFilesToDocument();
+ } else {
+ files = findFilesToDocumentInPackage(
+ config.inputDir, config.autoIncludeDependencies);
}
-
- files.addAll(_includeExternalsFrom(files));
+ files = quiverIterables.concat([files, _includeExternalsFrom(files)]);
return new Set.from(files.map((s) => new File(s).absolute.path));
}
- Future<void> getLibraries(
- Map<LibraryElement, ResolvedLibraryResult> libraryResults,
- Map<LibraryElement, ResolvedLibraryResult> specialLibraryResults,
- Set<String> files,
- Set<String> specialFiles) async {
- libraryResults.addAll(await _parseLibraries(files));
- specialLibraryResults
- .addAll(await _parseLibraries(specialFiles.difference(files)));
+ Iterable<String> getEmbedderSdkFiles() sync* {
+ if (embedderSdk != null &&
+ embedderSdk.urlMappings.isNotEmpty &&
+ !config.topLevelPackageMeta.isSdk) {
+ for (String dartUri in embedderSdk.urlMappings.keys) {
+ Source source = embedderSdk.mapDartUri(dartUri);
+ yield (new File(source.fullName)).absolute.path;
+ }
+ }
+ }
+
+ bool get hasEmbedderSdkFiles =>
+ embedderSdk != null && getEmbedderSdkFiles().isNotEmpty;
+
+ Future<void> getLibraries(PackageGraph uninitializedPackageGraph) async {
+ DartSdk findSpecialsSdk = sdk;
+ if (embedderSdk != null && embedderSdk.urlMappings.isNotEmpty) {
+ findSpecialsSdk = embedderSdk;
+ }
+ Set<String> files = getFiles()..addAll(getEmbedderSdkFiles());
+ Set<String> specialFiles = specialLibraryFiles(findSpecialsSdk).toSet();
+
+ /// Returns true if this library element should be included according
+ /// to the configuration.
+ bool isLibraryIncluded(LibraryElement libraryElement) {
+ if (config.include.isNotEmpty &&
+ !config.include.contains(libraryElement.name)) {
+ return false;
+ }
+ return true;
+ }
+
+ Set<LibraryElement> foundLibraries = new Set();
+ await _parseLibraries(uninitializedPackageGraph.addLibraryToGraph,
+ foundLibraries, files, isLibraryIncluded);
if (config.include.isNotEmpty) {
- Iterable knownLibraryNames = libraryResults.values.map((l) => l.element.name);
+ Iterable knownLibraryNames = foundLibraries.map((l) => l.name);
Set notFound = new Set.from(config.include)
.difference(new Set.from(knownLibraryNames))
.difference(new Set.from(config.exclude));
@@ -6758,9 +6844,10 @@
throw 'Did not find: [${notFound.join(', ')}] in '
'known libraries: [${knownLibraryNames.join(', ')}]';
}
- libraryResults.removeWhere(
- (element, result) => !config.include.contains(result.element.name));
}
+ // Include directive does not apply to special libraries.
+ await _parseLibraries(uninitializedPackageGraph.addSpecialLibraryToGraph,
+ foundLibraries, specialFiles.difference(files));
}
/// If [dir] contains both a `lib` directory and a `pubspec.yaml` file treat
diff --git a/lib/src/model_utils.dart b/lib/src/model_utils.dart
index 52964d3..984180b 100644
--- a/lib/src/model_utils.dart
+++ b/lib/src/model_utils.dart
@@ -7,7 +7,9 @@
import 'dart:convert';
import 'dart:io';
+import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:analyzer/src/generated/source_io.dart';
@@ -15,6 +17,24 @@
final Map<String, String> _fileContents = <String, String>{};
+/// Returns the [AstNode] for a given [Element].
+///
+/// Uses a precomputed map of [element.source.fullName] to [CompilationUnit]
+/// to avoid linear traversal in [ResolvedLibraryElementImpl.getElementDeclaration].
+AstNode getAstNode(
+ Element element, Map<String, CompilationUnit> compilationUnitMap) {
+ if (element?.source?.fullName != null &&
+ !element.isSynthetic &&
+ element.nameOffset != -1) {
+ CompilationUnit unit = compilationUnitMap[element.source.fullName];
+ if (unit != null) {
+ var locator = new NodeLocator2(element.nameOffset);
+ return (locator.searchWithin(unit)?.parent);
+ }
+ }
+ return null;
+}
+
/// Remove elements that aren't documented.
Iterable<T> filterNonDocumented<T extends Documentable>(
Iterable<T> maybeDocumentedItems) {
@@ -131,4 +151,4 @@
line = line.trimRight();
return line.startsWith(indent) ? line.substring(indent.length) : line;
}).join('\n');
-}
\ No newline at end of file
+}