blob: 7177fc3b598746edbb3add48f92dd074594bebf9 [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.
library fasta.loader;
import 'dart:async' show Future;
import 'dart:collection' show Queue;
import 'package:kernel/ast.dart' show Class, DartType, Library;
import 'builder/builder.dart'
show Builder, ClassBuilder, LibraryBuilder, Scope, TypeBuilder;
import 'builder/declaration_builder.dart' show DeclarationBuilder;
import 'builder/modifier_builder.dart' show ModifierBuilder;
import 'crash.dart' show firstSourceUri;
import 'kernel/body_builder.dart' show BodyBuilder;
import 'messages.dart'
show
FormattedMessage,
LocatedMessage,
Message,
noLength,
SummaryTemplate,
Template,
messagePlatformPrivateLibraryAccess,
templateInternalProblemContextSeverity,
templateSourceBodySummary;
import 'problems.dart' show internalProblem, unhandled;
import 'severity.dart' show Severity;
import 'target_implementation.dart' show TargetImplementation;
import 'ticker.dart' show Ticker;
const String untranslatableUriScheme = "org-dartlang-untranslatable-uri";
abstract class Loader {
final Map<Uri, LibraryBuilder> builders = <Uri, LibraryBuilder>{};
final Queue<LibraryBuilder> unparsedLibraries = new Queue<LibraryBuilder>();
final List<Library> libraries = <Library>[];
final TargetImplementation target;
/// List of all handled compile-time errors seen so far by libraries loaded
/// by this loader.
///
/// A handled error is an error that has been added to the generated AST
/// already, for example, as a throw expression.
final List<LocatedMessage> handledErrors = <LocatedMessage>[];
/// List of all unhandled compile-time errors seen so far by libraries loaded
/// by this loader.
///
/// An unhandled error is an error that hasn't been handled, see
/// [handledErrors].
final List<LocatedMessage> unhandledErrors = <LocatedMessage>[];
/// List of all problems seen so far by libraries loaded by this loader that
/// does not belong directly to a library.
final List<FormattedMessage> allComponentProblems = <FormattedMessage>[];
final Set<String> seenMessages = new Set<String>();
LibraryBuilder coreLibrary;
/// The first library that we've been asked to compile. When compiling a
/// program (aka script), this is the library that should have a main method.
LibraryBuilder first;
int byteCount = 0;
Uri currentUriForCrashReporting;
Loader(this.target);
Ticker get ticker => target.ticker;
Template<SummaryTemplate> get outlineSummaryTemplate;
bool get isSourceLoader => false;
/// Look up a library builder by the name [uri], or if such doesn't
/// exist, create one. The canonical URI of the library is [uri], and its
/// actual location is [fileUri].
///
/// Canonical URIs have schemes like "dart", or "package", and the actual
/// location is often a file URI.
///
/// The [accessor] is the library that's trying to import, export, or include
/// as part [uri], and [charOffset] is the location of the corresponding
/// directive. If [accessor] isn't allowed to access [uri], it's a
/// compile-time error.
LibraryBuilder read(Uri uri, int charOffset,
{Uri fileUri, LibraryBuilder accessor, LibraryBuilder origin}) {
LibraryBuilder builder = builders.putIfAbsent(uri, () {
if (fileUri != null &&
(fileUri.scheme == "dart" ||
fileUri.scheme == "package" ||
fileUri.scheme == "dart-ext")) {
fileUri = null;
}
String packageFragment;
if (fileUri == null) {
switch (uri.scheme) {
case "package":
case "dart":
fileUri = target.translateUri(uri) ??
new Uri(
scheme: untranslatableUriScheme,
path: Uri.encodeComponent("$uri"));
packageFragment = target.uriTranslator.getPackageFragment(uri);
break;
default:
fileUri = uri;
// Check for empty package name entry (redirecting to package name
// from which we should get the fragment part).
packageFragment = target.uriTranslator?.getDefaultPackageFragment();
break;
}
}
bool hasPackageSpecifiedLanguageVersion = false;
int packageSpecifiedLanguageVersionMajor;
int packageSpecifiedLanguageVersionMinor;
if (packageFragment != null) {
List<String> properties = packageFragment.split("&");
int foundEntries = 0;
for (int i = 0; i < properties.length; ++i) {
String property = properties[i];
if (property.startsWith("dart=")) {
if (++foundEntries > 1) {
// Force error to be issued if more than one "dart=" entry.
// (The error will be issued in library.setLanguageVersion below
// when giving it `null` version numbers.)
packageSpecifiedLanguageVersionMajor = null;
packageSpecifiedLanguageVersionMinor = null;
break;
}
hasPackageSpecifiedLanguageVersion = true;
String langaugeVersionString = property.substring(5);
// Verify that the version is x.y[whatever]
List<String> dotSeparatedParts = langaugeVersionString.split(".");
if (dotSeparatedParts.length >= 2) {
packageSpecifiedLanguageVersionMajor =
int.tryParse(dotSeparatedParts[0]);
packageSpecifiedLanguageVersionMinor =
int.tryParse(dotSeparatedParts[1]);
}
}
}
}
LibraryBuilder library =
target.createLibraryBuilder(uri, fileUri, origin);
if (library == null) {
throw new StateError("createLibraryBuilder for uri $uri, "
"fileUri $fileUri returned null.");
}
if (hasPackageSpecifiedLanguageVersion) {
library.setLanguageVersion(packageSpecifiedLanguageVersionMajor,
packageSpecifiedLanguageVersionMinor,
explicit: false);
}
if (uri.scheme == "dart" && uri.path == "core") {
coreLibrary = library;
}
if (library.loader != this) {
if (coreLibrary == library) {
target.loadExtraRequiredLibraries(this);
}
// This library isn't owned by this loader, so not further processing
// should be attempted.
return library;
}
{
// Add any additional logic after this block. Setting the
// firstSourceUri and first library should be done as early as
// possible.
firstSourceUri ??= uri;
first ??= library;
}
if (coreLibrary == library) {
target.loadExtraRequiredLibraries(this);
}
if (target.backendTarget.mayDefineRestrictedType(origin?.uri ?? uri)) {
library.mayImplementRestrictedTypes = true;
}
if (uri.scheme == "dart") {
target.readPatchFiles(library);
}
unparsedLibraries.addLast(library);
return library;
});
if (accessor == null) {
if (builder.loader == this && first != builder && isSourceLoader) {
unhandled("null", "accessor", charOffset, uri);
}
} else {
builder.recordAccess(charOffset, noLength, accessor.fileUri);
if (!accessor.isPatch &&
!accessor.isPart &&
!target.backendTarget
.allowPlatformPrivateLibraryAccess(accessor.uri, uri)) {
accessor.addProblem(messagePlatformPrivateLibraryAccess, charOffset,
noLength, accessor.fileUri);
}
}
return builder;
}
void ensureCoreLibrary() {
if (coreLibrary == null) {
read(Uri.parse("dart:core"), 0, accessor: first);
// TODO(askesc): When all backends support set literals, we no longer
// need to index dart:collection, as it is only needed for desugaring of
// const sets. We can remove it from this list at that time.
read(Uri.parse("dart:collection"), 0, accessor: first);
assert(coreLibrary != null);
}
}
Future<Null> buildBodies() async {
assert(coreLibrary != null);
for (LibraryBuilder library in builders.values) {
if (library.loader == this) {
currentUriForCrashReporting = library.uri;
await buildBody(library);
}
}
currentUriForCrashReporting = null;
logSummary(templateSourceBodySummary);
}
Future<Null> buildOutlines() async {
ensureCoreLibrary();
while (unparsedLibraries.isNotEmpty) {
LibraryBuilder library = unparsedLibraries.removeFirst();
currentUriForCrashReporting = library.uri;
await buildOutline(library);
}
currentUriForCrashReporting = null;
logSummary(outlineSummaryTemplate);
}
void logSummary(Template<SummaryTemplate> template) {
ticker.log((Duration elapsed, Duration sinceStart) {
int libraryCount = 0;
builders.forEach((Uri uri, LibraryBuilder library) {
if (library.loader == this) libraryCount++;
});
double ms = elapsed.inMicroseconds / Duration.microsecondsPerMillisecond;
Message message = template.withArguments(
libraryCount, byteCount, ms, byteCount / ms, ms / libraryCount);
print("$sinceStart: ${message.message}");
});
}
Future<Null> buildOutline(covariant LibraryBuilder library);
/// Builds all the method bodies found in the given [library].
Future<Null> buildBody(covariant LibraryBuilder library);
/// Register [message] as a problem with a severity determined by the
/// intrinsic severity of the message.
FormattedMessage addProblem(
Message message, int charOffset, int length, Uri fileUri,
{bool wasHandled: false,
List<LocatedMessage> context,
Severity severity,
bool problemOnLibrary: false}) {
return addMessage(message, charOffset, length, fileUri, severity,
wasHandled: wasHandled,
context: context,
problemOnLibrary: problemOnLibrary);
}
/// All messages reported by the compiler (errors, warnings, etc.) are routed
/// through this method.
///
/// Returns a FormattedMessage if the message is new, that is, not previously
/// reported. This is important as some parser errors may be reported up to
/// three times by `OutlineBuilder`, `DietListener`, and `BodyBuilder`.
/// If the message is not new, [null] is reported.
///
/// If [severity] is `Severity.error`, the message is added to
/// [handledErrors] if [wasHandled] is true or to [unhandledErrors] if
/// [wasHandled] is false.
FormattedMessage addMessage(Message message, int charOffset, int length,
Uri fileUri, Severity severity,
{bool wasHandled: false,
List<LocatedMessage> context,
bool problemOnLibrary: false}) {
severity = target.fixSeverity(severity, message, fileUri);
if (severity == Severity.ignored) return null;
String trace = """
message: ${message.message}
charOffset: $charOffset
fileUri: $fileUri
severity: $severity
""";
// TODO(askesc): Swap message and context around for interface checks
// and mixin overrides to make comparing context here unnecessary.
if (context != null) {
for (LocatedMessage contextMessage in context) {
trace += """
message: ${contextMessage.message}
charOffset: ${contextMessage.charOffset}
fileUri: ${contextMessage.uri}
""";
}
}
if (!seenMessages.add(trace)) return null;
if (message.code.severity == Severity.context) {
internalProblem(
templateInternalProblemContextSeverity
.withArguments(message.code.name),
charOffset,
fileUri);
}
target.context.report(
message.withLocation(fileUri, charOffset, length), severity,
context: context);
if (severity == Severity.error) {
(wasHandled ? handledErrors : unhandledErrors)
.add(message.withLocation(fileUri, charOffset, length));
}
FormattedMessage formattedMessage = target.createFormattedMessage(
message, charOffset, length, fileUri, context, severity);
if (!problemOnLibrary) {
allComponentProblems.add(formattedMessage);
}
return formattedMessage;
}
Builder getAbstractClassInstantiationError() {
return target.getAbstractClassInstantiationError(this);
}
Builder getCompileTimeError() => target.getCompileTimeError(this);
Builder getDuplicatedFieldInitializerError() {
return target.getDuplicatedFieldInitializerError(this);
}
Builder getNativeAnnotation() => target.getNativeAnnotation(this);
ClassBuilder computeClassBuilderFromTargetClass(Class cls);
TypeBuilder computeTypeBuilder(DartType type);
BodyBuilder createBodyBuilderForOutlineExpression(
LibraryBuilder library,
DeclarationBuilder declarationBuilder,
ModifierBuilder member,
Scope scope,
Uri fileUri) {
return new BodyBuilder.forOutlineExpression(
library, declarationBuilder, member, scope, fileUri);
}
}