Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(242)

Unified Diff: pkg/analysis_server/lib/src/pub_summary.dart

Issue 2220703002: Initial implementation of pub summary manager. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: tweak Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/analysis_server/lib/src/analysis_server.dart ('k') | pkg/analysis_server/test/pub_summary_test.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/analysis_server/lib/src/pub_summary.dart
diff --git a/pkg/analysis_server/lib/src/pub_summary.dart b/pkg/analysis_server/lib/src/pub_summary.dart
new file mode 100644
index 0000000000000000000000000000000000000000..479cdc2b156d2fa3d0d8add1d3e9c62336c57acb
--- /dev/null
+++ b/pkg/analysis_server/lib/src/pub_summary.dart
@@ -0,0 +1,274 @@
+// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:core' hide Resource;
+
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/context_manager.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/summary/format.dart';
+import 'package:analyzer/src/summary/summarize_ast.dart'
+ show serializeAstUnlinked;
+import 'package:analyzer/src/summary/summarize_elements.dart'
+ show PackageBundleAssembler;
+import 'package:analyzer/src/task/dart.dart';
+import 'package:analyzer/src/util/fast_uri.dart';
+import 'package:analyzer/task/dart.dart';
+import 'package:analyzer/task/model.dart';
+import 'package:path/src/context.dart' as pathos;
+
+const visibleForTesting = const Object();
+
+/**
+ * A package in the pub cache.
+ */
+@visibleForTesting
Brian Wilkerson 2016/08/05 22:09:14 nit: There have been requests for this annotation;
+class PubPackage {
+ final String name;
+ final Folder libFolder;
+
+ PubPackage(this.name, this.libFolder);
+
+ Folder get folder => libFolder.parent;
+
+ @override
+ int get hashCode => libFolder.hashCode;
+
+ @override
+ bool operator ==(other) {
+ return other is PubPackage && other.libFolder == libFolder;
+ }
+
+ @override
+ String toString() => '($name in $folder)';
+}
+
+/**
+ * Class the manages summaries for pub packages.
Paul Berry 2016/08/05 21:43:02 s/the/that/
scheglov 2016/08/05 21:47:41 Done.
+ */
+class PubSummaryManager {
Brian Wilkerson 2016/08/05 22:09:14 I'd love to see an example of how clients are expe
+ final ResourceProvider resourceProvider;
+ final AnalysisServer server;
+
+// /**
+// * The map from absolute paths of pub packages in the pub cache to their
+// * unlinked summary bundles.
+// */
+// final Map<String, PackageBundle> unlinkedBundleMap =
+// new HashMap<String, PackageBundle>();
+
+ /**
+ * The set of packages to compute summaries for.
+ */
+ final Set<PubPackage> packagesToSummarize = new Set<PubPackage>();
+
+ /**
+ * The set of already processed packages, which we have already checked
+ * for their unlinked summary existence, or scheduled its computing.
+ */
+ final Set<PubPackage> seenPackages = new Set<PubPackage>();
+
+ /**
+ * The [Completer] that completes when analysis is complete.
+ */
+ Completer _onCompleteCompleter;
+
+ /**
+ * Create a new instance and start listening for [AnalysisServer] and
+ * [AnalysisContext] events, and schedule creating pub summaries.
+ */
+ PubSummaryManager(this.resourceProvider, this.server) {
+ server.onContextsChanged.listen((ContextsChangedEvent event) {
+ for (AnalysisContext context in event.added) {
+ context
+ .onResultChanged(LIBRARY_ELEMENT1)
+ .listen(handleNewLibraryElementEvent);
+ }
+ });
+ }
+
+ /**
+ * The [Future] that completes when computing of all package summaries is
+ * complete.
+ */
+ Future get onComplete {
+ if (packagesToSummarize.isEmpty) {
+ return new Future.value();
+ }
+ _onCompleteCompleter ??= new Completer();
+ return _onCompleteCompleter.future;
+ }
+
+ /**
+ * Return the [pathos.Context] corresponding to the [resourceProvider].
+ */
+ pathos.Context get pathContext => resourceProvider.pathContext;
+
+ /**
+ * If the given [source] has the 'package' scheme, and its path is in the
+ * pub cache, return information about the package that contains the [source].
+ * Otherwise return `null`.
+ */
+ @visibleForTesting
+ PubPackage getPackageInPubCache(AnalysisContext context, Source source) {
+ if (source.uri.scheme == 'package') {
+ String path = source.fullName;
+ if (isPathInPubCache(pathContext, path)) {
+ String packageName = getPackageName(source.uri);
+ if (packageName != null) {
+ List<Folder> libFolders =
+ context.sourceFactory.packageMap[packageName];
+ if (libFolders != null && libFolders.length == 1) {
+ return new PubPackage(packageName, libFolders.first);
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Handle [ResultChangedEvent] for [LIBRARY_ELEMENT1] and schedule computing
+ * summary for the library, if it is not ready yet.
+ */
+ @visibleForTesting
+ void handleNewLibraryElementEvent(ResultChangedEvent event) {
+ AnalysisTarget source = event.target;
+ if (event.wasComputed && source is Source) {
+ PubPackage package = getPackageInPubCache(event.context, source);
+ if (package != null && seenPackages.add(package)) {
+ packagesToSummarize.add(package);
+ if (packagesToSummarize.length == 1) {
+ _scheduleNextPackageSummary();
Brian Wilkerson 2016/08/05 22:09:13 If a package 'a' depends on a package 'b', do we n
Paul Berry 2016/08/05 22:29:28 That is not a constraint when building unlinked su
+ }
+ }
+ }
+ }
+
+ /**
+ * Compute summary for a package from [packagesToSummarize], and schedule
+ * delayed computation of the next package summary, if any.
+ */
+ void _computeNextPackageSummary() {
+ if (packagesToSummarize.isNotEmpty) {
+ PubPackage package = packagesToSummarize.first;
+ _computeUnlinkedPackageSummary(package);
+ packagesToSummarize.remove(package);
+ _scheduleNextPackageSummary();
+ } else {
+ if (_onCompleteCompleter != null) {
+ _onCompleteCompleter.complete(true);
+ _onCompleteCompleter = null;
+ }
+ }
+ }
+
+ /**
+ * Compute the unlinked summary for the package with the given path, put
+ * it in the [unlinkedBundleMap] and store into the [resourceProvider].
+ *
+ * TODO(scheglov) Consider moving into separate isolate(s).
+ */
+ void _computeUnlinkedPackageSummary(PubPackage package) {
+ AnalysisContext context = AnalysisEngine.instance.createAnalysisContext();
+ context.sourceFactory = new SourceFactory(
+ <UriResolver>[new DartUriResolver(server.sdkManager.anySdk)]);
Brian Wilkerson 2016/08/05 22:09:14 This won't let us distinguish between packages bui
Paul Berry 2016/08/05 22:29:28 I'm not aware of any constraints Flutter imposes t
+
+ Folder libFolder = package.libFolder;
+ String libPath = libFolder.path + pathContext.separator;
+ PackageBundleAssembler assembler = new PackageBundleAssembler();
+
+ /**
+ * If the given [file] is a Dart file, the unlinked summary of it.
+ */
+ void addDartFile(File file) {
+ String path = file.path;
+ if (AnalysisEngine.isDartFileName(path)) {
+ String pathInLib = path.substring(libPath.length);
+ String uriStr = 'package:${package.name}/$pathInLib';
Paul Berry 2016/08/05 21:43:02 This looks incorrect for Windows, since on Windows
scheglov 2016/08/05 21:47:41 Fixed.
+ Uri uri = FastUri.parse(uriStr);
+ Source source = file.createSource(uri);
+ CompilationUnit unit = context.computeResult(source, PARSED_UNIT);
+ UnlinkedUnitBuilder unlinkedUnit = serializeAstUnlinked(unit);
+ assembler.addUnlinkedUnit(source, unlinkedUnit);
+ }
+ }
+
+ /**
+ * Visit the [folder] recursively.
+ */
+ void addDartFiles(Folder folder) {
+ List<Resource> children = folder.getChildren();
+ for (Resource child in children) {
+ if (child is File) {
+ addDartFile(child);
+ }
+ }
+ for (Resource child in children) {
Brian Wilkerson 2016/08/05 22:09:14 Does the order in which we add the children matter
+ if (child is Folder) {
+ addDartFiles(child);
+ }
+ }
+ }
+
+ try {
+ addDartFiles(libFolder);
+ List<int> bytes = assembler.assemble().toBuffer();
+ package.folder
+ .getChildAssumingFile('summary_spec.full.ds')
+ .writeAsBytesSync(bytes);
+ } on FileSystemException {
+ // Ignore file system exceptions.
+ }
+ }
+
+ /**
+ * Schedule delayed computation of the next package summary from the set of
+ * [packagesToSummarize]. We delay each computation because we want
+ * operations in analysis server to proceed, and computing summaries of
+ * packages is a background task.
+ */
+ void _scheduleNextPackageSummary() {
+ new Future.delayed(
+ new Duration(milliseconds: 10), _computeNextPackageSummary);
+ }
+
+ /**
+ * If the given [uri] has the `package` scheme, return the names of the
Paul Berry 2016/08/05 21:43:02 s/names/name/
scheglov 2016/08/05 21:47:41 Done.
+ * package that contains the referenced resource. Otherwise return `null`.
+ *
+ * For example `package:foo/bar.dart` => `foo`.
+ */
+ static String getPackageName(Uri uri) {
+ const String PACKAGE_SCHEME = 'package:';
+ String text = uri.toString();
+ if (text.startsWith(PACKAGE_SCHEME)) {
+ int index = text.indexOf('/');
+ if (index != -1) {
+ return text.substring(PACKAGE_SCHEME.length, index);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return `true` if the given absolute [path] is in the pub cache.
+ */
+ static bool isPathInPubCache(pathos.Context pathContext, String path) {
+ List<String> parts = pathContext.split(path);
+ for (int i = 0; i < parts.length - 1; i++) {
+ if (parts[i] == '.pub-cache') {
+ return true;
+ }
+ if (parts[i] == 'Pub' && parts[i + 1] == 'Cache') {
+ return true;
+ }
+ }
+ return false;
+ }
+}
« no previous file with comments | « pkg/analysis_server/lib/src/analysis_server.dart ('k') | pkg/analysis_server/test/pub_summary_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698