| // 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:convert' show jsonDecode; |
| |
| import 'package:kernel/ast.dart'; |
| |
| import '../base/combinator.dart'; |
| import '../base/export.dart'; |
| import '../base/loader.dart'; |
| import '../base/lookup_result.dart'; |
| import '../base/name_space.dart'; |
| import '../base/problems.dart' show internalProblem, unhandled; |
| import '../base/scope.dart'; |
| import '../base/uris.dart'; |
| import '../builder/builder.dart'; |
| import '../builder/compilation_unit.dart'; |
| import '../builder/declaration_builders.dart'; |
| import '../builder/dynamic_type_declaration_builder.dart'; |
| import '../builder/library_builder.dart'; |
| import '../builder/member_builder.dart'; |
| import '../builder/never_type_declaration_builder.dart'; |
| import '../builder/property_builder.dart'; |
| import '../codes/cfe_codes.dart' |
| show LocatedMessage, Message, CfeSeverity, noLength, codeUnspecified; |
| import '../kernel/constructor_tearoff_lowering.dart'; |
| import '../kernel/utils.dart'; |
| import '../source/name_scheme.dart'; |
| import '../util/reference_map.dart'; |
| import 'dill_class_builder.dart' show DillClassBuilder; |
| import 'dill_extension_builder.dart'; |
| import 'dill_extension_type_declaration_builder.dart'; |
| import 'dill_loader.dart' show DillLoader; |
| import 'dill_member_builder.dart'; |
| import 'dill_type_alias_builder.dart' show DillTypeAliasBuilder; |
| |
| class DillCompilationUnitImpl extends DillCompilationUnit { |
| final DillLibraryBuilder _dillLibraryBuilder; |
| |
| @override |
| final List<Export> exporters = <Export>[]; |
| |
| DillCompilationUnitImpl(this._dillLibraryBuilder); |
| |
| @override |
| void addExporter( |
| SourceCompilationUnit exporter, |
| List<CombinatorBuilder>? combinators, |
| int charOffset, |
| ) { |
| exporters.add(new Export(exporter, this, combinators, charOffset)); |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| void addProblem( |
| Message message, |
| int charOffset, |
| int length, |
| Uri? fileUri, { |
| bool wasHandled = false, |
| List<LocatedMessage>? context, |
| CfeSeverity? severity, |
| bool problemOnLibrary = false, |
| }) { |
| _dillLibraryBuilder.addProblem( |
| message, |
| charOffset, |
| length, |
| fileUri, |
| wasHandled: wasHandled, |
| context: context, |
| severity: severity, |
| problemOnLibrary: problemOnLibrary, |
| ); |
| } |
| |
| @override |
| Iterable<Uri> get dependencies => _dillLibraryBuilder.dependencies; |
| |
| @override |
| Uri get fileUri => _dillLibraryBuilder.fileUri; |
| |
| @override |
| Uri get importUri => _dillLibraryBuilder.importUri; |
| |
| @override |
| bool get isAugmenting => false; |
| |
| @override |
| bool get isPart => _dillLibraryBuilder.isPart; |
| |
| @override |
| bool get isSynthetic => _dillLibraryBuilder.isSynthetic; |
| |
| @override |
| bool get isUnsupported => _dillLibraryBuilder.isUnsupported; |
| |
| @override |
| LibraryBuilder get libraryBuilder => _dillLibraryBuilder; |
| |
| @override |
| Loader get loader => _dillLibraryBuilder.loader; |
| |
| @override |
| Null get partOfLibrary => _dillLibraryBuilder.partOfLibrary; |
| |
| @override |
| void recordAccess( |
| CompilationUnit accessor, |
| int charOffset, |
| int length, |
| Uri fileUri, |
| ) { |
| _dillLibraryBuilder.recordAccess(accessor, charOffset, length, fileUri); |
| } |
| } |
| |
| class DillLibraryBuilder extends LibraryBuilderImpl { |
| late final LibraryNameSpace _nameSpace; |
| |
| final DillExportNameSpace _exportNameSpace = new DillExportNameSpace(); |
| |
| @override |
| final Library library; |
| |
| @override |
| late final LibraryName libraryName = new LibraryName(library.reference); |
| |
| @override |
| DillLoader loader; |
| |
| late final CompilationUnit mainCompilationUnit = new DillCompilationUnitImpl( |
| this, |
| ); |
| |
| /// Exports that can't be serialized. |
| /// |
| /// The elements of this map are documented in |
| /// [../kernel/kernel_library_builder.dart]. |
| Map<String, String>? unserializableExports; |
| |
| // TODO(jensj): These 5 booleans could potentially be merged into a single |
| // state field. |
| bool isReadyToBuild = false; |
| bool isReadyToFinalizeExports = false; |
| bool suppressFinalizationErrors = false; |
| bool isBuilt = false; |
| bool isBuiltAndMarked = false; |
| |
| @override |
| bool mayImplementRestrictedTypes = false; |
| |
| final List<NamedBuilder> _memberBuilders = []; |
| |
| DillLibraryBuilder(this.library, this.loader) : super(library.fileUri); |
| @override |
| NameSpace get libraryNameSpace { |
| ensureLoaded(); |
| return _nameSpace; |
| } |
| |
| @override |
| ComputedNameSpace get exportNameSpace { |
| ensureLoaded(); |
| return _exportNameSpace; |
| } |
| |
| @override |
| List<Export> get exporters => mainCompilationUnit.exporters; |
| |
| @override |
| Null get partOfLibrary => null; |
| |
| @override |
| Iterable<Uri> get dependencies sync* { |
| for (LibraryDependency dependency in library.dependencies) { |
| yield dependency.targetLibrary.importUri; |
| } |
| for (LibraryPart part in library.parts) { |
| yield getPartUri(importUri, part); |
| } |
| } |
| |
| void ensureLoaded() { |
| if (!isReadyToBuild) { |
| throw new StateError("Not ready to build."); |
| } |
| if (isBuilt && !isBuiltAndMarked) { |
| // Coverage-ignore-block(suite): Not run. |
| isBuiltAndMarked = true; |
| finalizeExports(); |
| return; |
| } |
| isBuiltAndMarked = true; |
| if (isBuilt) return; |
| isBuilt = true; |
| |
| Map<String, LookupResult> content = {}; |
| Set<ExtensionBuilder>? extensions = {}; |
| |
| void _addBuilder(String name, NamedBuilder builder) { |
| assert( |
| !content.containsKey(name), |
| "Unexpected existing declaration ${content[name]}, " |
| "trying to add $builder.", |
| ); |
| content[name] = builder as LookupResult; |
| if (!name.startsWith("_") && !name.contains('#')) { |
| _exportNameSpace.addLocalMember(name, builder, setter: false); |
| } |
| } |
| |
| void _addGetable(String name, MemberBuilder builder) { |
| LookupResult? existing = content[name]; |
| if (existing != null) { |
| assert( |
| existing.getable == null && existing.setable is MemberBuilder, |
| "Unexpected existing member $existing, " |
| "trying to add $builder.", |
| ); |
| content[name] = new GetableSetableMemberResult( |
| builder, |
| existing.setable as MemberBuilder, |
| isStatic: true, |
| ); |
| } else { |
| content[name] = builder; |
| } |
| if (!name.startsWith("_") && !name.contains('#')) { |
| _exportNameSpace.addLocalMember(name, builder, setter: false); |
| } |
| } |
| |
| void _addSetable(String name, MemberBuilder builder) { |
| LookupResult? existing = content[name]; |
| if (existing != null) { |
| assert( |
| existing.getable is MemberBuilder && existing.setable == null, |
| "Unexpected existing member $existing, " |
| "trying to add $builder.", |
| ); |
| content[name] = new GetableSetableMemberResult( |
| existing.getable as MemberBuilder, |
| builder, |
| isStatic: true, |
| ); |
| } else { |
| content[name] = builder; |
| } |
| if (!name.startsWith("_") && !name.contains('#')) { |
| _exportNameSpace.addLocalMember(name, builder, setter: true); |
| } |
| } |
| |
| for (Class cls in library.classes) { |
| DillClassBuilder builder = new DillClassBuilder(cls, this); |
| _addBuilder(cls.name, builder); |
| _memberBuilders.add(builder); |
| } |
| for (Extension extension in library.extensions) { |
| DillExtensionBuilder builder = new DillExtensionBuilder(extension, this); |
| if (!extension.isUnnamedExtension) { |
| _addBuilder(extension.name, builder); |
| } |
| extensions.add(builder); |
| _memberBuilders.add(builder); |
| } |
| for (ExtensionTypeDeclaration extensionTypeDeclaration |
| in library.extensionTypeDeclarations) { |
| DillExtensionTypeDeclarationBuilder builder = |
| new DillExtensionTypeDeclarationBuilder( |
| extensionTypeDeclaration, |
| this, |
| ); |
| _addBuilder(extensionTypeDeclaration.name, builder); |
| _memberBuilders.add(builder); |
| } |
| |
| Map<String, Map<Name, Procedure>> tearOffs = {}; |
| List<Procedure> nonTearOffs = []; |
| for (Procedure procedure in library.procedures) { |
| List<Object>? names = extractTypedefNameFromTearOff(procedure.name); |
| if (names != null) { |
| Map<Name, Procedure> map = tearOffs[names[0] as String] ??= {}; |
| map[names[1] as Name] = procedure; |
| } else { |
| nonTearOffs.add(procedure); |
| } |
| } |
| for (Procedure member in nonTearOffs) { |
| if (member.isExtensionMember || member.isExtensionTypeMember) { |
| continue; |
| } |
| if (_isPrivateFromOtherLibrary(member)) { |
| continue; |
| } |
| |
| String name = member.name.text; |
| switch (member.kind) { |
| case ProcedureKind.Setter: |
| DillSetterBuilder builder = new DillSetterBuilder(member, this); |
| _addSetable(name, builder); |
| _memberBuilders.add(builder); |
| break; |
| case ProcedureKind.Getter: |
| DillGetterBuilder builder = new DillGetterBuilder(member, this); |
| _addGetable(name, builder); |
| _memberBuilders.add(builder); |
| break; |
| case ProcedureKind.Method: |
| DillMethodBuilder builder = new DillMethodBuilder(member, this); |
| _addBuilder(name, builder); |
| _memberBuilders.add(builder); |
| break; |
| // Coverage-ignore(suite): Not run. |
| case ProcedureKind.Operator: |
| case ProcedureKind.Factory: |
| throw new UnsupportedError( |
| "Unexpected library procedure ${member.kind} for ${member}", |
| ); |
| } |
| } |
| for (Typedef typedef in library.typedefs) { |
| DillTypeAliasBuilder builder = new DillTypeAliasBuilder( |
| typedef, |
| tearOffs[typedef.name], |
| this, |
| ); |
| _addBuilder(typedef.name, builder); |
| _memberBuilders.add(builder); |
| } |
| for (Field field in library.fields) { |
| if (field.isExtensionMember || field.isExtensionTypeMember) { |
| continue; |
| } |
| |
| String name = field.name.text; |
| if (name == unserializableExportName) { |
| String stringValue; |
| if (field.initializer is ConstantExpression) { |
| ConstantExpression constantExpression = |
| field.initializer as ConstantExpression; |
| StringConstant string = constantExpression.constant as StringConstant; |
| stringValue = string.value; |
| } else { |
| // Coverage-ignore-block(suite): Not run. |
| StringLiteral string = field.initializer as StringLiteral; |
| stringValue = string.value; |
| } |
| Map<dynamic, dynamic>? json = jsonDecode(stringValue); |
| unserializableExports = json != null |
| ? new Map<String, String>.from(json) |
| : null; |
| } else { |
| if (!_isPrivateFromOtherLibrary(field)) { |
| DillFieldBuilder builder = new DillFieldBuilder(field, this); |
| _addGetable(name, builder); |
| _memberBuilders.add(builder); |
| } |
| } |
| } |
| _nameSpace = new LibraryNameSpace(content: content, extensions: extensions); |
| |
| if (isReadyToFinalizeExports) { |
| finalizeExports(); |
| } else { |
| throw new StateError("Not ready to finalize exports."); |
| } |
| } |
| |
| @override |
| bool get isUnsupported => library.isUnsupported; |
| |
| @override |
| bool get isSynthetic => library.isSynthetic; |
| |
| @override |
| Uri get importUri => library.importUri; |
| |
| @override |
| Uri get fileUri => library.fileUri; |
| |
| @override |
| LibraryBuilder get nameOriginBuilder => this; |
| |
| @override |
| void becomeCoreLibrary() { |
| const String dynamicName = "dynamic"; |
| if (libraryNameSpace.lookup(dynamicName)?.getable == null) { |
| DynamicTypeDeclarationBuilder builder = new DynamicTypeDeclarationBuilder( |
| const DynamicType(), |
| this, |
| -1, |
| ); |
| _nameSpace.addLocalMember(dynamicName, builder); |
| _exportNameSpace.addLocalMember(dynamicName, builder, setter: false); |
| _memberBuilders.add(builder); |
| } |
| const String neverName = "Never"; |
| if (libraryNameSpace.lookup(neverName)?.getable == null) { |
| NeverTypeDeclarationBuilder builder = new NeverTypeDeclarationBuilder( |
| const NeverType.nonNullable(), |
| this, |
| -1, |
| ); |
| _nameSpace.addLocalMember(neverName, builder); |
| _exportNameSpace.addLocalMember(neverName, builder, setter: false); |
| _memberBuilders.add(builder); |
| } |
| assert( |
| libraryNameSpace.lookup("Null")?.getable != null, |
| "No class 'Null' found in dart:core.", |
| ); |
| } |
| |
| bool _isPrivateFromOtherLibrary(Member member) { |
| Name name = member.name; |
| return name.isPrivate && name.libraryReference != library.reference; |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| String get fullNameForErrors { |
| return library.name ?? "<library '${library.fileUri}'>"; |
| } |
| |
| void markAsReadyToBuild() { |
| isReadyToBuild = true; |
| } |
| |
| void markAsReadyToFinalizeExports({bool suppressFinalizationErrors = false}) { |
| isReadyToFinalizeExports = true; |
| this.suppressFinalizationErrors = suppressFinalizationErrors; |
| } |
| |
| void finalizeExports() { |
| unserializableExports?.forEach((String name, String messageText) { |
| NamedBuilder declaration; |
| if (messageText == exportDynamicSentinel) { |
| assert( |
| name == 'dynamic', |
| "Unexpected export name for 'dynamic': '$name'", |
| ); |
| declaration = loader.coreLibrary.exportNameSpace.lookup(name)!.getable!; |
| } else if (messageText == exportNeverSentinel) { |
| assert(name == 'Never', "Unexpected export name for 'Never': '$name'"); |
| declaration = loader.coreLibrary.exportNameSpace.lookup(name)!.getable!; |
| } else { |
| Message message = codeUnspecified.withArguments(messageText); |
| if (!suppressFinalizationErrors) { |
| addProblem(message, -1, noLength, null); |
| } |
| declaration = new InvalidBuilder(name, message.withoutLocation()); |
| } |
| _exportNameSpace.addLocalMember(name, declaration, setter: false); |
| }); |
| |
| ReferenceMap? sourceBuildersMap = loader.currentSourceLoader?.referenceMap; |
| for (Reference reference in library.additionalExports) { |
| NamedNode node = reference.node as NamedNode; |
| NamedBuilder? declaration = sourceBuildersMap?.lookupNamedBuilder( |
| reference, |
| ); |
| String name; |
| if (declaration != null) { |
| // Coverage-ignore-block(suite): Not run. |
| if (declaration is TypeDeclarationBuilder) { |
| name = declaration.name; |
| } else if (declaration is MemberBuilder) { |
| name = declaration.name; |
| } else { |
| throw new StateError( |
| "Unexpected: $declaration (${declaration.runtimeType}", |
| ); |
| } |
| |
| if (isMappedAsSetter(declaration)) { |
| _exportNameSpace.addLocalMember(name, declaration, setter: true); |
| } else { |
| _exportNameSpace.addLocalMember(name, declaration, setter: false); |
| } |
| } else { |
| Uri libraryUri; |
| bool isSetter = false; |
| if (node is Class) { |
| libraryUri = node.enclosingLibrary.importUri; |
| name = node.name; |
| } else if (node is Procedure) { |
| libraryUri = node.enclosingLibrary.importUri; |
| name = node.name.text; |
| isSetter = node.isSetter; |
| } else if (node is Member) { |
| libraryUri = node.enclosingLibrary.importUri; |
| name = node.name.text; |
| } else if (node is Typedef) { |
| libraryUri = node.enclosingLibrary.importUri; |
| name = node.name; |
| } else if (node is Extension) { |
| libraryUri = node.enclosingLibrary.importUri; |
| name = node.name; |
| } else if (node is ExtensionTypeDeclaration) { |
| libraryUri = node.enclosingLibrary.importUri; |
| name = node.name; |
| } else { |
| unhandled("${node.runtimeType}", "finalizeExports", -1, fileUri); |
| } |
| LibraryBuilder? library = loader.lookupLibraryBuilder(libraryUri); |
| if (library == null) { |
| internalProblem( |
| codeUnspecified.withArguments("No builder for '$libraryUri'."), |
| -1, |
| fileUri, |
| ); |
| } |
| assert( |
| library is DillLibraryBuilder, |
| "No reference for source declaration of $node.", |
| ); |
| if (isSetter) { |
| declaration = library.exportNameSpace.lookup(name)!.setable!; |
| _exportNameSpace.addLocalMember(name, declaration, setter: true); |
| } else { |
| declaration = library.exportNameSpace.lookup(name)!.getable!; |
| _exportNameSpace.addLocalMember(name, declaration, setter: false); |
| } |
| } |
| |
| assert( |
| (declaration is ClassBuilder && reference == declaration.reference) || |
| (declaration is TypeAliasBuilder && |
| reference == declaration.reference) || |
| (declaration is MemberBuilder && |
| (reference == declaration.readTargetReference || |
| reference == declaration.invokeTargetReference || |
| reference == declaration.writeTargetReference)) || |
| (declaration is ExtensionBuilder && |
| reference == declaration.reference) || |
| (declaration is ExtensionTypeDeclarationBuilder && |
| reference == declaration.reference), |
| "Unexpected declaration ${declaration} (${declaration.runtimeType}) " |
| "for node ${node} (${node.runtimeType}).", |
| ); |
| } |
| } |
| |
| @override |
| Iterator<T> filteredMembersIterator<T extends NamedBuilder>({ |
| required bool includeDuplicates, |
| }) { |
| ensureLoaded(); |
| return new FilteredIterator<T>( |
| _memberBuilders.iterator, |
| includeDuplicates: includeDuplicates, |
| ); |
| } |
| |
| @override |
| Version get languageVersion => library.languageVersion; |
| |
| /// Patch up the export scope, using the two replacement maps to replace |
| /// builders in the export scope. The replacement maps from old LibraryBuilder |
| /// to map, mapping from name to new (replacement) builder. |
| void patchUpExportScope( |
| Map<LibraryBuilder, NameSpace> replacementNameSpaceMap, |
| ) { |
| _exportNameSpace.patchUpScope(replacementNameSpaceMap); |
| } |
| } |