blob: 86289ee6a946d44d3af08700af5bd8c81dc1c56e [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 'dart:collection' show Queue;
import 'package:_fe_analyzer_shared/src/messages/severity.dart'
show CfeSeverity;
import 'package:kernel/ast.dart'
show Class, Component, DartType, ExtensionTypeDeclaration, Library;
import '../base/crash.dart' show firstSourceUri;
import '../base/loader.dart';
import '../base/messages.dart'
show
FormattedMessage,
LocatedMessage,
Message,
noLength,
SummaryTemplate,
Template,
codePlatformPrivateLibraryAccess,
codeInternalProblemContextSeverity;
import '../base/problems.dart' show internalProblem;
import '../base/ticker.dart' show Ticker;
import '../base/uris.dart';
import '../builder/compilation_unit.dart';
import '../builder/declaration_builders.dart';
import '../builder/library_builder.dart';
import '../builder/type_builder.dart';
import '../codes/cfe_codes.dart'
show SummaryTemplate, Template, codeDillOutlineSummary;
import '../kernel/type_builder_computer.dart' show TypeBuilderComputer;
import '../source/source_loader.dart' show SourceLoader;
import 'dill_library_builder.dart' show DillLibraryBuilder;
import 'dill_target.dart' show DillTarget;
class DillLoader extends Loader {
SourceLoader? currentSourceLoader;
final Map<Uri, DillLibraryBuilder> _knownLibraryBuilders =
<Uri, DillLibraryBuilder>{};
final Map<Uri, DillLibraryBuilder> _builders = <Uri, DillLibraryBuilder>{};
final Queue<DillLibraryBuilder> _unparsedLibraries =
new Queue<DillLibraryBuilder>();
final List<Library> libraries = <Library>[];
final DillTarget 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>[];
final Set<String> seenMessages = new Set<String>();
DillLibraryBuilder? _coreLibrary;
/// The first compilation unit loaded by this [DillLoader].
// TODO(johnniwinther): Do we need this?
CompilationUnit? first;
int byteCount = 0;
DillLoader(this.target);
@override
LibraryBuilder get coreLibrary => _coreLibrary!;
@override
CompilationUnit get coreLibraryCompilationUnit =>
_coreLibrary!.mainCompilationUnit;
Ticker get ticker => target.ticker;
void registerKnownLibrary(Library library) {
_knownLibraryBuilders[library.importUri] = new DillLibraryBuilder(
library,
this,
);
}
/// Look up a library builder by the [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.
DillLibraryBuilder read(
Uri uri,
int charOffset, {
CompilationUnit? accessor,
}) {
DillLibraryBuilder? libraryBuilder = _builders[uri];
if (libraryBuilder == null) {
libraryBuilder = _knownLibraryBuilders.remove(uri);
assert(libraryBuilder != null, "No library found for $uri.");
_builders[uri] = libraryBuilder!;
assert(libraryBuilder.loader == this);
if (uri.isScheme("dart")) {
if (uri.path == "core") {
_coreLibrary = libraryBuilder;
}
}
{
// Add any additional logic after this block. Setting the
// firstSourceUri and first library should be done as early as
// possible.
firstSourceUri ??= uri;
first ??= libraryBuilder.mainCompilationUnit;
}
if (_coreLibrary == libraryBuilder) {
target.loadExtraRequiredLibraries(this);
}
if (target.backendTarget.mayDefineRestrictedType(uri)) {
libraryBuilder.mayImplementRestrictedTypes = true;
}
_unparsedLibraries.addLast(libraryBuilder);
}
if (accessor != null) {
libraryBuilder.recordAccess(
accessor,
charOffset,
noLength,
accessor.fileUri,
);
if (!accessor.isAugmenting &&
!accessor.isPart &&
!target.backendTarget.allowPlatformPrivateLibraryAccess(
accessor.importUri,
uri,
)) {
// Coverage-ignore-block(suite): Not run.
accessor.addProblem(
codePlatformPrivateLibraryAccess,
charOffset,
noLength,
accessor.fileUri,
);
}
}
return libraryBuilder;
}
void _ensureCoreLibrary() {
if (_coreLibrary == null) {
// Coverage-ignore-block(suite): Not run.
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);
}
}
void buildOutlines() {
_ensureCoreLibrary();
while (_unparsedLibraries.isNotEmpty) {
DillLibraryBuilder library = _unparsedLibraries.removeFirst();
buildOutline(library);
}
_logSummary(outlineSummaryTemplate);
}
void _logSummary(Template<SummaryTemplate, Function> template) {
ticker.log(
// Coverage-ignore(suite): Not run.
(Duration elapsed, Duration sinceStart) {
int libraryCount = 0;
for (DillLibraryBuilder library in libraryBuilders) {
assert(library.loader == this);
libraryCount++;
}
double ms =
elapsed.inMicroseconds / Duration.microsecondsPerMillisecond;
Message message = template.withArgumentsOld(
libraryCount,
byteCount,
ms,
byteCount / ms,
ms / libraryCount,
);
print("$sinceStart: ${message.problemMessage}");
},
);
}
/// Register [message] as a problem with a severity determined by the
/// intrinsic severity of the message.
// TODO(johnniwinther): Avoid the need for this. If this is ever used, it is
// inconsistent with messages reported through the [SourceLoader] since they
// each have their own list of unhandled/unhandled errors and seen messages,
// and only those of the [SourceLoader] are used elsewhere. Use
// [currentSourceLoader] to forward messages to the [SourceLoader] instead.
@override
FormattedMessage? addProblem(
Message message,
int charOffset,
int length,
Uri? fileUri, {
bool wasHandled = false,
List<LocatedMessage>? context,
CfeSeverity? severity,
bool problemOnLibrary = false,
List<Uri>? involvedFiles,
}) {
return _addMessage(
message,
charOffset,
length,
fileUri,
severity,
wasHandled: wasHandled,
context: context,
problemOnLibrary: problemOnLibrary,
involvedFiles: involvedFiles,
);
}
/// 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,
CfeSeverity? severity, {
bool wasHandled = false,
List<LocatedMessage>? context,
bool problemOnLibrary = false,
List<Uri>? involvedFiles,
}) {
assert(
fileUri != missingUri,
"Message unexpectedly reported on missing uri.",
);
severity ??= message.code.severity;
if (severity == CfeSeverity.ignored) return null;
String trace =
"""
message: ${message.problemMessage}
charOffset: $charOffset
fileUri: $fileUri
severity: $severity
""";
if (!seenMessages.add(trace)) return null;
if (message.code.severity == CfeSeverity.context) {
internalProblem(
codeInternalProblemContextSeverity.withArgumentsOld(message.code.name),
charOffset,
fileUri,
);
}
target.context.report(
fileUri != null
? message.withLocation(fileUri, charOffset, length)
:
// Coverage-ignore(suite): Not run.
message.withoutLocation(),
severity,
context: context,
involvedFiles: involvedFiles,
);
if (severity == CfeSeverity.error) {
(wasHandled
?
// Coverage-ignore(suite): Not run.
handledErrors
: unhandledErrors)
.add(
fileUri != null
? message.withLocation(fileUri, charOffset, length)
:
// Coverage-ignore(suite): Not run.
message.withoutLocation(),
);
}
FormattedMessage formattedMessage = target.createFormattedMessage(
message,
charOffset,
length,
fileUri,
context,
severity,
involvedFiles: involvedFiles,
);
return formattedMessage;
}
Template<SummaryTemplate, Function> get outlineSummaryTemplate =>
codeDillOutlineSummary;
/// Append compiled libraries from the given [component]. If the [filter] is
/// provided, append only libraries whose [Uri] is accepted by the [filter].
List<DillLibraryBuilder> appendLibraries(
Component component, {
bool Function(Uri uri)? filter,
int byteCount = 0,
}) {
List<Library> componentLibraries = component.libraries;
List<Uri> requestedLibraries = <Uri>[];
for (int i = 0; i < componentLibraries.length; i++) {
Library library = componentLibraries[i];
Uri uri = library.importUri;
if (filter == null ||
// Coverage-ignore(suite): Not run.
filter(library.importUri)) {
libraries.add(library);
registerKnownLibrary(library);
requestedLibraries.add(uri);
}
}
List<DillLibraryBuilder> result = <DillLibraryBuilder>[];
for (int i = 0; i < requestedLibraries.length; i++) {
result.add(read(requestedLibraries[i], -1));
}
target.uriToSource.addAll(component.uriToSource);
this.byteCount += byteCount;
return result;
}
/// Append single compiled library.
///
/// Note that as this only takes a library, no new sources is added to the
/// uriToSource map.
DillLibraryBuilder appendLibrary(Library library) {
// Add to list of libraries in the loader, used for e.g. linking.
libraries.add(library);
// Weird interaction begins.
//
// Create dill library builder (adds it to a map where it's fetched
// again momentarily).
registerKnownLibrary(library);
// Set up the dill library builder (fetch it from the map again, add it to
// another map and setup some auxiliary things).
return read(library.importUri, -1);
}
void buildOutline(DillLibraryBuilder builder) {
builder.markAsReadyToBuild();
}
void finalizeExports({bool suppressFinalizationErrors = false}) {
for (DillLibraryBuilder builder in libraryBuilders) {
builder.markAsReadyToFinalizeExports(
suppressFinalizationErrors: suppressFinalizationErrors,
);
}
}
@override
ClassBuilder computeClassBuilderFromTargetClass(Class cls) {
ClassBuilder? classBuilder = currentSourceLoader?.referenceMap
.lookupClassBuilder(cls.reference);
if (classBuilder != null) {
return classBuilder;
}
Library kernelLibrary = cls.enclosingLibrary;
LibraryBuilder? library = lookupLibraryBuilder(kernelLibrary.importUri);
if (library == null) {
// Coverage-ignore-block(suite): Not run.
library = currentSourceLoader?.lookupLoadedLibraryBuilder(
kernelLibrary.importUri,
);
}
return library!.lookupRequiredLocalMember(cls.name) as ClassBuilder;
}
@override
ExtensionTypeDeclarationBuilder
computeExtensionTypeBuilderFromTargetExtensionType(
ExtensionTypeDeclaration extensionType,
) {
ExtensionTypeDeclarationBuilder? extensionTypeDeclarationBuilder =
currentSourceLoader?.referenceMap.lookupExtensionTypeDeclarationBuilder(
extensionType.reference,
);
if (extensionTypeDeclarationBuilder != null) {
return extensionTypeDeclarationBuilder;
}
Library library = extensionType.enclosingLibrary;
LibraryBuilder? libraryBuilder = lookupLibraryBuilder(library.importUri);
if (libraryBuilder == null) {
// Coverage-ignore-block(suite): Not run.
libraryBuilder = currentSourceLoader?.lookupLoadedLibraryBuilder(
library.importUri,
);
}
return libraryBuilder!.lookupRequiredLocalMember(extensionType.name)
as ExtensionTypeDeclarationBuilder;
}
late TypeBuilderComputer _typeBuilderComputer = new TypeBuilderComputer(this);
@override
TypeBuilder computeTypeBuilder(DartType type) {
return _typeBuilderComputer.visit(type);
}
// Coverage-ignore(suite): Not run.
bool containsLibraryBuilder(Uri importUri) =>
_builders.containsKey(importUri);
DillLibraryBuilder? lookupLibraryBuilder(Uri importUri) =>
_builders[importUri];
Iterable<DillLibraryBuilder> get libraryBuilders => _builders.values;
// Coverage-ignore(suite): Not run.
Iterable<Uri> get libraryImportUris => _builders.keys;
// Coverage-ignore(suite): Not run.
void registerLibraryBuilder(DillLibraryBuilder libraryBuilder) {
Uri importUri = libraryBuilder.importUri;
libraryBuilder.loader = this;
if (importUri.isScheme("dart") && importUri.path == "core") {
_coreLibrary = libraryBuilder;
}
_builders[importUri] = libraryBuilder;
}
DillLibraryBuilder? deregisterLibraryBuilder(Uri importUri) {
return _builders.remove(importUri);
}
}