blob: f40aeb3d81ff6715c4c105eaa70d03e61ad61e3a [file] [log] [blame]
// Copyright (c) 2016, 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 'common.dart';
import 'elements/elements.dart' show LibraryElement;
import 'util/emptyset.dart';
/// API used by the library loader to translate internal SDK URIs into file
/// system readable URIs.
abstract class ResolvedUriTranslator {
factory ResolvedUriTranslator(
Map<String, Uri> sdkLibraries,
DiagnosticReporter reporter,
Uri platformConfigUri) = _ResolvedUriTranslator;
/// The set of platform libraries reported as unsupported.
///
/// For instance when importing 'dart:io' without '--categories=Server'.
Set<Uri> get disallowedLibraryUris;
/// Whether or not a mockable library has been translated.
bool get mockableLibraryUsed;
/// A mapping from dart: library names to their location.
Map<String, Uri> get sdkLibraries;
/// Translates the resolved [uri] into a readable URI.
///
/// The [importingLibrary] holds the library importing [uri] or `null` if
/// [uri] is loaded as the main library. The [importingLibrary] is used to
/// grant access to internal libraries from platform libraries and patch
/// libraries.
///
/// If the [uri] is not accessible from [importingLibrary], this method is
/// responsible for reporting errors.
///
/// See [LibraryLoader] for terminology on URIs.
Uri translate(LibraryElement importingLibrary, Uri uri, Spannable spannable);
}
/// A translator that forwards all methods to an internal
/// [ResolvedUriTranslator].
///
/// The translator to forward to may be set after the instance is constructed.
/// This is useful for the compiler because some tasks that are instantiated at
/// compiler construction time need a [ResolvedUriTranslator], but the data
/// required to instantiate it cannot be obtained at construction time. So a
/// [ForwardingResolvedUriTranslator] may be passed instead, and the translator
/// to forward to can be set once the required data has been retrieved.
class ForwardingResolvedUriTranslator implements ResolvedUriTranslator {
ResolvedUriTranslator resolvedUriTranslator;
/// Returns `true` if [resolvedUriTranslator] is not `null`.
bool get isSet => resolvedUriTranslator != null;
/// The opposite of [isSet].
bool get isNotSet => resolvedUriTranslator == null;
@override
Uri translate(LibraryElement importingLibrary, Uri resolvedUri,
Spannable spannable) =>
resolvedUriTranslator.translate(importingLibrary, resolvedUri, spannable);
@override
Set<Uri> get disallowedLibraryUris =>
resolvedUriTranslator?.disallowedLibraryUris ??
const ImmutableEmptySet<Uri>();
@override
bool get mockableLibraryUsed => resolvedUriTranslator.mockableLibraryUsed;
@override
Map<String, Uri> get sdkLibraries => resolvedUriTranslator.sdkLibraries;
}
class _ResolvedUriTranslator implements ResolvedUriTranslator {
final Map<String, Uri> _sdkLibraries;
final DiagnosticReporter _reporter;
final Uri _platformConfigUri;
Set<Uri> disallowedLibraryUris = new Set<Uri>();
bool mockableLibraryUsed = false;
_ResolvedUriTranslator(
this._sdkLibraries, this._reporter, this._platformConfigUri);
Map<String, Uri> get sdkLibraries => _sdkLibraries;
@override
Uri translate(LibraryElement importingLibrary, Uri uri, Spannable spannable) {
if (uri.scheme == 'dart') {
return translateDartUri(importingLibrary, uri, spannable);
}
return uri;
}
/// Translates "resolvedUri" with scheme "dart" to a [uri] resolved relative
/// to `options.platformConfigUri` according to the information in the file at
/// `options.platformConfigUri`.
///
/// Returns `null` and emits an error if the library could not be found or
/// imported into [importingLibrary].
///
/// Internal libraries (whose name starts with '_') can be only resolved if
/// [importingLibrary] is a platform or patch library.
Uri translateDartUri(
LibraryElement importingLibrary, Uri resolvedUri, Spannable spannable) {
Uri location = lookupLibraryUri(resolvedUri.path);
if (location == null) {
_reporter.reportErrorMessage(spannable, MessageKind.LIBRARY_NOT_FOUND,
{'resolvedUri': resolvedUri});
return null;
}
if (resolvedUri.path.startsWith('_')) {
bool allowInternalLibraryAccess = importingLibrary != null &&
(importingLibrary.isPlatformLibrary ||
importingLibrary.isPatch ||
importingLibrary.canonicalUri.scheme == 'memory' ||
importingLibrary.canonicalUri.path
.contains('tests/compiler/dart2js_native') ||
importingLibrary.canonicalUri.path
.contains('tests/compiler/dart2js_extra') ||
(importingLibrary.canonicalUri.scheme == 'package' &&
importingLibrary.canonicalUri.path
.startsWith('dart_internal/')));
if (!allowInternalLibraryAccess) {
if (importingLibrary != null) {
_reporter.reportErrorMessage(
spannable, MessageKind.INTERNAL_LIBRARY_FROM, {
'resolvedUri': resolvedUri,
'importingUri': importingLibrary.canonicalUri
});
} else {
_reporter.reportErrorMessage(spannable, MessageKind.INTERNAL_LIBRARY,
{'resolvedUri': resolvedUri});
registerDisallowedLibraryUse(resolvedUri);
}
return null;
}
}
if (location.scheme == "unsupported") {
if (location.path == "") {
_reporter.reportErrorMessage(spannable,
MessageKind.LIBRARY_NOT_SUPPORTED, {'resolvedUri': resolvedUri});
registerDisallowedLibraryUse(resolvedUri);
} else {
// If the specification includes a path, we treat it as "partially"
// unsupported: it is allowed to be imported unconditionally, but we
// will not expose it as being supported in the const variable
// `dart.library.name`.
//
// This is a stopgap measure to support packages like `http` that need
// to import `dart:io` conditionally. Once config-imports are supported
// in the language, we can make it an error again to import it
// unconditionally.
//
// The plaform configuration files contain a URI of the form
// `unsupported:path/to/library.dart` to indicate this partially
// supported mode. We resolve the path with respect to the configuration
// file.
return _platformConfigUri.resolve(location.path);
}
return null;
}
if (resolvedUri.path == 'html' || resolvedUri.path == 'io') {
// TODO(ahe): Get rid of mockableLibraryUsed when test.dart
// supports this use case better.
mockableLibraryUsed = true;
}
return location;
}
void registerDisallowedLibraryUse(Uri uri) {
disallowedLibraryUris.add(uri);
}
Uri lookupLibraryUri(String libraryName) {
return _sdkLibraries[libraryName];
}
}