// 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. import 'dart:collection'; import 'package:analyzer/exception/exception.dart'; import 'package:analyzer/file_system/file_system.dart'; import 'package:analyzer/source/source.dart'; import 'package:analyzer/src/generated/engine.dart'; import 'package:analyzer/src/generated/sdk.dart'; import 'package:analyzer/src/generated/source.dart' show DartUriResolver, SourceFactory, UriResolver; import 'package:analyzer/src/source/package_map_resolver.dart'; import 'package:analyzer/src/util/file_paths.dart' as file_paths; import 'package:analyzer/src/utilities/uri_cache.dart'; import 'package:analyzer/src/workspace/pub.dart'; /// Return `true` if the given [source] refers to a file that is assumed to be /// generated. bool isGeneratedSource(Source? source) { if (source == null) { return false; } return file_paths.isGenerated(source.fullName); } /// Instances of the class `SourceFactory` resolve possibly relative URI's /// against an existing [Source]. class SourceFactoryImpl implements SourceFactory { /// The resolvers used to resolve absolute URI's. final List resolvers; /// Cache of mapping of absolute [Uri]s to [Source]s. final HashMap _absoluteUriToSourceCache = HashMap(); /// Initialize a newly created source factory with the given absolute URI /// [resolvers]. SourceFactoryImpl(this.resolvers); @override DartSdk? get dartSdk { int length = resolvers.length; for (int i = 0; i < length; i++) { var resolver = resolvers[i]; if (resolver is DartUriResolver) { return resolver.dartSdk; } } return null; } @override Map>? get packageMap { for (var resolver in resolvers) { if (resolver is PackageMapUriResolver) { return resolver.packageMap; } if (resolver is PackageConfigPackageUriResolver) { return resolver.packageMap; } } return null; } @override Source? forUri(String absoluteUri) { try { Uri uri; try { uri = uriCache.parse(absoluteUri); } catch (exception, stackTrace) { AnalysisEngine.instance.instrumentationService.logInfo( 'Could not resolve URI: $absoluteUri $stackTrace', ); return null; } if (uri.isAbsolute) { return _internalResolveUri(null, uri); } } catch (exception, stackTrace) { // TODO(39284): should this exception be silent? AnalysisEngine.instance.instrumentationService.logException( SilentException( "Could not resolve URI: $absoluteUri", exception, stackTrace, ), ); } return null; } @override Source? forUri2(Uri absoluteUri) { if (absoluteUri.isAbsolute) { try { return _internalResolveUri(null, absoluteUri); } on AnalysisException catch (exception, stackTrace) { // TODO(39284): should this exception be silent? AnalysisEngine.instance.instrumentationService.logException( SilentException( "Could not resolve URI: $absoluteUri", exception, stackTrace, ), ); } } return null; } @override Uri? pathToUri(String path) { for (var resolver in resolvers) { var uri = resolver.pathToUri(path); if (uri != null) { return uri; } } return null; } @override Source? resolveUri(Source? containingSource, String? containedUri) { if (containedUri == null) { return null; } if (containedUri.isEmpty) { return containingSource; } try { // Force the creation of an escaped URI to deal with spaces, etc. return _internalResolveUri( containingSource, uriCache.parse(containedUri), ); } on FormatException { return null; } catch (exception, stackTrace) { String containingFullName = containingSource != null ? containingSource.fullName : ''; // TODO(39284): should this exception be silent? AnalysisEngine.instance.instrumentationService.logException( SilentException( "Could not resolve URI ($containedUri) " "relative to source ($containingFullName)", exception, stackTrace, ), ); return null; } } /// 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 AnalysisException( "Cannot resolve a relative URI without a containing source: " "$containedUri", ); } containedUri = uriCache.resolveRelative( containingSource.uri, containedUri, ); } var result = _absoluteUriToSourceCache[containedUri]; if (result == null) { for (UriResolver resolver in resolvers) { result = resolver.resolveAbsolute(containedUri); if (result != null) { _absoluteUriToSourceCache[containedUri] = result; break; } } } return result; } }