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

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

Issue 2227393002: Add support for package cycles linking, more tests. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: 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
« no previous file with comments | « no previous file | pkg/analyzer/test/src/summary/pub_summary_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file 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 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 import 'dart:async'; 5 import 'dart:async';
6 import 'dart:collection'; 6 import 'dart:collection';
7 import 'dart:core' hide Resource; 7 import 'dart:core' hide Resource;
8 8
9 import 'package:analyzer/dart/ast/ast.dart'; 9 import 'package:analyzer/dart/ast/ast.dart';
10 import 'package:analyzer/dart/ast/token.dart'; 10 import 'package:analyzer/dart/ast/token.dart';
(...skipping 10 matching lines...) Expand all
21 import 'package:analyzer/src/summary/package_bundle_reader.dart' 21 import 'package:analyzer/src/summary/package_bundle_reader.dart'
22 show ResynthesizerResultProvider, SummaryDataStore; 22 show ResynthesizerResultProvider, SummaryDataStore;
23 import 'package:analyzer/src/summary/summarize_ast.dart' 23 import 'package:analyzer/src/summary/summarize_ast.dart'
24 show serializeAstUnlinked; 24 show serializeAstUnlinked;
25 import 'package:analyzer/src/summary/summarize_elements.dart' 25 import 'package:analyzer/src/summary/summarize_elements.dart'
26 show PackageBundleAssembler; 26 show PackageBundleAssembler;
27 import 'package:analyzer/src/util/fast_uri.dart'; 27 import 'package:analyzer/src/util/fast_uri.dart';
28 import 'package:path/path.dart' as pathos; 28 import 'package:path/path.dart' as pathos;
29 29
30 /** 30 /**
31 * Unlinked and linked information about a [PubPackage].
32 */
33 class LinkedPubPackage {
34 final PubPackage package;
35 final PackageBundle unlinked;
36 final PackageBundle linked;
37
38 LinkedPubPackage(this.package, this.unlinked, this.linked);
39
40 @override
41 String toString() => package.toString();
42 }
43
44 /**
31 * A package in the pub cache. 45 * A package in the pub cache.
32 */ 46 */
33 class PubPackage { 47 class PubPackage {
34 final String name; 48 final String name;
35 final Folder libFolder; 49 final Folder libFolder;
36 50
37 PubPackage(this.name, this.libFolder); 51 PubPackage(this.name, this.libFolder);
38 52
39 Folder get folder => libFolder.parent; 53 Folder get folder => libFolder.parent;
40 54
41 @override 55 @override
42 int get hashCode => libFolder.hashCode; 56 int get hashCode => libFolder.hashCode;
43 57
44 @override 58 @override
45 bool operator ==(other) { 59 bool operator ==(other) {
46 return other is PubPackage && other.libFolder == libFolder; 60 return other is PubPackage && other.libFolder == libFolder;
47 } 61 }
48 62
49 @override 63 @override
50 String toString() => '($name in $folder)'; 64 String toString() => '($name in $folder)';
51 } 65 }
52 66
53 /** 67 /**
54 * Unlinked and linked information about a [PubPackage].
55 */
56 class LinkedPubPackage {
57 final PubPackage package;
58 final PackageBundle unlinked;
59 final PackageBundle linked;
60 LinkedPubPackage(this.package, this.unlinked, this.linked);
61 }
62
63 /**
64 * Class that manages summaries for pub packages. 68 * Class that manages summaries for pub packages.
65 * 69 *
66 * The client should call [getLinkedBundles] after creating a new 70 * The client should call [getLinkedBundles] after creating a new
67 * [AnalysisContext] and configuring its source factory, but before computing 71 * [AnalysisContext] and configuring its source factory, but before computing
68 * any analysis results. The returned linked bundles can be used to create and 72 * any analysis results. The returned linked bundles can be used to create and
69 * configure [ResynthesizerResultProvider] for the context. 73 * configure [ResynthesizerResultProvider] for the context.
70 */ 74 */
71 class PubSummaryManager { 75 class PubSummaryManager {
72 static const UNLINKED_BUNDLE_FILE_NAME = 'unlinked.ds'; 76 static const UNLINKED_BUNDLE_FILE_NAME = 'unlinked.ds';
73 77
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
133 Map<PubPackage, PackageBundle> unlinkedBundles = 137 Map<PubPackage, PackageBundle> unlinkedBundles =
134 getUnlinkedBundles(context); 138 getUnlinkedBundles(context);
135 139
136 // If no unlinked bundles, there is nothing we can try to link. 140 // If no unlinked bundles, there is nothing we can try to link.
137 if (unlinkedBundles.isEmpty) { 141 if (unlinkedBundles.isEmpty) {
138 return <LinkedPubPackage>[]; 142 return <LinkedPubPackage>[];
139 } 143 }
140 144
141 // Create graph nodes for packages. 145 // Create graph nodes for packages.
142 List<_LinkedNode> nodes = <_LinkedNode>[]; 146 List<_LinkedNode> nodes = <_LinkedNode>[];
143 Map<String, _LinkedNode> uriToNode = <String, _LinkedNode>{}; 147 Map<String, _LinkedNode> packageToNode = <String, _LinkedNode>{};
144 unlinkedBundles.forEach((package, unlinked) { 148 unlinkedBundles.forEach((package, unlinked) {
145 _LinkedNode node = new _LinkedNode(package, unlinked, uriToNode); 149 _LinkedNode node = new _LinkedNode(package, unlinked, packageToNode);
146 nodes.add(node); 150 nodes.add(node);
147 for (String uri in unlinked.unlinkedUnitUris) { 151 packageToNode[package.name] = node;
148 uriToNode[uri] = node;
149 }
150 }); 152 });
151 153
152 // Fill the store with unlinked bundles. 154 // Fill the store with unlinked bundles.
153 SummaryDataStore store = new SummaryDataStore(const <String>[]); 155 SummaryDataStore store = new SummaryDataStore(const <String>[]);
154 store.addBundle(null, sdkBundle); 156 store.addBundle(null, sdkBundle);
155 for (PackageBundle unlinked in unlinkedBundles.values) { 157 for (PackageBundle unlinked in unlinkedBundles.values) {
156 store.addBundle(null, unlinked); 158 store.addBundle(null, unlinked);
157 } 159 }
158 160
159 // Link each package node. 161 // Link each package node.
160 for (_LinkedNode node in nodes) { 162 for (_LinkedNode node in nodes) {
161 if (!node.isEvaluated) { 163 if (!node.isEvaluated) {
162 new _LinkedWalker(store).walk(node); 164 new _LinkedWalker(store).walk(node);
163 } 165 }
164 } 166 }
165 167
166 // Create successfully linked packages. 168 // Create successfully linked packages.
167 List<LinkedPubPackage> linkedPackages = <LinkedPubPackage>[]; 169 List<LinkedPubPackage> linkedPackages = <LinkedPubPackage>[];
168 for (_LinkedNode node in nodes) { 170 for (_LinkedNode node in nodes) {
169 if (node.linkedBuilder != null) { 171 if (node.linkedBuilder != null) {
170 List<int> bytes = node.linkedBuilder.toBuffer(); 172 List<int> bytes = node.linkedBuilder.toBuffer();
171 PackageBundle linkedBundle = new PackageBundle.fromBuffer(bytes); 173 PackageBundle linkedBundle = new PackageBundle.fromBuffer(bytes);
172 linkedPackages.add( 174 linkedPackages.add(
173 new LinkedPubPackage(node.package, node.unlinked, linkedBundle)); 175 new LinkedPubPackage(node.package, node.unlinked, linkedBundle));
174 } 176 }
175 } 177 }
176 178
177 // TODO(scheglov) compute dependency hashes and write linked bundles. 179 // TODO(scheglov) compute dependency hashes and write linked bundles.
180 // TODO(scheglov) don't forget to include the SDK API signature.
178 181
179 // Done. 182 // Done.
180 return linkedPackages; 183 return linkedPackages;
181 } 184 }
182 185
183 /** 186 /**
184 * Return all available unlinked [PackageBundle]s for the given [context], 187 * Return all available unlinked [PackageBundle]s for the given [context],
185 * maybe an empty map, but not `null`. 188 * maybe an empty map, but not `null`.
186 */ 189 */
187 Map<PubPackage, PackageBundle> getUnlinkedBundles(AnalysisContext context) { 190 Map<PubPackage, PackageBundle> getUnlinkedBundles(AnalysisContext context) {
(...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after
349 * Atomically write the given [bytes] into the file in the [folder]. 352 * Atomically write the given [bytes] into the file in the [folder].
350 */ 353 */
351 void _writeAtomic(Folder folder, String fileName, List<int> bytes) { 354 void _writeAtomic(Folder folder, String fileName, List<int> bytes) {
352 String filePath = folder.getChildAssumingFile(fileName).path; 355 String filePath = folder.getChildAssumingFile(fileName).path;
353 File tempFile = folder.getChildAssumingFile(tempFileName); 356 File tempFile = folder.getChildAssumingFile(tempFileName);
354 tempFile.writeAsBytesSync(bytes); 357 tempFile.writeAsBytesSync(bytes);
355 tempFile.renameSync(filePath); 358 tempFile.renameSync(filePath);
356 } 359 }
357 360
358 /** 361 /**
362 * If the given [uri] has the `package` scheme, return the names of the
Paul Berry 2016/08/10 11:58:28 s/names/name/
scheglov 2016/08/10 16:08:18 Done.
363 * package that contains the referenced resource. Otherwise return `null`.
364 *
365 * For example `package:foo/bar.dart` => `foo`.
366 */
367 static String getPackageName(String uri) {
368 const String PACKAGE_SCHEME = 'package:';
369 if (uri.startsWith(PACKAGE_SCHEME)) {
370 int index = uri.indexOf('/');
371 if (index != -1) {
372 return uri.substring(PACKAGE_SCHEME.length, index);
373 }
374 }
375 return null;
376 }
377
378 /**
359 * Return `true` if the given absolute [path] is in the pub cache. 379 * Return `true` if the given absolute [path] is in the pub cache.
360 */ 380 */
361 static bool isPathInPubCache(pathos.Context pathContext, String path) { 381 static bool isPathInPubCache(pathos.Context pathContext, String path) {
362 List<String> parts = pathContext.split(path); 382 List<String> parts = pathContext.split(path);
363 for (int i = 0; i < parts.length - 1; i++) { 383 for (int i = 0; i < parts.length - 1; i++) {
364 if (parts[i] == '.pub-cache') { 384 if (parts[i] == '.pub-cache') {
365 return true; 385 return true;
366 } 386 }
367 if (parts[i] == 'Pub' && parts[i + 1] == 'Cache') { 387 if (parts[i] == 'Pub' && parts[i + 1] == 'Cache') {
368 return true; 388 return true;
369 } 389 }
370 } 390 }
371 return false; 391 return false;
372 } 392 }
373 } 393 }
374 394
375 /** 395 /**
376 * Specialization of [Node] for linking packages in proper dependency order. 396 * Specialization of [Node] for linking packages in proper dependency order.
377 */ 397 */
378 class _LinkedNode extends Node<_LinkedNode> { 398 class _LinkedNode extends Node<_LinkedNode> {
379 final PubPackage package; 399 final PubPackage package;
380 final PackageBundle unlinked; 400 final PackageBundle unlinked;
381 final Map<String, _LinkedNode> uriToNode; 401 final Map<String, _LinkedNode> packageToNode;
382 402
383 PackageBundleBuilder linkedBuilder; 403 PackageBundleBuilder linkedBuilder;
384 bool failed = false; 404 bool failed = false;
385 405
386 _LinkedNode(this.package, this.unlinked, this.uriToNode); 406 _LinkedNode(this.package, this.unlinked, this.packageToNode);
387 407
388 @override 408 @override
389 bool get isEvaluated => linkedBuilder != null || failed; 409 bool get isEvaluated => linkedBuilder != null || failed;
390 410
391 @override 411 @override
392 List<_LinkedNode> computeDependencies() { 412 List<_LinkedNode> computeDependencies() {
393 Set<String> referencedUris = new Set<String>(); 413 Set<_LinkedNode> dependencies = new Set<_LinkedNode>();
394 for (UnlinkedUnit unit in unlinked.unlinkedUnits) { 414 for (UnlinkedUnit unit in unlinked.unlinkedUnits) {
395 for (UnlinkedImport import in unit.imports) { 415 for (UnlinkedImport import in unit.imports) {
396 String uri = import.isImplicit ? 'dart:core' : import.uri; 416 String uriStr = import.isImplicit ? 'dart:core' : import.uri;
397 if (uri.startsWith('dart:')) { 417 Uri uri = FastUri.parse(uriStr);
398 // Ignore SDK imports. 418 if (!uri.hasScheme) {
399 } else if (uri.startsWith('package:')) { 419 // A relative path in this package, skip it.
400 referencedUris.add(uri); 420 } else if (uri.scheme == 'dart') {
421 // SDK is always available.
Paul Berry 2016/08/10 11:58:27 It looks like you're not going with my suggestion
scheglov 2016/08/10 16:08:18 getLinkedBundles() accepts "PackageBundle sdkBundl
Paul Berry 2016/08/10 21:39:37 Ok, based on our discussions, I'm happy landing th
422 // It's API signature is always mixed in.
Paul Berry 2016/08/10 11:58:27 s/It's/Its/
scheglov 2016/08/10 16:08:18 Done.
423 } else if (uriStr.startsWith('package:')) {
424 String package = PubSummaryManager.getPackageName(uriStr);
425 _LinkedNode packageNode = packageToNode[package];
426 if (packageNode == null) {
427 failed = true;
428 return const <_LinkedNode>[];
429 }
430 dependencies.add(packageNode);
401 } else { 431 } else {
402 failed = true; 432 failed = true;
403 return const <_LinkedNode>[]; 433 return const <_LinkedNode>[];
404 } 434 }
405 } 435 }
406 } 436 }
407 // TODO(scheglov) fail if no corresponding node 437 return dependencies.toList();
408 return referencedUris.map((uri) => uriToNode[uri]).toSet().toList();
409 } 438 }
410 439
411 @override 440 @override
412 String toString() => package.toString(); 441 String toString() => package.toString();
413 } 442 }
414 443
415 /** 444 /**
416 * Specialization of [DependencyWalker] for linking packages. 445 * Specialization of [DependencyWalker] for linking packages.
417 */ 446 */
418 class _LinkedWalker extends DependencyWalker<_LinkedNode> { 447 class _LinkedWalker extends DependencyWalker<_LinkedNode> {
419 final SummaryDataStore store; 448 final SummaryDataStore store;
420 449
421 _LinkedWalker(this.store); 450 _LinkedWalker(this.store);
422 451
423 @override 452 @override
424 void evaluate(_LinkedNode v) { 453 void evaluate(_LinkedNode node) {
Paul Berry 2016/08/10 11:58:28 There's a lot of commonality between evaluate() an
scheglov 2016/08/10 16:08:18 Done.
425 Set<String> libraryUris = v.unlinked.unlinkedUnitUris.toSet(); 454 Set<String> libraryUris = node.unlinked.unlinkedUnitUris.toSet();
426 Map<String, LinkedLibraryBuilder> map = link(libraryUris, (String absUri) { 455 Map<String, LinkedLibraryBuilder> linkedLibraries =
427 LinkedLibrary dependencyLibrary = store.linkedMap[absUri]; 456 link(libraryUris, (String absoluteUri) {
457 LinkedLibrary dependencyLibrary = store.linkedMap[absoluteUri];
428 if (dependencyLibrary == null) { 458 if (dependencyLibrary == null) {
429 // TODO(scheglov) add test 459 node.failed = true;
430 v.failed = true;
431 } 460 }
432 return dependencyLibrary; 461 return dependencyLibrary;
433 }, (String absUri) { 462 }, (String absoluteUri) {
434 UnlinkedUnit unlinkedUnit = store.unlinkedMap[absUri]; 463 UnlinkedUnit unlinkedUnit = store.unlinkedMap[absoluteUri];
435 if (unlinkedUnit == null) { 464 if (unlinkedUnit == null) {
436 // TODO(scheglov) add test 465 node.failed = true;
437 v.failed = true;
438 } 466 }
439 return unlinkedUnit; 467 return unlinkedUnit;
440 }, false); 468 }, false);
441 if (!v.failed) { 469 // Assemble the linked bundle and put it into the store.
470 if (!node.failed) {
442 PackageBundleAssembler assembler = new PackageBundleAssembler(); 471 PackageBundleAssembler assembler = new PackageBundleAssembler();
443 map.forEach((uri, linkedLibrary) { 472 linkedLibraries.forEach((uri, linkedLibrary) {
444 assembler.addLinkedLibrary(uri, linkedLibrary); 473 assembler.addLinkedLibrary(uri, linkedLibrary);
445 }); 474 });
446 v.linkedBuilder = assembler.assemble(); 475 node.linkedBuilder = assembler.assemble();
447 store.addBundle(null, v.linkedBuilder); 476 store.addBundle(null, node.linkedBuilder);
448 } 477 }
449 } 478 }
450 479
451 @override 480 @override
452 void evaluateScc(List<_LinkedNode> scc) { 481 void evaluateScc(List<_LinkedNode> scc) {
453 print('evaluateScc: $scc'); 482 Map<String, _LinkedNode> uriToNode = <String, _LinkedNode>{};
454 // TODO(scheglov): implement evaluateScc 483 for (_LinkedNode node in scc) {
484 for (String uri in node.unlinked.unlinkedUnitUris) {
485 uriToNode[uri] = node;
486 }
487 }
488 Set<String> libraryUris = uriToNode.keys.toSet();
489 // Perform linking.
490 bool failed = false;
491 Map<String, LinkedLibraryBuilder> linkedLibraries =
492 link(libraryUris, (String absoluteUri) {
493 LinkedLibrary dependencyLibrary = store.linkedMap[absoluteUri];
494 if (dependencyLibrary == null) {
495 failed = true;
496 }
497 return dependencyLibrary;
498 }, (String absoluteUri) {
499 UnlinkedUnit unlinkedUnit = store.unlinkedMap[absoluteUri];
500 if (unlinkedUnit == null) {
501 failed = true;
502 }
503 return unlinkedUnit;
504 }, false);
505 // Assemble linked bundles and put them into the store.
506 if (!failed) {
507 for (_LinkedNode node in scc) {
508 PackageBundleAssembler assembler = new PackageBundleAssembler();
509 linkedLibraries.forEach((uri, linkedLibrary) {
510 if (identical(uriToNode[uri], node)) {
511 assembler.addLinkedLibrary(uri, linkedLibrary);
512 }
513 });
514 node.linkedBuilder = assembler.assemble();
515 store.addBundle(null, node.linkedBuilder);
516 }
517 } else {
518 scc.forEach((node) => node.failed = true);
519 }
455 } 520 }
456 } 521 }
OLDNEW
« no previous file with comments | « no previous file | pkg/analyzer/test/src/summary/pub_summary_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698