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

Side by Side Diff: pkg/analyzer/lib/src/summary/pub_summary.dart

Issue 2220703002: Initial implementation of pub summary manager. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Rework to better fit actual use. 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:collection';
7 import 'dart:core' hide Resource;
8
9 import 'package:analyzer/dart/ast/ast.dart';
10 import 'package:analyzer/file_system/file_system.dart';
11 import 'package:analyzer/src/generated/engine.dart';
12 import 'package:analyzer/src/generated/sdk.dart';
13 import 'package:analyzer/src/generated/source.dart';
14 import 'package:analyzer/src/summary/format.dart';
15 import 'package:analyzer/src/summary/idl.dart';
16 import 'package:analyzer/src/summary/package_bundle_reader.dart'
17 show ResynthesizerResultProvider;
18 import 'package:analyzer/src/summary/summarize_ast.dart'
19 show serializeAstUnlinked;
20 import 'package:analyzer/src/summary/summarize_elements.dart'
21 show PackageBundleAssembler;
22 import 'package:analyzer/src/util/fast_uri.dart';
23 import 'package:analyzer/task/dart.dart';
24 import 'package:path/path.dart' as pathos;
25
26 /**
27 * A package in the pub cache.
28 */
29 class PubPackage {
30 final String name;
31 final Folder libFolder;
32
33 PubPackage(this.name, this.libFolder);
34
35 Folder get folder => libFolder.parent;
36
37 @override
38 int get hashCode => libFolder.hashCode;
39
40 @override
41 bool operator ==(other) {
42 return other is PubPackage && other.libFolder == libFolder;
43 }
44
45 @override
46 String toString() => '($name in $folder)';
47 }
48
49 /**
50 * Class that manages summaries for pub packages.
51 *
52 * The client should call [getLinkedBundles] after creating a new
53 * [AnalysisContext] and configuring its source factory, but before computing
54 * any analysis results. The returned linked bundles can be used to create and
55 * configure [ResynthesizerResultProvider] for the context.
56 */
57 class PubSummaryManager {
58 static const UNLINKED_BUNDLE_FILE_NAME = 'unlinked.ds';
59
60 final ResourceProvider resourceProvider;
61
62 /**
63 * A [DartSdk]. While we create only unlinked summaries, we just need it to
64 * be able to resolve `dart:core` (parse task requires this).
65 */
66 final DartSdk defaultDartSdk;
67
68 /**
69 * The map from [PubPackage]s to their unlinked [PackageBundle]s in the pub
70 * cache.
71 */
72 final Map<PubPackage, PackageBundle> unlinkedBundleMap =
73 new HashMap<PubPackage, PackageBundle>();
74
75 /**
76 * The set of packages to compute unlinked summaries for.
77 */
78 final Set<PubPackage> packagesToComputeUnlinked = new Set<PubPackage>();
79
80 /**
81 * The set of already processed packages, which we have already checked
82 * for their unlinked bundle existence, or scheduled its computing.
83 */
84 final Set<PubPackage> seenPackages = new Set<PubPackage>();
85
86 /**
87 * The [Completer] that completes when computing of all scheduled unlinked
88 * bundles is complete.
89 */
90 Completer _onUnlinkedCompleteCompleter;
91
92 PubSummaryManager(this.resourceProvider, this.defaultDartSdk);
93
94 /**
95 * The [Future] that completes when computing of all scheduled unlinked
96 * bundles is complete.
97 */
98 Future get onUnlinkedComplete {
99 if (packagesToComputeUnlinked.isEmpty) {
100 return new Future.value();
101 }
102 _onUnlinkedCompleteCompleter ??= new Completer();
103 return _onUnlinkedCompleteCompleter.future;
104 }
105
106 /**
107 * Return the [pathos.Context] corresponding to the [resourceProvider].
108 */
109 pathos.Context get pathContext => resourceProvider.pathContext;
110
111 /**
112 * Return the list of linked [PackageBundle]s that can be provided at this
113 * time for a subset of the packages used by the given [context]. If
114 * information about some of the used packages is not available yet, schedule
115 * its computation, so that it might be available later for other contexts
116 * referencing the same packages.
117 */
118 List<PackageBundle> getLinkedBundles(AnalysisContext context) {
Paul Berry 2016/08/08 12:24:59 Sorry for not picking up on this during the last r
scheglov 2016/08/09 03:41:07 Yes, I plan to perform profiling on some dependenc
119 Map<String, PackageBundle> unlinkedBundles = getUnlinkedBundles(context);
120 // TODO(scheglov) actually compute available linked bundles
121 return <PackageBundle>[];
122 }
123
124 /**
125 * Return all available unlinked [PackageBundle]s for the given [context],
126 * maybe an empty list, but not `null`.
127 */
128 Map<String, PackageBundle> getUnlinkedBundles(AnalysisContext context) {
Paul Berry 2016/08/08 12:24:59 Similar concern here; also ok if you want to defer
129 Map<String, PackageBundle> unlinkedBundles =
130 new HashMap<String, PackageBundle>();
131 Map<String, List<Folder>> packageMap = context.sourceFactory.packageMap;
132 if (packageMap != null) {
133 packageMap.forEach((String packageName, List<Folder> libFolders) {
134 if (libFolders.length == 1) {
135 Folder libFolder = libFolders.first;
136 if (isPathInPubCache(pathContext, libFolder.path)) {
137 PubPackage package = new PubPackage(packageName, libFolder);
138 PackageBundle unlinkedBundle = _getUnlinkedOrSchedule(package);
139 if (unlinkedBundle != null) {
140 unlinkedBundles[packageName] = unlinkedBundle;
141 }
142 }
143 }
144 });
145 }
146 return unlinkedBundles;
147 }
148
149 /**
150 * Compute unlinked bundle for a package from [packagesToComputeUnlinked],
151 * and schedule delayed computation for the next package, if any.
152 */
153 void _computeNextUnlinked() {
154 if (packagesToComputeUnlinked.isNotEmpty) {
155 PubPackage package = packagesToComputeUnlinked.first;
156 _computeUnlinked(package);
157 packagesToComputeUnlinked.remove(package);
158 _scheduleNextUnlinked();
159 } else {
160 if (_onUnlinkedCompleteCompleter != null) {
161 _onUnlinkedCompleteCompleter.complete(true);
162 _onUnlinkedCompleteCompleter = null;
163 }
164 }
165 }
166
167 /**
168 * Compute the unlinked bundle for the package with the given path, put
169 * it in the [unlinkedBundleMap] and store into the [resourceProvider].
170 *
171 * TODO(scheglov) Consider moving into separate isolate(s).
172 */
173 void _computeUnlinked(PubPackage package) {
174 AnalysisContext context = AnalysisEngine.instance.createAnalysisContext();
175 context.sourceFactory =
176 new SourceFactory(<UriResolver>[new DartUriResolver(defaultDartSdk)]);
177
178 Folder libFolder = package.libFolder;
179 String libPath = libFolder.path + pathContext.separator;
180 PackageBundleAssembler assembler = new PackageBundleAssembler();
181
182 /**
183 * If the given [file] is a Dart file, add its unlinked unit.
184 */
185 void addDartFile(File file) {
186 String path = file.path;
187 if (AnalysisEngine.isDartFileName(path)) {
188 String pathInLib = path.substring(libPath.length);
189 String uriPath = pathos.posix.joinAll(pathContext.split(pathInLib));
190 String uriStr = 'package:${package.name}/$uriPath';
191 Uri uri = FastUri.parse(uriStr);
192 Source source = file.createSource(uri);
193 CompilationUnit unit = context.computeResult(source, PARSED_UNIT);
Brian Wilkerson 2016/08/08 14:32:55 Do we want to discard all of the data computed whi
194 UnlinkedUnitBuilder unlinkedUnit = serializeAstUnlinked(unit);
195 assembler.addUnlinkedUnit(source, unlinkedUnit);
196 }
197 }
198
199 /**
200 * Visit the [folder] recursively.
201 */
202 void addDartFiles(Folder folder) {
203 List<Resource> children = folder.getChildren();
204 for (Resource child in children) {
205 if (child is File) {
206 addDartFile(child);
207 }
208 }
209 for (Resource child in children) {
210 if (child is Folder) {
211 addDartFiles(child);
212 }
213 }
214 }
215
216 try {
217 addDartFiles(libFolder);
218 List<int> bytes = assembler.assemble().toBuffer();
219 package.folder
220 .getChildAssumingFile(UNLINKED_BUNDLE_FILE_NAME)
221 .writeAsBytesSync(bytes);
222 } on FileSystemException {
223 // Ignore file system exceptions.
224 }
225 }
226
227 /**
228 * Return the unlinked [PackageBundle] for the given [package]. If the bundle
229 * has not been compute yet, return `null` and schedule its computation.
230 */
231 PackageBundle _getUnlinkedOrSchedule(PubPackage package) {
232 // Try to find in the cache.
233 PackageBundle bundle = unlinkedBundleMap[package];
234 if (bundle != null) {
235 return bundle;
236 }
237 // Try to read from the file system.
238 File unlinkedFile =
239 package.folder.getChildAssumingFile(UNLINKED_BUNDLE_FILE_NAME);
240 if (unlinkedFile.exists) {
241 try {
242 List<int> bytes = unlinkedFile.readAsBytesSync();
243 bundle = new PackageBundle.fromBuffer(bytes);
244 unlinkedBundleMap[package] = bundle;
245 return bundle;
246 } on FileSystemException {
247 // Ignore file system exceptions.
248 }
249 }
250 // Schedule computation in the background.
251 if (package != null && seenPackages.add(package)) {
252 packagesToComputeUnlinked.add(package);
253 if (packagesToComputeUnlinked.length == 1) {
Brian Wilkerson 2016/08/08 14:32:56 Given that `packagesToComputeUnlinked` is a set, I
scheglov 2016/08/09 03:41:07 Fixed.
254 _scheduleNextUnlinked();
255 }
256 }
257 // The bundle is for available.
258 return null;
259 }
260
261 /**
262 * Schedule delayed computation of the next package unlinked bundle from the
263 * set of [packagesToComputeUnlinked]. We delay each computation because we
264 * want operations in analysis server to proceed, and computing bundles of
265 * packages is a background task.
266 */
267 void _scheduleNextUnlinked() {
268 new Future.delayed(new Duration(milliseconds: 10), _computeNextUnlinked);
Brian Wilkerson 2016/08/08 14:32:55 It seems odd to me that we're scheduling work here
269 }
270
271 /**
272 * Return `true` if the given absolute [path] is in the pub cache.
273 */
274 static bool isPathInPubCache(pathos.Context pathContext, String path) {
275 List<String> parts = pathContext.split(path);
276 for (int i = 0; i < parts.length - 1; i++) {
277 if (parts[i] == '.pub-cache') {
278 return true;
279 }
280 if (parts[i] == 'Pub' && parts[i + 1] == 'Cache') {
281 return true;
282 }
283 }
284 return false;
285 }
286 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698