| // 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); |
| } |
| } |