| 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 library analyzer.src.context.context_builder; | 5 library analyzer.src.context.context_builder; |
| 6 | 6 |
| 7 import 'dart:collection'; | 7 import 'dart:collection'; |
| 8 import 'dart:core' hide Resource; | 8 import 'dart:core' hide Resource; |
| 9 | 9 |
| 10 import 'package:analyzer/context/declared_variables.dart'; | 10 import 'package:analyzer/context/declared_variables.dart'; |
| 11 import 'package:analyzer/file_system/file_system.dart'; | 11 import 'package:analyzer/file_system/file_system.dart'; |
| 12 import 'package:analyzer/plugin/resolver_provider.dart'; | 12 import 'package:analyzer/plugin/resolver_provider.dart'; |
| 13 import 'package:analyzer/source/analysis_options_provider.dart'; | 13 import 'package:analyzer/source/analysis_options_provider.dart'; |
| 14 import 'package:analyzer/source/package_map_resolver.dart'; | 14 import 'package:analyzer/source/package_map_resolver.dart'; |
| 15 import 'package:analyzer/src/dart/sdk/sdk.dart'; | 15 import 'package:analyzer/src/dart/sdk/sdk.dart'; |
| 16 import 'package:analyzer/src/generated/engine.dart'; | 16 import 'package:analyzer/src/generated/engine.dart'; |
| 17 import 'package:analyzer/src/generated/sdk.dart'; | 17 import 'package:analyzer/src/generated/sdk.dart'; |
| 18 import 'package:analyzer/src/generated/source.dart'; | 18 import 'package:analyzer/src/generated/source.dart'; |
| 19 import 'package:analyzer/src/task/options.dart'; | 19 import 'package:analyzer/src/task/options.dart'; |
| 20 import 'package:charcode/ascii.dart'; | |
| 21 import 'package:package_config/packages.dart'; | 20 import 'package:package_config/packages.dart'; |
| 22 import 'package:package_config/packages_file.dart'; | 21 import 'package:package_config/packages_file.dart'; |
| 23 import 'package:package_config/src/packages_impl.dart'; | 22 import 'package:package_config/src/packages_impl.dart'; |
| 24 import 'package:yaml/yaml.dart'; | 23 import 'package:yaml/yaml.dart'; |
| 25 | 24 |
| 26 /** | 25 /** |
| 27 * A utility class used to build an analysis context for a given directory. | 26 * A utility class used to build an analysis context for a given directory. |
| 28 * | 27 * |
| 29 * The construction of analysis contexts is as follows: | 28 * The construction of analysis contexts is as follows: |
| 30 * | 29 * |
| (...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 209 UriResolver packageResolver = packageResolverProvider(folder()); | 208 UriResolver packageResolver = packageResolverProvider(folder()); |
| 210 if (packageResolver != null) { | 209 if (packageResolver != null) { |
| 211 // TODO(brianwilkerson) This doesn't support either embedder files or | 210 // TODO(brianwilkerson) This doesn't support either embedder files or |
| 212 // sdk extensions because we don't have a way to get the package map | 211 // sdk extensions because we don't have a way to get the package map |
| 213 // from the resolver. | 212 // from the resolver. |
| 214 List<UriResolver> resolvers = <UriResolver>[ | 213 List<UriResolver> resolvers = <UriResolver>[ |
| 215 new DartUriResolver(findSdk(null, options)), | 214 new DartUriResolver(findSdk(null, options)), |
| 216 packageResolver, | 215 packageResolver, |
| 217 fileResolver | 216 fileResolver |
| 218 ]; | 217 ]; |
| 219 return new SourceFactory(resolvers, null, resourceProvider); | 218 return new SourceFactory(resolvers); |
| 220 } | 219 } |
| 221 } | 220 } |
| 222 // Packages packages = new _ResolvedLinkPackages( | |
| 223 // resourceProvider, createPackageMap(rootDirectoryPath)); | |
| 224 Packages packages = createPackageMap(rootDirectoryPath); | 221 Packages packages = createPackageMap(rootDirectoryPath); |
| 225 Map<String, List<Folder>> packageMap = convertPackagesToMap(packages); | 222 Map<String, List<Folder>> packageMap = convertPackagesToMap(packages); |
| 226 List<UriResolver> resolvers = <UriResolver>[]; | 223 List<UriResolver> resolvers = <UriResolver>[]; |
| 227 resolvers.add(new DartUriResolver(findSdk(packageMap, options))); | 224 resolvers.add(new DartUriResolver(findSdk(packageMap, options))); |
| 228 if (packageMap != null) { | 225 if (packageMap != null) { |
| 229 // TODO(brianwilkerson) I think that we don't need a PackageUriResolver | 226 // TODO(brianwilkerson) I think that we don't need a PackageUriResolver |
| 230 // when we can pass the packages object to the source factory directly. | 227 // when we can pass the packages object to the source factory directly. |
| 231 resolvers.add(new PackageMapUriResolver(resourceProvider, packageMap)); | 228 resolvers.add(new PackageMapUriResolver(resourceProvider, packageMap)); |
| 232 } | 229 } |
| 233 resolvers.add(fileResolver); | 230 resolvers.add(fileResolver); |
| 234 return new SourceFactory(resolvers, packages, resourceProvider); | 231 return new SourceFactory(resolvers); |
| 235 } | 232 } |
| 236 | 233 |
| 237 /** | 234 /** |
| 238 * Add any [declaredVariables] to the list of declared variables used by the | 235 * Add any [declaredVariables] to the list of declared variables used by the |
| 239 * given [context]. | 236 * given [context]. |
| 240 */ | 237 */ |
| 241 void declareVariables(InternalAnalysisContext context) { | 238 void declareVariables(InternalAnalysisContext context) { |
| 242 if (declaredVariables != null && declaredVariables.isNotEmpty) { | 239 if (declaredVariables != null && declaredVariables.isNotEmpty) { |
| 243 DeclaredVariables contextVariables = context.declaredVariables; | 240 DeclaredVariables contextVariables = context.declaredVariables; |
| 244 declaredVariables.forEach((String variableName, String value) { | 241 declaredVariables.forEach((String variableName, String value) { |
| (...skipping 289 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 534 String _readEmbedderYaml(Folder libDir) { | 531 String _readEmbedderYaml(Folder libDir) { |
| 535 File file = libDir.getChild(EMBEDDER_FILE_NAME); | 532 File file = libDir.getChild(EMBEDDER_FILE_NAME); |
| 536 try { | 533 try { |
| 537 return file.readAsStringSync(); | 534 return file.readAsStringSync(); |
| 538 } on FileSystemException { | 535 } on FileSystemException { |
| 539 // File can't be read. | 536 // File can't be read. |
| 540 return null; | 537 return null; |
| 541 } | 538 } |
| 542 } | 539 } |
| 543 } | 540 } |
| 544 | |
| 545 /// Can we remove this class by resolving symbolic links when creating the | |
| 546 /// original Packages object? | |
| 547 class _ResolvedLinkPackages implements Packages { | |
| 548 /// All ASCII characters that are valid in a package name, with space | |
| 549 /// for all the invalid ones (including space). | |
| 550 static const String _validPackageNameCharacters = | |
| 551 r" ! $ &'()*+,-. 0123456789 ; = " | |
| 552 r"@ABCDEFGHIJKLMNOPQRSTUVWXYZ _ abcdefghijklmnopqrstuvwxyz ~ "; | |
| 553 | |
| 554 final ResourceProvider resourceProvider; | |
| 555 | |
| 556 final Packages basePackages; | |
| 557 | |
| 558 Map<String, Uri> map; | |
| 559 | |
| 560 _ResolvedLinkPackages(this.resourceProvider, this.basePackages); | |
| 561 | |
| 562 @override | |
| 563 Iterable<String> get packages => asMap().keys; | |
| 564 | |
| 565 @override | |
| 566 Map<String, Uri> asMap() { | |
| 567 if (map == null) { | |
| 568 map = new HashMap<String, Uri>(); | |
| 569 basePackages.asMap().forEach((String packageName, Uri uri) { | |
| 570 File file = | |
| 571 resourceProvider.getFile(resourceProvider.pathContext.fromUri(uri)); | |
| 572 map[packageName] = | |
| 573 resourceProvider.pathContext.toUri(file.resolveSymbolicLinksSync()); | |
| 574 }); | |
| 575 } | |
| 576 return map; | |
| 577 } | |
| 578 | |
| 579 /// Validate that a Uri is a valid package:URI. | |
| 580 String checkValidPackageUri(Uri packageUri) { | |
| 581 if (packageUri.scheme != "package") { | |
| 582 throw new ArgumentError.value( | |
| 583 packageUri, "packageUri", "Not a package: URI"); | |
| 584 } | |
| 585 if (packageUri.hasAuthority) { | |
| 586 throw new ArgumentError.value( | |
| 587 packageUri, "packageUri", "Package URIs must not have a host part"); | |
| 588 } | |
| 589 if (packageUri.hasQuery) { | |
| 590 // A query makes no sense if resolved to a file: URI. | |
| 591 throw new ArgumentError.value( | |
| 592 packageUri, "packageUri", "Package URIs must not have a query part"); | |
| 593 } | |
| 594 if (packageUri.hasFragment) { | |
| 595 // We could leave the fragment after the URL when resolving, | |
| 596 // but it would be odd if "package:foo/foo.dart#1" and | |
| 597 // "package:foo/foo.dart#2" were considered different libraries. | |
| 598 // Keep the syntax open in case we ever get multiple libraries in one file
. | |
| 599 throw new ArgumentError.value(packageUri, "packageUri", | |
| 600 "Package URIs must not have a fragment part"); | |
| 601 } | |
| 602 if (packageUri.path.startsWith('/')) { | |
| 603 throw new ArgumentError.value( | |
| 604 packageUri, "packageUri", "Package URIs must not start with a '/'"); | |
| 605 } | |
| 606 int firstSlash = packageUri.path.indexOf('/'); | |
| 607 if (firstSlash == -1) { | |
| 608 throw new ArgumentError.value(packageUri, "packageUri", | |
| 609 "Package URIs must start with the package name followed by a '/'"); | |
| 610 } | |
| 611 String packageName = packageUri.path.substring(0, firstSlash); | |
| 612 int badIndex = _findInvalidCharacter(packageName); | |
| 613 if (badIndex >= 0) { | |
| 614 if (packageName.isEmpty) { | |
| 615 throw new ArgumentError.value( | |
| 616 packageUri, "packageUri", "Package names mus be non-empty"); | |
| 617 } | |
| 618 if (badIndex == packageName.length) { | |
| 619 throw new ArgumentError.value(packageUri, "packageUri", | |
| 620 "Package names must contain at least one non-'.' character"); | |
| 621 } | |
| 622 assert(badIndex < packageName.length); | |
| 623 int badCharCode = packageName.codeUnitAt(badIndex); | |
| 624 var badChar = "U+" + badCharCode.toRadixString(16).padLeft(4, '0'); | |
| 625 if (badCharCode >= 0x20 && badCharCode <= 0x7e) { | |
| 626 // Printable character. | |
| 627 badChar = "'${packageName[badIndex]}' ($badChar)"; | |
| 628 } | |
| 629 throw new ArgumentError.value( | |
| 630 packageUri, "packageUri", "Package names must not contain $badChar"); | |
| 631 } | |
| 632 return packageName; | |
| 633 } | |
| 634 | |
| 635 @override | |
| 636 Uri resolve(Uri packageUri, {Uri notFound(Uri packageUri)}) { | |
| 637 packageUri = new Uri().resolveUri(packageUri); | |
| 638 String packageName = checkValidPackageUri(packageUri); | |
| 639 Uri packageBase = asMap()[packageName]; | |
| 640 if (packageBase == null) { | |
| 641 if (notFound != null) return notFound(packageUri); | |
| 642 throw new ArgumentError.value( | |
| 643 packageUri, "packageUri", 'No package named "$packageName"'); | |
| 644 } | |
| 645 String packagePath = packageUri.path.substring(packageName.length + 1); | |
| 646 return packageBase.resolve(packagePath); | |
| 647 } | |
| 648 | |
| 649 /// Check if a string is a valid package name. | |
| 650 /// | |
| 651 /// Valid package names contain only characters in [_validPackageNameCharacter
s] | |
| 652 /// and must contain at least one non-'.' character. | |
| 653 /// | |
| 654 /// Returns `-1` if the string is valid. | |
| 655 /// Otherwise returns the index of the first invalid character, | |
| 656 /// or `string.length` if the string contains no non-'.' character. | |
| 657 int _findInvalidCharacter(String string) { | |
| 658 // Becomes non-zero if any non-'.' character is encountered. | |
| 659 int nonDot = 0; | |
| 660 for (int i = 0; i < string.length; i++) { | |
| 661 var c = string.codeUnitAt(i); | |
| 662 if (c > 0x7f || _validPackageNameCharacters.codeUnitAt(c) <= $space) { | |
| 663 return i; | |
| 664 } | |
| 665 nonDot += c ^ $dot; | |
| 666 } | |
| 667 if (nonDot == 0) return string.length; | |
| 668 return -1; | |
| 669 } | |
| 670 } | |
| OLD | NEW |