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

Side by Side Diff: packages/analyzer/lib/src/summary/bazel_summary.dart

Issue 2990843002: Removed fixed dependencies (Closed)
Patch Set: Created 3 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:convert';
6
7 import 'package:analyzer/file_system/file_system.dart';
8 import 'package:analyzer/src/generated/engine.dart';
9 import 'package:analyzer/src/generated/source.dart';
10 import 'package:analyzer/src/generated/utilities_collection.dart';
11 import 'package:analyzer/src/summary/api_signature.dart';
12 import 'package:analyzer/src/summary/format.dart';
13 import 'package:analyzer/src/summary/idl.dart';
14 import 'package:analyzer/src/summary/link.dart';
15 import 'package:analyzer/src/summary/package_bundle_reader.dart';
16 import 'package:analyzer/src/summary/summarize_elements.dart';
17 import 'package:analyzer/src/util/fast_uri.dart';
18 import 'package:convert/convert.dart';
19 import 'package:crypto/crypto.dart';
20 import 'package:meta/meta.dart';
21
22 /**
23 * Return the [Folder] where bundles for the given [absoluteUri] should be
24 * looked for. This folder should contain corresponding `*.full.ds` files,
25 * possibly more than one one. Return `null` if the given [absoluteUri]
26 * does not have the expected structure, so the output path cannot be computed.
27 */
28 typedef Folder GetOutputFolder(Uri absoluteUri);
29
30 /**
31 * Load linked packages on demand from [SummaryProvider].
32 */
33 class BazelResultProvider extends ResynthesizerResultProvider {
34 final SummaryDataStore dataStore;
35 final SummaryProvider summaryProvider;
36
37 final Map<Source, bool> sourceToSuccessMap = <Source, bool>{};
38 final Set<Package> addedPackages = new Set<Package>();
39
40 factory BazelResultProvider(SummaryProvider summaryProvider) {
41 SummaryDataStore dataStore = new SummaryDataStore(const <String>[]);
42 return new BazelResultProvider._(dataStore, summaryProvider);
43 }
44
45 BazelResultProvider._(
46 SummaryDataStore dataStore, SummaryProvider summaryProvider)
47 : dataStore = dataStore,
48 summaryProvider = summaryProvider,
49 super(summaryProvider.context, dataStore) {
50 AnalysisContext sdkContext = context.sourceFactory.dartSdk.context;
51 createResynthesizer(sdkContext, sdkContext.typeProvider);
52 }
53
54 @override
55 bool hasResultsForSource(Source source) {
56 return sourceToSuccessMap.putIfAbsent(source, () {
57 List<Package> packages = summaryProvider.getLinkedPackages(source);
58 if (packages == null) {
59 return false;
60 }
61 for (Package package in packages) {
62 if (addedPackages.add(package)) {
63 dataStore.addBundle(null, package.unlinked);
64 dataStore.addBundle(null, package.linked);
65 }
66 }
67 String uriString = source.uri.toString();
68 return resynthesizer.hasLibrarySummary(uriString);
69 });
70 }
71 }
72
73 /**
74 * Information about a Dart package in Bazel.
75 */
76 class Package {
77 final File unlinkedFile;
78 final PackageBundle unlinked;
79 final Set<String> _unitUris = new Set<String>();
80
81 PackageBundle _linked;
82
83 Package(this.unlinkedFile, this.unlinked) {
84 _unitUris.addAll(unlinked.unlinkedUnitUris);
85 }
86
87 PackageBundle get linked => _linked;
88
89 @override
90 String toString() => '$unlinkedFile';
91 }
92
93 /**
94 * Class that reads summaries of Bazel packages.
95 *
96 * When the client needs to produce a resolution result for a new [Source], it
97 * should call [getLinkedPackages] to check whether there is the set of
98 * packages to resynthesize resolution results.
99 */
100 class SummaryProvider {
101 final ResourceProvider provider;
102 final String tempFileName;
103 final GetOutputFolder getOutputFolder;
104 final Folder linkedCacheFolder;
105 final AnalysisContext context;
106 final PackageBundle sdkBundle;
107
108 /**
109 * If `true` (by default), then linking new bundles is allowed.
110 * Otherwise only using existing cached bundles can be used.
111 */
112 final bool allowLinking;
113
114 /**
115 * See [PackageBundleAssembler.currentMajorVersion].
116 */
117 final int majorVersion;
118
119 /**
120 * Mapping from bundle paths to corresponding [Package]s. The packages in
121 * the map were consistent with their constituent sources at the moment when
122 * they were put into the map.
123 */
124 final Map<Folder, List<Package>> folderToPackagesMap = {};
125
126 /**
127 * Mapping from [Uri]s to corresponding [Package]s.
128 */
129 final Map<Uri, Package> uriToPackageMap = {};
130
131 /**
132 * Mapping from [Package]s to corresponding [_LinkNode]s.
133 */
134 final Map<Package, _LinkNode> packageToNodeMap = {};
135
136 SummaryProvider(
137 this.provider,
138 this.tempFileName,
139 this.getOutputFolder,
140 this.linkedCacheFolder,
141 AnalysisContext context,
142 {@visibleForTesting
143 this.allowLinking: true,
144 @visibleForTesting
145 this.majorVersion: PackageBundleAssembler.currentMajorVersion})
146 : context = context,
147 sdkBundle = context.sourceFactory.dartSdk?.getLinkedBundle();
148
149 /**
150 * Return the complete list of [Package]s that are required to provide all
151 * resolution results for the given [source].
152 *
153 * The same list of packages is returned for the same [Source], i.e. always
154 * the full list, not a difference with a previous request. It is up to the
155 * client to decide whether some of the returned packages should be excluded
156 * as already mixed into a resynthesizer.
157 *
158 * If the full set of packages cannot be produced, for example because some
159 * bundles are not built, or out of date, etc, then `null` is returned.
160 */
161 List<Package> getLinkedPackages(Source source) {
162 // Find the node that contains the source.
163 _LinkNode node = _getLinkNodeForUri(source.uri);
164 if (node == null) {
165 return null;
166 }
167
168 // Compute all transitive dependencies.
169 node.computeTransitiveDependencies();
170 List<_LinkNode> nodes = node.transitiveDependencies.toList();
171 nodes.forEach((dependency) => dependency.computeTransitiveDependencies());
172
173 // Fail if any dependency cannot be resolved.
174 if (node.failed) {
175 return null;
176 }
177
178 // Read existing cached linked bundles.
179 for (_LinkNode node in nodes) {
180 _readLinked(node);
181 }
182
183 // Link new packages, if allowed.
184 if (allowLinking) {
185 _link(nodes);
186 }
187
188 // Create successfully linked packages.
189 return nodes
190 .map((node) => node.package)
191 .where((package) => package.linked != null)
192 .toList();
193 }
194
195 /**
196 * Return the [Package] that contains information about the source with
197 * the given [uri], or `null` if such package does not exist.
198 */
199 @visibleForTesting
200 Package getUnlinkedForUri(Uri uri) {
201 return uriToPackageMap.putIfAbsent(uri, () {
202 Folder outputFolder = getOutputFolder(uri);
203 if (outputFolder != null) {
204 String uriStr = uri.toString();
205 List<Package> packages = _getUnlinkedPackages(outputFolder);
206 for (Package package in packages) {
207 if (package._unitUris.contains(uriStr)) {
208 return package;
209 }
210 }
211 }
212 return null;
213 });
214 }
215
216 /**
217 * Return the hexadecimal string of the MD5 hash of the contents of the
218 * given [source] in [context].
219 */
220 String _computeSourceHashHex(Source source) {
221 String text = context.getContents(source).data;
222 List<int> bytes = UTF8.encode(text);
223 List<int> hashBytes = md5.convert(bytes).bytes;
224 return hex.encode(hashBytes);
225 }
226
227 /**
228 * Return the name of the file for a linked bundle, in strong or spec mode.
229 */
230 String _getLinkedName(String hash) {
231 if (context.analysisOptions.strongMode) {
232 return 'linked_$hash.ds';
233 } else {
234 return 'linked_spec_$hash.ds';
235 }
236 }
237
238 /**
239 * Return the node for the given [uri], or `null` if there is no unlinked
240 * bundle that contains [uri].
241 */
242 _LinkNode _getLinkNodeForUri(Uri uri) {
243 Package package = getUnlinkedForUri(uri);
244 return packageToNodeMap.putIfAbsent(package, () {
245 if (package == null) {
246 return null;
247 }
248 return new _LinkNode(this, package);
249 });
250 }
251
252 /**
253 * Return all consistent unlinked [Package]s in the given [folder]. Some of
254 * the returned packages might be already linked.
255 */
256 List<Package> _getUnlinkedPackages(Folder folder) {
257 List<Package> packages = folderToPackagesMap[folder];
258 if (packages == null) {
259 packages = <Package>[];
260 try {
261 List<Resource> children = folder.getChildren();
262 for (Resource child in children) {
263 if (child is File) {
264 String packagePath = child.path;
265 if (packagePath.toLowerCase().endsWith('.full.ds')) {
266 Package package = _readUnlinkedPackage(child);
267 if (package != null) {
268 packages.add(package);
269 }
270 }
271 }
272 }
273 } on FileSystemException {}
274 folderToPackagesMap[folder] = packages;
275 }
276 return packages;
277 }
278
279 /**
280 * Return `true` if the unlinked information of the [bundle] is consistent
281 * with its constituent sources in [context].
282 */
283 bool _isUnlinkedBundleConsistent(PackageBundle bundle) {
284 try {
285 // Compute hashes of the constituent sources.
286 List<String> actualHashes = <String>[];
287 for (String uri in bundle.unlinkedUnitUris) {
288 Source source = context.sourceFactory.resolveUri(null, uri);
289 if (source == null) {
290 return false;
291 }
292 String hash = _computeSourceHashHex(source);
293 actualHashes.add(hash);
294 }
295 // Compare sorted actual and bundle unit hashes.
296 List<String> bundleHashes = bundle.unlinkedUnitHashes.toList()..sort();
297 actualHashes.sort();
298 return listsEqual(actualHashes, bundleHashes);
299 } on FileSystemException {}
300 return false;
301 }
302
303 /**
304 * Link the given [nodes].
305 */
306 void _link(List<_LinkNode> nodes) {
307 // Fill the store with bundles.
308 // Append the linked SDK bundle.
309 // Append unlinked and (if read from a cache) linked package bundles.
310 SummaryDataStore store = new SummaryDataStore(const <String>[]);
311 store.addBundle(null, sdkBundle);
312 for (_LinkNode node in nodes) {
313 store.addBundle(null, node.package.unlinked);
314 if (node.package.linked != null) {
315 store.addBundle(null, node.package.linked);
316 }
317 }
318
319 // Prepare URIs to link.
320 Map<String, _LinkNode> uriToNode = <String, _LinkNode>{};
321 for (_LinkNode node in nodes) {
322 if (!node.isReady) {
323 for (String uri in node.package.unlinked.unlinkedUnitUris) {
324 uriToNode[uri] = node;
325 }
326 }
327 }
328 Set<String> libraryUris = uriToNode.keys.toSet();
329
330 // Perform linking.
331 Map<String, LinkedLibraryBuilder> linkedLibraries =
332 link(libraryUris, (String uri) {
333 return store.linkedMap[uri];
334 }, (String uri) {
335 return store.unlinkedMap[uri];
336 }, context.declaredVariables.get, context.analysisOptions.strongMode);
337
338 // Assemble newly linked bundles.
339 for (_LinkNode node in nodes) {
340 if (!node.isReady) {
341 PackageBundleAssembler assembler = new PackageBundleAssembler();
342 linkedLibraries.forEach((uri, linkedLibrary) {
343 if (identical(uriToNode[uri], node)) {
344 assembler.addLinkedLibrary(uri, linkedLibrary);
345 }
346 });
347 List<int> bytes = assembler.assemble().toBuffer();
348 node.package._linked = new PackageBundle.fromBuffer(bytes);
349 _writeLinked(node, bytes);
350 }
351 }
352 }
353
354 /**
355 * Attempt to read the linked bundle that corresponds to the given [node]
356 * with all its transitive dependencies.
357 */
358 void _readLinked(_LinkNode node) {
359 String hash = node.linkedHash;
360 if (!node.isReady && hash != null) {
361 String fileName = _getLinkedName(hash);
362 File file = linkedCacheFolder.getChildAssumingFile(fileName);
363 // Try to read from the file system.
364 if (file.exists) {
365 try {
366 List<int> bytes = file.readAsBytesSync();
367 node.package._linked = new PackageBundle.fromBuffer(bytes);
368 } on FileSystemException {
369 // Ignore file system exceptions.
370 }
371 }
372 }
373 }
374
375 /**
376 * Read the unlinked [Package] from the given [file], or return `null` if the
377 * file does not exist, or it cannot be read, or is not consistent with the
378 * constituent sources on the file system.
379 */
380 Package _readUnlinkedPackage(File file) {
381 try {
382 List<int> bytes = file.readAsBytesSync();
383 PackageBundle bundle = new PackageBundle.fromBuffer(bytes);
384 // Check the major version.
385 if (bundle.majorVersion != majorVersion) {
386 return null;
387 }
388 // Check for consistency, and fail if it's not.
389 if (!_isUnlinkedBundleConsistent(bundle)) {
390 return null;
391 }
392 // OK, use the bundle.
393 return new Package(file, bundle);
394 } on FileSystemException {}
395 return null;
396 }
397
398 /**
399 * Atomically write the given [bytes] into the file in the [folder].
400 */
401 void _writeAtomic(Folder folder, String fileName, List<int> bytes) {
402 String filePath = folder.getChildAssumingFile(fileName).path;
403 File tempFile = folder.getChildAssumingFile(tempFileName);
404 tempFile.writeAsBytesSync(bytes);
405 tempFile.renameSync(filePath);
406 }
407
408 /**
409 * If a new linked bundle was linked for the given [node], write the bundle
410 * into the memory cache and the file system.
411 */
412 void _writeLinked(_LinkNode node, List<int> bytes) {
413 String hash = node.linkedHash;
414 if (hash != null) {
415 String fileName = _getLinkedName(hash);
416 _writeAtomic(linkedCacheFolder, fileName, bytes);
417 }
418 }
419 }
420
421 /**
422 * Information about a single [Package].
423 */
424 class _LinkNode {
425 final SummaryProvider linker;
426 final Package package;
427
428 bool failed = false;
429 Set<_LinkNode> transitiveDependencies;
430
431 List<_LinkNode> _dependencies;
432 String _linkedHash;
433
434 _LinkNode(this.linker, this.package);
435
436 /**
437 * Retrieve the dependencies of this node.
438 */
439 List<_LinkNode> get dependencies {
440 if (_dependencies == null) {
441 Set<_LinkNode> dependencies = new Set<_LinkNode>();
442
443 void appendDependency(String uriStr) {
444 Uri uri = FastUri.parse(uriStr);
445 if (!uri.hasScheme) {
446 // A relative path in this package, skip it.
447 } else if (uri.scheme == 'dart') {
448 // Dependency on the SDK is implicit and always added.
449 // The SDK linked bundle is precomputed before linking packages.
450 } else if (uri.scheme == 'package') {
451 _LinkNode packageNode = linker._getLinkNodeForUri(uri);
452 if (packageNode == null) {
453 failed = true;
454 }
455 if (packageNode != null) {
456 dependencies.add(packageNode);
457 }
458 } else {
459 failed = true;
460 }
461 }
462
463 for (UnlinkedUnit unit in package.unlinked.unlinkedUnits) {
464 for (UnlinkedImport import in unit.imports) {
465 if (!import.isImplicit) {
466 appendDependency(import.uri);
467 }
468 }
469 for (UnlinkedExportPublic export in unit.publicNamespace.exports) {
470 appendDependency(export.uri);
471 }
472 }
473
474 _dependencies = dependencies.toList();
475 }
476 return _dependencies;
477 }
478
479 /**
480 * Return `true` is the node is ready - has the linked bundle or failed (does
481 * not have all required dependencies).
482 */
483 bool get isReady => package.linked != null || failed;
484
485 /**
486 * Return the hash string that corresponds to this linked bundle in the
487 * context of its SDK bundle and transitive dependencies. Return `null` if
488 * the hash computation fails, because for example the full transitive
489 * dependencies cannot computed.
490 */
491 String get linkedHash {
492 if (_linkedHash == null && transitiveDependencies != null && !failed) {
493 ApiSignature signature = new ApiSignature();
494 // Add all unlinked API signatures.
495 List<String> signatures = <String>[];
496 signatures.add(linker.sdkBundle.apiSignature);
497 transitiveDependencies
498 .map((node) => node.package.unlinked.apiSignature)
499 .forEach(signatures.add);
500 signatures.sort();
501 signatures.forEach(signature.addString);
502 // Combine into a single hash.
503 appendDeclaredVariables(signature);
504 _linkedHash = signature.toHex();
505 }
506 return _linkedHash;
507 }
508
509 /**
510 * Append names and values of all referenced declared variables (even the
511 * ones without actually declared values) to the given [signature].
512 */
513 void appendDeclaredVariables(ApiSignature signature) {
514 Set<String> nameSet = new Set<String>();
515 for (_LinkNode node in transitiveDependencies) {
516 for (UnlinkedUnit unit in node.package.unlinked.unlinkedUnits) {
517 for (UnlinkedImport import in unit.imports) {
518 for (UnlinkedConfiguration configuration in import.configurations) {
519 nameSet.add(configuration.name);
520 }
521 }
522 for (UnlinkedExportPublic export in unit.publicNamespace.exports) {
523 for (UnlinkedConfiguration configuration in export.configurations) {
524 nameSet.add(configuration.name);
525 }
526 }
527 }
528 }
529 List<String> sortedNameList = nameSet.toList()..sort();
530 signature.addInt(sortedNameList.length);
531 for (String name in sortedNameList) {
532 signature.addString(name);
533 signature.addString(linker.context.declaredVariables.get(name) ?? '');
534 }
535 }
536
537 /**
538 * Compute the set of existing transitive dependencies for this node.
539 * If any dependency cannot be resolved, then set [failed] to `true`.
540 * Only unlinked bundle is used, so this method can be called before linking.
541 */
542 void computeTransitiveDependencies() {
543 if (transitiveDependencies == null) {
544 transitiveDependencies = new Set<_LinkNode>();
545
546 void appendDependencies(_LinkNode node) {
547 if (transitiveDependencies.add(node)) {
548 node.dependencies.forEach(appendDependencies);
549 }
550 }
551
552 appendDependencies(this);
553 if (transitiveDependencies.any((node) => node.failed)) {
554 failed = true;
555 }
556 }
557 }
558
559 @override
560 String toString() => package.toString();
561 }
OLDNEW
« no previous file with comments | « packages/analyzer/lib/src/summary/base.dart ('k') | packages/analyzer/lib/src/summary/flat_buffers.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698