| 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 16 matching lines...) Expand all Loading... |
| 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]. | 31 * Unlinked and linked information about a [PubPackage]. |
| 32 */ | 32 */ |
| 33 class LinkedPubPackage { | 33 class LinkedPubPackage { |
| 34 final PubPackage package; | 34 final PubPackage package; |
| 35 final PackageBundle unlinked; | 35 final PackageBundle unlinked; |
| 36 final PackageBundle linked; | 36 final PackageBundle linked; |
| 37 |
| 37 LinkedPubPackage(this.package, this.unlinked, this.linked); | 38 LinkedPubPackage(this.package, this.unlinked, this.linked); |
| 39 |
| 40 @override |
| 41 String toString() => package.toString(); |
| 38 } | 42 } |
| 39 | 43 |
| 40 /** | 44 /** |
| 41 * A package in the pub cache. | 45 * A package in the pub cache. |
| 42 */ | 46 */ |
| 43 class PubPackage { | 47 class PubPackage { |
| 44 final String name; | 48 final String name; |
| 45 final Folder libFolder; | 49 final Folder libFolder; |
| 46 | 50 |
| 47 PubPackage(this.name, this.libFolder); | 51 PubPackage(this.name, this.libFolder); |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 134 Map<PubPackage, PackageBundle> unlinkedBundles = | 138 Map<PubPackage, PackageBundle> unlinkedBundles = |
| 135 getUnlinkedBundles(context); | 139 getUnlinkedBundles(context); |
| 136 | 140 |
| 137 // If no unlinked bundles, there is nothing we can try to link. | 141 // If no unlinked bundles, there is nothing we can try to link. |
| 138 if (unlinkedBundles.isEmpty) { | 142 if (unlinkedBundles.isEmpty) { |
| 139 return <LinkedPubPackage>[]; | 143 return <LinkedPubPackage>[]; |
| 140 } | 144 } |
| 141 | 145 |
| 142 // Create graph nodes for packages. | 146 // Create graph nodes for packages. |
| 143 List<_LinkedNode> nodes = <_LinkedNode>[]; | 147 List<_LinkedNode> nodes = <_LinkedNode>[]; |
| 144 Map<String, _LinkedNode> uriToNode = <String, _LinkedNode>{}; | 148 Map<String, _LinkedNode> packageToNode = <String, _LinkedNode>{}; |
| 145 unlinkedBundles.forEach((package, unlinked) { | 149 unlinkedBundles.forEach((package, unlinked) { |
| 146 _LinkedNode node = new _LinkedNode(package, unlinked, uriToNode); | 150 _LinkedNode node = new _LinkedNode(package, unlinked, packageToNode); |
| 147 nodes.add(node); | 151 nodes.add(node); |
| 148 for (String uri in unlinked.unlinkedUnitUris) { | 152 packageToNode[package.name] = node; |
| 149 uriToNode[uri] = node; | |
| 150 } | |
| 151 }); | 153 }); |
| 152 | 154 |
| 153 // Fill the store with unlinked bundles. | 155 // Fill the store with unlinked bundles. |
| 154 SummaryDataStore store = new SummaryDataStore(const <String>[]); | 156 SummaryDataStore store = new SummaryDataStore(const <String>[]); |
| 155 store.addBundle(null, sdkBundle); | 157 store.addBundle(null, sdkBundle); |
| 156 for (PackageBundle unlinked in unlinkedBundles.values) { | 158 for (PackageBundle unlinked in unlinkedBundles.values) { |
| 157 store.addBundle(null, unlinked); | 159 store.addBundle(null, unlinked); |
| 158 } | 160 } |
| 159 | 161 |
| 160 // Link each package node. | 162 // Link each package node. |
| 161 for (_LinkedNode node in nodes) { | 163 for (_LinkedNode node in nodes) { |
| 162 if (!node.isEvaluated) { | 164 if (!node.isEvaluated) { |
| 163 bool strong = context.analysisOptions.strongMode; | 165 bool strong = context.analysisOptions.strongMode; |
| 164 new _LinkedWalker(store, strong).walk(node); | 166 new _LinkedWalker(store, strong).walk(node); |
| 165 } | 167 } |
| 166 } | 168 } |
| 167 | 169 |
| 168 // Create successfully linked packages. | 170 // Create successfully linked packages. |
| 169 List<LinkedPubPackage> linkedPackages = <LinkedPubPackage>[]; | 171 List<LinkedPubPackage> linkedPackages = <LinkedPubPackage>[]; |
| 170 for (_LinkedNode node in nodes) { | 172 for (_LinkedNode node in nodes) { |
| 171 if (node.linkedBuilder != null) { | 173 if (node.linkedBuilder != null) { |
| 172 List<int> bytes = node.linkedBuilder.toBuffer(); | 174 List<int> bytes = node.linkedBuilder.toBuffer(); |
| 173 PackageBundle linkedBundle = new PackageBundle.fromBuffer(bytes); | 175 PackageBundle linkedBundle = new PackageBundle.fromBuffer(bytes); |
| 174 linkedPackages.add( | 176 linkedPackages.add( |
| 175 new LinkedPubPackage(node.package, node.unlinked, linkedBundle)); | 177 new LinkedPubPackage(node.package, node.unlinked, linkedBundle)); |
| 176 } | 178 } |
| 177 } | 179 } |
| 178 | 180 |
| 179 // TODO(scheglov) compute dependency hashes and write linked bundles. | 181 // TODO(scheglov) compute dependency hashes and write linked bundles. |
| 182 // TODO(scheglov) don't forget to include the SDK API signature. |
| 180 | 183 |
| 181 // Done. | 184 // Done. |
| 182 return linkedPackages; | 185 return linkedPackages; |
| 183 } | 186 } |
| 184 | 187 |
| 185 /** | 188 /** |
| 186 * Return all available unlinked [PackageBundle]s for the given [context], | 189 * Return all available unlinked [PackageBundle]s for the given [context], |
| 187 * maybe an empty map, but not `null`. | 190 * maybe an empty map, but not `null`. |
| 188 */ | 191 */ |
| 189 Map<PubPackage, PackageBundle> getUnlinkedBundles(AnalysisContext context) { | 192 Map<PubPackage, PackageBundle> getUnlinkedBundles(AnalysisContext context) { |
| 190 bool strong = context.analysisOptions.strongMode; | 193 bool strong = context.analysisOptions.strongMode; |
| 191 Map<PubPackage, PackageBundle> unlinkedBundles = | 194 Map<PubPackage, PackageBundle> unlinkedBundles = |
| 192 new HashMap<PubPackage, PackageBundle>(); | 195 new HashMap<PubPackage, PackageBundle>(); |
| 196 // TODO(scheglov) get _sdkext bundles. |
| 193 Map<String, List<Folder>> packageMap = context.sourceFactory.packageMap; | 197 Map<String, List<Folder>> packageMap = context.sourceFactory.packageMap; |
| 194 if (packageMap != null) { | 198 if (packageMap != null) { |
| 195 packageMap.forEach((String packageName, List<Folder> libFolders) { | 199 packageMap.forEach((String packageName, List<Folder> libFolders) { |
| 196 if (libFolders.length == 1) { | 200 if (libFolders.length == 1) { |
| 197 Folder libFolder = libFolders.first; | 201 Folder libFolder = libFolders.first; |
| 202 // TODO(scheglov) handle Flutter packages, outside of the pub cache. |
| 198 if (isPathInPubCache(pathContext, libFolder.path)) { | 203 if (isPathInPubCache(pathContext, libFolder.path)) { |
| 199 PubPackage package = new PubPackage(packageName, libFolder); | 204 PubPackage package = new PubPackage(packageName, libFolder); |
| 200 PackageBundle unlinkedBundle = | 205 PackageBundle unlinkedBundle = |
| 201 _getUnlinkedOrSchedule(package, strong); | 206 _getUnlinkedOrSchedule(package, strong); |
| 202 if (unlinkedBundle != null) { | 207 if (unlinkedBundle != null) { |
| 203 unlinkedBundles[package] = unlinkedBundle; | 208 unlinkedBundles[package] = unlinkedBundle; |
| 204 } | 209 } |
| 205 } | 210 } |
| 206 } | 211 } |
| 207 }); | 212 }); |
| (...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 368 * Atomically write the given [bytes] into the file in the [folder]. | 373 * Atomically write the given [bytes] into the file in the [folder]. |
| 369 */ | 374 */ |
| 370 void _writeAtomic(Folder folder, String fileName, List<int> bytes) { | 375 void _writeAtomic(Folder folder, String fileName, List<int> bytes) { |
| 371 String filePath = folder.getChildAssumingFile(fileName).path; | 376 String filePath = folder.getChildAssumingFile(fileName).path; |
| 372 File tempFile = folder.getChildAssumingFile(tempFileName); | 377 File tempFile = folder.getChildAssumingFile(tempFileName); |
| 373 tempFile.writeAsBytesSync(bytes); | 378 tempFile.writeAsBytesSync(bytes); |
| 374 tempFile.renameSync(filePath); | 379 tempFile.renameSync(filePath); |
| 375 } | 380 } |
| 376 | 381 |
| 377 /** | 382 /** |
| 383 * If the given [uri] has the `package` scheme, return the name of the |
| 384 * package that contains the referenced resource. Otherwise return `null`. |
| 385 * |
| 386 * For example `package:foo/bar.dart` => `foo`. |
| 387 */ |
| 388 static String getPackageName(String uri) { |
| 389 const String PACKAGE_SCHEME = 'package:'; |
| 390 if (uri.startsWith(PACKAGE_SCHEME)) { |
| 391 int index = uri.indexOf('/'); |
| 392 if (index != -1) { |
| 393 return uri.substring(PACKAGE_SCHEME.length, index); |
| 394 } |
| 395 } |
| 396 return null; |
| 397 } |
| 398 |
| 399 /** |
| 378 * Return `true` if the given absolute [path] is in the pub cache. | 400 * Return `true` if the given absolute [path] is in the pub cache. |
| 379 */ | 401 */ |
| 380 static bool isPathInPubCache(pathos.Context pathContext, String path) { | 402 static bool isPathInPubCache(pathos.Context pathContext, String path) { |
| 381 List<String> parts = pathContext.split(path); | 403 List<String> parts = pathContext.split(path); |
| 382 for (int i = 0; i < parts.length - 1; i++) { | 404 for (int i = 0; i < parts.length - 1; i++) { |
| 383 if (parts[i] == '.pub-cache') { | 405 if (parts[i] == '.pub-cache') { |
| 384 return true; | 406 return true; |
| 385 } | 407 } |
| 386 if (parts[i] == 'Pub' && parts[i + 1] == 'Cache') { | 408 if (parts[i] == 'Pub' && parts[i + 1] == 'Cache') { |
| 387 return true; | 409 return true; |
| 388 } | 410 } |
| 389 } | 411 } |
| 390 return false; | 412 return false; |
| 391 } | 413 } |
| 392 } | 414 } |
| 393 | 415 |
| 394 /** | 416 /** |
| 395 * Specialization of [Node] for linking packages in proper dependency order. | 417 * Specialization of [Node] for linking packages in proper dependency order. |
| 396 */ | 418 */ |
| 397 class _LinkedNode extends Node<_LinkedNode> { | 419 class _LinkedNode extends Node<_LinkedNode> { |
| 398 final PubPackage package; | 420 final PubPackage package; |
| 399 final PackageBundle unlinked; | 421 final PackageBundle unlinked; |
| 400 final Map<String, _LinkedNode> uriToNode; | 422 final Map<String, _LinkedNode> packageToNode; |
| 401 | 423 |
| 402 PackageBundleBuilder linkedBuilder; | 424 PackageBundleBuilder linkedBuilder; |
| 403 bool failed = false; | 425 bool failed = false; |
| 404 | 426 |
| 405 _LinkedNode(this.package, this.unlinked, this.uriToNode); | 427 _LinkedNode(this.package, this.unlinked, this.packageToNode); |
| 406 | 428 |
| 407 @override | 429 @override |
| 408 bool get isEvaluated => linkedBuilder != null || failed; | 430 bool get isEvaluated => linkedBuilder != null || failed; |
| 409 | 431 |
| 410 @override | 432 @override |
| 411 List<_LinkedNode> computeDependencies() { | 433 List<_LinkedNode> computeDependencies() { |
| 412 Set<String> referencedUris = new Set<String>(); | 434 Set<_LinkedNode> dependencies = new Set<_LinkedNode>(); |
| 413 for (UnlinkedUnit unit in unlinked.unlinkedUnits) { | 435 for (UnlinkedUnit unit in unlinked.unlinkedUnits) { |
| 414 for (UnlinkedImport import in unit.imports) { | 436 for (UnlinkedImport import in unit.imports) { |
| 415 String uri = import.isImplicit ? 'dart:core' : import.uri; | 437 String uriStr = import.isImplicit ? 'dart:core' : import.uri; |
| 416 if (uri.startsWith('dart:')) { | 438 Uri uri = FastUri.parse(uriStr); |
| 417 // Ignore SDK imports. | 439 if (!uri.hasScheme) { |
| 418 } else if (uri.startsWith('package:')) { | 440 // A relative path in this package, skip it. |
| 419 referencedUris.add(uri); | 441 } else if (uri.scheme == 'dart') { |
| 442 // TODO(scheglov) link _sdkext bundles. |
| 443 } else if (uriStr.startsWith('package:')) { |
| 444 String package = PubSummaryManager.getPackageName(uriStr); |
| 445 _LinkedNode packageNode = packageToNode[package]; |
| 446 if (packageNode == null) { |
| 447 failed = true; |
| 448 return const <_LinkedNode>[]; |
| 449 } |
| 450 dependencies.add(packageNode); |
| 420 } else { | 451 } else { |
| 421 failed = true; | 452 failed = true; |
| 422 return const <_LinkedNode>[]; | 453 return const <_LinkedNode>[]; |
| 423 } | 454 } |
| 424 } | 455 } |
| 425 } | 456 } |
| 426 // TODO(scheglov) fail if no corresponding node | 457 return dependencies.toList(); |
| 427 return referencedUris.map((uri) => uriToNode[uri]).toSet().toList(); | |
| 428 } | 458 } |
| 429 | 459 |
| 430 @override | 460 @override |
| 431 String toString() => package.toString(); | 461 String toString() => package.toString(); |
| 432 } | 462 } |
| 433 | 463 |
| 434 /** | 464 /** |
| 435 * Specialization of [DependencyWalker] for linking packages. | 465 * Specialization of [DependencyWalker] for linking packages. |
| 436 */ | 466 */ |
| 437 class _LinkedWalker extends DependencyWalker<_LinkedNode> { | 467 class _LinkedWalker extends DependencyWalker<_LinkedNode> { |
| 438 final SummaryDataStore store; | 468 final SummaryDataStore store; |
| 439 final bool strong; | 469 final bool strong; |
| 440 | 470 |
| 441 _LinkedWalker(this.store, this.strong); | 471 _LinkedWalker(this.store, this.strong); |
| 442 | 472 |
| 443 @override | 473 @override |
| 444 void evaluate(_LinkedNode v) { | 474 void evaluate(_LinkedNode node) { |
| 445 Set<String> libraryUris = v.unlinked.unlinkedUnitUris.toSet(); | 475 evaluateScc([node]); |
| 446 Map<String, LinkedLibraryBuilder> map = link(libraryUris, (String absUri) { | |
| 447 LinkedLibrary dependencyLibrary = store.linkedMap[absUri]; | |
| 448 if (dependencyLibrary == null) { | |
| 449 // TODO(scheglov) add test | |
| 450 v.failed = true; | |
| 451 } | |
| 452 return dependencyLibrary; | |
| 453 }, (String absUri) { | |
| 454 UnlinkedUnit unlinkedUnit = store.unlinkedMap[absUri]; | |
| 455 if (unlinkedUnit == null) { | |
| 456 // TODO(scheglov) add test | |
| 457 v.failed = true; | |
| 458 } | |
| 459 return unlinkedUnit; | |
| 460 }, strong); | |
| 461 if (!v.failed) { | |
| 462 PackageBundleAssembler assembler = new PackageBundleAssembler(); | |
| 463 map.forEach((uri, linkedLibrary) { | |
| 464 assembler.addLinkedLibrary(uri, linkedLibrary); | |
| 465 }); | |
| 466 v.linkedBuilder = assembler.assemble(); | |
| 467 store.addBundle(null, v.linkedBuilder); | |
| 468 } | |
| 469 } | 476 } |
| 470 | 477 |
| 471 @override | 478 @override |
| 472 void evaluateScc(List<_LinkedNode> scc) { | 479 void evaluateScc(List<_LinkedNode> scc) { |
| 473 print('evaluateScc: $scc'); | 480 Map<String, _LinkedNode> uriToNode = <String, _LinkedNode>{}; |
| 474 // TODO(scheglov): implement evaluateScc | 481 for (_LinkedNode node in scc) { |
| 482 for (String uri in node.unlinked.unlinkedUnitUris) { |
| 483 uriToNode[uri] = node; |
| 484 } |
| 485 } |
| 486 Set<String> libraryUris = uriToNode.keys.toSet(); |
| 487 // Perform linking. |
| 488 bool failed = false; |
| 489 Map<String, LinkedLibraryBuilder> linkedLibraries = |
| 490 link(libraryUris, (String absoluteUri) { |
| 491 LinkedLibrary dependencyLibrary = store.linkedMap[absoluteUri]; |
| 492 if (dependencyLibrary == null) { |
| 493 failed = true; |
| 494 } |
| 495 return dependencyLibrary; |
| 496 }, (String absoluteUri) { |
| 497 UnlinkedUnit unlinkedUnit = store.unlinkedMap[absoluteUri]; |
| 498 if (unlinkedUnit == null) { |
| 499 failed = true; |
| 500 } |
| 501 return unlinkedUnit; |
| 502 }, strong); |
| 503 // Assemble linked bundles and put them into the store. |
| 504 if (!failed) { |
| 505 for (_LinkedNode node in scc) { |
| 506 PackageBundleAssembler assembler = new PackageBundleAssembler(); |
| 507 linkedLibraries.forEach((uri, linkedLibrary) { |
| 508 if (identical(uriToNode[uri], node)) { |
| 509 assembler.addLinkedLibrary(uri, linkedLibrary); |
| 510 } |
| 511 }); |
| 512 node.linkedBuilder = assembler.assemble(); |
| 513 store.addBundle(null, node.linkedBuilder); |
| 514 } |
| 515 } else { |
| 516 scc.forEach((node) => node.failed = true); |
| 517 } |
| 475 } | 518 } |
| 476 } | 519 } |
| OLD | NEW |