Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 } |
| OLD | NEW |