| // Copyright (c) 2024, 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 'package:kernel/ast.dart'; |
| import 'package:kernel/reference_from_index.dart'; |
| |
| import '../base/messages.dart'; |
| import '../base/modifiers.dart'; |
| import '../base/name_space.dart'; |
| import '../base/problems.dart'; |
| import '../base/scope.dart'; |
| import '../builder/builder.dart'; |
| import '../builder/declaration_builders.dart'; |
| import '../builder/function_builder.dart'; |
| import '../builder/member_builder.dart'; |
| import '../builder/prefix_builder.dart'; |
| import '../builder/type_builder.dart'; |
| import '../fragment/fragment.dart'; |
| import 'name_scheme.dart'; |
| import 'source_builder_factory.dart'; |
| import 'source_class_builder.dart'; |
| import 'source_constructor_builder.dart'; |
| import 'source_enum_builder.dart'; |
| import 'source_extension_builder.dart'; |
| import 'source_extension_type_declaration_builder.dart'; |
| import 'source_factory_builder.dart'; |
| import 'source_field_builder.dart'; |
| import 'source_library_builder.dart'; |
| import 'source_loader.dart'; |
| import 'source_procedure_builder.dart'; |
| import 'source_type_alias_builder.dart'; |
| |
| class _Added { |
| final Fragment fragment; |
| |
| _Added(this.fragment); |
| |
| void getAddBuilders( |
| {required ProblemReporting problemReporting, |
| required SourceLoader loader, |
| required SourceLibraryBuilder enclosingLibraryBuilder, |
| DeclarationBuilder? declarationBuilder, |
| required List<NominalVariableBuilder> unboundNominalVariables, |
| required Map<SourceClassBuilder, TypeBuilder> mixinApplications, |
| required List<_AddBuilder> builders, |
| required IndexedLibrary? indexedLibrary, |
| required ContainerType containerType, |
| IndexedContainer? indexedContainer, |
| ContainerName? containerName}) { |
| Fragment fragment = this.fragment; |
| switch (fragment) { |
| case TypedefFragment(): |
| Reference? reference = indexedLibrary?.lookupTypedef(fragment.name); |
| SourceTypeAliasBuilder typedefBuilder = new SourceTypeAliasBuilder( |
| name: fragment.name, |
| enclosingLibraryBuilder: enclosingLibraryBuilder, |
| fileUri: fragment.fileUri, |
| fileOffset: fragment.fileOffset, |
| fragment: fragment, |
| reference: reference); |
| builders.add(new _AddBuilder(fragment.name, typedefBuilder, |
| fragment.fileUri, fragment.fileOffset)); |
| if (reference != null) { |
| loader.buildersCreatedWithReferences[reference] = typedefBuilder; |
| } |
| case ClassFragment(): |
| IndexedClass? indexedClass = |
| indexedLibrary?.lookupIndexedClass(fragment.name); |
| SourceClassBuilder classBuilder = new SourceClassBuilder( |
| fragment.metadata, |
| fragment.modifiers, |
| fragment.name, |
| fragment.typeParameters, |
| BuilderFactoryImpl.applyMixins( |
| unboundNominalVariables: unboundNominalVariables, |
| compilationUnitScope: fragment.compilationUnitScope, |
| problemReporting: problemReporting, |
| objectTypeBuilder: loader.target.objectType, |
| enclosingLibraryBuilder: enclosingLibraryBuilder, |
| fileUri: fragment.fileUri, |
| indexedLibrary: indexedLibrary, |
| supertype: fragment.supertype, |
| mixinApplicationBuilder: fragment.mixins, |
| mixinApplications: mixinApplications, |
| startCharOffset: fragment.startOffset, |
| charOffset: fragment.charOffset, |
| charEndOffset: fragment.endOffset, |
| subclassName: fragment.name, |
| isMixinDeclaration: false, |
| typeVariables: fragment.typeParameters, |
| modifiers: Modifiers.empty, |
| addBuilder: (String name, Builder declaration, int charOffset, |
| {Reference? getterReference}) { |
| if (getterReference != null) { |
| loader.buildersCreatedWithReferences[getterReference] = |
| declaration; |
| } |
| builders.add(new _AddBuilder( |
| name, declaration, fragment.fileUri, charOffset)); |
| }), |
| fragment.interfaces, |
| /* onTypes = */ null, |
| fragment.typeParameterScope, |
| fragment.toDeclarationNameSpaceBuilder(), |
| enclosingLibraryBuilder, |
| fragment.constructorReferences, |
| fragment.fileUri, |
| fragment.startOffset, |
| fragment.charOffset, |
| fragment.endOffset, |
| indexedClass, |
| isMixinDeclaration: false); |
| fragment.builder = classBuilder; |
| fragment.bodyScope.declarationBuilder = classBuilder; |
| builders.add(new _AddBuilder(fragment.name, classBuilder, |
| fragment.fileUri, fragment.fileOffset)); |
| if (indexedClass != null) { |
| loader.buildersCreatedWithReferences[indexedClass.reference] = |
| classBuilder; |
| } |
| case MixinFragment(): |
| IndexedClass? indexedClass = |
| indexedLibrary?.lookupIndexedClass(fragment.name); |
| SourceClassBuilder mixinBuilder = new SourceClassBuilder( |
| fragment.metadata, |
| fragment.modifiers, |
| fragment.name, |
| fragment.typeParameters, |
| BuilderFactoryImpl.applyMixins( |
| unboundNominalVariables: unboundNominalVariables, |
| compilationUnitScope: fragment.compilationUnitScope, |
| problemReporting: problemReporting, |
| objectTypeBuilder: loader.target.objectType, |
| enclosingLibraryBuilder: enclosingLibraryBuilder, |
| fileUri: fragment.fileUri, |
| indexedLibrary: indexedLibrary, |
| supertype: fragment.supertype, |
| mixinApplicationBuilder: fragment.mixins, |
| mixinApplications: mixinApplications, |
| startCharOffset: fragment.startOffset, |
| charOffset: fragment.charOffset, |
| charEndOffset: fragment.endOffset, |
| subclassName: fragment.name, |
| isMixinDeclaration: true, |
| typeVariables: fragment.typeParameters, |
| modifiers: Modifiers.empty, |
| addBuilder: (String name, Builder declaration, int charOffset, |
| {Reference? getterReference}) { |
| if (getterReference != null) { |
| loader.buildersCreatedWithReferences[getterReference] = |
| declaration; |
| } |
| builders.add(new _AddBuilder( |
| name, declaration, fragment.fileUri, charOffset)); |
| }), |
| fragment.interfaces, |
| // TODO(johnniwinther): Add the `on` clause types of a mixin |
| // declaration here. |
| /* onTypes = */ null, |
| fragment.typeParameterScope, |
| fragment.toDeclarationNameSpaceBuilder(), |
| enclosingLibraryBuilder, |
| fragment.constructorReferences, |
| fragment.fileUri, |
| fragment.startOffset, |
| fragment.charOffset, |
| fragment.endOffset, |
| indexedClass, |
| isMixinDeclaration: true); |
| fragment.builder = mixinBuilder; |
| fragment.bodyScope.declarationBuilder = mixinBuilder; |
| builders.add(new _AddBuilder(fragment.name, mixinBuilder, |
| fragment.fileUri, fragment.fileOffset)); |
| if (indexedClass != null) { |
| loader.buildersCreatedWithReferences[indexedClass.reference] = |
| mixinBuilder; |
| } |
| case NamedMixinApplicationFragment(): |
| BuilderFactoryImpl.applyMixins( |
| unboundNominalVariables: unboundNominalVariables, |
| compilationUnitScope: fragment.compilationUnitScope, |
| problemReporting: problemReporting, |
| objectTypeBuilder: loader.target.objectType, |
| enclosingLibraryBuilder: enclosingLibraryBuilder, |
| fileUri: fragment.fileUri, |
| indexedLibrary: indexedLibrary, |
| supertype: fragment.supertype, |
| mixinApplicationBuilder: fragment.mixins, |
| mixinApplications: mixinApplications, |
| startCharOffset: fragment.startCharOffset, |
| charOffset: fragment.charOffset, |
| charEndOffset: fragment.charEndOffset, |
| subclassName: fragment.name, |
| isMixinDeclaration: false, |
| metadata: fragment.metadata, |
| name: fragment.name, |
| typeVariables: fragment.typeParameters, |
| modifiers: fragment.modifiers, |
| interfaces: fragment.interfaces, |
| addBuilder: (String name, Builder declaration, int charOffset, |
| {Reference? getterReference}) { |
| if (getterReference != null) { |
| loader.buildersCreatedWithReferences[getterReference] = |
| declaration; |
| } |
| builders.add(new _AddBuilder( |
| name, declaration, fragment.fileUri, charOffset)); |
| }); |
| |
| case EnumFragment(): |
| IndexedClass? indexedClass = |
| indexedLibrary?.lookupIndexedClass(fragment.name); |
| SourceEnumBuilder enumBuilder = new SourceEnumBuilder( |
| fragment.metadata, |
| fragment.name, |
| fragment.typeParameters, |
| loader.target.underscoreEnumType, |
| BuilderFactoryImpl.applyMixins( |
| unboundNominalVariables: unboundNominalVariables, |
| compilationUnitScope: fragment.compilationUnitScope, |
| problemReporting: problemReporting, |
| objectTypeBuilder: loader.target.objectType, |
| enclosingLibraryBuilder: enclosingLibraryBuilder, |
| fileUri: fragment.fileUri, |
| indexedLibrary: indexedLibrary, |
| supertype: loader.target.underscoreEnumType, |
| mixinApplicationBuilder: fragment.supertypeBuilder, |
| mixinApplications: mixinApplications, |
| startCharOffset: fragment.startCharOffset, |
| charOffset: fragment.charOffset, |
| charEndOffset: fragment.charEndOffset, |
| subclassName: fragment.name, |
| isMixinDeclaration: false, |
| typeVariables: fragment.typeParameters, |
| modifiers: Modifiers.empty, |
| addBuilder: (String name, Builder declaration, int charOffset, |
| {Reference? getterReference}) { |
| if (getterReference != null) { |
| loader.buildersCreatedWithReferences[getterReference] = |
| declaration; |
| } |
| builders.add(new _AddBuilder( |
| name, declaration, fragment.fileUri, charOffset)); |
| }), |
| fragment.interfaces, |
| fragment.enumConstantInfos, |
| enclosingLibraryBuilder, |
| fragment.constructorReferences, |
| fragment.fileUri, |
| fragment.startCharOffset, |
| fragment.charOffset, |
| fragment.charEndOffset, |
| indexedClass, |
| fragment.typeParameterScope, |
| fragment.toDeclarationNameSpaceBuilder()); |
| fragment.builder = enumBuilder; |
| fragment.bodyScope.declarationBuilder = enumBuilder; |
| builders.add(new _AddBuilder( |
| fragment.name, enumBuilder, fragment.fileUri, fragment.fileOffset)); |
| if (indexedClass != null) { |
| loader.buildersCreatedWithReferences[indexedClass.reference] = |
| enumBuilder; |
| } |
| case ExtensionFragment(): |
| Reference? reference; |
| if (!fragment.extensionName.isUnnamedExtension) { |
| reference = indexedLibrary?.lookupExtension(fragment.name); |
| } |
| SourceExtensionBuilder extensionBuilder = new SourceExtensionBuilder( |
| metadata: fragment.metadata, |
| modifiers: fragment.modifiers, |
| extensionName: fragment.extensionName, |
| typeParameters: fragment.typeParameters, |
| onType: fragment.onType, |
| typeParameterScope: fragment.typeParameterScope, |
| nameSpaceBuilder: fragment.toDeclarationNameSpaceBuilder(), |
| enclosingLibraryBuilder: enclosingLibraryBuilder, |
| fileUri: fragment.fileUri, |
| startOffset: fragment.startOffset, |
| nameOffset: fragment.nameOffset, |
| endOffset: fragment.endOffset, |
| reference: reference); |
| fragment.builder = extensionBuilder; |
| fragment.bodyScope.declarationBuilder = extensionBuilder; |
| builders.add(new _AddBuilder(fragment.name, extensionBuilder, |
| fragment.fileUri, fragment.fileOffset)); |
| if (reference != null) { |
| loader.buildersCreatedWithReferences[reference] = extensionBuilder; |
| } |
| case ExtensionTypeFragment(): |
| IndexedContainer? indexedContainer = indexedLibrary |
| ?.lookupIndexedExtensionTypeDeclaration(fragment.name); |
| List<FieldFragment>? primaryConstructorFields = |
| fragment.primaryConstructorFields; |
| FieldFragment? representationFieldFragment; |
| if (primaryConstructorFields != null && |
| primaryConstructorFields.isNotEmpty) { |
| representationFieldFragment = primaryConstructorFields.first; |
| } |
| SourceExtensionTypeDeclarationBuilder extensionTypeDeclarationBuilder = |
| new SourceExtensionTypeDeclarationBuilder( |
| metadata: fragment.metadata, |
| modifiers: fragment.modifiers, |
| name: fragment.name, |
| typeParameters: fragment.typeParameters, |
| interfaceBuilders: fragment.interfaces, |
| typeParameterScope: fragment.typeParameterScope, |
| nameSpaceBuilder: fragment.toDeclarationNameSpaceBuilder(), |
| enclosingLibraryBuilder: enclosingLibraryBuilder, |
| constructorReferences: fragment.constructorReferences, |
| fileUri: fragment.fileUri, |
| startOffset: fragment.startOffset, |
| nameOffset: fragment.nameOffset, |
| endOffset: fragment.endOffset, |
| indexedContainer: indexedContainer, |
| representationFieldFragment: representationFieldFragment); |
| fragment.builder = extensionTypeDeclarationBuilder; |
| fragment.bodyScope.declarationBuilder = extensionTypeDeclarationBuilder; |
| builders.add(new _AddBuilder( |
| fragment.name, |
| extensionTypeDeclarationBuilder, |
| fragment.fileUri, |
| fragment.fileOffset)); |
| case FieldFragment(): |
| String name = fragment.name; |
| |
| final bool fieldIsLateWithLowering = fragment.modifiers.isLate && |
| (loader.target.backendTarget.isLateFieldLoweringEnabled( |
| hasInitializer: fragment.modifiers.hasInitializer, |
| isFinal: fragment.modifiers.isFinal, |
| isStatic: |
| fragment.isTopLevel || fragment.modifiers.isStatic) || |
| (loader.target.backendTarget.useStaticFieldLowering && |
| // Coverage-ignore(suite): Not run. |
| (fragment.modifiers.isStatic || fragment.isTopLevel))); |
| |
| final bool isInstanceMember = containerType != ContainerType.Library && |
| !fragment.modifiers.isStatic; |
| final bool isExtensionMember = containerType == ContainerType.Extension; |
| final bool isExtensionTypeMember = |
| containerType == ContainerType.ExtensionType; |
| |
| NameScheme nameScheme = new NameScheme( |
| isInstanceMember: isInstanceMember, |
| containerName: containerName, |
| containerType: containerType, |
| libraryName: indexedLibrary != null |
| ? new LibraryName(indexedLibrary.reference) |
| : enclosingLibraryBuilder.libraryName); |
| indexedContainer ??= indexedLibrary; |
| |
| Reference? fieldReference; |
| Reference? fieldGetterReference; |
| Reference? fieldSetterReference; |
| Reference? lateIsSetFieldReference; |
| Reference? lateIsSetGetterReference; |
| Reference? lateIsSetSetterReference; |
| Reference? lateGetterReference; |
| Reference? lateSetterReference; |
| if (indexedContainer != null) { |
| if ((isExtensionMember || isExtensionTypeMember) && |
| isInstanceMember && |
| fragment.modifiers.isExternal) { |
| /// An external extension (type) instance field is special. It is |
| /// treated as an external getter/setter pair and is therefore |
| /// encoded as a pair of top level methods using the extension |
| /// instance member naming convention. |
| fieldGetterReference = indexedContainer.lookupGetterReference( |
| nameScheme |
| .getProcedureMemberName(ProcedureKind.Getter, name) |
| .name); |
| fieldSetterReference = indexedContainer.lookupGetterReference( |
| nameScheme |
| .getProcedureMemberName(ProcedureKind.Setter, name) |
| .name); |
| } else if (isExtensionTypeMember && isInstanceMember) { |
| Name nameToLookup = nameScheme |
| .getFieldMemberName(FieldNameType.RepresentationField, name, |
| isSynthesized: true) |
| .name; |
| fieldGetterReference = |
| indexedContainer.lookupGetterReference(nameToLookup); |
| } else { |
| Name nameToLookup = nameScheme |
| .getFieldMemberName(FieldNameType.Field, name, |
| isSynthesized: fieldIsLateWithLowering) |
| .name; |
| fieldReference = |
| indexedContainer.lookupFieldReference(nameToLookup); |
| fieldGetterReference = |
| indexedContainer.lookupGetterReference(nameToLookup); |
| fieldSetterReference = |
| indexedContainer.lookupSetterReference(nameToLookup); |
| } |
| |
| if (fieldIsLateWithLowering) { |
| Name lateIsSetName = nameScheme |
| .getFieldMemberName(FieldNameType.IsSetField, name, |
| isSynthesized: fieldIsLateWithLowering) |
| .name; |
| lateIsSetFieldReference = |
| indexedContainer.lookupFieldReference(lateIsSetName); |
| lateIsSetGetterReference = |
| indexedContainer.lookupGetterReference(lateIsSetName); |
| lateIsSetSetterReference = |
| indexedContainer.lookupSetterReference(lateIsSetName); |
| lateGetterReference = indexedContainer.lookupGetterReference( |
| nameScheme |
| .getFieldMemberName(FieldNameType.Getter, name, |
| isSynthesized: fieldIsLateWithLowering) |
| .name); |
| lateSetterReference = indexedContainer.lookupSetterReference( |
| nameScheme |
| .getFieldMemberName(FieldNameType.Setter, name, |
| isSynthesized: fieldIsLateWithLowering) |
| .name); |
| } |
| } |
| SourceFieldBuilder fieldBuilder = new SourceFieldBuilder( |
| fragment.metadata, |
| fragment.type, |
| name, |
| fragment.modifiers, |
| fragment.isTopLevel, |
| enclosingLibraryBuilder, |
| declarationBuilder, |
| fragment.fileUri, |
| fragment.charOffset, |
| fragment.charEndOffset, |
| nameScheme, |
| fieldReference: fieldReference, |
| fieldGetterReference: fieldGetterReference, |
| fieldSetterReference: fieldSetterReference, |
| lateIsSetFieldReference: lateIsSetFieldReference, |
| lateIsSetGetterReference: lateIsSetGetterReference, |
| lateIsSetSetterReference: lateIsSetSetterReference, |
| lateGetterReference: lateGetterReference, |
| lateSetterReference: lateSetterReference, |
| initializerToken: fragment.initializerToken, |
| constInitializerToken: fragment.constInitializerToken); |
| fragment.builder = fieldBuilder; |
| builders.add(new _AddBuilder(fragment.name, fieldBuilder, |
| fragment.fileUri, fragment.charOffset)); |
| if (fieldGetterReference != null) { |
| loader.buildersCreatedWithReferences[fieldGetterReference] = |
| fieldBuilder; |
| } |
| if (fieldSetterReference != null) { |
| loader.buildersCreatedWithReferences[fieldSetterReference] = |
| fieldBuilder; |
| } |
| case MethodFragment(): |
| String name = fragment.name; |
| ProcedureKind kind = fragment.kind; |
| |
| final bool isInstanceMember = containerType != ContainerType.Library && |
| !fragment.modifiers.isStatic; |
| final bool isExtensionMember = containerType == ContainerType.Extension; |
| final bool isExtensionTypeMember = |
| containerType == ContainerType.ExtensionType; |
| |
| NameScheme nameScheme = new NameScheme( |
| containerName: containerName, |
| containerType: containerType, |
| isInstanceMember: isInstanceMember, |
| libraryName: indexedLibrary != null |
| ? new LibraryName(indexedLibrary.library.reference) |
| : enclosingLibraryBuilder.libraryName); |
| |
| Reference? procedureReference; |
| Reference? tearOffReference; |
| indexedContainer ??= indexedLibrary; |
| |
| bool isAugmentation = enclosingLibraryBuilder.isAugmenting && |
| fragment.modifiers.isAugment; |
| if (indexedContainer != null && !isAugmentation) { |
| Name nameToLookup = |
| nameScheme.getProcedureMemberName(kind, name).name; |
| if (kind == ProcedureKind.Setter) { |
| if ((isExtensionMember || isExtensionTypeMember) && |
| isInstanceMember) { |
| // Extension (type) instance setters are encoded as methods. |
| procedureReference = |
| indexedContainer.lookupGetterReference(nameToLookup); |
| } else { |
| procedureReference = |
| indexedContainer.lookupSetterReference(nameToLookup); |
| } |
| } else { |
| procedureReference = |
| indexedContainer.lookupGetterReference(nameToLookup); |
| if ((isExtensionMember || isExtensionTypeMember) && |
| kind == ProcedureKind.Method) { |
| tearOffReference = indexedContainer.lookupGetterReference( |
| nameScheme |
| .getProcedureMemberName(ProcedureKind.Getter, name) |
| .name); |
| } |
| } |
| } |
| |
| SourceProcedureBuilder procedureBuilder = new SourceProcedureBuilder( |
| fragment.metadata, |
| fragment.modifiers, |
| fragment.returnType, |
| name, |
| fragment.typeParameters, |
| fragment.formals, |
| kind, |
| enclosingLibraryBuilder, |
| declarationBuilder, |
| fragment.fileUri, |
| fragment.startCharOffset, |
| fragment.charOffset, |
| fragment.charOpenParenOffset, |
| fragment.charEndOffset, |
| procedureReference, |
| tearOffReference, |
| fragment.asyncModifier, |
| nameScheme, |
| nativeMethodName: fragment.nativeMethodName); |
| fragment.builder = procedureBuilder; |
| builders.add(new _AddBuilder(fragment.name, procedureBuilder, |
| fragment.fileUri, fragment.charOffset)); |
| if (procedureReference != null) { |
| loader.buildersCreatedWithReferences[procedureReference] = |
| procedureBuilder; |
| } |
| case ConstructorFragment(): |
| String name = fragment.name; |
| |
| NameScheme nameScheme = new NameScheme( |
| isInstanceMember: false, |
| containerName: containerName, |
| containerType: containerType, |
| libraryName: indexedLibrary != null |
| ? new LibraryName(indexedLibrary.library.reference) |
| : enclosingLibraryBuilder.libraryName); |
| |
| Reference? constructorReference; |
| Reference? tearOffReference; |
| |
| if (indexedContainer != null) { |
| constructorReference = indexedContainer.lookupConstructorReference( |
| nameScheme.getConstructorMemberName(name, isTearOff: false).name); |
| tearOffReference = indexedContainer.lookupGetterReference( |
| nameScheme.getConstructorMemberName(name, isTearOff: true).name); |
| } |
| |
| AbstractSourceConstructorBuilder constructorBuilder; |
| if (declarationBuilder is SourceExtensionTypeDeclarationBuilder) { |
| constructorBuilder = new SourceExtensionTypeConstructorBuilder( |
| fragment.metadata, |
| fragment.modifiers, |
| fragment.returnType, |
| name, |
| fragment.typeParameters, |
| fragment.formals, |
| enclosingLibraryBuilder, |
| declarationBuilder, |
| fragment.fileUri, |
| fragment.startCharOffset, |
| fragment.charOffset, |
| fragment.charOpenParenOffset, |
| fragment.charEndOffset, |
| constructorReference, |
| tearOffReference, |
| nameScheme, |
| nativeMethodName: fragment.nativeMethodName, |
| forAbstractClassOrEnumOrMixin: fragment.forAbstractClassOrMixin, |
| beginInitializers: fragment.beginInitializers); |
| } else { |
| constructorBuilder = new DeclaredSourceConstructorBuilder( |
| fragment.metadata, |
| fragment.modifiers, |
| fragment.returnType, |
| fragment.name, |
| fragment.typeParameters, |
| fragment.formals, |
| enclosingLibraryBuilder, |
| declarationBuilder!, |
| fragment.fileUri, |
| fragment.startCharOffset, |
| fragment.charOffset, |
| fragment.charOpenParenOffset, |
| fragment.charEndOffset, |
| constructorReference, |
| tearOffReference, |
| nameScheme, |
| nativeMethodName: fragment.nativeMethodName, |
| forAbstractClassOrEnumOrMixin: fragment.forAbstractClassOrMixin, |
| beginInitializers: fragment.beginInitializers); |
| } |
| fragment.builder = constructorBuilder; |
| builders.add(new _AddBuilder(fragment.name, constructorBuilder, |
| fragment.fileUri, fragment.charOffset)); |
| |
| // TODO(johnniwinther): There is no way to pass the tear off reference |
| // here. |
| if (constructorReference != null) { |
| loader.buildersCreatedWithReferences[constructorReference] = |
| constructorBuilder; |
| } |
| case FactoryFragment(): |
| String name = fragment.name; |
| |
| NameScheme nameScheme = new NameScheme( |
| containerName: containerName, |
| containerType: containerType, |
| isInstanceMember: false, |
| libraryName: indexedLibrary != null |
| ? new LibraryName(indexedLibrary.library.reference) |
| : enclosingLibraryBuilder.libraryName); |
| |
| Reference? constructorReference; |
| Reference? tearOffReference; |
| if (indexedContainer != null) { |
| constructorReference = indexedContainer.lookupConstructorReference( |
| nameScheme.getConstructorMemberName(name, isTearOff: false).name); |
| tearOffReference = indexedContainer.lookupGetterReference( |
| nameScheme.getConstructorMemberName(name, isTearOff: true).name); |
| } |
| // Coverage-ignore(suite): Not run. |
| else if (indexedLibrary != null) { |
| constructorReference = indexedLibrary.lookupGetterReference( |
| nameScheme.getConstructorMemberName(name, isTearOff: false).name); |
| tearOffReference = indexedLibrary.lookupGetterReference( |
| nameScheme.getConstructorMemberName(name, isTearOff: true).name); |
| } |
| |
| SourceFactoryBuilder factoryBuilder; |
| if (fragment.redirectionTarget != null) { |
| factoryBuilder = new RedirectingFactoryBuilder( |
| fragment.metadata, |
| fragment.modifiers, |
| fragment.returnType, |
| name, |
| fragment.typeParameters, |
| fragment.formals, |
| enclosingLibraryBuilder, |
| declarationBuilder!, |
| fragment.fileUri, |
| fragment.startCharOffset, |
| fragment.charOffset, |
| fragment.charOpenParenOffset, |
| fragment.charEndOffset, |
| constructorReference, |
| tearOffReference, |
| nameScheme, |
| fragment.nativeMethodName, |
| fragment.redirectionTarget!); |
| (enclosingLibraryBuilder.redirectingFactoryBuilders ??= []) |
| .add(factoryBuilder as RedirectingFactoryBuilder); |
| } else { |
| factoryBuilder = new SourceFactoryBuilder( |
| fragment.metadata, |
| fragment.modifiers, |
| fragment.returnType, |
| name, |
| fragment.typeParameters, |
| fragment.formals, |
| enclosingLibraryBuilder, |
| declarationBuilder!, |
| fragment.fileUri, |
| fragment.startCharOffset, |
| fragment.charOffset, |
| fragment.charOpenParenOffset, |
| fragment.charEndOffset, |
| constructorReference, |
| tearOffReference, |
| fragment.asyncModifier, |
| nameScheme, |
| nativeMethodName: fragment.nativeMethodName); |
| } |
| fragment.builder = factoryBuilder; |
| builders.add(new _AddBuilder(fragment.name, factoryBuilder, |
| fragment.fileUri, fragment.charOffset)); |
| // TODO(johnniwinther): There is no way to pass the tear off reference |
| // here. |
| if (constructorReference != null) { |
| loader.buildersCreatedWithReferences[constructorReference] = |
| factoryBuilder; |
| } |
| } |
| } |
| } |
| |
| class LibraryNameSpaceBuilder { |
| final Map<String, List<Builder>> augmentations = {}; |
| |
| final Map<String, List<Builder>> setterAugmentations = {}; |
| |
| List<_Added> _added = []; |
| |
| void addFragment(Fragment fragment) { |
| _added.add(new _Added(fragment)); |
| } |
| |
| void includeBuilders(LibraryNameSpaceBuilder other) { |
| _added.addAll(other._added); |
| } |
| |
| NameSpace toNameSpace({ |
| required SourceLibraryBuilder enclosingLibraryBuilder, |
| required IndexedLibrary? indexedLibrary, |
| required ProblemReporting problemReporting, |
| required List<NominalVariableBuilder> unboundNominalVariables, |
| required Map<SourceClassBuilder, TypeBuilder> mixinApplications, |
| }) { |
| Map<String, Builder> getables = {}; |
| |
| Map<String, MemberBuilder> setables = {}; |
| |
| Set<ExtensionBuilder> extensions = {}; |
| |
| NameSpace nameSpace = new NameSpaceImpl( |
| getables: getables, setables: setables, extensions: extensions); |
| |
| void _addBuilder( |
| String name, Builder declaration, Uri fileUri, int charOffset) { |
| if (declaration is SourceExtensionBuilder && |
| declaration.isUnnamedExtension) { |
| extensions.add(declaration); |
| return; |
| } |
| |
| if (declaration is MemberBuilder || |
| declaration is TypeDeclarationBuilder) { |
| // Expected. |
| } else { |
| // Coverage-ignore-block(suite): Not run. |
| // Prefix builders are added when computing the import scope. |
| assert(declaration is! PrefixBuilder, |
| "Unexpected prefix builder $declaration."); |
| unhandled( |
| "${declaration.runtimeType}", "addBuilder", charOffset, fileUri); |
| } |
| |
| assert( |
| !(declaration is FunctionBuilder && |
| (declaration.isConstructor || declaration.isFactory)), |
| "Unexpected constructor in library: $declaration."); |
| |
| Map<String, Builder> members = declaration.isSetter ? setables : getables; |
| |
| Builder? existing = members[name]; |
| |
| if (existing == declaration) return; |
| |
| if (declaration.next != null && |
| // Coverage-ignore(suite): Not run. |
| declaration.next != existing) { |
| unexpected( |
| "${declaration.next!.fileUri}@${declaration.next!.charOffset}", |
| "${existing?.fileUri}@${existing?.charOffset}", |
| declaration.charOffset, |
| declaration.fileUri); |
| } |
| declaration.next = existing; |
| if (isDuplicatedDeclaration(existing, declaration)) { |
| String fullName = name; |
| problemReporting.addProblem( |
| templateDuplicatedDeclaration.withArguments(fullName), |
| charOffset, |
| fullName.length, |
| declaration.fileUri!, |
| context: <LocatedMessage>[ |
| templateDuplicatedDeclarationCause |
| .withArguments(fullName) |
| .withLocation( |
| existing!.fileUri!, existing.charOffset, fullName.length) |
| ]); |
| } else if (declaration.isExtension) { |
| // We add the extension declaration to the extension scope only if its |
| // name is unique. Only the first of duplicate extensions is accessible |
| // by name or by resolution and the remaining are dropped for the |
| // output. |
| extensions.add(declaration as SourceExtensionBuilder); |
| } else if (declaration.isAugment) { |
| if (existing != null) { |
| if (declaration.isSetter) { |
| (setterAugmentations[name] ??= []).add(declaration); |
| } else { |
| (augmentations[name] ??= []).add(declaration); |
| } |
| } else { |
| // TODO(cstefantsova): Report an error. |
| } |
| } |
| members[name] = declaration; |
| } |
| |
| for (_Added added in _added) { |
| List<_AddBuilder> addBuilders = []; |
| added.getAddBuilders( |
| loader: enclosingLibraryBuilder.loader, |
| problemReporting: problemReporting, |
| enclosingLibraryBuilder: enclosingLibraryBuilder, |
| unboundNominalVariables: unboundNominalVariables, |
| mixinApplications: mixinApplications, |
| builders: addBuilders, |
| indexedLibrary: indexedLibrary, |
| containerType: ContainerType.Library); |
| for (_AddBuilder addBuilder in addBuilders) { |
| _addBuilder(addBuilder.name, addBuilder.declaration, addBuilder.fileUri, |
| addBuilder.charOffset); |
| } |
| } |
| return nameSpace; |
| } |
| } |
| |
| class NominalParameterScope extends AbstractTypeParameterScope { |
| final NominalParameterNameSpace _nameSpace; |
| |
| NominalParameterScope(super._parent, this._nameSpace); |
| |
| @override |
| Builder? getTypeParameter(String name) => _nameSpace.getTypeParameter(name); |
| } |
| |
| class NominalParameterNameSpace { |
| Map<String, NominalVariableBuilder> _typeParametersByName = {}; |
| |
| NominalVariableBuilder? getTypeParameter(String name) => |
| _typeParametersByName[name]; |
| |
| void addTypeVariables(ProblemReporting _problemReporting, |
| List<NominalVariableBuilder>? typeVariables, |
| {required String? ownerName, required bool allowNameConflict}) { |
| if (typeVariables == null || typeVariables.isEmpty) return; |
| for (NominalVariableBuilder tv in typeVariables) { |
| NominalVariableBuilder? existing = _typeParametersByName[tv.name]; |
| if (tv.isWildcard) continue; |
| if (existing != null) { |
| if (existing.kind == TypeVariableKind.extensionSynthesized) { |
| // The type parameter from the extension is shadowed by the type |
| // parameter from the member. Rename the shadowed type parameter. |
| existing.parameter.name = '#${existing.name}'; |
| _typeParametersByName[tv.name] = tv; |
| } else { |
| _problemReporting.addProblem(messageTypeVariableDuplicatedName, |
| tv.charOffset, tv.name.length, tv.fileUri, |
| context: [ |
| templateTypeVariableDuplicatedNameCause |
| .withArguments(tv.name) |
| .withLocation(existing.fileUri!, existing.charOffset, |
| existing.name.length) |
| ]); |
| } |
| } else { |
| _typeParametersByName[tv.name] = tv; |
| // Only classes and extension types and type variables can't have the |
| // same name. See |
| // [#29555](https://github.com/dart-lang/sdk/issues/29555) and |
| // [#54602](https://github.com/dart-lang/sdk/issues/54602). |
| if (tv.name == ownerName && !allowNameConflict) { |
| _problemReporting.addProblem(messageTypeVariableSameNameAsEnclosing, |
| tv.charOffset, tv.name.length, tv.fileUri); |
| } |
| } |
| } |
| } |
| } |
| |
| enum DeclarationFragmentKind { |
| classDeclaration, |
| mixinDeclaration, |
| enumDeclaration, |
| extensionDeclaration, |
| extensionTypeDeclaration, |
| } |
| |
| abstract class DeclarationFragment { |
| final Uri fileUri; |
| final LookupScope typeParameterScope; |
| final DeclarationBuilderScope bodyScope = new DeclarationBuilderScope(); |
| final List<_Added> _added = []; |
| |
| List<FieldFragment>? primaryConstructorFields; |
| |
| final List<NominalVariableBuilder>? typeParameters; |
| |
| final NominalParameterNameSpace _nominalParameterNameSpace; |
| |
| DeclarationFragment(this.fileUri, this.typeParameters, |
| this.typeParameterScope, this._nominalParameterNameSpace); |
| |
| String get name; |
| |
| int get fileOffset; |
| |
| DeclarationFragmentKind get kind; |
| |
| bool declaresConstConstructor = false; |
| |
| DeclarationBuilder get builder; |
| |
| void addPrimaryConstructorField(FieldFragment builder) { |
| (primaryConstructorFields ??= []).add(builder); |
| } |
| |
| void addFragment(Fragment fragment) { |
| _added.add(new _Added(fragment)); |
| } |
| |
| DeclarationNameSpaceBuilder toDeclarationNameSpaceBuilder() { |
| return new DeclarationNameSpaceBuilder._( |
| name, _nominalParameterNameSpace, _added); |
| } |
| } |
| |
| class _AddBuilder { |
| final String name; |
| final Builder declaration; |
| final Uri fileUri; |
| final int charOffset; |
| |
| _AddBuilder(this.name, this.declaration, this.fileUri, this.charOffset); |
| } |
| |
| class DeclarationNameSpaceBuilder { |
| final String _name; |
| final NominalParameterNameSpace? _nominalParameterNameSpace; |
| final List<_Added> _added; |
| |
| DeclarationNameSpaceBuilder.empty() |
| : _name = '', |
| _nominalParameterNameSpace = null, |
| _added = const []; |
| |
| DeclarationNameSpaceBuilder._( |
| this._name, this._nominalParameterNameSpace, this._added); |
| |
| void _addBuilder( |
| ProblemReporting problemReporting, |
| Map<String, Builder> getables, |
| Map<String, MemberBuilder> setables, |
| Map<String, MemberBuilder> constructors, |
| _AddBuilder addBuilder) { |
| String name = addBuilder.name; |
| Builder declaration = addBuilder.declaration; |
| Uri fileUri = addBuilder.fileUri; |
| int charOffset = addBuilder.charOffset; |
| |
| bool isConstructor = declaration is FunctionBuilder && |
| (declaration.isConstructor || declaration.isFactory); |
| if (!isConstructor && name == _name) { |
| problemReporting.addProblem( |
| messageMemberWithSameNameAsClass, charOffset, noLength, fileUri); |
| } |
| Map<String, Builder> members = isConstructor |
| ? constructors |
| : (declaration.isSetter ? setables : getables); |
| |
| Builder? existing = members[name]; |
| |
| if (existing == declaration) return; |
| |
| if (declaration.next != null && |
| // Coverage-ignore(suite): Not run. |
| declaration.next != existing) { |
| unexpected( |
| "${declaration.next!.fileUri}@${declaration.next!.charOffset}", |
| "${existing?.fileUri}@${existing?.charOffset}", |
| declaration.charOffset, |
| declaration.fileUri); |
| } |
| declaration.next = existing; |
| if (isDuplicatedDeclaration(existing, declaration)) { |
| String fullName = name; |
| if (isConstructor) { |
| if (name.isEmpty) { |
| fullName = _name; |
| } else { |
| fullName = "${_name}.$name"; |
| } |
| } |
| problemReporting.addProblem( |
| templateDuplicatedDeclaration.withArguments(fullName), |
| charOffset, |
| fullName.length, |
| declaration.fileUri!, |
| context: <LocatedMessage>[ |
| templateDuplicatedDeclarationCause |
| .withArguments(fullName) |
| .withLocation( |
| existing!.fileUri!, existing.charOffset, fullName.length) |
| ]); |
| } else if (declaration.isAugment) { |
| // Coverage-ignore-block(suite): Not run. |
| if (existing != null) { |
| if (declaration.isSetter) { |
| // TODO(johnniwinther): Collection augment setables. |
| } else { |
| // TODO(johnniwinther): Collection augment getables. |
| } |
| } else { |
| // TODO(cstefantsova): Report an error. |
| } |
| } |
| members[name] = declaration; |
| } |
| |
| void checkTypeVariableConflict(ProblemReporting _problemReporting, |
| String name, Builder member, Uri fileUri) { |
| if (_nominalParameterNameSpace != null) { |
| NominalVariableBuilder? tv = |
| _nominalParameterNameSpace.getTypeParameter(name); |
| if (tv != null) { |
| _problemReporting.addProblem( |
| templateConflictsWithTypeVariable.withArguments(name), |
| member.charOffset, |
| name.length, |
| fileUri, |
| context: [ |
| messageConflictsWithTypeVariableCause.withLocation( |
| tv.fileUri!, tv.charOffset, name.length) |
| ]); |
| } |
| } |
| } |
| |
| DeclarationNameSpace buildNameSpace( |
| {required SourceLoader loader, |
| required ProblemReporting problemReporting, |
| required SourceLibraryBuilder enclosingLibraryBuilder, |
| required DeclarationBuilder declarationBuilder, |
| required IndexedLibrary? indexedLibrary, |
| required IndexedContainer? indexedContainer, |
| required ContainerType containerType, |
| required ContainerName containerName, |
| bool includeConstructors = true}) { |
| Map<String, Builder> getables = {}; |
| Map<String, MemberBuilder> setables = {}; |
| Map<String, MemberBuilder> constructors = {}; |
| |
| for (_Added added in _added) { |
| List<_AddBuilder> addBuilders = []; |
| added.getAddBuilders( |
| loader: loader, |
| problemReporting: problemReporting, |
| enclosingLibraryBuilder: enclosingLibraryBuilder, |
| declarationBuilder: declarationBuilder, |
| builders: addBuilders, |
| // TODO(johnniwinther): Avoid passing these: |
| unboundNominalVariables: const [], |
| mixinApplications: const {}, |
| indexedLibrary: indexedLibrary, |
| indexedContainer: indexedContainer, |
| containerType: containerType, |
| containerName: containerName); |
| for (_AddBuilder addBuilder in addBuilders) { |
| _addBuilder( |
| problemReporting, getables, setables, constructors, addBuilder); |
| } |
| } |
| |
| void checkConflicts(String name, Builder member) { |
| checkTypeVariableConflict( |
| problemReporting, name, member, member.fileUri!); |
| } |
| |
| getables.forEach(checkConflicts); |
| setables.forEach(checkConflicts); |
| constructors.forEach(checkConflicts); |
| |
| return new DeclarationNameSpaceImpl( |
| getables: getables, |
| setables: setables, |
| // TODO(johnniwinther): Handle constructors in extensions consistently. |
| // Currently they are not part of the name space but still processed |
| // for instance when inferring redirecting factories. |
| constructors: includeConstructors ? constructors : null); |
| } |
| } |
| |
| enum TypeScopeKind { |
| library, |
| declarationTypeParameters, |
| classDeclaration, |
| mixinDeclaration, |
| enumDeclaration, |
| extensionDeclaration, |
| extensionTypeDeclaration, |
| memberTypeParameters, |
| functionTypeParameters, |
| unnamedMixinApplication, |
| } |
| |
| class TypeScope { |
| final TypeScopeKind kind; |
| |
| List<NamedTypeBuilder> _unresolvedNamedTypes = []; |
| |
| List<TypeScope> _childScopes = []; |
| |
| final LookupScope lookupScope; |
| |
| TypeScope(this.kind, this.lookupScope, [TypeScope? parent]) { |
| parent?._childScopes.add(this); |
| } |
| |
| void registerUnresolvedNamedType(NamedTypeBuilder namedTypeBuilder) { |
| _unresolvedNamedTypes.add(namedTypeBuilder); |
| } |
| |
| int resolveTypes(ProblemReporting problemReporting) { |
| int typeCount = _unresolvedNamedTypes.length; |
| if (_unresolvedNamedTypes.isNotEmpty) { |
| for (NamedTypeBuilder namedTypeBuilder in _unresolvedNamedTypes) { |
| namedTypeBuilder.resolveIn(lookupScope, namedTypeBuilder.charOffset!, |
| namedTypeBuilder.fileUri!, problemReporting); |
| } |
| _unresolvedNamedTypes.clear(); |
| } |
| for (TypeScope childScope in _childScopes) { |
| typeCount += childScope.resolveTypes(problemReporting); |
| } |
| return typeCount; |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| bool get isEmpty => _unresolvedNamedTypes.isEmpty && _childScopes.isEmpty; |
| |
| @override |
| String toString() => 'TypeScope($kind,$_unresolvedNamedTypes)'; |
| } |
| |
| class DeclarationBuilderScope implements LookupScope { |
| DeclarationBuilder? _declarationBuilder; |
| |
| DeclarationBuilderScope(); |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| void forEachExtension(void Function(ExtensionBuilder) f) { |
| _declarationBuilder?.scope.forEachExtension(f); |
| } |
| |
| void set declarationBuilder(DeclarationBuilder value) { |
| assert(_declarationBuilder == null, |
| "declarationBuilder has already been set."); |
| _declarationBuilder = value; |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| ScopeKind get kind => |
| _declarationBuilder?.scope.kind ?? ScopeKind.declaration; |
| |
| @override |
| Builder? lookupGetable(String name, int charOffset, Uri fileUri) { |
| return _declarationBuilder?.scope.lookupGetable(name, charOffset, fileUri); |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| Builder? lookupSetable(String name, int charOffset, Uri fileUri) { |
| return _declarationBuilder?.scope.lookupSetable(name, charOffset, fileUri); |
| } |
| } |
| |
| bool isDuplicatedDeclaration(Builder? existing, Builder other) { |
| if (existing == null) return false; |
| if (other.isAugment) return false; |
| Builder? next = existing.next; |
| if (next == null) { |
| if (existing.isGetter && other.isSetter) return false; |
| if (existing.isSetter && other.isGetter) return false; |
| } else { |
| if (next is ClassBuilder && !next.isMixinApplication) return true; |
| } |
| if (existing is ClassBuilder && other is ClassBuilder) { |
| // We allow multiple mixin applications with the same name. An |
| // alternative is to share these mixin applications. This situation can |
| // happen if you have `class A extends Object with Mixin {}` and `class B |
| // extends Object with Mixin {}` in the same library. |
| return !existing.isMixinApplication || |
| // Coverage-ignore(suite): Not run. |
| !other.isMixinApplication; |
| } |
| return true; |
| } |