blob: 8c466fd36c16b4860a4a0c77252ba160aab3b42c [file] [log] [blame]
// 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 ClassBuilder implements DeclarationBuilder {
/// 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;
@override
Uri get fileUri;
bool get isAbstract;
bool get isSealed;
bool get isBase;
bool get isInterface;
bool get isFinal;
bool get declaresConstConstructor;
bool get isMixinClass;
bool get isMixinDeclaration;
bool get isMixinApplication;
bool get isAnonymousMixinApplication;
TypeBuilder? get mixedInTypeBuilder;
bool get isFutureOr;
/// The [Class] built by this builder.
///
/// For an augmentation class the origin class is returned.
Class get cls;
/// Reference for the class built by this builder.
Reference get reference;
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
with DeclarationBuilderMixin
implements ClassBuilder {
@override
bool isNullClass = false;
InterfaceType? _nullableRawType;
InterfaceType? _nonNullableRawType;
InterfaceType? _thisType;
@override
bool get isMixinApplication => mixedInTypeBuilder != null;
@override
bool get isAnonymousMixinApplication {
return isMixinApplication &&
// Coverage-ignore(suite): Not run.
!isNamedMixinApplication;
}
@override
InterfaceType get thisType {
return _thisType ??= new InterfaceType(
cls,
Nullability.nonNullable,
getAsTypeArguments(cls.typeParameters, libraryBuilder.library),
);
}
InterfaceType get nullableRawType {
return _nullableRawType ??= new InterfaceType(
cls,
Nullability.nullable,
new List<DartType>.filled(typeParametersCount, const DynamicType()),
);
}
InterfaceType get nonNullableRawType {
return _nonNullableRawType ??= new InterfaceType(
cls,
Nullability.nonNullable,
new List<DartType>.filled(typeParametersCount, const DynamicType()),
);
}
InterfaceType rawType(Nullability nullability) {
switch (nullability) {
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") {
if (parent.importUri.isScheme("dart") &&
parent.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(typeParametersCount == 0);
if (nullability == Nullability.nonNullable) {
aliasedTypeWithBuiltArgumentsCacheNonNullable = type;
} else if (nullability == Nullability.nullable) {
aliasedTypeWithBuiltArgumentsCacheNullable = type;
}
}
if (typeParametersCount != 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,
) {
if (arguments != null) {
List<DartType> typeArguments = buildAliasedTypeArguments(
library,
arguments,
/* hierarchy = */ null,
);
typeArguments = unaliasTypes(typeArguments)!;
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 (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<TypeParameterBuilder, TraversalState>
typeParametersTraversalState,
}) {
if (isNullClass) {
return Nullability.nullable;
} else if (isFutureOr) {
if (typeArguments != null && typeArguments.length == 1) {
return typeArguments.single.computeNullability(
typeParametersTraversalState: typeParametersTraversalState,
);
} else {
// This is `FutureOr<dynamic>`.
return Nullability.nullable;
}
}
return Nullability.nonNullable;
}
}
class ConstructorRedirection {
String target;
bool cycleReported;
ConstructorRedirection(this.target) : cycleReported = false;
}