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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 import 'dart:async';
6 import 'dart:core' hide Resource;
7
8 import 'package:analysis_server/src/analysis_server.dart';
9 import 'package:analysis_server/src/context_manager.dart';
10 import 'package:analyzer/dart/ast/ast.dart';
11 import 'package:analyzer/file_system/file_system.dart';
12 import 'package:analyzer/src/generated/engine.dart';
13 import 'package:analyzer/src/generated/source.dart';
14 import 'package:analyzer/src/summary/format.dart';
15 import 'package:analyzer/src/summary/summarize_ast.dart'
16 show serializeAstUnlinked;
17 import 'package:analyzer/src/summary/summarize_elements.dart'
18 show PackageBundleAssembler;
19 import 'package:analyzer/src/task/dart.dart';
20 import 'package:analyzer/src/util/fast_uri.dart';
21 import 'package:analyzer/task/dart.dart';
22 import 'package:analyzer/task/model.dart';
23 import 'package:path/src/context.dart' as pathos;
24
25 const visibleForTesting = const Object();
26
27 /**
28 * A package in the pub cache.
29 */
30 @visibleForTesting
Brian Wilkerson 2016/08/05 22:09:14 nit: There have been requests for this annotation;
31 class PubPackage {
32 final String name;
33 final Folder libFolder;
34
35 PubPackage(this.name, this.libFolder);
36
37 Folder get folder => libFolder.parent;
38
39 @override
40 int get hashCode => libFolder.hashCode;
41
42 @override
43 bool operator ==(other) {
44 return other is PubPackage && other.libFolder == libFolder;
45 }
46
47 @override
48 String toString() => '($name in $folder)';
49 }
50
51 /**
52 * 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.
53 */
54 class PubSummaryManager {
Brian Wilkerson 2016/08/05 22:09:14 I'd love to see an example of how clients are expe
55 final ResourceProvider resourceProvider;
56 final AnalysisServer server;
57
58 // /**
59 // * The map from absolute paths of pub packages in the pub cache to their
60 // * unlinked summary bundles.
61 // */
62 // final Map<String, PackageBundle> unlinkedBundleMap =
63 // new HashMap<String, PackageBundle>();
64
65 /**
66 * The set of packages to compute summaries for.
67 */
68 final Set<PubPackage> packagesToSummarize = new Set<PubPackage>();
69
70 /**
71 * The set of already processed packages, which we have already checked
72 * for their unlinked summary existence, or scheduled its computing.
73 */
74 final Set<PubPackage> seenPackages = new Set<PubPackage>();
75
76 /**
77 * The [Completer] that completes when analysis is complete.
78 */
79 Completer _onCompleteCompleter;
80
81 /**
82 * Create a new instance and start listening for [AnalysisServer] and
83 * [AnalysisContext] events, and schedule creating pub summaries.
84 */
85 PubSummaryManager(this.resourceProvider, this.server) {
86 server.onContextsChanged.listen((ContextsChangedEvent event) {
87 for (AnalysisContext context in event.added) {
88 context
89 .onResultChanged(LIBRARY_ELEMENT1)
90 .listen(handleNewLibraryElementEvent);
91 }
92 });
93 }
94
95 /**
96 * The [Future] that completes when computing of all package summaries is
97 * complete.
98 */
99 Future get onComplete {
100 if (packagesToSummarize.isEmpty) {
101 return new Future.value();
102 }
103 _onCompleteCompleter ??= new Completer();
104 return _onCompleteCompleter.future;
105 }
106
107 /**
108 * Return the [pathos.Context] corresponding to the [resourceProvider].
109 */
110 pathos.Context get pathContext => resourceProvider.pathContext;
111
112 /**
113 * If the given [source] has the 'package' scheme, and its path is in the
114 * pub cache, return information about the package that contains the [source].
115 * Otherwise return `null`.
116 */
117 @visibleForTesting
118 PubPackage getPackageInPubCache(AnalysisContext context, Source source) {
119 if (source.uri.scheme == 'package') {
120 String path = source.fullName;
121 if (isPathInPubCache(pathContext, path)) {
122 String packageName = getPackageName(source.uri);
123 if (packageName != null) {
124 List<Folder> libFolders =
125 context.sourceFactory.packageMap[packageName];
126 if (libFolders != null && libFolders.length == 1) {
127 return new PubPackage(packageName, libFolders.first);
128 }
129 }
130 }
131 }
132 return null;
133 }
134
135 /**
136 * Handle [ResultChangedEvent] for [LIBRARY_ELEMENT1] and schedule computing
137 * summary for the library, if it is not ready yet.
138 */
139 @visibleForTesting
140 void handleNewLibraryElementEvent(ResultChangedEvent event) {
141 AnalysisTarget source = event.target;
142 if (event.wasComputed && source is Source) {
143 PubPackage package = getPackageInPubCache(event.context, source);
144 if (package != null && seenPackages.add(package)) {
145 packagesToSummarize.add(package);
146 if (packagesToSummarize.length == 1) {
147 _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
148 }
149 }
150 }
151 }
152
153 /**
154 * Compute summary for a package from [packagesToSummarize], and schedule
155 * delayed computation of the next package summary, if any.
156 */
157 void _computeNextPackageSummary() {
158 if (packagesToSummarize.isNotEmpty) {
159 PubPackage package = packagesToSummarize.first;
160 _computeUnlinkedPackageSummary(package);
161 packagesToSummarize.remove(package);
162 _scheduleNextPackageSummary();
163 } else {
164 if (_onCompleteCompleter != null) {
165 _onCompleteCompleter.complete(true);
166 _onCompleteCompleter = null;
167 }
168 }
169 }
170
171 /**
172 * Compute the unlinked summary for the package with the given path, put
173 * it in the [unlinkedBundleMap] and store into the [resourceProvider].
174 *
175 * TODO(scheglov) Consider moving into separate isolate(s).
176 */
177 void _computeUnlinkedPackageSummary(PubPackage package) {
178 AnalysisContext context = AnalysisEngine.instance.createAnalysisContext();
179 context.sourceFactory = new SourceFactory(
180 <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
181
182 Folder libFolder = package.libFolder;
183 String libPath = libFolder.path + pathContext.separator;
184 PackageBundleAssembler assembler = new PackageBundleAssembler();
185
186 /**
187 * If the given [file] is a Dart file, the unlinked summary of it.
188 */
189 void addDartFile(File file) {
190 String path = file.path;
191 if (AnalysisEngine.isDartFileName(path)) {
192 String pathInLib = path.substring(libPath.length);
193 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.
194 Uri uri = FastUri.parse(uriStr);
195 Source source = file.createSource(uri);
196 CompilationUnit unit = context.computeResult(source, PARSED_UNIT);
197 UnlinkedUnitBuilder unlinkedUnit = serializeAstUnlinked(unit);
198 assembler.addUnlinkedUnit(source, unlinkedUnit);
199 }
200 }
201
202 /**
203 * Visit the [folder] recursively.
204 */
205 void addDartFiles(Folder folder) {
206 List<Resource> children = folder.getChildren();
207 for (Resource child in children) {
208 if (child is File) {
209 addDartFile(child);
210 }
211 }
212 for (Resource child in children) {
Brian Wilkerson 2016/08/05 22:09:14 Does the order in which we add the children matter
213 if (child is Folder) {
214 addDartFiles(child);
215 }
216 }
217 }
218
219 try {
220 addDartFiles(libFolder);
221 List<int> bytes = assembler.assemble().toBuffer();
222 package.folder
223 .getChildAssumingFile('summary_spec.full.ds')
224 .writeAsBytesSync(bytes);
225 } on FileSystemException {
226 // Ignore file system exceptions.
227 }
228 }
229
230 /**
231 * Schedule delayed computation of the next package summary from the set of
232 * [packagesToSummarize]. We delay each computation because we want
233 * operations in analysis server to proceed, and computing summaries of
234 * packages is a background task.
235 */
236 void _scheduleNextPackageSummary() {
237 new Future.delayed(
238 new Duration(milliseconds: 10), _computeNextPackageSummary);
239 }
240
241 /**
242 * 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.
243 * package that contains the referenced resource. Otherwise return `null`.
244 *
245 * For example `package:foo/bar.dart` => `foo`.
246 */
247 static String getPackageName(Uri uri) {
248 const String PACKAGE_SCHEME = 'package:';
249 String text = uri.toString();
250 if (text.startsWith(PACKAGE_SCHEME)) {
251 int index = text.indexOf('/');
252 if (index != -1) {
253 return text.substring(PACKAGE_SCHEME.length, index);
254 }
255 }
256 return null;
257 }
258
259 /**
260 * Return `true` if the given absolute [path] is in the pub cache.
261 */
262 static bool isPathInPubCache(pathos.Context pathContext, String path) {
263 List<String> parts = pathContext.split(path);
264 for (int i = 0; i < parts.length - 1; i++) {
265 if (parts[i] == '.pub-cache') {
266 return true;
267 }
268 if (parts[i] == 'Pub' && parts[i + 1] == 'Cache') {
269 return true;
270 }
271 }
272 return false;
273 }
274 }
OLDNEW
« 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