blob: 2e0f2fb3041c64248c171b9f0252abc2a01391e0 [file] [log] [blame]
// Copyright (c) 2018, 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.
// @dart = 2.9
library fasta.class_hierarchy_builder;
import 'package:kernel/ast.dart';
import 'package:kernel/class_hierarchy.dart'
show ClassHierarchy, ClassHierarchyBase;
import 'package:kernel/core_types.dart' show CoreTypes;
import 'package:kernel/type_algebra.dart' show Substitution, uniteNullabilities;
import 'package:kernel/type_environment.dart';
import 'package:kernel/src/legacy_erasure.dart';
import 'package:kernel/src/nnbd_top_merge.dart';
import 'package:kernel/src/norm.dart';
import 'package:kernel/src/standard_bounds.dart';
import 'package:kernel/src/types.dart' show Types;
import '../../base/common.dart';
import '../../testing/id_testing_utils.dart' show typeToText;
import '../builder/builder.dart';
import '../builder/class_builder.dart';
import '../builder/field_builder.dart';
import '../builder/formal_parameter_builder.dart';
import '../builder/library_builder.dart';
import '../builder/member_builder.dart';
import '../builder/named_type_builder.dart';
import '../builder/procedure_builder.dart';
import '../builder/type_alias_builder.dart';
import '../builder/type_builder.dart';
import '../builder/type_declaration_builder.dart';
import '../loader.dart' show Loader;
import '../messages.dart'
show
LocatedMessage,
Message,
messageDeclaredMemberConflictsWithInheritedMember,
messageDeclaredMemberConflictsWithInheritedMemberCause,
messageDeclaredMemberConflictsWithOverriddenMembersCause,
messageInheritedMembersConflict,
messageInheritedMembersConflictCause1,
messageInheritedMembersConflictCause2,
messageStaticAndInstanceConflict,
messageStaticAndInstanceConflictCause,
templateCantInferTypesDueToNoCombinedSignature,
templateCantInferReturnTypeDueToNoCombinedSignature,
templateCantInferTypeDueToNoCombinedSignature,
templateCombinedMemberSignatureFailed,
templateDuplicatedDeclaration,
templateDuplicatedDeclarationCause,
templateMissingImplementationCause,
templateMissingImplementationNotAbstract;
import '../names.dart' show noSuchMethodName;
import '../scope.dart' show Scope;
import '../source/source_class_builder.dart';
import '../source/source_library_builder.dart' show SourceLibraryBuilder;
import '../source/source_loader.dart' show SourceLoader;
import '../type_inference/standard_bounds.dart' show TypeSchemaStandardBounds;
import '../type_inference/type_constraint_gatherer.dart'
show TypeConstraintGatherer;
import '../type_inference/type_inferrer.dart' show MixinInferrer;
import '../type_inference/type_schema.dart' show UnknownType;
import '../type_inference/type_schema_environment.dart' show TypeConstraint;
import 'combined_member_signature.dart';
import 'member_covariance.dart';
import 'forwarding_node.dart' show ForwardingNode;
const DebugLogger debug =
const bool.fromEnvironment("debug.hierarchy") ? const DebugLogger() : null;
class DebugLogger {
const DebugLogger();
void log(Object message) => print(message);
}
int compareDeclarations(ClassMember a, ClassMember b) {
if (a == b) return 0;
return ClassHierarchy.compareNames(a.name, b.name);
}
int compareClassMembers(ClassMember a, ClassMember b) {
if (a.forSetter == b.forSetter) {
return compareDeclarations(a, b);
} else if (a.forSetter) {
return 1;
} else {
return -1;
}
}
bool isNameVisibleIn(Name name, LibraryBuilder libraryBuilder) {
return !name.isPrivate || name.library == libraryBuilder.library;
}
class Tuple {
final Name name;
ClassMember _declaredMember;
ClassMember _declaredSetter;
ClassMember _mixedInMember;
ClassMember _mixedInSetter;
ClassMember _extendedMember;
ClassMember _extendedSetter;
List<ClassMember> _implementedMembers;
List<ClassMember> _implementedSetters;
Tuple.declareMember(this._declaredMember)
: assert(!_declaredMember.forSetter),
this.name = _declaredMember.name;
Tuple.mixInMember(this._mixedInMember)
: assert(!_mixedInMember.forSetter),
this.name = _mixedInMember.name;
Tuple.extendMember(this._extendedMember)
: assert(!_extendedMember.forSetter),
this.name = _extendedMember.name;
Tuple.implementMember(ClassMember implementedMember)
: assert(!implementedMember.forSetter),
this.name = implementedMember.name,
_implementedMembers = <ClassMember>[implementedMember];
Tuple.declareSetter(this._declaredSetter)
: assert(_declaredSetter.forSetter),
this.name = _declaredSetter.name;
Tuple.mixInSetter(this._mixedInSetter)
: assert(_mixedInSetter.forSetter),
this.name = _mixedInSetter.name;
Tuple.extendSetter(this._extendedSetter)
: assert(_extendedSetter.forSetter),
this.name = _extendedSetter.name;
Tuple.implementSetter(ClassMember implementedSetter)
: assert(implementedSetter.forSetter),
this.name = implementedSetter.name,
_implementedSetters = <ClassMember>[implementedSetter];
ClassMember get declaredMember => _declaredMember;
void set declaredMember(ClassMember value) {
assert(!value.forSetter);
assert(
_declaredMember == null,
"Declared member already set to $_declaredMember, "
"trying to set it to $value.");
_declaredMember = value;
}
ClassMember get declaredSetter => _declaredSetter;
void set declaredSetter(ClassMember value) {
assert(value.forSetter);
assert(
_declaredSetter == null,
"Declared setter already set to $_declaredSetter, "
"trying to set it to $value.");
_declaredSetter = value;
}
ClassMember get extendedMember => _extendedMember;
void set extendedMember(ClassMember value) {
assert(!value.forSetter);
assert(
_extendedMember == null,
"Extended member already set to $_extendedMember, "
"trying to set it to $value.");
_extendedMember = value;
}
ClassMember get extendedSetter => _extendedSetter;
void set extendedSetter(ClassMember value) {
assert(value.forSetter);
assert(
_extendedSetter == null,
"Extended setter already set to $_extendedSetter, "
"trying to set it to $value.");
_extendedSetter = value;
}
ClassMember get mixedInMember => _mixedInMember;
void set mixedInMember(ClassMember value) {
assert(!value.forSetter);
assert(
_mixedInMember == null,
"Mixed in member already set to $_mixedInMember, "
"trying to set it to $value.");
_mixedInMember = value;
}
ClassMember get mixedInSetter => _mixedInSetter;
void set mixedInSetter(ClassMember value) {
assert(value.forSetter);
assert(
_mixedInSetter == null,
"Mixed in setter already set to $_mixedInSetter, "
"trying to set it to $value.");
_mixedInSetter = value;
}
List<ClassMember> get implementedMembers => _implementedMembers;
void addImplementedMember(ClassMember value) {
assert(!value.forSetter);
_implementedMembers ??= <ClassMember>[];
_implementedMembers.add(value);
}
List<ClassMember> get implementedSetters => _implementedSetters;
void addImplementedSetter(ClassMember value) {
assert(value.forSetter);
_implementedSetters ??= <ClassMember>[];
_implementedSetters.add(value);
}
@override
String toString() {
StringBuffer sb = new StringBuffer();
String comma = '';
sb.write('Tuple(');
if (_declaredMember != null) {
sb.write(comma);
sb.write('declaredMember=');
sb.write(_declaredMember);
comma = ',';
}
if (_declaredSetter != null) {
sb.write(comma);
sb.write('declaredSetter=');
sb.write(_declaredSetter);
comma = ',';
}
if (_mixedInMember != null) {
sb.write(comma);
sb.write('mixedInMember=');
sb.write(_mixedInMember);
comma = ',';
}
if (_mixedInSetter != null) {
sb.write(comma);
sb.write('mixedInSetter=');
sb.write(_mixedInSetter);
comma = ',';
}
if (_extendedMember != null) {
sb.write(comma);
sb.write('extendedMember=');
sb.write(_extendedMember);
comma = ',';
}
if (_extendedSetter != null) {
sb.write(comma);
sb.write('extendedSetter=');
sb.write(_extendedSetter);
comma = ',';
}
if (_implementedMembers != null) {
sb.write(comma);
sb.write('implementedMembers=');
sb.write(_implementedMembers);
comma = ',';
}
if (_implementedSetters != null) {
sb.write(comma);
sb.write('implementedSetters=');
sb.write(_implementedSetters);
comma = ',';
}
sb.write(')');
return sb.toString();
}
}
abstract class ClassMember {
Name get name;
bool get isStatic;
bool get isField;
bool get isAssignable;
bool get isSetter;
bool get isGetter;
bool get isFinal;
bool get isConst;
bool get forSetter;
/// Returns `true` if this member corresponds to a declaration in the source
/// code.
bool get isSourceDeclaration;
/// Returns `true` if this member is a field, getter or setter.
bool get isProperty;
/// Computes the [Member] node resulting from this class member.
Member getMember(ClassHierarchyBuilder hierarchy);
/// Returns the member [Covariance] for this class member.
Covariance getCovariance(ClassHierarchyBuilder hierarchy);
bool get isDuplicate;
String get fullName;
String get fullNameForErrors;
ClassBuilder get classBuilder;
/// Returns `true` if this class member is declared in Object from dart:core.
bool isObjectMember(ClassBuilder objectClass);
Uri get fileUri;
int get charOffset;
/// Returns `true` if this class member is an interface member.
bool get isAbstract;
/// Returns `true` if this member doesn't corresponds to a declaration in the
/// source code.
bool get isSynthesized;
// If `true` this member is not part of the interface but only part of the
// class members.
//
// This is `true` for instance for synthesized fields added for the late
// lowering.
bool get isInternalImplementation;
/// Returns `true` if this member is composed from a list of class members
/// accessible through [declarations].
bool get hasDeclarations;
/// If [hasDeclaration] is `true`, this returns the list of class members
/// from which this class member is composed.
///
/// This is used in [unfoldDeclarations] to retrieve all underlying member
/// source declarations, and in [toSet] to retrieve all members used for
/// this class member wrt. certain level of the hierarchy.
/// TODO(johnniwinther): Can the use of [toSet] be replaced with a direct
/// use of [declarations]?
List<ClassMember> get declarations;
/// The interface member corresponding to this member.
///
/// If this member is declared on the source, the interface member is
/// the member itself. For instance
///
/// abstract class Class {
/// void concreteMethod() {}
/// void abstractMethod();
/// }
///
/// the interface members for `concreteMethod` and `abstractMethod` are the
/// members themselves.
///
/// If this member is a synthesized interface member, the
/// interface member is the member itself. For instance
///
/// abstract class Interface1 {
/// void method() {}
/// }
/// abstract class Interface2 {
/// void method() {}
/// }
/// abstract class Class implements Interface1, Interface2 {}
///
/// the interface member for `method` in `Class` is the synthesized interface
/// member created for the implemented members `Interface1.method` and
/// `Interface2.method`.
///
/// If this member is a concrete member that implements an interface member,
/// the interface member is the implemented interface member. For instance
///
/// class Super {
/// void method() {}
/// }
/// class Interface {
/// void method() {}
/// }
/// class Class extends Super implements Interface {}
///
/// the interface member for `Super.method` implementing `method` in `Class`
/// is the synthesized interface member created for the implemented members
/// `Super.method` and `Interface.method`.
ClassMember get interfaceMember;
void inferType(ClassHierarchyBuilder hierarchy);
void registerOverrideDependency(Set<ClassMember> overriddenMembers);
/// Returns `true` if this has the same underlying declaration as [other].
///
/// This is used for avoiding unnecessary checks and can this trivially
/// return `false`.
bool isSameDeclaration(ClassMember other);
}
bool hasSameSignature(FunctionNode a, FunctionNode b) {
List<TypeParameter> aTypeParameters = a.typeParameters;
List<TypeParameter> bTypeParameters = b.typeParameters;
int typeParameterCount = aTypeParameters.length;
if (typeParameterCount != bTypeParameters.length) return false;
Substitution substitution;
if (typeParameterCount != 0) {
List<DartType> types = new List<DartType>.filled(typeParameterCount, null);
for (int i = 0; i < typeParameterCount; i++) {
types[i] = new TypeParameterType.forAlphaRenaming(
bTypeParameters[i], aTypeParameters[i]);
}
substitution = Substitution.fromPairs(bTypeParameters, types);
for (int i = 0; i < typeParameterCount; i++) {
DartType aBound = aTypeParameters[i].bound;
DartType bBound = substitution.substituteType(bTypeParameters[i].bound);
if (aBound != bBound) return false;
}
}
if (a.requiredParameterCount != b.requiredParameterCount) return false;
List<VariableDeclaration> aPositionalParameters = a.positionalParameters;
List<VariableDeclaration> bPositionalParameters = b.positionalParameters;
if (aPositionalParameters.length != bPositionalParameters.length) {
return false;
}
for (int i = 0; i < aPositionalParameters.length; i++) {
VariableDeclaration aParameter = aPositionalParameters[i];
VariableDeclaration bParameter = bPositionalParameters[i];
if (aParameter.isCovariant != bParameter.isCovariant) return false;
DartType aType = aParameter.type;
DartType bType = bParameter.type;
if (substitution != null) {
bType = substitution.substituteType(bType);
}
if (aType != bType) return false;
}
List<VariableDeclaration> aNamedParameters = a.namedParameters;
List<VariableDeclaration> bNamedParameters = b.namedParameters;
if (aNamedParameters.length != bNamedParameters.length) return false;
for (int i = 0; i < aNamedParameters.length; i++) {
VariableDeclaration aParameter = aNamedParameters[i];
VariableDeclaration bParameter = bNamedParameters[i];
if (aParameter.isCovariant != bParameter.isCovariant) return false;
if (aParameter.name != bParameter.name) return false;
DartType aType = aParameter.type;
DartType bType = bParameter.type;
if (substitution != null) {
bType = substitution.substituteType(bType);
}
if (aType != bType) return false;
}
DartType aReturnType = a.returnType;
DartType bReturnType = b.returnType;
if (substitution != null) {
bReturnType = substitution.substituteType(bReturnType);
}
return aReturnType == bReturnType;
}
class ClassHierarchyBuilder implements ClassHierarchyBase {
final Map<Class, ClassHierarchyNode> nodes = <Class, ClassHierarchyNode>{};
final Map<ClassBuilder, Map<Class, Substitution>> substitutions =
<ClassBuilder, Map<Class, Substitution>>{};
final ClassBuilder objectClassBuilder;
final Loader loader;
final Class objectClass;
final Class futureClass;
final Class functionClass;
final List<DelayedTypeComputation> _delayedTypeComputations =
<DelayedTypeComputation>[];
final List<DelayedCheck> _delayedChecks = <DelayedCheck>[];
final List<ClassMember> _delayedMemberComputations = <ClassMember>[];
final CoreTypes coreTypes;
Types types;
ClassHierarchyBuilder(this.objectClassBuilder, this.loader, this.coreTypes)
: objectClass = objectClassBuilder.cls,
futureClass = coreTypes.futureClass,
functionClass = coreTypes.functionClass {
types = new Types(this);
}
void clear() {
nodes.clear();
substitutions.clear();
_delayedChecks.clear();
_delayedTypeComputations.clear();
_delayedMemberComputations.clear();
}
void registerDelayedTypeComputation(DelayedTypeComputation computation) {
_delayedTypeComputations.add(computation);
}
void registerOverrideCheck(SourceClassBuilder classBuilder,
ClassMember declaredMember, Set<ClassMember> overriddenMembers) {
_delayedChecks.add(new DelayedOverrideCheck(
classBuilder, declaredMember, overriddenMembers));
}
void registerGetterSetterCheck(
SourceClassBuilder classBuilder, ClassMember getter, ClassMember setter) {
_delayedChecks
.add(new DelayedGetterSetterCheck(classBuilder, getter, setter));
}
void registerMemberComputation(ClassMember member) {
_delayedMemberComputations.add(member);
}
List<DelayedTypeComputation> takeDelayedTypeComputations() {
List<DelayedTypeComputation> list = _delayedTypeComputations.toList();
_delayedTypeComputations.clear();
return list;
}
List<DelayedCheck> takeDelayedChecks() {
List<DelayedCheck> list = _delayedChecks.toList();
_delayedChecks.clear();
return list;
}
List<ClassMember> takeDelayedMemberComputations() {
List<ClassMember> list = _delayedMemberComputations.toList();
_delayedMemberComputations.clear();
return list;
}
void inferFieldType(SourceFieldBuilder declaredMember,
Iterable<ClassMember> overriddenMembers) {
ClassHierarchyNodeBuilder.inferFieldType(
this,
declaredMember.classBuilder,
substitutions[declaredMember.classBuilder],
declaredMember,
overriddenMembers);
}
void inferGetterType(SourceProcedureBuilder declaredMember,
Iterable<ClassMember> overriddenMembers) {
ClassHierarchyNodeBuilder.inferGetterType(
this,
declaredMember.classBuilder,
substitutions[declaredMember.classBuilder],
declaredMember,
overriddenMembers);
}
void inferSetterType(SourceProcedureBuilder declaredMember,
Iterable<ClassMember> overriddenMembers) {
ClassHierarchyNodeBuilder.inferSetterType(
this,
declaredMember.classBuilder,
substitutions[declaredMember.classBuilder],
declaredMember,
overriddenMembers);
}
void inferMethodType(SourceProcedureBuilder declaredMember,
Iterable<ClassMember> overriddenMembers) {
ClassHierarchyNodeBuilder.inferMethodType(
this,
declaredMember.classBuilder,
substitutions[declaredMember.classBuilder],
declaredMember,
overriddenMembers);
}
ClassHierarchyNode getNodeFromClassBuilder(ClassBuilder classBuilder) {
return nodes[classBuilder.cls] ??= new ClassHierarchyNodeBuilder(
this, classBuilder, substitutions[classBuilder] ??= {})
.build();
}
ClassHierarchyNode getNodeFromTypeBuilder(TypeBuilder type) {
ClassBuilder cls = getClass(type);
return cls == null ? null : getNodeFromClassBuilder(cls);
}
ClassHierarchyNode getNodeFromClass(Class cls) {
return nodes[cls] ??
getNodeFromClassBuilder(loader.computeClassBuilderFromTargetClass(cls));
}
Supertype asSupertypeOf(InterfaceType subtype, Class supertype) {
if (subtype.classNode == supertype) {
return new Supertype(supertype, subtype.typeArguments);
}
ClassHierarchyNode clsNode = getNodeFromClass(subtype.classNode);
ClassHierarchyNode supertypeNode = getNodeFromClass(supertype);
List<Supertype> superclasses = clsNode.superclasses;
int depth = supertypeNode.depth;
if (depth < superclasses.length) {
Supertype superclass = superclasses[depth];
if (superclass.classNode == supertype) {
return Substitution.fromInterfaceType(subtype)
.substituteSupertype(superclass);
}
}
List<Supertype> superinterfaces = clsNode.interfaces;
for (int i = 0; i < superinterfaces.length; i++) {
Supertype superinterface = superinterfaces[i];
if (superinterface.classNode == supertype) {
return Substitution.fromInterfaceType(subtype)
.substituteSupertype(superinterface);
}
}
return null;
}
InterfaceType getTypeAsInstanceOf(
InterfaceType type, Class superclass, Library clientLibrary) {
if (type.classNode == superclass) return type;
return asSupertypeOf(type, superclass)
.asInterfaceType
.withDeclaredNullability(type.nullability);
}
List<DartType> getTypeArgumentsAsInstanceOf(
InterfaceType type, Class superclass) {
if (type.classNode == superclass) return type.typeArguments;
return asSupertypeOf(type, superclass)?.typeArguments;
}
@override
InterfaceType getLegacyLeastUpperBound(
InterfaceType type1, InterfaceType type2, Library clientLibrary) {
if (type1 == type2) return type1;
// LLUB(Null, List<dynamic>*) works differently for opt-in and opt-out
// libraries. In opt-out libraries the legacy behavior is preserved, so
// LLUB(Null, List<dynamic>*) = List<dynamic>*. In opt-out libraries the
// rules imply that LLUB(Null, List<dynamic>*) = List<dynamic>?.
if (!clientLibrary.isNonNullableByDefault) {
if (type1 is NullType) {
return type2;
}
if (type2 is NullType) {
return type1;
}
}
ClassHierarchyNode node1 = getNodeFromClass(type1.classNode);
ClassHierarchyNode node2 = getNodeFromClass(type2.classNode);
Set<ClassHierarchyNode> nodes1 = node1.computeAllSuperNodes(this).toSet();
List<ClassHierarchyNode> nodes2 = node2.computeAllSuperNodes(this);
List<ClassHierarchyNode> common = <ClassHierarchyNode>[];
for (int i = 0; i < nodes2.length; i++) {
ClassHierarchyNode node = nodes2[i];
if (node == null) continue;
if (node.classBuilder.cls.isAnonymousMixin) {
// Never find unnamed mixin application in least upper bound.
continue;
}
if (nodes1.contains(node)) {
DartType candidate1 =
getTypeAsInstanceOf(type1, node.classBuilder.cls, clientLibrary);
DartType candidate2 =
getTypeAsInstanceOf(type2, node.classBuilder.cls, clientLibrary);
if (candidate1 == candidate2) {
common.add(node);
}
}
}
if (common.length == 1) {
return coreTypes.objectRawType(
uniteNullabilities(type1.nullability, type2.nullability));
}
common.sort(ClassHierarchyNode.compareMaxInheritancePath);
for (int i = 0; i < common.length - 1; i++) {
ClassHierarchyNode node = common[i];
if (node.maxInheritancePath != common[i + 1].maxInheritancePath) {
return getTypeAsInstanceOf(type1, node.classBuilder.cls, clientLibrary)
.withDeclaredNullability(
uniteNullabilities(type1.nullability, type2.nullability));
} else {
do {
i++;
} while (node.maxInheritancePath == common[i + 1].maxInheritancePath);
}
}
return coreTypes.objectRawType(
uniteNullabilities(type1.nullability, type2.nullability));
}
Member getInterfaceMember(Class cls, Name name, {bool setter: false}) {
return getNodeFromClass(cls)
.getInterfaceMember(name, setter)
?.getMember(this);
}
ClassMember getInterfaceClassMember(Class cls, Name name,
{bool setter: false}) {
return getNodeFromClass(cls).getInterfaceMember(name, setter);
}
static ClassHierarchyBuilder build(ClassBuilder objectClass,
List<ClassBuilder> classes, SourceLoader loader, CoreTypes coreTypes) {
ClassHierarchyBuilder hierarchy =
new ClassHierarchyBuilder(objectClass, loader, coreTypes);
for (int i = 0; i < classes.length; i++) {
ClassBuilder classBuilder = classes[i];
if (!classBuilder.isPatch) {
hierarchy.nodes[classBuilder.cls] = new ClassHierarchyNodeBuilder(
hierarchy,
classBuilder,
hierarchy.substitutions[classBuilder] ??= {})
.build();
} else {
// TODO(ahe): Merge the injected members of patch into the hierarchy
// node of `cls.origin`.
}
}
return hierarchy;
}
void computeTypes() {
List<DelayedTypeComputation> typeComputations =
takeDelayedTypeComputations();
for (int i = 0; i < typeComputations.length; i++) {
typeComputations[i].compute(this);
}
}
}
class ClassHierarchyNodeBuilder {
final ClassHierarchyBuilder hierarchy;
final ClassBuilder classBuilder;
bool hasNoSuchMethod = false;
final Map<Class, Substitution> substitutions;
ClassHierarchyNodeBuilder(
this.hierarchy, this.classBuilder, this.substitutions);
ClassBuilder get objectClass => hierarchy.objectClassBuilder;
bool get shouldModifyKernel =>
classBuilder.library.loader == hierarchy.loader;
ClassMember checkInheritanceConflict(ClassMember a, ClassMember b) {
if (a.isStatic || a.isProperty != b.isProperty) {
reportInheritanceConflict(a, b);
return a;
}
return null;
}
static void inferMethodType(
ClassHierarchyBuilder hierarchy,
ClassBuilder classBuilder,
Map<Class, Substitution> substitutions,
SourceProcedureBuilder declaredMember,
Iterable<ClassMember> overriddenMembers) {
assert(!declaredMember.isGetter && !declaredMember.isSetter);
if (declaredMember.classBuilder == classBuilder &&
(declaredMember.returnType == null ||
declaredMember.formals != null &&
declaredMember.formals
.any((parameter) => parameter.type == null))) {
Procedure declaredProcedure = declaredMember.member;
FunctionNode declaredFunction = declaredProcedure.function;
List<TypeParameter> declaredTypeParameters =
declaredFunction.typeParameters;
List<VariableDeclaration> declaredPositional =
declaredFunction.positionalParameters;
List<VariableDeclaration> declaredNamed =
declaredFunction.namedParameters;
declaredNamed = declaredNamed.toList()..sort(compareNamedParameters);
DartType inferredReturnType;
Map<FormalParameterBuilder, DartType> inferredParameterTypes = {};
Set<ClassMember> overriddenMemberSet =
toSet(declaredMember.classBuilder, overriddenMembers);
CombinedClassMemberSignature combinedMemberSignature =
new CombinedClassMemberSignature(
hierarchy, classBuilder, overriddenMemberSet.toList(),
forSetter: false);
FunctionType combinedMemberSignatureType = combinedMemberSignature
.getCombinedSignatureTypeInContext(declaredTypeParameters);
bool cantInferReturnType = false;
List<FormalParameterBuilder> cantInferParameterTypes;
if (declaredMember.returnType == null) {
if (combinedMemberSignatureType == null) {
inferredReturnType = const InvalidType();
cantInferReturnType = true;
} else {
inferredReturnType = combinedMemberSignatureType.returnType;
}
}
if (declaredMember.formals != null) {
for (int i = 0; i < declaredPositional.length; i++) {
FormalParameterBuilder declaredParameter = declaredMember.formals[i];
if (declaredParameter.type != null) {
continue;
}
DartType inferredParameterType;
if (combinedMemberSignatureType == null) {
inferredParameterType = const InvalidType();
cantInferParameterTypes ??= [];
cantInferParameterTypes.add(declaredParameter);
} else if (i <
combinedMemberSignatureType.positionalParameters.length) {
inferredParameterType =
combinedMemberSignatureType.positionalParameters[i];
}
inferredParameterTypes[declaredParameter] = inferredParameterType;
}
Map<String, DartType> namedParameterTypes;
for (int i = declaredPositional.length;
i < declaredMember.formals.length;
i++) {
FormalParameterBuilder declaredParameter = declaredMember.formals[i];
if (declaredParameter.type != null) {
continue;
}
DartType inferredParameterType;
if (combinedMemberSignatureType == null) {
inferredParameterType = const InvalidType();
cantInferParameterTypes ??= [];
cantInferParameterTypes.add(declaredParameter);
} else {
if (namedParameterTypes == null) {
namedParameterTypes = {};
for (NamedType namedType
in combinedMemberSignatureType.namedParameters) {
namedParameterTypes[namedType.name] = namedType.type;
}
}
inferredParameterType = namedParameterTypes[declaredParameter.name];
}
inferredParameterTypes[declaredParameter] = inferredParameterType;
}
}
if ((cantInferReturnType && cantInferParameterTypes != null) ||
(cantInferParameterTypes != null &&
cantInferParameterTypes.length > 1)) {
reportCantInferTypes(
classBuilder, declaredMember, hierarchy, overriddenMembers);
} else if (cantInferReturnType) {
reportCantInferReturnType(
classBuilder, declaredMember, hierarchy, overriddenMembers);
} else if (cantInferParameterTypes != null) {
reportCantInferParameterType(classBuilder,
cantInferParameterTypes.single, hierarchy, overriddenMembers);
}
if (declaredMember.returnType == null) {
inferredReturnType ??= const DynamicType();
declaredFunction.returnType = inferredReturnType;
}
if (declaredMember.formals != null) {
for (FormalParameterBuilder declaredParameter
in declaredMember.formals) {
if (declaredParameter.type == null) {
DartType inferredParameterType =
inferredParameterTypes[declaredParameter] ??
const DynamicType();
declaredParameter.variable.type = inferredParameterType;
}
}
}
}
}
void inferMethodSignature(ClassHierarchyBuilder hierarchy,
ClassMember declaredMember, Iterable<ClassMember> overriddenMembers) {
assert(!declaredMember.isGetter && !declaredMember.isSetter);
// Trigger computation of method type.
Procedure declaredProcedure = declaredMember.getMember(hierarchy);
for (ClassMember overriddenMember
in toSet(declaredMember.classBuilder, overriddenMembers)) {
Covariance covariance = overriddenMember.getCovariance(hierarchy);
covariance.applyCovariance(declaredProcedure);
}
}
void inferGetterSignature(ClassHierarchyBuilder hierarchy,
ClassMember declaredMember, Iterable<ClassMember> overriddenMembers) {
assert(declaredMember.isGetter);
// Trigger computation of the getter type.
declaredMember.getMember(hierarchy);
// Otherwise nothing to do. Getters have no variance.
}
void inferSetterSignature(ClassHierarchyBuilder hierarchy,
ClassMember declaredMember, Iterable<ClassMember> overriddenMembers) {
assert(declaredMember.isSetter);
// Trigger computation of the getter type.
Procedure declaredSetter = declaredMember.getMember(hierarchy);
for (ClassMember overriddenMember
in toSet(declaredMember.classBuilder, overriddenMembers)) {
Covariance covariance = overriddenMember.getCovariance(hierarchy);
covariance.applyCovariance(declaredSetter);
}
}
static void inferGetterType(
ClassHierarchyBuilder hierarchy,
ClassBuilder classBuilder,
Map<Class, Substitution> substitutions,
SourceProcedureBuilder declaredMember,
Iterable<ClassMember> overriddenMembers) {
assert(declaredMember.isGetter);
if (declaredMember.classBuilder == classBuilder &&
declaredMember.returnType == null) {
DartType inferredType;
overriddenMembers = toSet(classBuilder, overriddenMembers);
List<ClassMember> overriddenGetters = [];
List<ClassMember> overriddenSetters = [];
for (ClassMember overriddenMember in overriddenMembers) {
if (overriddenMember.forSetter) {
overriddenSetters.add(overriddenMember);
} else {
overriddenGetters.add(overriddenMember);
}
}
void inferFrom(List<ClassMember> members, {bool forSetter}) {
assert(forSetter != null);
CombinedClassMemberSignature combinedMemberSignature =
new CombinedClassMemberSignature(hierarchy, classBuilder, members,
forSetter: forSetter);
DartType combinedMemberSignatureType =
combinedMemberSignature.combinedMemberSignatureType;
if (combinedMemberSignatureType == null) {
inferredType = const InvalidType();
reportCantInferReturnType(
classBuilder, declaredMember, hierarchy, members);
} else {
inferredType = combinedMemberSignatureType;
}
}
if (overriddenGetters.isNotEmpty) {
// 1) The return type of a getter, parameter type of a setter or type
// of a field which overrides/implements only one or more getters is
// inferred to be the return type of the combined member signature of
// said getter in the direct superinterfaces.
// 2) The return type of a getter which overrides/implements both a
// setter and a getter is inferred to be the return type of the
// combined member signature of said getter in the direct
// superinterfaces.
inferFrom(overriddenGetters, forSetter: false);
} else {
// The return type of a getter, parameter type of a setter or type of
// a field which overrides/implements only one or more setters is
// inferred to be the parameter type of the combined member signature
// of said setter in the direct superinterfaces.
inferFrom(overriddenSetters, forSetter: true);
}
inferredType ??= const DynamicType();
declaredMember.procedure.function.returnType = inferredType;
}
}
static void inferSetterType(
ClassHierarchyBuilder hierarchy,
ClassBuilder classBuilder,
Map<Class, Substitution> substitutions,
SourceProcedureBuilder declaredMember,
Iterable<ClassMember> overriddenMembers) {
assert(declaredMember.isSetter);
FormalParameterBuilder parameter = declaredMember.formals.first;
if (declaredMember.classBuilder == classBuilder && parameter.type == null) {
DartType inferredType;
overriddenMembers = toSet(classBuilder, overriddenMembers);
List<ClassMember> overriddenGetters = [];
List<ClassMember> overriddenSetters = [];
for (ClassMember overriddenMember in overriddenMembers) {
if (overriddenMember.forSetter) {
overriddenSetters.add(overriddenMember);
} else {
overriddenGetters.add(overriddenMember);
}
}
void inferFrom(List<ClassMember> members, {bool forSetter}) {
assert(forSetter != null);
CombinedClassMemberSignature combinedMemberSignature =
new CombinedClassMemberSignature(hierarchy, classBuilder, members,
forSetter: forSetter);
DartType combinedMemberSignatureType =
combinedMemberSignature.combinedMemberSignatureType;
if (combinedMemberSignatureType == null) {
inferredType = const InvalidType();
reportCantInferReturnType(
classBuilder, declaredMember, hierarchy, members);
} else {
inferredType = combinedMemberSignatureType;
}
}
if (overriddenSetters.isNotEmpty) {
// 1) The return type of a getter, parameter type of a setter or type
// of a field which overrides/implements only one or more setters is
// inferred to be the parameter type of the combined member signature
// of said setter in the direct superinterfaces.
//
// 2) The parameter type of a setter which overrides/implements both a
// setter and a getter is inferred to be the parameter type of the
// combined member signature of said setter in the direct
// superinterfaces.
inferFrom(overriddenSetters, forSetter: true);
} else {
// The return type of a getter, parameter type of a setter or type of
// a field which overrides/implements only one or more getters is
// inferred to be the return type of the combined member signature of
// said getter in the direct superinterfaces.
inferFrom(overriddenGetters, forSetter: false);
}
inferredType ??= const DynamicType();
parameter.variable.type = inferredType;
}
}
/// Merge the [inheritedType] with the currently [inferredType] using
/// nnbd-top-merge or legacy-top-merge depending on whether [classBuilder] is
/// defined in an opt-in or opt-out library. If the types could not be merged
/// `null` is returned and an error should be reported by the caller.
static DartType mergeTypeInLibrary(
ClassHierarchyBuilder hierarchy,
ClassBuilder classBuilder,
DartType inferredType,
DartType inheritedType) {
if (classBuilder.library.isNonNullableByDefault) {
if (inferredType == null) {
return inheritedType;
} else {
return nnbdTopMerge(
hierarchy.coreTypes,
norm(hierarchy.coreTypes, inferredType),
norm(hierarchy.coreTypes, inheritedType));
}
} else {
inheritedType = legacyErasure(inheritedType);
if (inferredType == null) {
return inheritedType;
} else {
if (inferredType is DynamicType &&
inheritedType == hierarchy.coreTypes.objectLegacyRawType) {
return inferredType;
} else if (inheritedType is DynamicType &&
inferredType == hierarchy.coreTypes.objectLegacyRawType) {
return inheritedType;
}
if (inferredType != inheritedType) {
return null;
}
return inferredType;
}
}
}
/// Infers the field type of [fieldBuilder] based on [overriddenMembers].
static void inferFieldType(
ClassHierarchyBuilder hierarchy,
ClassBuilder classBuilder,
Map<Class, Substitution> substitutions,
SourceFieldBuilder fieldBuilder,
Iterable<ClassMember> overriddenMembers) {
if (fieldBuilder.classBuilder == classBuilder &&
fieldBuilder.type == null) {
DartType inferredType;
overriddenMembers = toSet(classBuilder, overriddenMembers);
List<ClassMember> overriddenGetters = [];
List<ClassMember> overriddenSetters = [];
for (ClassMember overriddenMember in overriddenMembers) {
if (overriddenMember.forSetter) {
overriddenSetters.add(overriddenMember);
} else {
overriddenGetters.add(overriddenMember);
}
}
DartType inferFrom(List<ClassMember> members, {bool forSetter}) {
assert(forSetter != null);
CombinedClassMemberSignature combinedMemberSignature =
new CombinedClassMemberSignature(hierarchy, classBuilder, members,
forSetter: forSetter);
return combinedMemberSignature.combinedMemberSignatureType;
}
DartType combinedMemberSignatureType;
if (fieldBuilder.isAssignable &&
overriddenGetters.isNotEmpty &&
overriddenSetters.isNotEmpty) {
// The type of a non-final field which overrides/implements both a
// setter and a getter is inferred to be the parameter type of the
// combined member signature of said setter in the direct
// superinterfaces, if this type is the same as the return type of the
// combined member signature of said getter in the direct
// superinterfaces. If the types are not the same then inference fails
// with an error.
DartType getterType = inferFrom(overriddenGetters, forSetter: false);
DartType setterType = inferFrom(overriddenSetters, forSetter: true);
if (getterType == setterType) {
combinedMemberSignatureType = getterType;
}
} else if (overriddenGetters.isNotEmpty) {
// 1) The return type of a getter, parameter type of a setter or type
// of a field which overrides/implements only one or more getters is
// inferred to be the return type of the combined member signature of
// said getter in the direct superinterfaces.
//
// 2) The type of a final field which overrides/implements both a
// setter and a getter is inferred to be the return type of the
// combined member signature of said getter in the direct
// superinterfaces.
combinedMemberSignatureType =
inferFrom(overriddenGetters, forSetter: false);
} else {
// The return type of a getter, parameter type of a setter or type of
// a field which overrides/implements only one or more setters is
// inferred to be the parameter type of the combined member signature
// of said setter in the direct superinterfaces.
combinedMemberSignatureType =
inferFrom(overriddenSetters, forSetter: true);
}
if (combinedMemberSignatureType == null) {
inferredType = const InvalidType();
reportCantInferFieldType(classBuilder, fieldBuilder, overriddenMembers);
} else {
inferredType = combinedMemberSignatureType;
}
inferredType ??= const DynamicType();
fieldBuilder.fieldType = inferredType;
}
}
/// Infers the field signature of [declaredMember] based on
/// [overriddenMembers].
void inferFieldSignature(ClassHierarchyBuilder hierarchy,
ClassMember declaredMember, Iterable<ClassMember> overriddenMembers) {
Field declaredField = declaredMember.getMember(hierarchy);
for (ClassMember overriddenMember
in toSet(declaredMember.classBuilder, overriddenMembers)) {
Covariance covariance = overriddenMember.getCovariance(hierarchy);
covariance.applyCovariance(declaredField);
}
}
void reportInheritanceConflict(ClassMember a, ClassMember b) {
String name = a.fullNameForErrors;
if (a.classBuilder != b.classBuilder) {
if (a.classBuilder == classBuilder) {
classBuilder.addProblem(
messageDeclaredMemberConflictsWithInheritedMember,
a.charOffset,
name.length,
context: <LocatedMessage>[
messageDeclaredMemberConflictsWithInheritedMemberCause
.withLocation(b.fileUri, b.charOffset, name.length)
]);
} else if (b.classBuilder == classBuilder) {
classBuilder.addProblem(
messageDeclaredMemberConflictsWithInheritedMember,
b.charOffset,
name.length,
context: <LocatedMessage>[
messageDeclaredMemberConflictsWithInheritedMemberCause
.withLocation(a.fileUri, a.charOffset, name.length)
]);
} else {
classBuilder.addProblem(messageInheritedMembersConflict,
classBuilder.charOffset, classBuilder.fullNameForErrors.length,
context: _inheritedConflictContext(a, b));
}
} else if (a.isStatic != b.isStatic) {
ClassMember staticMember;
ClassMember instanceMember;
if (a.isStatic) {
staticMember = a;
instanceMember = b;
} else {
staticMember = b;
instanceMember = a;
}
classBuilder.library.addProblem(messageStaticAndInstanceConflict,
staticMember.charOffset, name.length, staticMember.fileUri,
context: <LocatedMessage>[
messageStaticAndInstanceConflictCause.withLocation(
instanceMember.fileUri, instanceMember.charOffset, name.length)
]);
} else {
// This message can be reported twice (when merging localMembers with
// classSetters, or localSetters with classMembers). By ensuring that
// we always report the one with higher charOffset as the duplicate,
// the message duplication logic ensures that we only report this
// problem once.
ClassMember existing;
ClassMember duplicate;
assert(a.fileUri == b.fileUri);
if (a.charOffset < b.charOffset) {
existing = a;
duplicate = b;
} else {
existing = b;
duplicate = a;
}
classBuilder.library.addProblem(
templateDuplicatedDeclaration.withArguments(name),
duplicate.charOffset,
name.length,
duplicate.fileUri,
context: <LocatedMessage>[
templateDuplicatedDeclarationCause.withArguments(name).withLocation(
existing.fileUri, existing.charOffset, name.length)
]);
}
}
ClassHierarchyNode build() {
assert(!classBuilder.isPatch);
ClassHierarchyNode supernode;
if (objectClass != classBuilder.origin) {
supernode =
hierarchy.getNodeFromTypeBuilder(classBuilder.supertypeBuilder);
if (supernode == null) {
supernode = hierarchy.getNodeFromClassBuilder(objectClass);
}
assert(supernode != null);
}
/// Set to `true` if the class needs interfaces, that is, if it has any
/// members where the interface member is different from its corresponding
/// class members.
///
/// This is an optimization to avoid unnecessary computation of interface
/// members.
bool hasInterfaces = false;
Map<Name, Tuple> memberMap = {};
Scope scope = classBuilder.scope;
for (MemberBuilder memberBuilder in scope.localMembers) {
for (ClassMember classMember in memberBuilder.localMembers) {
if (classMember.isAbstract) {
hasInterfaces = true;
}
Tuple tuple = memberMap[classMember.name];
if (tuple == null) {
memberMap[classMember.name] = new Tuple.declareMember(classMember);
} else {
tuple.declaredMember = classMember;
}
}
for (ClassMember classMember in memberBuilder.localSetters) {
if (classMember.isAbstract) {
hasInterfaces = true;
}
Tuple tuple = memberMap[classMember.name];
if (tuple == null) {
memberMap[classMember.name] = new Tuple.declareSetter(classMember);
} else {
tuple.declaredSetter = classMember;
}
}
}
for (MemberBuilder memberBuilder in scope.localSetters) {
for (ClassMember classMember in memberBuilder.localMembers) {
if (classMember.isAbstract) {
hasInterfaces = true;
}
Tuple tuple = memberMap[classMember.name];
if (tuple == null) {
memberMap[classMember.name] = new Tuple.declareMember(classMember);
} else {
tuple.declaredMember = classMember;
}
}
for (ClassMember classMember in memberBuilder.localSetters) {
if (classMember.isAbstract) {
hasInterfaces = true;
}
Tuple tuple = memberMap[classMember.name];
if (tuple == null) {
memberMap[classMember.name] = new Tuple.declareSetter(classMember);
} else {
tuple.declaredSetter = classMember;
}
}
}
if (classBuilder.isMixinApplication) {
TypeBuilder mixedInTypeBuilder = classBuilder.mixedInTypeBuilder;
TypeDeclarationBuilder mixin = mixedInTypeBuilder.declaration;
inferMixinApplication();
while (mixin.isNamedMixinApplication) {
ClassBuilder named = mixin;
mixedInTypeBuilder = named.mixedInTypeBuilder;
mixin = mixedInTypeBuilder.declaration;
}
if (mixin is TypeAliasBuilder) {
TypeAliasBuilder aliasBuilder = mixin;
NamedTypeBuilder namedBuilder = mixedInTypeBuilder;
mixin = aliasBuilder.unaliasDeclaration(namedBuilder.arguments,
isUsedAsClass: true,
usedAsClassCharOffset: namedBuilder.charOffset,
usedAsClassFileUri: namedBuilder.fileUri);
}
if (mixin is ClassBuilder) {
scope = mixin.scope.computeMixinScope();
for (MemberBuilder memberBuilder in scope.localMembers) {
for (ClassMember classMember in memberBuilder.localMembers) {
if (classMember.isAbstract) {
hasInterfaces = true;
}
Tuple tuple = memberMap[classMember.name];
if (tuple == null) {
memberMap[classMember.name] = new Tuple.mixInMember(classMember);
} else {
tuple.mixedInMember = classMember;
}
}
for (ClassMember classMember in memberBuilder.localSetters) {
if (classMember.isAbstract) {
hasInterfaces = true;
}
Tuple tuple = memberMap[classMember.name];
if (tuple == null) {
memberMap[classMember.name] = new Tuple.mixInSetter(classMember);
} else {
tuple.mixedInSetter = classMember;
}
}
}
for (MemberBuilder memberBuilder in scope.localSetters) {
for (ClassMember classMember in memberBuilder.localMembers) {
if (classMember.isAbstract) {
hasInterfaces = true;
}
Tuple tuple = memberMap[classMember.name];
if (tuple == null) {
memberMap[classMember.name] = new Tuple.mixInMember(classMember);
} else {
tuple.mixedInMember = classMember;
}
}
for (ClassMember classMember in memberBuilder.localSetters) {
if (classMember.isAbstract) {
hasInterfaces = true;
}
Tuple tuple = memberMap[classMember.name];
if (tuple == null) {
memberMap[classMember.name] = new Tuple.mixInSetter(classMember);
} else {
tuple.mixedInSetter = classMember;
}
}
}
}
}
List<Supertype> superclasses;
List<Supertype> interfaces;
int maxInheritancePath;
void extend(Map<Name, ClassMember> superClassMembers) {
if (superClassMembers == null) return;
for (Name name in superClassMembers.keys) {
ClassMember superClassMember = superClassMembers[name];
Tuple tuple = memberMap[name];
if (tuple != null) {
if (superClassMember.forSetter) {
tuple.extendedSetter = superClassMember;
} else {
tuple.extendedMember = superClassMember;
}
} else {
if (superClassMember.forSetter) {
memberMap[name] = new Tuple.extendSetter(superClassMember);
} else {
memberMap[name] = new Tuple.extendMember(superClassMember);
}
}
}
}
void implement(Map<Name, ClassMember> superInterfaceMembers) {
if (superInterfaceMembers == null) return;
for (Name name in superInterfaceMembers.keys) {
ClassMember superInterfaceMember = superInterfaceMembers[name];
Tuple tuple = memberMap[name];
if (tuple != null) {
if (superInterfaceMember.forSetter) {
tuple.addImplementedSetter(superInterfaceMember);
} else {
tuple.addImplementedMember(superInterfaceMember);
}
} else {
if (superInterfaceMember.forSetter) {
memberMap[superInterfaceMember.name] =
new Tuple.implementSetter(superInterfaceMember);
} else {
memberMap[superInterfaceMember.name] =
new Tuple.implementMember(superInterfaceMember);
}
}
}
}
if (supernode == null) {
// This should be Object.
superclasses = new List<Supertype>.filled(0, null);
interfaces = new List<Supertype>.filled(0, null);
maxInheritancePath = 0;
} else {
maxInheritancePath = supernode.maxInheritancePath + 1;
superclasses =
new List<Supertype>.filled(supernode.superclasses.length + 1, null);
Supertype supertype = classBuilder.supertypeBuilder.buildSupertype(
classBuilder.library, classBuilder.charOffset, classBuilder.fileUri);
if (supertype == null) {
// If the superclass is not an interface type we use Object instead.
// A similar normalization is performed on [supernode] above.
supertype =
new Supertype(hierarchy.coreTypes.objectClass, const <DartType>[]);
}
superclasses.setRange(0, superclasses.length - 1,
substSupertypes(supertype, supernode.superclasses));
superclasses[superclasses.length - 1] = supertype;
if (!classBuilder.library.isNonNullableByDefault &&
supernode.classBuilder.library.isNonNullableByDefault) {
for (int i = 0; i < superclasses.length; i++) {
superclasses[i] = legacyErasureSupertype(superclasses[i]);
}
}
List<TypeBuilder> directInterfaceBuilders =
ignoreFunction(classBuilder.interfaceBuilders);
if (classBuilder.isMixinApplication) {
if (directInterfaceBuilders == null) {
directInterfaceBuilders = <TypeBuilder>[
classBuilder.mixedInTypeBuilder
];
} else {
directInterfaceBuilders = <TypeBuilder>[
classBuilder.mixedInTypeBuilder
]..addAll(directInterfaceBuilders);
}
}
List<Supertype> superclassInterfaces = supernode.interfaces;
if (superclassInterfaces != null) {
superclassInterfaces = substSupertypes(supertype, superclassInterfaces);
}
extend(supernode.classMemberMap);
extend(supernode.classSetterMap);
if (supernode.interfaceMemberMap != null ||
supernode.interfaceSetterMap != null) {
hasInterfaces = true;
}
if (hasInterfaces) {
implement(supernode.interfaceMemberMap ?? supernode.classMemberMap);
implement(supernode.interfaceSetterMap ?? supernode.classSetterMap);
}
if (directInterfaceBuilders != null) {
for (int i = 0; i < directInterfaceBuilders.length; i++) {
ClassHierarchyNode interfaceNode =
hierarchy.getNodeFromTypeBuilder(directInterfaceBuilders[i]);
if (interfaceNode != null) {
hasInterfaces = true;
implement(interfaceNode.interfaceMemberMap ??
interfaceNode.classMemberMap);
implement(interfaceNode.interfaceSetterMap ??
interfaceNode.classSetterMap);
}
}
interfaces = <Supertype>[];
if (superclassInterfaces != null) {
for (int i = 0; i < superclassInterfaces.length; i++) {
addInterface(interfaces, superclasses, superclassInterfaces[i]);
}
}
for (int i = 0; i < directInterfaceBuilders.length; i++) {
Supertype directInterface = directInterfaceBuilders[i].buildSupertype(
classBuilder.library,
classBuilder.charOffset,
classBuilder.fileUri);
if (directInterface != null) {
addInterface(interfaces, superclasses, directInterface);
ClassHierarchyNode interfaceNode =
hierarchy.getNodeFromClass(directInterface.classNode);
if (interfaceNode != null) {
if (maxInheritancePath < interfaceNode.maxInheritancePath + 1) {
maxInheritancePath = interfaceNode.maxInheritancePath + 1;
}
List<Supertype> types =
substSupertypes(directInterface, interfaceNode.superclasses);
for (int i = 0; i < types.length; i++) {
addInterface(interfaces, superclasses, types[i]);
}
if (interfaceNode.interfaces != null) {
List<Supertype> types =
substSupertypes(directInterface, interfaceNode.interfaces);
for (int i = 0; i < types.length; i++) {
addInterface(interfaces, superclasses, types[i]);
}
}
}
}
}
} else if (superclassInterfaces != null &&
!classBuilder.library.isNonNullableByDefault &&
supernode.classBuilder.library.isNonNullableByDefault) {
interfaces = <Supertype>[];
for (int i = 0; i < superclassInterfaces.length; i++) {
addInterface(interfaces, superclasses, superclassInterfaces[i]);
}
} else {
interfaces = superclassInterfaces;
}
}
for (Supertype superclass in superclasses) {
recordSupertype(superclass);
}
if (interfaces != null) {
for (Supertype superinterface in interfaces) {
recordSupertype(superinterface);
}
}
/// Members (excluding setters) declared in [cls] or its superclasses. This
/// includes static methods of [cls], but not its superclasses.
Map<Name, ClassMember> classMemberMap = {};
/// Setters declared in [cls] or its superclasses. This includes static
/// setters of [cls], but not its superclasses.
Map<Name, ClassMember> classSetterMap = {};
/// Members (excluding setters) inherited from interfaces. This contains no
/// static members. If no interfaces are implemented by this class or its
/// superclasses this is identical to [classMemberMap] and we do not store
/// it in the [ClassHierarchyNode].
Map<Name, ClassMember> interfaceMemberMap = {};
/// Setters inherited from interfaces. This contains no static setters. If
/// no interfaces are implemented by this class or its superclasses this is
/// identical to [classSetterMap] and we do not store it in the
/// [ClassHierarchyNode].
Map<Name, ClassMember> interfaceSetterMap = {};
/// Map for members declared in this class to the members that they
/// override. This is used for checking valid overrides and to ensure that
/// override inference correctly propagates inferred types through the
/// class hierarchy.
Map<ClassMember, Set<ClassMember>> declaredOverridesMap = {};
/// In case this class is a mixin application, this maps members declared in
/// the mixin to the members that they override. This is used for checking
/// valid overrides but _not_ as for [declaredOverridesMap] for override
/// inference.
Map<ClassMember, Set<ClassMember>> mixinApplicationOverridesMap = {};
/// In case this class is concrete, this maps concrete members that are
/// inherited into this class to the members they should override to validly
/// implement the interface of this class.
Map<ClassMember, Set<ClassMember>> inheritedImplementsMap = {};
/// In case this class is concrete, this holds the interface members
/// without a corresponding class member. These are either reported as
/// missing implementations or trigger insertion of noSuchMethod forwarders.
List<ClassMember> abstractMembers = [];
ClassHierarchyNodeDataForTesting dataForTesting;
if (retainDataForTesting) {
dataForTesting = new ClassHierarchyNodeDataForTesting(
abstractMembers,
declaredOverridesMap,
mixinApplicationOverridesMap,
inheritedImplementsMap);
}
/// Registers that the current class has an interface member without a
/// corresponding class member.
///
/// This is used to report missing implementation or, in the case the class
/// has a user defined concrete noSuchMethod, to insert noSuchMethod
/// forwarders. (Currently, insertion of forwarders is handled elsewhere.)
///
/// For instance:
///
/// abstract class Interface {
/// method();
/// }
/// class Class1 implements Interface {
/// // Missing implementation for `Interface.method`.
/// }
/// class Class2 implements Interface {
/// noSuchMethod(_) {}
/// // A noSuchMethod forwarder is added for `Interface.method`.
/// }
///
void registerAbstractMember(ClassMember abstractMember) {
if (!abstractMember.isInternalImplementation) {
/// If `isInternalImplementation` is `true`, the member is synthesized
/// implementation that does not require implementation in other
/// classes.
///
/// This is for instance used for late lowering where
///
/// class Interface {
/// late int? field;
/// }
/// class Class implements Interface {
/// int? field;
/// }
///
/// is encoded as
///
/// class Interface {
/// bool _#field#isSet = false;
/// int? _#field = null;
/// int? get field => _#field#isSet ? _#field : throw ...;
/// void set field(int? value) { ... }
/// }
/// class Class implements Interface {
/// int? field;
/// }
///
/// and `Class` should not be required to implement
/// `Interface._#field#isSet` and `Interface._#field`.
abstractMembers.add(abstractMember);
}
}
/// Registers that [inheritedMember] should be checked to validly override
/// [overrides].
///
/// This is needed in the case where a concrete member is inherited into
/// a concrete subclass. For instance:
///
/// class Super {
/// void method() {}
/// }
/// abstract class Interface {
/// void method();
/// }
/// class Class extends Super implements Interface {}
///
/// Here `Super.method` must be checked to be a valid implementation for
/// `Interface.method` by being a valid override of it.
void registerInheritedImplements(
ClassMember inheritedMember, Set<ClassMember> overrides,
{ClassMember aliasForTesting}) {
if (classBuilder is SourceClassBuilder) {
assert(
inheritedMember.classBuilder != classBuilder,
"Only inherited members can implement by inheritance: "
"${inheritedMember}");
inheritedImplementsMap[inheritedMember] = overrides;
if (dataForTesting != null && aliasForTesting != null) {
dataForTesting.aliasMap[aliasForTesting] = inheritedMember;
}
}
}
/// Returns `true` if the current class is from an opt-out library and
/// [classMember] is from an opt-in library.
///
/// In this case a member signature needs to be inserted to show the
/// legacy erased type of the interface member. For instance:
///
/// // Opt-in library:
/// class Super {
/// int? method(int i) {}
/// }
/// // Opt-out library:
/// class Class extends Super {
/// // A member signature is inserted:
/// // int* method(int* i);
/// }
///
bool needsMemberSignatureFor(ClassMember classMember) {
return !classBuilder.library.isNonNullableByDefault &&
classMember.classBuilder.library.isNonNullableByDefault;
}
memberMap.forEach((Name name, Tuple tuple) {
/// The computation starts by sanitizing the members. Conflicts between
/// methods and properties (getters/setters) or between static and
/// instance members are reported. Conflicting members and members
/// overridden by duplicates are removed.
///
/// For this [definingGetable] and [definingSetable] hold the first member
/// of its kind found among declared, mixed in, extended and implemented
/// members.
///
/// Conflicts between [definingGetable] and [definingSetable] are reported
/// afterwards.
ClassMember definingGetable;
ClassMember definingSetable;
ClassMember declaredGetable = tuple.declaredMember;
if (declaredGetable != null) {
/// class Class {
/// method() {}
/// }
definingGetable = declaredGetable;
}
ClassMember declaredSetable = tuple.declaredSetter;
if (declaredSetable != null) {
/// class Class {
/// set setter(value) {}
/// }
definingSetable = declaredSetable;
}
ClassMember mixedInGetable;
if (tuple.mixedInMember != null &&
!tuple.mixedInMember.isStatic &&
!tuple.mixedInMember.isDuplicate &&
!tuple.mixedInMember.isSynthesized) {
/// We treat
///
/// opt-in:
/// class Interface {
/// method3() {}
/// }
/// opt-out:
/// class Mixin implements Interface {
/// static method1() {}
/// method2() {}
/// method2() {}
/// /*member-signature*/ method3() {}
/// }
/// class Class with Mixin {}
///
/// as
///
/// class Mixin {}
/// class Class with Mixin {}
///
/// Note that skipped synthetic getable 'method3' is still included
/// in the implemented getables, but its type will not define the type
/// when mixed in. For instance
///
/// opt-in:
/// abstract class Interface {
/// num get getter;
/// }
/// opt-out:
/// abstract class Super {
/// int get getter;
/// }
/// abstract class Mixin implements Interface {
/// /*member-signature*/ num get getter;
/// }
/// abstract class Class extends Super with Mixin {}
///
/// Here the type of `Class.getter` should not be defined from the
/// synthetic member signature `Mixin.getter` but as a combined member
/// signature of `Super.getter` and `Mixin.getter`, resulting in type
/// `int` instead of `num`.
if (definingGetable == null) {
/// class Mixin {
/// method() {}
/// }
/// class Class with Mixin {}
definingGetable = mixedInGetable = tuple.mixedInMember;
} else if (!definingGetable.isDuplicate) {
// This case is currently unreachable from source code since classes
// cannot both declare and mix in members. From dill, this can occur
// but should not conflicting members.
//
// The case is handled for consistency.
if (definingGetable.isStatic ||
definingGetable.isProperty != tuple.mixedInMember.isProperty) {
reportInheritanceConflict(definingGetable, tuple.mixedInMember);
} else {
mixedInGetable = tuple.mixedInMember;
}
}
}
ClassMember mixedInSetable;
if (tuple.mixedInSetter != null &&
!tuple.mixedInSetter.isStatic &&
!tuple.mixedInSetter.isDuplicate &&
!tuple.mixedInSetter.isSynthesized) {
/// We treat
///
/// class Mixin {
/// static set setter1(value) {}
/// set setter2(value) {}
/// set setter2(value) {}
/// /*member-signature*/ setter3() {}
/// }
/// class Class with Mixin {}
///
/// as
///
/// class Mixin {}
/// class Class with Mixin {}
///
/// Note that skipped synthetic setable 'setter3' is still included
/// in the implemented setables, but its type will not define the type
/// when mixed in. For instance
///
/// opt-in:
/// abstract class Interface {
/// void set setter(int value);
/// }
/// opt-out:
/// abstract class Super {
/// void set setter(num value);
/// }
/// abstract class Mixin implements Interface {
/// /*member-signature*/ num get getter;
/// }
/// abstract class Class extends Super with Mixin {}
///
/// Here the type of `Class.setter` should not be defined from the
/// synthetic member signature `Mixin.setter` but as a combined member
/// signature of `Super.setter` and `Mixin.setter`, resulting in type
/// `num` instead of `int`.
if (definingSetable == null) {
/// class Mixin {
/// set setter(value) {}
/// }
/// class Class with Mixin {}
definingSetable = mixedInSetable = tuple.mixedInSetter;
} else if (!definingSetable.isDuplicate) {
if (definingSetable.isStatic ||
definingSetable.isProperty != tuple.mixedInSetter.isProperty) {
reportInheritanceConflict(definingGetable, tuple.mixedInSetter);
} else {
mixedInSetable = tuple.mixedInSetter;
}
}
}
ClassMember extendedGetable;
if (tuple.extendedMember != null &&
!tuple.extendedMember.isStatic &&
!tuple.extendedMember.isDuplicate) {
/// We treat
///
/// class Super {
/// static method1() {}
/// method2() {}
/// method2() {}
/// }
/// class Class extends Super {}
///
/// as
///
/// class Super {}
/// class Class extends Super {}
///
if (definingGetable == null) {
/// class Super {
/// method() {}
/// }
/// class Class extends Super {}
definingGetable = extendedGetable = tuple.extendedMember;
} else if (!definingGetable.isDuplicate) {
if (definingGetable.isStatic ||
definingGetable.isProperty != tuple.extendedMember.isProperty) {
/// class Super {
/// method() {}
/// }
/// class Class extends Super {
/// static method() {}
/// }
///
/// or
///
/// class Super {
/// method() {}
/// }
/// class Class extends Super {
/// get getter => 0;
/// }
reportInheritanceConflict(definingGetable, tuple.extendedMember);
} else {
extendedGetable = tuple.extendedMember;
}
}
}
ClassMember extendedSetable;
if (tuple.extendedSetter != null &&
!tuple.extendedSetter.isStatic &&
!tuple.extendedSetter.isDuplicate) {
/// We treat
///
/// class Super {
/// static set setter1(value) {}
/// set setter2(value) {}
/// set setter2(value) {}
/// }
/// class Class extends Super {}
///
/// as
///
/// class Super {}
/// class Class extends Super {}
///
if (definingSetable == null) {
/// class Super {
/// set setter(value) {}
/// }
/// class Class extends Super {}
definingSetable = extendedSetable = tuple.extendedSetter;
} else if (!definingSetable.isDuplicate) {
if (definingSetable.isStatic ||
definingSetable.isProperty != tuple.extendedSetter.isProperty) {
reportInheritanceConflict(definingSetable, tuple.extendedSetter);
} else {
extendedSetable = tuple.extendedSetter;
}
}
}
// TODO(johnniwinther): Remove extended and mixed in members/setters
// from implemented members/setters. Mixin applications always implement
// the mixin class leading to unnecessary interface members.
List<ClassMember> implementedGetables;
if (tuple.implementedMembers != null &&
// Skip implemented members if we already have a duplicate.
!(definingGetable != null && definingGetable.isDuplicate)) {
for (int i = 0; i < tuple.implementedMembers.length; i++) {
ClassMember implementedGetable = tuple.implementedMembers[i];
if (implementedGetable.isStatic || implementedGetable.isDuplicate) {
/// We treat
///
/// class Interface {
/// static method1() {}
/// method2() {}
/// method2() {}
/// }
/// class Class implements Interface {}
///
/// as
///
/// class Interface {}
/// class Class implements Interface {}
///
implementedGetable = null;
} else {
if (definingGetable == null) {
/// class Interface {
/// method() {}
/// }
/// class Class implements Interface {}
definingGetable = implementedGetable;
} else if (definingGetable.isStatic ||
definingGetable.isProperty != implementedGetable.isProperty) {
/// class Interface {
/// method() {}
/// }
/// class Class implements Interface {
/// static method() {}
/// }
///
/// or
///
/// class Interface {
/// method() {}
/// }
/// class Class implements Interface {
/// get getter => 0;
/// }
reportInheritanceConflict(definingGetable, implementedGetable);
implementedGetable = null;
}
}
if (implementedGetable == null) {
// On the first skipped member we add all previous.
implementedGetables ??= tuple.implementedMembers.take(i).toList();
} else if (implementedGetables != null) {
// If already skipping members we add [implementedGetable]
// explicitly.
implementedGetables.add(implementedGetable);
}
}
if (implementedGetables == null) {
// No members were skipped so we use the full list.
implementedGetables = tuple.implementedMembers;
} else if (implementedGetables.isEmpty) {
// No members were included.
implementedGetables = null;
}
}
List<ClassMember> implementedSetables;
if (tuple.implementedSetters != null &&
// Skip implemented setters if we already have a duplicate.
!(definingSetable != null && definingSetable.isDuplicate)) {
for (int i = 0; i < tuple.implementedSetters.length; i++) {
ClassMember implementedSetable = tuple.implementedSetters[i];
if (implementedSetable.isStatic || implementedSetable.isDuplicate) {
/// We treat
///
/// class Interface {
/// static set setter1(value) {}
/// set setter2(value) {}
/// set setter2(value) {}
/// }
/// class Class implements Interface {}
///
/// as
///
/// class Interface {}
/// class Class implements Interface {}
///
implementedSetable = null;
} else {
if (definingSetable == null) {
/// class Interface {
/// set setter(value) {}
/// }
/// class Class implements Interface {}
definingSetable = implementedSetable;
} else if (definingSetable.isStatic ||
definingSetable.isProperty != implementedSetable.isProperty) {
/// class Interface {
/// set setter(value) {}
/// }
/// class Class implements Interface {
/// static set setter(value) {}
/// }
reportInheritanceConflict(definingSetable, implementedSetable);
implementedSetable = null;
}
}
if (implementedSetable == null) {
// On the first skipped setter we add all previous.
implementedSetables ??= tuple.implementedSetters.take(i).toList();
} else if (implementedSetables != null) {
// If already skipping setters we add [implementedSetable]
// explicitly.
implementedSetables.add(implementedSetable);
}
}
if (implementedSetables == null) {
// No setters were skipped so we use the full list.
implementedSetables = tuple.implementedSetters;
} else if (implementedSetables.isEmpty) {
// No setters were included.
implementedSetables = null;
}
}
if (definingGetable != null && definingSetable != null) {
if (definingGetable.isStatic != definingSetable.isStatic ||
definingGetable.isProperty != definingSetable.isProperty) {
reportInheritanceConflict(definingGetable, definingSetable);
// TODO(johnniwinther): Should we remove [definingSetable]? If we
// leave it in this conflict will also be reported in subclasses. If
// we remove it, any write to the setable will be unresolved.
}
}
// TODO(johnniwinther): Handle declared members together with mixed in
// members. This should only occur from .dill, though.
if (mixedInGetable != null) {
declaredGetable = null;
}
if (mixedInSetable != null) {
declaredSetable = null;
}
/// Set to `true` if declared members have been registered in
/// [registerDeclaredOverride] or [registerMixedInOverride].
bool hasDeclaredMembers = false;
/// Declared methods, getters and setters registered in
/// [registerDeclaredOverride].
ClassMember declaredMethod;
List<ClassMember> declaredProperties;
/// Declared methods, getters and setters registered in
/// [registerDeclaredOverride].
ClassMember mixedInMethod;
List<ClassMember> mixedInProperties;
/// Registers that [declaredMember] overrides extended and implemented
/// members.
///
/// Getters and setters share overridden members so the registration
/// of override relations is performed after the interface members have
/// been computed.
///
/// Declared members must be checked for valid override of the overridden
/// members _and_ must register an override dependency with the overridden
/// members so that override inference can propagate inferred types
/// correctly. For instance:
///
/// class Super {
/// int get property => 42;
/// }
/// class Class extends Super {
/// void set property(value) {}
/// }
///
/// Here the parameter type of the setter `Class.property` must be
/// inferred from the type of the getter `Super.property`.
void registerDeclaredOverride(ClassMember declaredMember,
{ClassMember aliasForTesting}) {
if (classBuilder is SourceClassBuilder && !declaredMember.isStatic) {
assert(
declaredMember.isSourceDeclaration &&
declaredMember.classBuilder == classBuilder,
"Only declared members can override: ${declaredMember}");
hasDeclaredMembers = true;
if (declaredMember.isProperty) {
declaredProperties ??= [];
declaredProperties.add(declaredMember);
} else {
assert(
declaredMethod == null,
"Multiple methods unexpectedly declared: "
"${declaredMethod} and ${declaredMember}.");
declaredMethod = declaredMember;
}
if (dataForTesting != null && aliasForTesting != null) {
dataForTesting.aliasMap[aliasForTesting] = declaredMember;
}
}
}
/// Registers that [mixedMember] overrides extended and implemented
/// members through application.
///
/// Getters and setters share overridden members so the registration
/// of override relations in performed after the interface members have
/// been computed.
///
/// Declared mixed in members must be checked for valid override of the
/// overridden members but _not_ register an override dependency with the
/// overridden members. This is in contrast to declared members. For
/// instance:
///
/// class Super {
/// int get property => 42;
/// }
/// class Mixin {
/// void set property(value) {}
/// }
/// class Class = Super with Mixin;
///
/// Here the parameter type of the setter `Mixin.property` must _not_ be
/// inferred from the type of the getter `Super.property`, but should
/// instead default to `dynamic`.
void registerMixedInOverride(ClassMember mixedInMember,
{ClassMember aliasForTesting}) {
assert(mixedInMember.classBuilder != classBuilder,
"Only mixin members can override by application: ${mixedInMember}");
if (classBuilder is SourceClassBuilder) {
hasDeclaredMembers = true;
if (mixedInMember.isProperty) {
mixedInProperties ??= [];
mixedInProperties.add(mixedInMember);
} else {
assert(
mixedInMethod == null,
"Multiple methods unexpectedly declared in mixin: "
"${mixedInMethod} and ${mixedInMember}.");
mixedInMethod = mixedInMember;
}
if (dataForTesting != null && aliasForTesting != null) {
dataForTesting.aliasMap[aliasForTesting] = mixedInMember;
}
}
}
/// Computes the class and interface members for a method, getter, or
/// setter in the current [tuple].
///
/// [definingMember] is the member which defines whether the computation
/// is for a method, a getter or a setter.
/// [declaredMember] is the member declared in the current class, if any.
/// [mixedInMember] is the member declared in a mixin that is mixed into
/// the current current class, if any.
/// [extendedMember] is the member inherited from the super class.
/// [implementedMembers] are the members inherited from the super
/// interfaces, if none this is `null`.
///
/// The computed class and interface members are added to [classMemberMap]
/// and [interfaceMemberMap], respectively.
ClassMember computeMembers(
{ClassMember definingMember,
ClassMember declaredMember,
ClassMember mixedInMember,
ClassMember extendedMember,
List<ClassMember> implementedMembers,
Map<Name, ClassMember> classMemberMap,
Map<Name, ClassMember> interfaceMemberMap}) {
ClassMember classMember;
ClassMember interfaceMember;
if (mixedInMember != null) {
if (mixedInMember.isAbstract) {
/// class Mixin {
/// method();
/// }
/// class Class = Object with Mixin;
/// Interface members from the extended, mixed in, and implemented
/// members define the combined member signature.
Set<ClassMember> interfaceMembers = {};
if (extendedMember != null) {
/// class Super {
/// method() {}
/// }
/// class Mixin {
/// method();
/// }
/// class Class = Super with Mixin;
interfaceMembers.add(extendedMember.interfaceMember);
}
interfaceMembers.add(mixedInMember);
if (implementedMembers != null) {
/// class Interface {
/// method() {}
/// }
/// class Mixin {
/// method();
/// }
/// class Class = Object with Mixin implements Interface;
interfaceMembers.addAll(implementedMembers);
}
/// We always create a synthesized interface member, even in the
/// case of [interfaceMembers] being a singleton, to insert the
/// abstract mixin stub.
interfaceMember = new SynthesizedInterfaceMember(
classBuilder, name, interfaceMembers.toList(),
superClassMember: extendedMember,
// [definingMember] and [mixedInMember] are always the same
// here. Use the latter here and the former below to show the
// the member is canonical _because_ its the mixed in member and
// it defines the isProperty/forSetter properties _because_ it
// is the defining member.
canonicalMember: mixedInMember,
mixedInMember: mixedInMember,
isProperty: definingMember.isProperty,
forSetter: definingMember.forSetter,
shouldModifyKernel: shouldModifyKernel);
hierarchy.registerMemberComputation(interfaceMember);
if (extendedMember != null) {
/// class Super {
/// method() {}
/// }
/// class Mixin {
/// method();
/// }
/// class Class = Super with Mixin;
///
/// The concrete extended member is the class member but might
/// be overwritten by a concrete forwarding stub:
///
/// class Super {
/// method(int i) {}
/// }
/// class Interface {
/// method(covariant int i) {}
/// }
/// class Mixin {
/// method(int i);
/// }
/// // A concrete forwarding stub
/// // method(covariant int i) => super.method(i);
/// // will be inserted.
/// class Class = Super with Mixin implements Interface;
///
classMember = new InheritedClassMemberImplementsInterface(
classBuilder, name,
inheritedClassMember: extendedMember,
implementedInterfaceMember: interfaceMember,
forSetter: definingMember.forSetter,
isProperty: definingMember.isProperty);
hierarchy.registerMemberComputation(classMember);
if (!classBuilder.isAbstract) {
registerInheritedImplements(extendedMember, {interfaceMember},
aliasForTesting: classMember);
}
} else if (!classBuilder.isAbstract) {
/// class Mixin {
/// method(); // Missing implementation.
/// }
/// class Class = Object with Mixin;
registerAbstractMember(interfaceMember);
}
assert(!mixedInMember.isSynthesized);
if (!mixedInMember.isSynthesized) {
/// Members declared in the mixin must override extended and
/// implemented members.
///
/// When loading from .dill the mixed in member might be
/// synthesized, for instance a member signature or forwarding
/// stub, and this should not be checked to override the extended
/// and implemented members:
///
/// // Opt-out library, from source:
/// class Mixin {}
/// // Opt-out library, from .dill:
/// class Mixin {
/// ...
/// String* toString(); // member signature
/// }
/// // Opt-out library, from source:
/// class Class = Object with Mixin;
/// // Mixin.toString should not be checked to override
/// // Object.toString.
///
registerMixedInOverride(mixedInMember,
aliasForTesting: interfaceMember);
}
} else {
assert(!mixedInMember.isAbstract);
/// class Mixin {
/// method() {}
/// }
/// class Class = Object with Mixin;
///
/// Interface members from the extended, mixed in, and implemented
/// members define the combined member signature.
Set<ClassMember> interfaceMembers = {};
if (extendedMember != null) {
/// class Super {
/// method() {}
/// }
/// class Mixin {
/// method() {}
/// }
/// class Class = Super with Mixin;
interfaceMembers.add(extendedMember.interfaceMember);
}
interfaceMembers.add(mixedInMember);
if (implementedMembers != null) {
/// class Interface {
/// method() {}
/// }
/// class Mixin {
/// method() {}
/// }
/// class Class = Object with Mixin implements Interface;
interfaceMembers.addAll(implementedMembers);
}
/// We always create a synthesized interface member, even in the
/// case of [interfaceMembers] being a singleton, to insert the
/// concrete mixin stub.
interfaceMember = new SynthesizedInterfaceMember(
classBuilder, name, interfaceMembers.toList(),
superClassMember: mixedInMember,
// [definingMember] and [mixedInMember] are always the same
// here. Use the latter here and the former below to show the
// the member is canonical _because_ its the mixed in member and
// it defines the isProperty/forSetter properties _because_ it
// is the defining member.
canonicalMember: mixedInMember,
mixedInMember: mixedInMember,
isProperty: definingMember.isProperty,
forSetter: definingMember.forSetter,
shouldModifyKernel: shouldModifyKernel);
hierarchy.registerMemberComputation(interfaceMember);
/// The concrete mixed in member is the class member but will
/// be overwritten by a concrete mixin stub:
///
/// class Mixin {
/// method() {}
/// }
/// // A concrete mixin stub
/// // method() => super.method();
/// // will be inserted.
/// class Class = Object with Mixin;
///
classMember = new InheritedClassMemberImplementsInterface(
classBuilder, name,
inheritedClassMember: mixedInMember,
implementedInterfaceMember: interfaceMember,
forSetter: definingMember.forSetter,
isProperty: definingMember.isProperty);
hierarchy.registerMemberComputation(classMember);
if (!classBuilder.isAbstract) {
/// class Interface {
/// method() {}
/// }
/// class Mixin {
/// method() {}
/// }
/// class Class = Object with Mixin;
///
/// [mixinMember] must implemented interface member.
registerInheritedImplements(mixedInMember, {interfaceMember},
aliasForTesting: classMember);
}
assert(!mixedInMember.isSynthesized);
if (!mixedInMember.isSynthesized) {
/// Members declared in the mixin must override extended and
/// implemented members.
///
/// When loading from .dill the mixed in member might be
/// synthesized, for instance a member signature or forwarding
/// stub, and this should not be checked to override the extended
/// and implemented members.
///
/// These synthesized mixed in members should always be abstract
/// and therefore not be handled here, but we handled them here
/// for consistency.
registerMixedInOverride(mixedInMember);
}
}
} else if (declaredMember != null) {
if (declaredMember.isAbstract) {
/// class Class {
/// method();
/// }
interfaceMember = declaredMember;
/// Interface members from the declared, extended, and implemented
/// members define the combined member signature.
Set<ClassMember> interfaceMembers = {};
if (extendedMember != null) {
/// class Super {
/// method() {}
/// }
/// class Class extends Super {
/// method();
/// }
interfaceMembers.add(extendedMember);
}
interfaceMembers.add(declaredMember);
if (implementedMembers != null) {
/// class Interface {
/// method() {}
/// }
/// class Class implements Interface {
/// method();
/// }
interfaceMembers.addAll(implementedMembers);
}
/// If only one member defines the interface member there is no
/// need for a synthesized interface member, since its result will
/// simply be that one member.
if (interfaceMembers.length > 1) {
/// class Super {
/// method() {}
/// }
/// class Interface {
/// method() {}
/// }
/// class Class extends Super implements Interface {
/// method();
/// }
interfaceMember = new SynthesizedInterfaceMember(
classBuilder, name, interfaceMembers.toList(),
superClassMember: extendedMember,
// [definingMember] and [declaredMember] are always the same
// here. Use the latter here and the former below to show the
// the member is canonical _because_ its the declared member
// and it defines the isProperty/forSetter properties
// _because_ it is the defining member.
canonicalMember: declaredMember,
isProperty: definingMember.isProperty,
forSetter: definingMember.forSetter,
shouldModifyKernel: shouldModifyKernel);
hierarchy.registerMemberComputation(interfaceMember);
}
if (extendedMember != null) {
/// class Super {
/// method() {}
/// }
/// class Class extends Super {
/// method();
/// }
///
/// The concrete extended member is the class member but might
/// be overwritten by a concrete forwarding stub:
///
/// class Super {
/// method(int i) {}
/// }
/// class Interface {
/// method(covariant int i) {}
/// }
/// class Class extends Super implements Interface {
/// // This will be turned into the concrete forwarding stub
/// // method(covariant int i) => super.method(i);
/// method(int i);
/// }
///
classMember = new InheritedClassMemberImplementsInterface(
classBuilder, name,
inheritedClassMember: extendedMember,
implementedInterfaceMember: interfaceMember,
forSetter: definingMember.forSetter,
isProperty: definingMember.isProperty);
hierarchy.registerMemberComputation(classMember);
if (!classBuilder.isAbstract) {
/// class Super {
/// method() {}
/// }
/// class Class extends Super {
/// method();
/// }
///
/// [extendedMember] must implemented interface member.
registerInheritedImplements(extendedMember, {interfaceMember},
aliasForTesting: classMember);
}
} else if (!classBuilder.isAbstract) {
/// class Class {
/// method(); // Missing implementation.
/// }
registerAbstractMember(declaredMember);
}
/// The declared member must override extended and implemented
/// members.
registerDeclaredOverride(declaredMember,
aliasForTesting: interfaceMember);
} else {
assert(!declaredMember.isAbstract);
/// class Class {
/// method() {}
/// }
classMember = declaredMember;
/// The declared member must override extended and implemented
/// members.
registerDeclaredOverride(declaredMember);
}
} else if (extendedMember != null) {
/// class Super {
/// method() {}
/// }
/// class Class extends Super {}
assert(!extendedMember.isAbstract,
"Abstract extended member: ${extendedMember}");
classMember = extendedMember;
if (implementedMembers != null) {
/// class Super {
/// method() {}
/// }
/// class Interface {
/// method() {}
/// }
/// class Class extends Super implements Interface {}
ClassMember extendedInterfaceMember =
extendedMember.interfaceMember;
/// Interface members from the extended and implemented
/// members define the combined member signature.
Set<ClassMember> interfaceMembers = {extendedInterfaceMember};
// TODO(johnniwinther): The extended member might be included in
// a synthesized implemented member. For instance:
//
// class Super {
// void method() {}
// }
// class Interface {
// void method() {}
// }
// abstract class Class extends Super implements Interface {
// // Synthesized interface member of
// // {Super.method, Interface.method}
// }
// class Sub extends Class {
// // Super.method implements Class.method =
// // {Super.method, Interface.method}
// // Synthesized interface member of
// // {Super.method, Class.method}
// }
//
// Maybe we should recognized this.
interfaceMembers.addAll(implementedMembers);
/// Normally, if only one member defines the interface member there
/// is no need for a synthesized interface member, since its result
/// will simply be that one member, but if the extended member is
/// from an opt-in library and the current class is from an opt-out
/// library we need to create a member signature:
///
/// // Opt-in:
/// class Super {
/// int? method() => null;
/// }
/// class Interface implements Super {}
/// // Opt-out:
/// class Class extends Super implements Interface {
/// // Member signature added:
/// int* method();
/// }
///
if (interfaceMembers.length == 1 &&
!needsMemberSignatureFor(extendedInterfaceMember)) {
/// class Super {
/// method() {}
/// }
/// class Interface implements Super {}
/// class Class extends Super implements Interface {}
interfaceMember = interfaceMembers.first;
} else {
/// class Super {
/// method() {}
/// }
/// class Interface {
/// method() {}
/// }
/// class Class extends Super implements Interface {}
interfaceMember = new SynthesizedInterfaceMember(
classBuilder, name, interfaceMembers.toList(),
superClassMember: extendedMember,
isProperty: definingMember.isProperty,
forSetter: definingMember.forSetter,
shouldModifyKernel: shouldModifyKernel);
hierarchy.registerMemberComputation(interfaceMember);
}
if (interfaceMember == classMember) {
/// class Super {
/// method() {}
/// }
/// class Interface implements Super {}
/// class Class extends Super implements Interface {}
///
/// We keep track of whether a class needs interfaces, that is,
/// whether is has any members that have an interface member
/// different from its corresponding class member, so we set
/// [interfaceMember] to `null` so show that the interface member
/// is not needed.
interfaceMember = null;
} else {
/// class Super {
/// method() {}
/// }
/// class Interface {
/// method() {}
/// }
/// class Class extends Super implements Interface {}
///
/// The concrete extended member is the class member but might
/// be overwritten by a concrete forwarding stub:
///
/// class Super {
/// method(int i) {}
/// }
/// class Interface {
/// method(covariant int i) {}
/// }
/// class Class extends Super implements Interface {
/// // A concrete forwarding stub will be created:
/// // method(covariant int i) => super.method(i);
/// }
///
classMember = new InheritedClassMemberImplementsInterface(
classBuilder, name,
inheritedClassMember: extendedMember,
implementedInterfaceMember: interfaceMember,
isProperty: definingMember.isProperty,
forSetter: definingMember.forSetter);
hierarchy.registerMemberComputation(classMember);
if (!classBuilder.isAbstract) {
/// class Super {
/// method() {}
/// }
/// class Interface {
/// method() {}
/// }
/// class Class extends Super implements Interface {}
registerInheritedImplements(extendedMember, {interfaceMember},
aliasForTesting: classMember);
}
}
} else if (needsMemberSignatureFor(extendedMember)) {
/// // Opt-in library:
/// class Super {
/// method() {}
/// }
/// // opt-out library:
/// class Class extends Super {}
interfaceMember = new SynthesizedInterfaceMember(
classBuilder, name, [extendedMember],
superClassMember: extendedMember,
isProperty: definingMember.isProperty,
forSetter: definingMember.forSetter,
shouldModifyKernel: shouldModifyKernel);
hierarchy.registerMemberComputation(interfaceMember);
/// The concrete extended member is the class member and should
/// be able to be overwritten by a synthesized concrete member here,
/// but we handle the case for consistency.
classMember = new InheritedClassMemberImplementsInterface(
classBuilder, name,
inheritedClassMember: extendedMember,
implementedInterfaceMember: interfaceMember,
isProperty: definingMember.isProperty,
forSetter: definingMember.forSetter);
hierarchy.registerMemberComputation(classMember);
}
} else if (implementedMembers != null) {
/// class Interface {
/// method() {}
/// }
/// class Class implements Interface {}
Set<ClassMember> interfaceMembers = implementedMembers.toSet();
if (interfaceMembers.isNotEmpty) {
/// Normally, if only one member defines the interface member there
/// is no need for a synthesized interface member, since its result
/// will simply be that one member, but if the implemented member is
/// from an opt-in library and the current class is from an opt-out
/// library we need to create a member signature:
///
/// // Opt-in:
/// class Interface {
/// int? method() => null;
/// }
/// // Opt-out:
/// class Class implements Interface {
/// // Member signature added:
/// int* method();
/// }
///
if (interfaceMembers.length == 1 &&
!needsMemberSignatureFor(interfaceMembers.first)) {
/// class Interface {
/// method() {}
/// }
/// class Class implements Interface {}
interfaceMember = interfaceMembers.first;
} else {
/// class Interface1 {
/// method() {}
/// }
/// class Interface2 {
/// method() {}
/// }
/// class Class implements Interface1, Interface2 {}
interfaceMember = new SynthesizedInterfaceMember(
classBuilder, name, interfaceMembers.toList(),
isProperty: definingMember.isProperty,
forSetter: definingMember.forSetter,
shouldModifyKernel: shouldModifyKernel);
hierarchy.registerMemberComputation(interfaceMember);
}
if (!classBuilder.isAbstract) {
/// class Interface {
/// method() {}
/// }
/// class Class implements Interface {}
for (ClassMember abstractMember in interfaceMembers) {
registerAbstractMember(abstractMember);
}
}
}
}
if (interfaceMember != null) {
// We have an explicit interface.
hasInterfaces = true;
}
if (classMember != null) {
if (name == noSuchMethodName &&
!classMember.isObjectMember(objectClass)) {
hasNoSuchMethod = true;
}
classMemberMap[name] = classMember;
interfaceMember ??= classMember.interfaceMember;
}
if (interfaceMember != null) {
interfaceMemberMap[name] = interfaceMember;
}
return interfaceMember;
}
ClassMember interfaceGetable;
if (definingGetable != null) {
interfaceGetable = computeMembers(
definingMember: definingGetable,
declaredMember: declaredGetable,
mixedInMember: mixedInGetable,
extendedMember: extendedGetable,
implementedMembers: implementedGetables,
classMemberMap: classMemberMap,
interfaceMemberMap: interfaceMemberMap);
}
ClassMember interfaceSetable;
if (definingSetable != null) {
interfaceSetable = computeMembers(
definingMember: definingSetable,
declaredMember: declaredSetable,
mixedInMember: mixedInSetable,
extendedMember: extendedSetable,
implementedMembers: implementedSetables,
classMemberMap: classSetterMap,
interfaceMemberMap: interfaceSetterMap);
}
if (classBuilder is SourceClassBuilder) {
if (interfaceGetable != null &&
interfaceSetable != null &&
interfaceGetable.isProperty &&
interfaceSetable.isProperty &&
interfaceGetable.isStatic == interfaceSetable.isStatic &&
!interfaceGetable.isSameDeclaration(interfaceSetable)) {
/// We need to check that the getter type is a subtype of the setter
/// type. For instance
///
/// class Super {
/// int get property1 => null;
/// num get property2 => null;
/// }
/// class Mixin {
/// void set property1(num value) {}
/// void set property2(int value) {}
/// }
/// class Class = Super with Mixin;
///
/// Here `Super.property1` and `Mixin.property1` form a valid getter/
/// setter pair in `Class` because the type of the getter
/// `Super.property1` is a subtype of the setter `Mixin.property1`.
///
/// In contrast the pair `Super.property2` and `Mixin.property2` is
/// not a valid getter/setter in `Class` because the type of the getter
/// `Super.property2` is _not_ a subtype of the setter
/// `Mixin.property1`.
hierarchy.registerGetterSetterCheck(
classBuilder, interfaceGetable, interfaceSetable);
}
}
if (hasDeclaredMembers) {
Set<ClassMember> getableOverrides = {};
Set<ClassMember> setableOverrides = {};
if (extendedGetable != null) {
/// (abstract) class Super {
/// method() {}
/// int get property => 0;
/// }
/// (abstract) class Class extends Super {
/// method() {}
/// set property(int value) {}
/// }
getableOverrides.add(extendedGetable.interfaceMember);
}
if (extendedSetable != null) {
/// (abstract) class Super {
/// set setter(int value) {}
/// set property(int value) {}
/// }
/// (abstract) class Class extends Super {
/// set setter(int value) {}
/// int get property => 0;
/// }
setableOverrides.add(extendedSetable.interfaceMember);
}
if (implementedGetables != null) {
/// (abstract) class Interface {
/// method() {}
/// int get property => 0;
/// }
/// (abstract) class Class implements Interface {
/// method() {}
/// set property(int value) {}
/// }
getableOverrides.addAll(implementedGetables);
}
if (implementedSetables != null) {
/// (abstract) class Interface {
/// set setter(int value) {}
/// set property(int value) {}
/// }
/// (abstract) class Class implements Interface {
/// set setter(int value) {}
/// int get property => 0;
/// }
setableOverrides.addAll(implementedSetables);
}
if (getableOverrides.isNotEmpty || setableOverrides.isNotEmpty) {
if (declaredMethod != null && getableOverrides.isNotEmpty) {
/// class Super {
/// method() {}
/// }
/// class Class extends Super {
/// method() {}
/// }
declaredOverridesMap[declaredMethod] = getableOverrides;
}
if (declaredProperties != null) {
Set<ClassMember> overrides;
if (declaredMethod != null) {
/// class Super {
/// set setter() {}
/// }
/// class Class extends Super {
/// method() {}
/// }
overrides = setableOverrides;
} else {
/// class Super {
/// get property => null
/// void set property(value) {}
/// }
/// class Class extends Super {
/// get property => null
/// void set property(value) {}
/// }
overrides = {...getableOverrides, ...setableOverrides};
}
if (overrides.isNotEmpty) {
for (ClassMember declaredMember in declaredProperties) {
declaredOverridesMap[declaredMember] = overrides;
}
}
}
if (mixedInMethod != null && getableOverrides.isNotEmpty) {
/// class Super {
/// method() {}
/// }
/// class Mixin {
/// method() {}
/// }
/// class Class = Super with Mixin;
mixinApplicationOverridesMap[mixedInMethod] = getableOverrides;
}
if (mixedInProperties != null) {
Set<ClassMember> overrides;
if (mixedInMethod != null) {
/// class Super {
/// set setter() {}
/// }
/// class Mixin {
/// method() {}
/// }
/// class Class = Super with Mixin;
overrides = setableOverrides;
} else {
/// class Super {
/// method() {}
/// }
/// class Mixin extends Super {
/// method() {}
/// }
overrides = {...getableOverrides, ...setableOverrides};
}
if (overrides.isNotEmpty) {
for (ClassMember mixedInMember in mixedInProperties) {
mixinApplicationOverridesMap[mixedInMember] = overrides;
}
}
}
}
}
});
if (classBuilder is SourceClassBuilder) {
// TODO(johnniwinther): Avoid duplicate override check computations
// between [declaredOverridesMap], [mixinApplicationOverridesMap] and
// [inheritedImplementsMap].
// TODO(johnniwinther): Ensure that a class member is only checked to
// validly override another member once. Currently it can happen multiple
// times as an inherited implementation.
declaredOverridesMap.forEach(
(ClassMember classMember, Set<ClassMember> overriddenMembers) {
/// A declared member can inherit its type from the overridden members.
///
/// We register this with the class member itself so the it can force
/// computation of type on the overridden members before determining its
/// own type.
///
/// Member types can be queried at arbitrary points during top level
/// inference so we need to ensure that types are computed in dependency
/// order.
classMember.registerOverrideDependency(overriddenMembers);
/// Not all member type are queried during top level inference so we
/// register delayed computation to ensure that all types have been
/// computed before override checks are performed.
DelayedTypeComputation computation =
new DelayedTypeComputation(this, classMember, overriddenMembers);
hierarchy.registerDelayedTypeComputation(computation);
/// Declared members must be checked to validly override the the
/// overridden members.
hierarchy.registerOverrideCheck(
classBuilder, classMember, overriddenMembers);
});
mixinApplicationOverridesMap.forEach(
(ClassMember classMember, Set<ClassMember> overriddenMembers) {
/// Declared mixed in members must be checked to validly override the
/// overridden members.
hierarchy.registerOverrideCheck(
classBuilder, classMember, overriddenMembers);
});
inheritedImplementsMap.forEach(
(ClassMember classMember, Set<ClassMember> overriddenMembers) {
/// Concrete members must be checked to validly override the overridden
/// members in concrete classes.
hierarchy.registerOverrideCheck(
classBuilder, classMember, overriddenMembers);
});
}
if (!hasInterfaces) {
/// All interface members also class members to we don't need to store
/// the interface members separately.
assert(
classMemberMap.length == interfaceMemberMap.length,
"Class/interface member mismatch. Class members: "
"$classMemberMap, interface members: $interfaceMemberMap.");
assert(
classSetterMap.length == interfaceSetterMap.length,
"Class/interface setter mismatch. Class setters: "
"$classSetterMap, interface setters: $interfaceSetterMap.");
assert(
classMemberMap.keys.every((Name name) =>
identical(classMemberMap[name], interfaceMemberMap[name])),
"Class/interface member mismatch. Class members: "
"$classMemberMap, interface members: $interfaceMemberMap.");
assert(
classSetterMap.keys.every((Name name) =>
identical(classSetterMap[name], interfaceSetterMap[name])),
"Class/interface setter mismatch. Class setters: "
"$classSetterMap, interface setters: $interfaceSetterMap.");
interfaceMemberMap = null;
interfaceSetterMap = null;
}
if (abstractMembers != null && !classBuilder.isAbstract) {
if (!hasNoSuchMethod) {
reportMissingMembers(abstractMembers);
} else {
installNsmHandlers();
}
}
return new ClassHierarchyNode(
classBuilder,
classMemberMap,
classSetterMap,
interfaceMemberMap,
interfaceSetterMap,
superclasses,
interfaces,
maxInheritancePath,
hasNoSuchMethod,
dataForTesting);
}
Supertype recordSupertype(Supertype supertype) {
debug?.log("In ${this.classBuilder.fullNameForErrors} "
"recordSupertype(${supertype})");
Class cls = supertype.classNode;
List<TypeParameter> supertypeTypeParameters = cls.typeParameters;
if (supertypeTypeParameters.isEmpty) {
substitutions[cls] = Substitution.empty;
} else {
List<DartType> arguments = supertype.typeArguments;
List<DartType> typeArguments =
new List<DartType>.filled(arguments.length, null);
List<TypeParameter> typeParameters =
new List<TypeParameter>.filled(arguments.length, null);
for (int i = 0; i < arguments.length; i++) {
typeParameters[i] = supertypeTypeParameters[i];
typeArguments[i] = arguments[i];
}
substitutions[cls] =
Substitution.fromPairs(typeParameters, typeArguments);
}
return supertype;
}
List<Supertype> substSupertypes(
Supertype supertype, List<Supertype> supertypes) {
List<TypeParameter> typeVariables = supertype.classNode.typeParameters;
if (typeVariables.isEmpty) {
debug?.log("In ${this.classBuilder.fullNameForErrors} "
"$supertypes aren't substed");
return supertypes;
}
Map<TypeParameter, DartType> map = <TypeParameter, DartType>{};
List<DartType> arguments = supertype.typeArguments;
for (int i = 0; i < typeVariables.length; i++) {
map[typeVariables[i]] = arguments[i];
}
Substitution substitution = Substitution.fromMap(map);
List<Supertype> result;
for (int i = 0; i < supertypes.length; i++) {
Supertype supertype = supertypes[i];
Supertype substituted = substitution.substituteSupertype(supertype);
if (supertype != substituted) {
debug?.log("In ${this.classBuilder.fullNameForErrors} $supertype"
" -> $substituted");
result ??= supertypes.toList();
result[i] = substituted;
} else {
debug?.log("In ${this.classBuilder.fullNameForErrors} "
"$supertype isn't substed");
}
}
return result ?? supertypes;
}
void addInterface(List<Supertype> interfaces, List<Supertype> superclasses,
Supertype type) {
if (type == null) return null;
if (!classBuilder.library.isNonNullableByDefault) {
type = legacyErasureSupertype(type);
}
ClassHierarchyNode node = hierarchy.getNodeFromClass(type.classNode);
if (node == null) return null;
int depth = node.depth;
int myDepth = superclasses.length;
Supertype superclass = depth < myDepth ? superclasses[depth] : null;
if (superclass != null && superclass.classNode == type.classNode) {
// This is a potential conflict.
if (classBuilder.library.isNonNullableByDefault) {
superclass = nnbdTopMergeSupertype(
hierarchy.coreTypes,
normSupertype(hierarchy.coreTypes, superclass),
normSupertype(hierarchy.coreTypes, type));
if (superclass == null) {
// This is a conflict.
// TODO(johnniwinther): Report errors here instead of through
// the computation of the [ClassHierarchy].
superclass = superclasses[depth];
} else {
superclasses[depth] = superclass;
}
}
return;
} else {
for (int i = 0; i < interfaces.length; i++) {
// This is a quadratic algorithm, but normally, the number of
// interfaces is really small.
Supertype interface = interfaces[i];
if (interface.classNode == type.classNode) {
// This is a potential conflict.
if (classBuilder.library.isNonNullableByDefault) {
interface = nnbdTopMergeSupertype(
hierarchy.coreTypes,
normSupertype(hierarchy.coreTypes, interface),
normSupertype(hierarchy.coreTypes, type));
if (interface == null) {
// This is a conflict.
// TODO(johnniwinther): Report errors here instead of through
// the computation of the [ClassHierarchy].
interface = interfaces[i];
} else {
interfaces[i] = interface;
}
}
return;
}
}
}
interfaces.add(type);
}
void reportMissingMembers(List<ClassMember> abstractMembers) {
Map<String, LocatedMessage> contextMap = <String, LocatedMessage>{};
for (ClassMember declaration in unfoldDeclarations(abstractMembers)) {
if (isNameVisibleIn(declaration.name, classBuilder.library)) {
String name = declaration.fullNameForErrors;
String className = declaration.classBuilder?.fullNameForErrors;
String displayName =
declaration.isSetter ? "$className.$name=" : "$className.$name";
contextMap[displayName] = templateMissingImplementationCause
.withArguments(displayName)
.withLocation(
declaration.fileUri, declaration.charOffset, name.length);
}
}
if (contextMap.isEmpty) return;
List<String> names = new List<String>.from(contextMap.keys)..sort();
List<LocatedMessage> context = <LocatedMessage>[];
for (int i = 0; i < names.length; i++) {
context.add(contextMap[names[i]]);
}
classBuilder.addProblem(
templateMissingImplementationNotAbstract.withArguments(
classBuilder.fullNameForErrors, names),
classBuilder.charOffset,
classBuilder.fullNameForErrors.length,
context: context);
}
void installNsmHandlers() {
// TODO(ahe): Implement this.
}
void inferMixinApplication() {
Class cls = classBuilder.cls;
Supertype mixedInType = cls.mixedInType;
if (mixedInType == null) return;
List<DartType> typeArguments = mixedInType.typeArguments;
if (typeArguments.isEmpty || typeArguments.first is! UnknownType) return;
new BuilderMixinInferrer(
classBuilder,
hierarchy.coreTypes,
new TypeBuilderConstraintGatherer(hierarchy,
mixedInType.classNode.typeParameters, cls.enclosingLibrary))
.infer(cls);
List<TypeBuilder> inferredArguments =
new List<TypeBuilder>.filled(typeArguments.length, null);
for (int i = 0; i < typeArguments.length; i++) {
inferredArguments[i] =
hierarchy.loader.computeTypeBuilder(typeArguments[i]);
}
NamedTypeBuilder mixedInTypeBuilder = classBuilder.mixedInTypeBuilder;
mixedInTypeBuilder.arguments = inferredArguments;
}
/// The class Function from dart:core is supposed to be ignored when used as
/// an interface.
List<TypeBuilder> ignoreFunction(List<TypeBuilder> interfaces) {
if (interfaces == null) return null;
for (int i = 0; i < interfaces.length; i++) {
ClassBuilder classBuilder = getClass(interfaces[i]);
if (classBuilder != null && classBuilder.cls == hierarchy.functionClass) {
if (interfaces.length == 1) {
return null;
} else {
interfaces = interfaces.toList();
interfaces.removeAt(i);
return ignoreFunction(interfaces);
}
}
}
return interfaces;
}
}
class ClassHierarchyNode {
/// The class corresponding to this hierarchy node.
final ClassBuilder classBuilder;
/// All the members of this class including [classMembers] of its
/// superclasses. The members are sorted by [compareDeclarations].
final Map<Name, ClassMember> classMemberMap;
/// Similar to [classMembers] but for setters.
final Map<Name, ClassMember> classSetterMap;
/// All the interface members of this class including [interfaceMembers] of
/// its supertypes. The members are sorted by [compareDeclarations].
///
/// In addition to the members of [classMembers] this also contains members
/// from interfaces.
///
/// This may be null, in which case [classMembers] is the interface members.
final Map<Name, ClassMember> interfaceMemberMap;
/// Similar to [interfaceMembers] but for setters.
///
/// This may be null, in which case [classSetters] is the interface setters.
final Map<Name, ClassMember> interfaceSetterMap;
/// All superclasses of [classBuilder] excluding itself. The classes are
/// sorted by depth from the root (Object) in ascending order.
final List<Supertype> superclasses;
/// The list of all classes implemented by [classBuilder] and its supertypes
/// excluding any classes from [superclasses].
final List<Supertype> interfaces;
/// The longest inheritance path from [classBuilder] to `Object`.
final int maxInheritancePath;
int get depth => superclasses.length;
final bool hasNoSuchMethod;
final ClassHierarchyNodeDataForTesting dataForTesting;
ClassHierarchyNode(
this.classBuilder,
this.classMemberMap,
this.classSetterMap,
this.interfaceMemberMap,
this.interfaceSetterMap,
this.superclasses,
this.interfaces,
this.maxInheritancePath,
this.hasNoSuchMethod,
this.dataForTesting);
/// Returns a list of all supertypes of [classBuilder], including this node.
List<ClassHierarchyNode> computeAllSuperNodes(
ClassHierarchyBuilder hierarchy) {
List<ClassHierarchyNode> result = new List<ClassHierarchyNode>.filled(
1 + superclasses.length + interfaces.length, null);
for (int i = 0; i < superclasses.length; i++) {
Supertype type = superclasses[i];
result[i] = hierarchy.getNodeFromClass(type.classNode);
}
for (int i = 0; i < interfaces.length; i++) {
Supertype type = interfaces[i];
result[i + superclasses.length] =
hierarchy.getNodeFromClass(type.classNode);
}
return result..last = this;
}
String toString() {
StringBuffer sb = new StringBuffer();
sb
..write(classBuilder.fullNameForErrors)
..writeln(":");
if (maxInheritancePath != this.depth) {
sb
..write(" Longest path to Object: ")
..writeln(maxInheritancePath);
}
sb..writeln(" superclasses:");
int depth = 0;
for (Supertype superclass in superclasses) {
sb.write(" " * (depth + 2));
if (depth != 0) sb.write("-> ");
sb.write(typeToText(superclass.asInterfaceType));
sb.writeln();
depth++;
}
if (interfaces != null) {
sb.write(" interfaces:");
bool first = true;
for (Supertype i in interfaces) {
if (!first) sb.write(",");
sb.write(" ");
sb.write(typeToText(i.asInterfaceType));
first = false;
}
sb.writeln();
}
printMemberMap(classMemberMap, sb, "classMembers");
printMemberMap(classSetterMap, sb, "classSetters");
if (interfaceMemberMap != null) {
printMemberMap(interfaceMemberMap, sb, "interfaceMembers");
}
if (interfaceSetterMap != null) {
printMemberMap(interfaceSetterMap, sb, "interfaceSetters");
}
return "$sb";
}
void printMembers(
List<ClassMember> members, StringBuffer sb, String heading) {
sb.write(" ");
sb.write(heading);
sb.writeln(":");
for (ClassMember member in members) {
sb
..write(" ")
..write(member.classBuilder.fullNameForErrors)
..write(".")
..write(member.fullNameForErrors)
..writeln();
}
}
void printMemberMap(
Map<Name, ClassMember> memberMap, StringBuffer sb, String heading) {
List<ClassMember> members = memberMap.values.toList();
members.sort(compareDeclarations);
printMembers(members, sb, heading);
}
ClassMember getInterfaceMember(Name name, bool isSetter) {
return isSetter
? (interfaceSetterMap ?? classSetterMap)[name]
: (interfaceMemberMap ?? classMemberMap)[name];
}
ClassMember findMember(Name name, List<ClassMember> declarations) {
// TODO(ahe): Consider creating a map or scope. The obvious choice would be
// to use scopes, but they don't handle private names correctly.
// This is a copy of `ClassHierarchy.findMemberByName`.
int low = 0, high = declarations.length - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);
ClassMember pivot = declarations[mid];
int comparison = ClassHierarchy.compareNames(name, pivot.name);
if (comparison < 0) {
high = mid - 1;
} else if (comparison > 0) {
low = mid + 1;
} else if (high != mid) {
// Ensure we find the first element of the given name.
high = mid;
} else {
return pivot;
}
}
return null;
}
ClassMember getDispatchTarget(Name name, bool isSetter) {
return isSetter ? classSetterMap[name] : classMemberMap[name];
}
static int compareMaxInheritancePath(
ClassHierarchyNode a, ClassHierarchyNode b) {
return b.maxInheritancePath.compareTo(a.maxInheritancePath);
}
}
class ClassHierarchyNodeDataForTesting {
final List<ClassMember> abstractMembers;
final Map<ClassMember, Set<ClassMember>> declaredOverrides;
final Map<ClassMember, Set<ClassMember>> mixinApplicationOverrides;
final Map<ClassMember, Set<ClassMember>> inheritedImplements;
final Map<ClassMember, ClassMember> aliasMap = {};
ClassHierarchyNodeDataForTesting(this.abstractMembers, this.declaredOverrides,
this.mixinApplicationOverrides, this.inheritedImplements);
}
List<LocatedMessage> _inheritedConflictContext(ClassMember a, ClassMember b) {
int length = a.fullNameForErrors.length;
// TODO(ahe): Delete this method when it isn't used by [InterfaceResolver].
int compare = "${a.fileUri}".compareTo("${b.fileUri}");
if (compare == 0) {
compare = a.charOffset.compareTo(b.charOffset);
}
ClassMember first;
ClassMember second;
if (compare < 0) {
first = a;
second = b;
} else {
first = b;
second = a;
}
return <LocatedMessage>[
messageInheritedMembersConflictCause1.withLocation(
first.fileUri, first.charOffset, length),
messageInheritedMembersConflictCause2.withLocation(
second.fileUri, second.charOffset, length),
];
}
class BuilderMixinInferrer extends MixinInferrer {
final ClassBuilder cls;
BuilderMixinInferrer(
this.cls, CoreTypes coreTypes, TypeBuilderConstraintGatherer gatherer)
: super(coreTypes, gatherer);
Supertype asInstantiationOf(Supertype type, Class superclass) {
List<DartType> arguments =
gatherer.getTypeArgumentsAsInstanceOf(type.asInterfaceType, superclass);
if (arguments == null) return null;
return new Supertype(superclass, arguments);
}
void reportProblem(Message message, Class kernelClass) {
int length = cls.isMixinApplication ? 1 : cls.fullNameForErrors.length;
cls.addProblem(message, cls.charOffset, length);
}
}
class TypeBuilderConstraintGatherer extends TypeConstraintGatherer
with StandardBounds, TypeSchemaStandardBounds {
final ClassHierarchyBuilder hierarchy;
TypeBuilderConstraintGatherer(this.hierarchy,
Iterable<TypeParameter> typeParameters, Library currentLibrary)
: super.subclassing(typeParameters, currentLibrary);
@override
CoreTypes get coreTypes => hierarchy.coreTypes;
@override
void addLowerBound(
TypeConstraint constraint, DartType lower, Library clientLibrary) {
constraint.lower =
getStandardUpperBound(constraint.lower, lower, clientLibrary);
}
@override
void addUpperBound(
TypeConstraint constraint, DartType upper, Library clientLibrary) {
constraint.upper =
getStandardLowerBound(constraint.upper, upper, clientLibrary);
}
@override
Member getInterfaceMember(Class class_, Name name, {bool setter: false}) {
return null;
}
@override
InterfaceType getTypeAsInstanceOf(InterfaceType type, Class superclass,
Library clientLibrary, CoreTypes coreTypes) {
return hierarchy.getTypeAsInstanceOf(type, superclass, clientLibrary);
}
@override
List<DartType> getTypeArgumentsAsInstanceOf(
InterfaceType type, Class superclass) {
return hierarchy.getTypeArgumentsAsInstanceOf(type, superclass);
}
@override
InterfaceType futureType(DartType type, Nullability nullability) {
return new InterfaceType(
hierarchy.futureClass, nullability, <DartType>[type]);
}
@override
bool isSubtypeOf(
DartType subtype, DartType supertype, SubtypeCheckMode mode) {
return hierarchy.types.isSubtypeOf(subtype, supertype, mode);
}
@override
bool areMutualSubtypes(DartType s, DartType t, SubtypeCheckMode mode) {
return isSubtypeOf(s, t, mode) && isSubtypeOf(t, s, mode);
}
}
abstract class DelayedCheck {
void check(ClassHierarchyBuilder hierarchy);
}
class DelayedOverrideCheck implements DelayedCheck {
final SourceClassBuilder _classBuilder;
final ClassMember _declaredMember;
final Set<ClassMember> _overriddenMembers;
DelayedOverrideCheck(
this._classBuilder, this._declaredMember, this._overriddenMembers);
void check(ClassHierarchyBuilder hierarchy) {
Member declaredMember = _declaredMember.getMember(hierarchy);
/// If [_declaredMember] is a class member that is declared in an opt-in
/// library but inherited to [_classBuilder] through an opt-out class then
/// we need to apply legacy erasure to the declared type to get the
/// inherited type.
///
/// For interface members this is handled by member signatures but since
/// these are abstract they will never be the inherited class member.
///
/// For instance:
///
/// // Opt in:
/// class Super {
/// int extendedMethod(int i, {required int j}) => i;
/// }
/// class Mixin {
/// int mixedInMethod(int i, {required int j}) => i;
/// }
/// // Opt out:
/// class Legacy extends Super with Mixin {}
/// // Opt in:
/// class Class extends Legacy {
/// // Valid overrides since the type of `Legacy.extendedMethod` is
/// // `int* Function(int*, {int* j})`.
/// int? extendedMethod(int? i, {int? j}) => i;
/// // Valid overrides since the type of `Legacy.mixedInMethod` is
/// // `int* Function(int*, {int* j})`.
/// int? mixedInMethod(int? i, {int? j}) => i;
/// }
///
bool declaredNeedsLegacyErasure =
needsLegacyErasure(_classBuilder.cls, declaredMember.enclosingClass);
void callback(Member interfaceMember, bool isSetter) {
_classBuilder.checkOverride(
hierarchy.types, declaredMember, interfaceMember, isSetter, callback,
isInterfaceCheck: !_classBuilder.isMixinApplication,
declaredNeedsLegacyErasure: declaredNeedsLegacyErasure);
}
for (ClassMember overriddenMember in _overriddenMembers) {
callback(overriddenMember.getMember(hierarchy), _declaredMember.isSetter);
}
}
}
class DelayedGetterSetterCheck implements DelayedCheck {
final SourceClassBuilder classBuilder;
final ClassMember getter;
final ClassMember setter;
const DelayedGetterSetterCheck(this.classBuilder, this.getter, this.setter);
void check(ClassHierarchyBuilder hierarchy) {
classBuilder.checkGetterSetter(hierarchy.types, getter.getMember(hierarchy),
setter.getMember(hierarchy));
}
}
class DelayedTypeComputation {
final ClassHierarchyNodeBuilder builder;
final ClassMember declaredMember;
final Set<ClassMember> overriddenMembers;
bool _computed = false;
DelayedTypeComputation(
this.builder, this.declaredMember, this.overriddenMembers)
: assert(declaredMember.isSourceDeclaration);
void compute(ClassHierarchyBuilder hierarchy) {
if (_computed) return;
declaredMember.inferType(hierarchy);
_computed = true;
if (declaredMember.isField) {
builder.inferFieldSignature(hierarchy, declaredMember, overriddenMembers);
} else if (declaredMember.isGetter) {
builder.inferGetterSignature(
hierarchy, declaredMember, overriddenMembers);
} else if (declaredMember.isSetter) {
builder.inferSetterSignature(
hierarchy, declaredMember, overriddenMembers);
} else {
builder.inferMethodSignature(
hierarchy, declaredMember, overriddenMembers);
}
}
@override
String toString() => 'DelayedTypeComputation('
'${builder.classBuilder.name},$declaredMember,$overriddenMembers)';
}
int compareNamedParameters(VariableDeclaration a, VariableDeclaration b) {
return a.name.compareTo(b.name);
}
void reportCantInferParameterType(
ClassBuilder cls,
FormalParameterBuilder parameter,
ClassHierarchyBuilder hierarchy,
Iterable<ClassMember> overriddenMembers) {
String name = parameter.name;
List<LocatedMessage> context = overriddenMembers
.map((ClassMember overriddenMember) {
return messageDeclaredMemberConflictsWithOverriddenMembersCause
.withLocation(overriddenMember.fileUri, overriddenMember.charOffset,
overriddenMember.fullNameForErrors.length);
})
// Call toSet to avoid duplicate context for instance of fields that are
// overridden both as getters and setters.
.toSet()
.toList();
cls.addProblem(
templateCantInferTypeDueToNoCombinedSignature.withArguments(name),
parameter.charOffset,
name.length,
wasHandled: true,
context: context);
}
void reportCantInferTypes(ClassBuilder cls, SourceProcedureBuilder member,
ClassHierarchyBuilder hierarchy, Iterable<ClassMember> overriddenMembers) {
String name = member.fullNameForErrors;
List<LocatedMessage> context = overriddenMembers
.map((ClassMember overriddenMember) {
return messageDeclaredMemberConflictsWithOverriddenMembersCause
.withLocation(overriddenMember.fileUri, overriddenMember.charOffset,
overriddenMember.fullNameForErrors.length);
})
// Call toSet to avoid duplicate context for instance of fields that are
// overridden both as getters and setters.
.toSet()
.toList();
cls.addProblem(
templateCantInferTypesDueToNoCombinedSignature.withArguments(name),
member.charOffset,
name.length,
wasHandled: true,
context: context);
}
void reportCantInferReturnType(ClassBuilder cls, SourceProcedureBuilder member,
ClassHierarchyBuilder hierarchy, Iterable<ClassMember> overriddenMembers) {
String name = member.fullNameForErrors;
List<LocatedMessage> context = overriddenMembers
.map((ClassMember overriddenMember) {
return messageDeclaredMemberConflictsWithOverriddenMembersCause
.withLocation(overriddenMember.fileUri, overriddenMember.charOffset,
overriddenMember.fullNameForErrors.length);
})
// Call toSet to avoid duplicate context for instance of fields that are
// overridden both as getters and setters.
.toSet()
.toList();
// // TODO(ahe): The following is for debugging, but could be cleaned up and
// // used to improve this error message in general.
//
// context = <LocatedMessage>[];
// ClassHierarchyNode supernode = hierarchy.getNodeFromType(cls.supertype);
// // TODO(ahe): Wrong template.
// Template<Message Function(String)> template =
// templateMissingImplementationCause;
// if (supernode != null) {
// Declaration superMember =
// supernode.getInterfaceMember(new Name(name), false);
// if (superMember != null) {
// context.add(template
// .withArguments(name)
// .withLocation(
// superMember.fileUri, superMember.charOffset, name.length));
// }
// superMember = supernode.getInterfaceMember(new Name(name), true);
// if (superMember != null) {
// context.add(template
// .withArguments(name)
// .withLocation(
// superMember.fileUri, superMember.charOffset, name.length));
// }
// }
// List<TypeBuilder> directInterfaces = cls.interfaces;
// for (int i = 0; i < directInterfaces.length; i++) {
// ClassHierarchyNode supernode =
// hierarchy.getNodeFromType(directInterfaces[i]);
// if (supernode != null) {
// Declaration superMember =
// supernode.getInterfaceMember(new Name(name), false);
// if (superMember != null) {
// context.add(template
// .withArguments(name)
// .withLocation(
// superMember.fileUri, superMember.charOffset, name.length));
// }
// superMember = supernode.getInterfaceMember(new Name(name), true);
// if (superMember != null) {
// context.add(template
// .withArguments(name)
// .withLocation(
// superMember.fileUri, superMember.charOffset, name.length));
// }
// }
// }
cls.addProblem(
templateCantInferReturnTypeDueToNoCombinedSignature.withArguments(name),
member.charOffset,
name.length,
wasHandled: true,
context: context);
}
void reportCantInferFieldType(ClassBuilder cls, SourceFieldBuilder member,
Iterable<ClassMember> overriddenMembers) {
List<LocatedMessage> context = overriddenMembers
.map((ClassMember overriddenMember) {
return messageDeclaredMemberConflictsWithOverriddenMembersCause
.withLocation(overriddenMember.fileUri, overriddenMember.charOffset,
overriddenMember.fullNameForErrors.length);
})
// Call toSet to avoid duplicate context for instance of fields that are
// overridden both as getters and setters.
.toSet()
.toList();
String name = member.fullNameForErrors;
cls.addProblem(
templateCantInferTypeDueToNoCombinedSignature.withArguments(name),
member.charOffset,
name.length,
wasHandled: true,
context: context);
}
ClassBuilder getClass(TypeBuilder type) {
Builder declaration = type.declaration;
if (declaration is TypeAliasBuilder) {
TypeAliasBuilder aliasBuilder = declaration;
NamedTypeBuilder namedBuilder = type;
declaration = aliasBuilder.unaliasDeclaration(namedBuilder.arguments);
}
return declaration is ClassBuilder ? declaration : null;
}
Set<ClassMember> toSet(
ClassBuilder classBuilder, Iterable<ClassMember> members) {
Set<ClassMember> result = <ClassMember>{};
_toSet(classBuilder, members, result);
return result;
}
void _toSet(ClassBuilder classBuilder, Iterable<ClassMember> members,
Set<ClassMember> result) {
for (ClassMember member in members) {
if (member.hasDeclarations && classBuilder == member.classBuilder) {
_toSet(classBuilder, member.declarations, result);
} else {
result.add(member);
}
}
}
Set<ClassMember> unfoldDeclarations(Iterable<ClassMember> members) {
Set<ClassMember> result = <ClassMember>{};
_unfoldDeclarations(members, result);
return result;
}
void _unfoldDeclarations(
Iterable<ClassMember> members, Set<ClassMember> result) {
for (ClassMember member in members) {
if (member.hasDeclarations) {
_unfoldDeclarations(member.declarations, result);
} else {
result.add(member);
}
}
}
abstract class SynthesizedMember extends ClassMember {
@override
final ClassBuilder classBuilder;
@override
final Name name;
@override
final bool forSetter;
@override
final bool isProperty;
SynthesizedMember(this.classBuilder, this.name,
{this.forSetter, this.isProperty})
: assert(forSetter != null),
assert(isProperty != null);
@override
List<ClassMember> get declarations => throw new UnimplementedError();
@override
void inferType(ClassHierarchyBuilder hierarchy) {}
@override
bool get isAssignable => throw new UnimplementedError();
@override
bool get isConst => throw new UnimplementedError();
@override
bool get isDuplicate => false;
@override
bool get isField => throw new UnimplementedError();
@override
bool get isFinal => throw new UnimplementedError();
@override
bool get isGetter => throw new UnimplementedError();
@override
bool get isInternalImplementation => false;
@override
bool get isSetter => forSetter;
@override
bool get isSourceDeclaration => false;
@override
bool get isStatic => false;
@override
bool get isSynthesized => true;
@override
void registerOverrideDependency(Set<ClassMember> overriddenMembers) {}
}
/// Class member for a set of interface members.
///
/// This is used to compute combined member signature of a set of interface
/// members inherited into the same class, and to insert forwarding stubs,
/// mixin stubs, and member signatures where needed.
class SynthesizedInterfaceMember extends SynthesizedMember {
@override
final List<ClassMember> declarations;
/// The concrete member in the super class overridden by [declarations], if
/// any.
///
/// This is used to as the target when creating concrete forwarding and mixin
/// stub. For instance:
///
/// class Super {
/// method(int i) {}
/// }
/// class Interface {
/// method(covariant int i) {}
/// }
/// class Class extends Super implements Interface {
/// // Concrete forwarding stub calling [_superClassMember]:
/// method(covariant int i) => super.method(i);
///
final ClassMember _superClassMember;
/// The canonical member of the combined member signature if it is known by
/// construction. The canonical member defines the type of combined member
/// signature.
///
/// This is used when a declared member is part of a set of implemented
/// members. For instance
///
/// class Super {
/// method(int i) {}
/// }
/// class Interface {
/// method(covariant num i) {}
/// }
/// class Class implements Interface {
/// // This member is updated to be a concrete forwarding stub with an
/// // covariant parameter but with its declared parameter type:
/// // method(covariant int i) => super.method(i);
/// method(int i);
/// }
final ClassMember _canonicalMember;
/// The member in [declarations] that is mixed in, if any.
///
/// This is used to create mixin stubs. If the mixed in member is abstract,
/// an abstract mixin stub is created:
///
/// class Super {
/// void method() {}
/// }
/// class Mixin {
/// void method();
/// }
/// // Abstract mixin stub with `Mixin.method` as target inserted:
/// // void method();
/// class Class = Super with Mixin;
///
/// If the mixed in member is concrete, a concrete mixin member is created:
///
/// class Super {
/// void method() {}
/// }
/// class Mixin {
/// void method() {}
/// }
/// // Concrete mixin stub with `Mixin.method` as target inserted:
/// // void method() => super.method();
/// class Class = Super with Mixin;
///
/// If a forwarding stub is needed, the created stub will be a possibly
/// concrete forwarding stub:
///
/// class Super {
/// void method(int i) {}
/// }
/// class Interface {
/// void method(covariant num i) {}
/// }
/// class Mixin {
/// void method(int i);
/// }
/// // Concrete forwarding stub with `Super.method` as target inserted:
/// // void method(covariant int i) => super.method(i);
/// class Class = Super with Mixin implements Interface;
///
final ClassMember _mixedInMember;
/// If `true`, a stub should be inserted, if needed.
final bool _shouldModifyKernel;
Member _member;
Covariance _covariance;
SynthesizedInterfaceMember(
ClassBuilder classBuilder, Name name, this.declarations,
{ClassMember superClassMember,
ClassMember canonicalMember,
ClassMember mixedInMember,
bool isProperty,
bool forSetter,
bool shouldModifyKernel})
: this._superClassMember = superClassMember,
this._canonicalMember = canonicalMember,
this._mixedInMember = mixedInMember,
this._shouldModifyKernel = shouldModifyKernel,
super(classBuilder, name, isProperty: isProperty, forSetter: forSetter);
@override
bool get hasDeclarations => true;
void _ensureMemberAndCovariance(ClassHierarchyBuilder hierarchy) {
if (_member != null) {
return;
}
if (classBuilder.library is! SourceLibraryBuilder) {
if (_canonicalMember != null) {
_member = _canonicalMember.getMember(hierarchy);
_covariance = _canonicalMember.getCovariance(hierarchy);
} else {
_member = declarations.first.getMember(hierarchy);
_covariance = declarations.first.getCovariance(hierarchy);
}
return;
}
CombinedClassMemberSignature combinedMemberSignature;
if (_canonicalMember != null) {
combinedMemberSignature = new CombinedClassMemberSignature.internal(
hierarchy,
classBuilder,
declarations.indexOf(_canonicalMember),
declarations,
forSetter: isSetter);
} else {
combinedMemberSignature = new CombinedClassMemberSignature(
hierarchy, classBuilder, declarations,
forSetter: isSetter);
if (combinedMemberSignature.canonicalMember == null) {
String name = classBuilder.fullNameForErrors;
int length = classBuilder.isAnonymousMixinApplication ? 1 : name.length;
List<LocatedMessage> context = declarations.map((ClassMember d) {
return messageDeclaredMemberConflictsWithOverriddenMembersCause
.withLocation(
d.fileUri, d.charOffset, d.fullNameForErrors.length);
}).toList();
classBuilder.addProblem(
templateCombinedMemberSignatureFailed.withArguments(
classBuilder.fullNameForErrors,
declarations.first.fullNameForErrors),
classBuilder.charOffset,
length,
context: context);
// TODO(johnniwinther): Maybe we should have an invalid marker to avoid
// cascading errors.
_member = declarations.first.getMember(hierarchy);
_covariance = declarations.first.getCovariance(hierarchy);
return;
}
}
if (_shouldModifyKernel) {
ProcedureKind kind = ProcedureKind.Method;
Member canonicalMember =
combinedMemberSignature.canonicalMember.getMember(hierarchy);
if (combinedMemberSignature.canonicalMember.isProperty) {
kind = isSetter ? ProcedureKind.Setter : ProcedureKind.Getter;
} else if (canonicalMember is Procedure &&
canonicalMember.kind == ProcedureKind.Operator) {
kind = ProcedureKind.Operator;
}
Procedure stub = new ForwardingNode(
combinedMemberSignature, kind, _superClassMember, _mixedInMember)
.finalize();
if (stub != null) {
assert(classBuilder.cls == stub.enclosingClass);
assert(stub != canonicalMember);
classBuilder.cls.addProcedure(stub);
SourceLibraryBuilder library = classBuilder.library;
if (canonicalMember is Procedure) {
library.forwardersOrigins..add(stub)..add(canonicalMember);
}
_member = stub;
_covariance = combinedMemberSignature.combinedMemberSignatureCovariance;
assert(
_covariance ==
new Covariance.fromMember(_member, forSetter: forSetter),
"Unexpected covariance for combined members signature "
"$_member. Found $_covariance, expected "
"${new Covariance.fromMember(_member, forSetter: forSetter)}.");
return;
}
}
_member = combinedMemberSignature.canonicalMember.getMember(hierarchy);
_covariance = combinedMemberSignature.combinedMemberSignatureCovariance;
}
@override
Member getMember(ClassHierarchyBuilder hierarchy) {
_ensureMemberAndCovariance(hierarchy);
return _member;
}
@override
Covariance getCovariance(ClassHierarchyBuilder hierarchy) {
_ensureMemberAndCovariance(hierarchy);
return _covariance;
}
@override
ClassMember get interfaceMember => this;
@override
bool isObjectMember(ClassBuilder objectClass) {
return false;
}
@override
bool isSameDeclaration(ClassMember other) {
// TODO(johnniwinther): Optimize this.
return false;
}
@override
int get charOffset => declarations.first.charOffset;
@override
Uri get fileUri => declarations.first.fileUri;
@override
bool get isAbstract => true;
@override
String get fullNameForErrors =>
declarations.map((ClassMember m) => m.fullName).join("%");
@override
String get fullName {
String suffix = isSetter ? "=" : "";
return "${fullNameForErrors}$suffix";
}
String toString() => 'SynthesizedInterfaceMember($classBuilder,$name,'
'$declarations,forSetter=$forSetter)';
}
/// Class member for an inherited concrete member that implements an interface
/// member.
///
/// This is used to ensure that both the inherited concrete member and the
/// interface member is taken into account when computing the resulting [Member]
/// node.
///
/// This is needed because an interface member, though initially abstract, can
/// result in a concrete stub that overrides the concrete member. For instance
///
/// class Super {
/// method(int i) {}
/// }
/// class Interface {
/// method(covariant int i) {}
/// }
/// class Class extends Super implements Interface {
/// // A concrete forwarding stub is inserted:
/// method(covariant int i) => super.method(i);
/// }
/// class Sub extends Class implements Interface {
/// // No forwarding stub should be inserted since `Class.method` is
/// // adequate.
/// }
///
///
/// Here the create stub `Class.method` overrides `Super.method` and should
/// be used to determine whether to insert a forwarding stub in subclasses.
class InheritedClassMemberImplementsInterface extends SynthesizedMember {
final ClassMember inheritedClassMember;
final ClassMember implementedInterfaceMember;
Member _member;
Covariance _covariance;
InheritedClassMemberImplementsInterface(ClassBuilder classBuilder, Name name,
{this.inheritedClassMember,
this.implementedInterfaceMember,
bool isProperty,
bool forSetter})
: assert(inheritedClassMember != null),
assert(implementedInterfaceMember != null),
super(classBuilder, name, isProperty: isProperty, forSetter: forSetter);
void _ensureMemberAndCovariance(ClassHierarchyBuilder hierarchy) {
if (_member == null) {
Member classMember = inheritedClassMember.getMember(hierarchy);
Member interfaceMember = implementedInterfaceMember.getMember(hierarchy);
if (!interfaceMember.isAbstract &&
interfaceMember.enclosingClass == classBuilder.cls) {
/// The interface member resulted in a concrete stub being inserted.
/// For instance for `method1` but _not_ for `method2` here:
///
/// class Super {
/// method1(int i) {}
/// method2(covariant int i) {}
/// }
/// class Interface {
/// method1(covariant int i) {}
/// method2(int i) {}
/// }
/// class Class extends Super implements Interface {
/// // A concrete forwarding stub is inserted for `method1` since
/// // the parameter on `Super.method1` is _not_ marked as
/// // covariant:
/// method1(covariant int i) => super.method(i);
/// // No concrete forwarding stub is inserted for `method2` since
/// // the parameter on `Super.method2` is already marked as
/// // covariant.
/// }
///
/// The inserted stub should be used as the resulting member.
_member = interfaceMember;
_covariance = implementedInterfaceMember.getCovariance(hierarchy);
} else {
/// The interface member did not result in an inserted stub or the
/// inserted stub was abstract. For instance:
///
/// // Opt-in:
/// class Super {
/// method(int? i) {}
/// }
/// // Opt-out:
/// class Class extends Super {
/// // An abstract member signature stub is inserted:
/// method(int* i);
/// }
///
/// The inserted stub should _not_ be used as the resulting member
/// since it is abstract and therefore not a class member.
_member = classMember;
_covariance = inheritedClassMember.getCovariance(hierarchy);
}
}
}
@override
Member getMember(ClassHierarchyBuilder hierarchy) {
_ensureMemberAndCovariance(hierarchy);
return _member;
}
@override
Covariance getCovariance(ClassHierarchyBuilder hierarchy) {
_ensureMemberAndCovariance(hierarchy);
return _covariance;
}
@override
ClassMember get interfaceMember => implementedInterfaceMember;
@override
bool isObjectMember(ClassBuilder objectClass) {
return inheritedClassMember.isObjectMember(objectClass);
}
@override
bool isSameDeclaration(ClassMember other) {
// TODO(johnniwinther): Optimize this.
return false;
}
@override
int get charOffset => inheritedClassMember.charOffset;
@override
Uri get fileUri => inheritedClassMember.fileUri;
@override
bool get hasDeclarations => false;
@override
bool get isAbstract => false;
@override
String get fullNameForErrors => inheritedClassMember.fullNameForErrors;
String get fullName => inheritedClassMember.fullName;
@override
String toString() =>
'InheritedClassMemberImplementsInterface($classBuilder,$name,'
'inheritedClassMember=$inheritedClassMember,'
'implementedInterfaceMember=$implementedInterfaceMember,'
'forSetter=$forSetter)';
}