62b6e8995a
After FastUri was introduced, Dart's native Uri implementation was
significantly improved (see afbbbb97cf)
to the point where FastUri no longer provides a significant speed
boost.
R=scheglov@google.com
Review-Url: https://codereview.chromium.org/2667633004 .
270 lines
8.2 KiB
Dart
270 lines
8.2 KiB
Dart
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
library analyzer.src.context.source;
|
|
|
|
import 'dart:collection';
|
|
import 'dart:math' show min;
|
|
|
|
import 'package:analyzer/exception/exception.dart';
|
|
import 'package:analyzer/file_system/file_system.dart';
|
|
import 'package:analyzer/file_system/physical_file_system.dart';
|
|
import 'package:analyzer/source/package_map_resolver.dart';
|
|
import 'package:analyzer/src/generated/engine.dart';
|
|
import 'package:analyzer/src/generated/sdk.dart';
|
|
import 'package:analyzer/src/generated/source.dart';
|
|
import 'package:analyzer/src/generated/utilities_dart.dart' as utils;
|
|
import 'package:package_config/packages.dart';
|
|
|
|
/**
|
|
* Instances of the class `SourceFactory` resolve possibly relative URI's
|
|
* against an existing [Source].
|
|
*/
|
|
class SourceFactoryImpl implements SourceFactory {
|
|
@override
|
|
AnalysisContext context;
|
|
|
|
/**
|
|
* URI processor used to find mappings for `package:` URIs found in a
|
|
* `.packages` config file.
|
|
*/
|
|
final Packages _packages;
|
|
|
|
/**
|
|
* Resource provider used in working with package maps.
|
|
*/
|
|
final ResourceProvider _resourceProvider;
|
|
|
|
/**
|
|
* The resolvers used to resolve absolute URI's.
|
|
*/
|
|
final List<UriResolver> resolvers;
|
|
|
|
/**
|
|
* The predicate to determine is [Source] is local.
|
|
*/
|
|
LocalSourcePredicate _localSourcePredicate = LocalSourcePredicate.NOT_SDK;
|
|
|
|
/**
|
|
* Cache of mapping of absolute [Uri]s to [Source]s.
|
|
*/
|
|
final HashMap<Uri, Source> _absoluteUriToSourceCache =
|
|
new HashMap<Uri, Source>();
|
|
|
|
/**
|
|
* Initialize a newly created source factory with the given absolute URI
|
|
* [resolvers] and optional [_packages] resolution helper.
|
|
*/
|
|
SourceFactoryImpl(this.resolvers,
|
|
[this._packages, ResourceProvider resourceProvider])
|
|
: _resourceProvider =
|
|
resourceProvider ?? PhysicalResourceProvider.INSTANCE;
|
|
|
|
@override
|
|
DartSdk get dartSdk {
|
|
List<UriResolver> resolvers = this.resolvers;
|
|
int length = resolvers.length;
|
|
for (int i = 0; i < length; i++) {
|
|
UriResolver resolver = resolvers[i];
|
|
if (resolver is DartUriResolver) {
|
|
DartUriResolver dartUriResolver = resolver;
|
|
return dartUriResolver.dartSdk;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@override
|
|
void set localSourcePredicate(LocalSourcePredicate localSourcePredicate) {
|
|
this._localSourcePredicate = localSourcePredicate;
|
|
}
|
|
|
|
@override
|
|
Map<String, List<Folder>> get packageMap {
|
|
// Start by looking in .packages.
|
|
if (_packages != null) {
|
|
Map<String, List<Folder>> packageMap = <String, List<Folder>>{};
|
|
_packages.asMap().forEach((String name, Uri uri) {
|
|
if (uri.scheme == 'file' || uri.scheme == '' /* unspecified */) {
|
|
packageMap[name] = <Folder>[
|
|
_resourceProvider.getFolder(uri.toFilePath())
|
|
];
|
|
}
|
|
});
|
|
return packageMap;
|
|
}
|
|
|
|
// Default to the PackageMapUriResolver.
|
|
PackageMapUriResolver resolver = resolvers
|
|
.firstWhere((r) => r is PackageMapUriResolver, orElse: () => null);
|
|
return resolver?.packageMap;
|
|
}
|
|
|
|
@override
|
|
SourceFactory clone() {
|
|
SourceFactory factory =
|
|
new SourceFactory(resolvers, _packages, _resourceProvider);
|
|
factory.localSourcePredicate = _localSourcePredicate;
|
|
return factory;
|
|
}
|
|
|
|
@override
|
|
Source forUri(String absoluteUri) {
|
|
try {
|
|
Uri uri = Uri.parse(absoluteUri);
|
|
if (uri.isAbsolute) {
|
|
return _internalResolveUri(null, uri);
|
|
}
|
|
} catch (exception, stackTrace) {
|
|
AnalysisEngine.instance.logger.logError(
|
|
"Could not resolve URI: $absoluteUri",
|
|
new CaughtException(exception, stackTrace));
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@override
|
|
Source forUri2(Uri absoluteUri) {
|
|
if (absoluteUri.isAbsolute) {
|
|
try {
|
|
return _internalResolveUri(null, absoluteUri);
|
|
} on AnalysisException catch (exception, stackTrace) {
|
|
AnalysisEngine.instance.logger.logError(
|
|
"Could not resolve URI: $absoluteUri",
|
|
new CaughtException(exception, stackTrace));
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@override
|
|
Source fromEncoding(String encoding) {
|
|
Source source = forUri(encoding);
|
|
if (source == null) {
|
|
throw new ArgumentError("Invalid source encoding: '$encoding'");
|
|
}
|
|
return source;
|
|
}
|
|
|
|
@override
|
|
bool isLocalSource(Source source) => _localSourcePredicate.isLocal(source);
|
|
|
|
@override
|
|
Source resolveUri(Source containingSource, String containedUri) {
|
|
if (containedUri == null || containedUri.isEmpty) {
|
|
return null;
|
|
}
|
|
try {
|
|
// Force the creation of an escaped URI to deal with spaces, etc.
|
|
return _internalResolveUri(containingSource, Uri.parse(containedUri));
|
|
} on FormatException {
|
|
return null;
|
|
} catch (exception, stackTrace) {
|
|
String containingFullName =
|
|
containingSource != null ? containingSource.fullName : '<null>';
|
|
AnalysisEngine.instance.logger.logInformation(
|
|
"Could not resolve URI ($containedUri) "
|
|
"relative to source ($containingFullName)",
|
|
new CaughtException(exception, stackTrace));
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@override
|
|
Uri restoreUri(Source source) {
|
|
// First see if a resolver can restore the URI.
|
|
for (UriResolver resolver in resolvers) {
|
|
Uri uri = resolver.restoreAbsolute(source);
|
|
if (uri != null) {
|
|
// Now see if there's a package mapping.
|
|
Uri packageMappedUri = _getPackageMapping(uri);
|
|
if (packageMappedUri != null) {
|
|
return packageMappedUri;
|
|
}
|
|
// Fall back to the resolver's computed URI.
|
|
return uri;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
Uri _getPackageMapping(Uri sourceUri) {
|
|
if (_packages == null) {
|
|
return null;
|
|
}
|
|
if (sourceUri.scheme != 'file') {
|
|
//TODO(pquitslund): verify this works for non-file URIs.
|
|
return null;
|
|
}
|
|
|
|
Uri packageUri;
|
|
_packages.asMap().forEach((String name, Uri uri) {
|
|
if (packageUri == null) {
|
|
if (utils.startsWith(sourceUri, uri)) {
|
|
String relativePath = sourceUri.path
|
|
.substring(min(uri.path.length, sourceUri.path.length));
|
|
packageUri = Uri.parse('package:$name/$relativePath');
|
|
}
|
|
}
|
|
});
|
|
return packageUri;
|
|
}
|
|
|
|
/**
|
|
* Return a source object representing the URI that results from resolving
|
|
* the given (possibly relative) contained URI against the URI associated
|
|
* with an existing source object, or `null` if the URI could not be resolved.
|
|
*
|
|
* @param containingSource the source containing the given URI
|
|
* @param containedUri the (possibly relative) URI to be resolved against the
|
|
* containing source
|
|
* @return the source representing the contained URI
|
|
* @throws AnalysisException if either the contained URI is invalid or if it
|
|
* cannot be resolved against the source object's URI
|
|
*/
|
|
Source _internalResolveUri(Source containingSource, Uri containedUri) {
|
|
if (!containedUri.isAbsolute) {
|
|
if (containingSource == null) {
|
|
throw new AnalysisException(
|
|
"Cannot resolve a relative URI without a containing source: "
|
|
"$containedUri");
|
|
}
|
|
containedUri =
|
|
utils.resolveRelativeUri(containingSource.uri, containedUri);
|
|
}
|
|
|
|
Uri actualUri = containedUri;
|
|
|
|
// Check .packages and update target and actual URIs as appropriate.
|
|
if (_packages != null && containedUri.scheme == 'package') {
|
|
Uri packageUri = null;
|
|
try {
|
|
packageUri =
|
|
_packages.resolve(containedUri, notFound: (Uri packageUri) => null);
|
|
} on ArgumentError {
|
|
// Fall through to try resolvers.
|
|
}
|
|
|
|
if (packageUri != null) {
|
|
// Ensure scheme is set.
|
|
if (packageUri.scheme == '') {
|
|
packageUri = packageUri.replace(scheme: 'file');
|
|
}
|
|
containedUri = packageUri;
|
|
}
|
|
}
|
|
|
|
return _absoluteUriToSourceCache.putIfAbsent(containedUri, () {
|
|
for (UriResolver resolver in resolvers) {
|
|
Source result = resolver.resolveAbsolute(containedUri, actualUri);
|
|
if (result != null) {
|
|
return result;
|
|
}
|
|
}
|
|
return null;
|
|
});
|
|
}
|
|
}
|