| // 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. |
| |
| part of 'declaration_builders.dart'; |
| |
| const Uri? noUri = null; |
| |
| abstract class ClassMemberAccess { |
| /// [Iterator] for all constructors declared in this class or any of its |
| /// augmentations. |
| /// |
| /// Duplicates and augmenting constructor are _not_ included. |
| /// |
| /// For instance: |
| /// |
| /// class Class { |
| /// Class(); // declared, so it is included |
| /// Class.named(); // declared, so it is included |
| /// Class.named(); // duplicate, so it is *not* included |
| /// } |
| /// |
| /// augment class Class { |
| /// augment Class(); // augmenting, so it is *not* included |
| /// Class.extra(); // declared, so it is included |
| /// } |
| /// |
| Iterator<T> fullConstructorIterator<T extends MemberBuilder>(); |
| |
| /// [NameIterator] for all constructors declared in this class or any of its |
| /// augmentations. |
| /// |
| /// Duplicates and augmenting constructors are _not_ included. |
| /// |
| /// For instance: |
| /// |
| /// class Class { |
| /// Class(); // declared, so it is included |
| /// Class.named(); // declared, so it is included |
| /// Class.named(); // duplicate, so it is *not* included |
| /// } |
| /// |
| /// augment class Class { |
| /// augment Class(); // augmenting, so it is *not* included |
| /// Class.extra(); // declared, so it is included |
| /// } |
| /// |
| NameIterator<T> fullConstructorNameIterator<T extends MemberBuilder>(); |
| |
| /// [Iterator] for all members declared in this class or any of its |
| /// augmentations. |
| /// |
| /// Duplicates and augmenting members are _not_ included. |
| /// |
| /// For instance: |
| /// |
| /// class Class { |
| /// method() {} // Declared, so it is included. |
| /// method2() {} // Declared, so it is included. |
| /// method2() {} // Duplicate, so it is *not* included. |
| /// } |
| /// |
| /// augment class Class { |
| /// augment method() {} // Augmenting, so it is *not* included. |
| /// extra() {} // Declared, so it is included. |
| /// } |
| /// |
| Iterator<T> fullMemberIterator<T extends Builder>(); |
| |
| /// [NameIterator] for all members declared in this class or any of its |
| /// augmentations. |
| /// |
| /// Duplicates and augmenting members are _not_ included. |
| /// |
| /// For instance: |
| /// |
| /// class Class { |
| /// method() {} // Declared, so it is included. |
| /// method2() {} // Declared, so it is included. |
| /// method2() {} // Duplicate, so it is *not* included. |
| /// } |
| /// |
| /// augment class Class { |
| /// augment method() {} // Augmenting, so it is *not* included. |
| /// extra() {} // Declared, so it is included. |
| /// } |
| /// |
| NameIterator<T> fullMemberNameIterator<T extends Builder>(); |
| } |
| |
| abstract class ClassBuilder implements DeclarationBuilder, ClassMemberAccess { |
| /// The type variables declared on a class, extension or mixin declaration. |
| List<NominalVariableBuilder>? get typeVariables; |
| |
| /// The type in the `extends` clause of a class declaration. |
| /// |
| /// Currently this also holds the synthesized super class for a mixin |
| /// declaration. |
| TypeBuilder? get supertypeBuilder; |
| |
| /// The type in the `implements` clause of a class or mixin declaration. |
| List<TypeBuilder>? get interfaceBuilders; |
| |
| /// The types in the `on` clause of an extension or mixin declaration. |
| List<TypeBuilder>? get onTypes; |
| |
| @override |
| Uri get fileUri; |
| |
| bool get isAbstract; |
| |
| bool get isMacro; |
| |
| bool get isSealed; |
| |
| bool get isBase; |
| |
| bool get isInterface; |
| |
| @override |
| bool get isFinal; |
| |
| bool get declaresConstConstructor; |
| |
| bool get isMixinClass; |
| |
| bool get isMixinDeclaration; |
| |
| bool get isMixinApplication; |
| |
| bool get isAnonymousMixinApplication; |
| |
| abstract TypeBuilder? mixedInTypeBuilder; |
| |
| bool get isFutureOr; |
| |
| /// The [Class] built by this builder. |
| /// |
| /// For an augmentation class the origin class is returned. |
| Class get cls; |
| |
| @override |
| ClassBuilder get origin; |
| |
| abstract bool isNullClass; |
| |
| @override |
| InterfaceType get thisType; |
| |
| Supertype buildMixedInType( |
| LibraryBuilder library, List<TypeBuilder>? arguments); |
| |
| /// Looks up the member by [name] on the class built by this class builder. |
| /// |
| /// If [isSetter] is `false`, only fields, methods, and getters with that name |
| /// will be found. If [isSetter] is `true`, only non-final fields and setters |
| /// will be found. |
| /// |
| /// If [isSuper] is `false`, the member is found among the interface members |
| /// the class built by this class builder. If [isSuper] is `true`, the member |
| /// is found among the class members of the superclass. |
| /// |
| /// If this class builder is an augmentation, interface members declared in |
| /// this augmentation are searched before searching the interface members in |
| /// the origin class. |
| /// |
| /// Unused in interface; left in on purpose. |
| Member? lookupInstanceMember(ClassHierarchy hierarchy, Name name, |
| {bool isSetter = false, bool isSuper = false}); |
| } |
| |
| abstract class ClassBuilderImpl extends DeclarationBuilderImpl |
| implements ClassBuilder { |
| @override |
| bool isNullClass = false; |
| |
| InterfaceType? _legacyRawType; |
| InterfaceType? _nullableRawType; |
| InterfaceType? _nonNullableRawType; |
| InterfaceType? _thisType; |
| |
| ClassBuilderImpl( |
| String name, LibraryBuilder parent, Uri fileUri, int fileOffset) |
| : super(name, parent, fileUri, fileOffset); |
| |
| @override |
| bool get isMixinApplication => mixedInTypeBuilder != null; |
| |
| @override |
| bool get isAnonymousMixinApplication { |
| return isMixinApplication && |
| // Coverage-ignore(suite): Not run. |
| !isNamedMixinApplication; |
| } |
| |
| @override |
| Builder? findStaticBuilder( |
| String name, int charOffset, Uri fileUri, LibraryBuilder accessingLibrary, |
| {bool isSetter = false}) { |
| if (accessingLibrary.nameOriginBuilder.origin != |
| libraryBuilder.nameOriginBuilder.origin && |
| name.startsWith("_")) { |
| return null; |
| } |
| Builder? declaration = normalizeLookup( |
| getable: nameSpace.lookupLocalMember(name, setter: false), |
| setable: nameSpace.lookupLocalMember(name, setter: true), |
| name: name, |
| charOffset: charOffset, |
| fileUri: fileUri, |
| classNameOrDebugName: this.name, |
| isSetter: isSetter, |
| forStaticAccess: true); |
| if (declaration == null && isAugmenting) { |
| return origin.findStaticBuilder( |
| name, charOffset, fileUri, accessingLibrary, |
| isSetter: isSetter); |
| } |
| return declaration; |
| } |
| |
| @override |
| Builder? lookupLocalMember(String name, |
| {bool setter = false, bool required = false}) { |
| Builder? builder = nameSpace.lookupLocalMember(name, setter: setter); |
| if (builder == null && isAugmenting) { |
| // Coverage-ignore-block(suite): Not run. |
| builder = origin.nameSpace.lookupLocalMember(name, setter: setter); |
| } |
| if (required && builder == null) { |
| internalProblem( |
| templateInternalProblemNotFoundIn.withArguments( |
| name, fullNameForErrors), |
| -1, |
| null); |
| } |
| return builder; |
| } |
| |
| /// Find the first member of this class with [name]. This method isn't |
| /// suitable for scope lookups as it will throw an error if the name isn't |
| /// declared. The [scope] should be used for that. This method is used to |
| /// find a member that is known to exist and it will pick the first |
| /// declaration if the name is ambiguous. |
| /// |
| /// For example, this method is convenient for use when building synthetic |
| /// members, such as those of an enum. |
| MemberBuilder? firstMemberNamed(String name) { |
| MemberBuilder declaration = |
| lookupLocalMember(name, required: true) as MemberBuilder; |
| while (declaration.next != null) { |
| declaration = declaration.next as MemberBuilder; |
| } |
| return declaration; |
| } |
| |
| @override |
| InterfaceType get thisType { |
| return _thisType ??= new InterfaceType(cls, Nullability.nonNullable, |
| getAsTypeArguments(cls.typeParameters, libraryBuilder.library)); |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| InterfaceType get legacyRawType { |
| return _legacyRawType ??= new InterfaceType(cls, Nullability.legacy, |
| new List<DartType>.filled(typeVariablesCount, const DynamicType())); |
| } |
| |
| InterfaceType get nullableRawType { |
| return _nullableRawType ??= new InterfaceType(cls, Nullability.nullable, |
| new List<DartType>.filled(typeVariablesCount, const DynamicType())); |
| } |
| |
| InterfaceType get nonNullableRawType { |
| return _nonNullableRawType ??= new InterfaceType( |
| cls, |
| Nullability.nonNullable, |
| new List<DartType>.filled(typeVariablesCount, const DynamicType())); |
| } |
| |
| InterfaceType rawType(Nullability nullability) { |
| switch (nullability) { |
| case Nullability.legacy: |
| // Coverage-ignore(suite): Not run. |
| return legacyRawType; |
| case Nullability.nullable: |
| return nullableRawType; |
| case Nullability.nonNullable: |
| return nonNullableRawType; |
| // Coverage-ignore(suite): Not run. |
| case Nullability.undetermined: |
| return unhandled("$nullability", "rawType", TreeNode.noOffset, noUri); |
| } |
| } |
| |
| InterfaceType? aliasedTypeWithBuiltArgumentsCacheNonNullable; |
| InterfaceType? aliasedTypeWithBuiltArgumentsCacheNullable; |
| |
| @override |
| bool get isFutureOr { |
| if (name == "FutureOr") { |
| LibraryBuilder parentLibrary = parent as LibraryBuilder; |
| if (parentLibrary.importUri.isScheme("dart") && |
| parentLibrary.importUri.path == "async") { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @override |
| DartType buildAliasedTypeWithBuiltArguments( |
| LibraryBuilder library, |
| Nullability nullability, |
| List<DartType> arguments, |
| TypeUse typeUse, |
| Uri fileUri, |
| int charOffset, |
| {required bool hasExplicitTypeArguments}) { |
| assert(cls.typeParameters.length == arguments.length); |
| if (isNullClass) { |
| return const NullType(); |
| } |
| if (isFutureOr) { |
| assert(arguments.length == 1); |
| return new FutureOrType(arguments.single, nullability); |
| } |
| if (arguments.isEmpty) { |
| return rawType(nullability); |
| } |
| if (aliasedTypeWithBuiltArgumentsCacheNonNullable != null && |
| // Coverage-ignore(suite): Not run. |
| nullability == Nullability.nonNullable) { |
| // Coverage-ignore-block(suite): Not run. |
| assert(aliasedTypeWithBuiltArgumentsCacheNonNullable!.classReference == |
| cls.reference); |
| assert(arguments.isEmpty); |
| return aliasedTypeWithBuiltArgumentsCacheNonNullable!; |
| } else if (aliasedTypeWithBuiltArgumentsCacheNullable != null && |
| // Coverage-ignore(suite): Not run. |
| nullability == Nullability.nullable) { |
| // Coverage-ignore-block(suite): Not run. |
| assert(aliasedTypeWithBuiltArgumentsCacheNullable!.classReference == |
| cls.reference); |
| assert(arguments.isEmpty); |
| return aliasedTypeWithBuiltArgumentsCacheNullable!; |
| } |
| InterfaceType type = new InterfaceType(cls, nullability, arguments); |
| if (arguments.isEmpty) { |
| // Coverage-ignore-block(suite): Not run. |
| assert(typeVariablesCount == 0); |
| if (nullability == Nullability.nonNullable) { |
| aliasedTypeWithBuiltArgumentsCacheNonNullable = type; |
| } else if (nullability == Nullability.nullable) { |
| aliasedTypeWithBuiltArgumentsCacheNullable = type; |
| } |
| } |
| |
| if (typeVariablesCount != 0 && library is SourceLibraryBuilder) { |
| library.registerBoundsCheck(type, fileUri, charOffset, typeUse, |
| inferred: !hasExplicitTypeArguments); |
| } |
| return type; |
| } |
| |
| @override |
| DartType buildAliasedType( |
| LibraryBuilder library, |
| NullabilityBuilder nullabilityBuilder, |
| List<TypeBuilder>? arguments, |
| TypeUse typeUse, |
| Uri fileUri, |
| int charOffset, |
| ClassHierarchyBase? hierarchy, |
| {required bool hasExplicitTypeArguments}) { |
| if (name == "Record" && |
| libraryBuilder.importUri.scheme == "dart" && |
| libraryBuilder.importUri.path == "core" && |
| library is SourceLibraryBuilder && |
| !isRecordAccessAllowed(library)) { |
| // Coverage-ignore-block(suite): Not run. |
| library.reportFeatureNotEnabled( |
| library.libraryFeatures.records, fileUri, charOffset, name.length); |
| return const InvalidType(); |
| } |
| return buildAliasedTypeWithBuiltArguments( |
| library, |
| nullabilityBuilder.build(), |
| buildAliasedTypeArguments(library, arguments, hierarchy), |
| typeUse, |
| fileUri, |
| charOffset, |
| hasExplicitTypeArguments: hasExplicitTypeArguments); |
| } |
| |
| @override |
| Supertype buildMixedInType( |
| LibraryBuilder library, List<TypeBuilder>? arguments) { |
| Class cls = isAugmenting |
| ? |
| // Coverage-ignore(suite): Not run. |
| origin.cls |
| : this.cls; |
| if (arguments != null) { |
| List<DartType> typeArguments = |
| buildAliasedTypeArguments(library, arguments, /* hierarchy = */ null); |
| typeArguments = unaliasTypes(typeArguments, legacyEraseAliases: false)!; |
| return new Supertype(cls, typeArguments); |
| } else { |
| return new Supertype( |
| cls, |
| new List<DartType>.filled( |
| cls.typeParameters.length, const UnknownType(), |
| growable: true)); |
| } |
| } |
| |
| @override |
| String get fullNameForErrors { |
| return isMixinApplication && !isNamedMixinApplication |
| ? "${supertypeBuilder!.fullNameForErrors} with " |
| "${mixedInTypeBuilder!.fullNameForErrors}" |
| : name; |
| } |
| |
| @override |
| Member? lookupInstanceMember(ClassHierarchy hierarchy, Name name, |
| {bool isSetter = false, bool isSuper = false}) { |
| Class? instanceClass = cls; |
| if (isAugmenting) { |
| // Coverage-ignore-block(suite): Not run. |
| assert(identical(instanceClass, origin.cls), |
| "Found ${origin.cls} expected $instanceClass"); |
| if (isSuper) { |
| // The super class is only correctly found through the origin class. |
| instanceClass = origin.cls; |
| } else { |
| Member? member = |
| hierarchy.getInterfaceMember(instanceClass, name, setter: isSetter); |
| if (member?.parent == instanceClass) { |
| // Only if the member is found in the augmentation can we use it. |
| return member; |
| } else { |
| // Otherwise, we need to keep searching in the origin class. |
| instanceClass = origin.cls; |
| } |
| } |
| } |
| |
| if (isSuper) { |
| instanceClass = instanceClass.superclass; |
| if (instanceClass == null) return null; |
| } |
| Member? target = isSuper |
| ? hierarchy.getDispatchTarget(instanceClass, name, setter: isSetter) |
| : |
| // Coverage-ignore(suite): Not run. |
| hierarchy.getInterfaceMember(instanceClass, name, setter: isSetter); |
| if (isSuper && target == null) { |
| if (cls.isMixinDeclaration) { |
| target = |
| hierarchy.getInterfaceMember(instanceClass, name, setter: isSetter); |
| } |
| } |
| return target; |
| } |
| |
| @override |
| Nullability computeNullabilityWithArguments(List<TypeBuilder>? typeArguments, |
| {required Map<TypeVariableBuilder, TraversalState> |
| typeVariablesTraversalState}) { |
| if (isNullClass) { |
| return Nullability.nullable; |
| } else if (isFutureOr) { |
| if (typeArguments != null && typeArguments.length == 1) { |
| return typeArguments.single.computeNullability( |
| typeVariablesTraversalState: typeVariablesTraversalState); |
| } else { |
| // This is `FutureOr<dynamic>`. |
| return Nullability.nullable; |
| } |
| } |
| return Nullability.nonNullable; |
| } |
| } |
| |
| class ConstructorRedirection { |
| String target; |
| bool cycleReported; |
| |
| ConstructorRedirection(this.target) : cycleReported = false; |
| } |