| // 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.dill_loader; |
| |
| import 'package:_fe_analyzer_shared/src/messages/severity.dart' show Severity; |
| |
| import 'package:kernel/ast.dart' show Class, Component, DartType, Library; |
| |
| import '../builder/class_builder.dart'; |
| import '../builder/library_builder.dart'; |
| import '../builder/type_builder.dart'; |
| |
| import '../crash.dart' show firstSourceUri; |
| |
| import '../fasta_codes.dart' |
| show SummaryTemplate, Template, templateDillOutlineSummary; |
| |
| import '../kernel/type_builder_computer.dart' show TypeBuilderComputer; |
| |
| import '../loader.dart'; |
| |
| import '../messages.dart' |
| show |
| FormattedMessage, |
| LocatedMessage, |
| Message, |
| noLength, |
| SummaryTemplate, |
| Template, |
| messagePlatformPrivateLibraryAccess, |
| templateInternalProblemContextSeverity; |
| |
| import '../problems.dart' show internalProblem, unhandled; |
| |
| import '../source/source_loader.dart' show SourceLoader; |
| |
| import '../ticker.dart' show Ticker; |
| |
| import 'dill_library_builder.dart' show DillLibraryBuilder; |
| |
| import 'dill_target.dart' show DillTarget; |
| |
| import 'dart:collection' show Queue; |
| |
| class DillLoader extends Loader { |
| SourceLoader? currentSourceLoader; |
| |
| @override |
| final Map<Uri, DillLibraryBuilder> builders = <Uri, DillLibraryBuilder>{}; |
| |
| final Queue<DillLibraryBuilder> _unparsedLibraries = |
| new Queue<DillLibraryBuilder>(); |
| |
| final List<Library> libraries = <Library>[]; |
| |
| @override |
| 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>(); |
| |
| LibraryBuilder? _coreLibrary; |
| |
| /// The first library loaded by this [DillLoader]. |
| // TODO(johnniwinther): Do we need this? |
| LibraryBuilder? first; |
| |
| int byteCount = 0; |
| |
| DillLoader(this.target); |
| |
| @override |
| LibraryBuilder get coreLibrary => _coreLibrary!; |
| |
| void set coreLibrary(LibraryBuilder value) { |
| _coreLibrary = value; |
| } |
| |
| Ticker get ticker => target.ticker; |
| |
| /// 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, {LibraryBuilder? accessor}) { |
| DillLibraryBuilder builder = builders.putIfAbsent(uri, () { |
| DillLibraryBuilder library = target.createLibraryBuilder(uri); |
| assert(library.loader == this); |
| if (uri.scheme == "dart") { |
| if (uri.path == "core") { |
| _coreLibrary = 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(uri)) { |
| library.mayImplementRestrictedTypes = true; |
| } |
| _unparsedLibraries.addLast(library); |
| return library; |
| }); |
| if (accessor != null) { |
| builder.recordAccess(charOffset, noLength, accessor.fileUri); |
| if (!accessor.isPatch && |
| !accessor.isPart && |
| !target.backendTarget |
| .allowPlatformPrivateLibraryAccess(accessor.importUri, 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); |
| } |
| } |
| |
| void buildOutlines() { |
| _ensureCoreLibrary(); |
| while (_unparsedLibraries.isNotEmpty) { |
| DillLibraryBuilder library = _unparsedLibraries.removeFirst(); |
| buildOutline(library); |
| } |
| _logSummary(outlineSummaryTemplate); |
| } |
| |
| void _logSummary(Template<SummaryTemplate> template) { |
| ticker.log((Duration elapsed, Duration sinceStart) { |
| int libraryCount = 0; |
| for (DillLibraryBuilder library in builders.values) { |
| assert(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}"); |
| }); |
| } |
| |
| /// 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, |
| Severity? 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, Severity? severity, |
| {bool wasHandled: false, |
| List<LocatedMessage>? context, |
| bool problemOnLibrary: false, |
| List<Uri>? involvedFiles}) { |
| severity ??= message.code.severity; |
| if (severity == Severity.ignored) return null; |
| String trace = """ |
| message: ${message.message} |
| charOffset: $charOffset |
| fileUri: $fileUri |
| severity: $severity |
| """; |
| if (!seenMessages.add(trace)) return null; |
| if (message.code.severity == Severity.context) { |
| internalProblem( |
| templateInternalProblemContextSeverity |
| .withArguments(message.code.name), |
| charOffset, |
| fileUri); |
| } |
| target.context.report( |
| fileUri != null |
| ? message.withLocation(fileUri, charOffset, length) |
| : message.withoutLocation(), |
| severity, |
| context: context, |
| involvedFiles: involvedFiles); |
| if (severity == Severity.error) { |
| (wasHandled ? handledErrors : unhandledErrors).add(fileUri != null |
| ? message.withLocation(fileUri, charOffset, length) |
| : message.withoutLocation()); |
| } |
| FormattedMessage formattedMessage = target.createFormattedMessage( |
| message, charOffset, length, fileUri, context, severity, |
| involvedFiles: involvedFiles); |
| return formattedMessage; |
| } |
| |
| Template<SummaryTemplate> get outlineSummaryTemplate => |
| templateDillOutlineSummary; |
| |
| /// 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>[]; |
| List<Uri> requestedLibrariesFileUri = <Uri>[]; |
| for (int i = 0; i < componentLibraries.length; i++) { |
| Library library = componentLibraries[i]; |
| Uri uri = library.importUri; |
| if (filter == null || filter(library.importUri)) { |
| libraries.add(library); |
| target.registerLibrary(library); |
| requestedLibraries.add(uri); |
| requestedLibrariesFileUri.add(library.fileUri); |
| } |
| } |
| 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). |
| target.registerLibrary(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) { |
| // ignore: unnecessary_null_comparison |
| if (builder.library == null) { |
| unhandled("null", "builder.library", 0, builder.fileUri); |
| } |
| builder.markAsReadyToBuild(); |
| } |
| |
| void finalizeExports({bool suppressFinalizationErrors: false}) { |
| for (LibraryBuilder builder in builders.values) { |
| DillLibraryBuilder library = builder as DillLibraryBuilder; |
| library.markAsReadyToFinalizeExports( |
| suppressFinalizationErrors: suppressFinalizationErrors); |
| } |
| } |
| |
| @override |
| ClassBuilder computeClassBuilderFromTargetClass(Class cls) { |
| Library kernelLibrary = cls.enclosingLibrary; |
| LibraryBuilder? library = builders[kernelLibrary.importUri]; |
| if (library == null) { |
| library = currentSourceLoader?.builders[kernelLibrary.importUri]; |
| } |
| return library!.lookupLocalMember(cls.name, required: true) as ClassBuilder; |
| } |
| |
| @override |
| TypeBuilder computeTypeBuilder(DartType type) { |
| return type.accept(new TypeBuilderComputer(this)); |
| } |
| } |