in pkg/front_end/lib/src/fasta/kernel/hierarchy/members_node.dart [568:2338]
ClassMembersNode build() {
ClassMembersNode? supernode = _hierarchyNode.supernode != null
? _membersBuilder
.getNodeFromClassBuilder(_hierarchyNode.supernode!.classBuilder)
: null;
List<TypeBuilder>? directInterfaceBuilders =
_hierarchyNode.directInterfaceBuilders;
/// 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 (Builder builder in scope.localMembers) {
MemberBuilder memberBuilder = builder as MemberBuilder;
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!;
while (mixin.isNamedMixinApplication) {
ClassBuilder named = mixin as ClassBuilder;
mixedInTypeBuilder = named.mixedInTypeBuilder!;
mixin = mixedInTypeBuilder.declaration!;
}
if (mixin is TypeAliasBuilder) {
TypeAliasBuilder aliasBuilder = mixin;
NamedTypeBuilder namedBuilder = mixedInTypeBuilder as NamedTypeBuilder;
mixin = aliasBuilder.unaliasDeclaration(namedBuilder.arguments,
isUsedAsClass: true,
usedAsClassCharOffset: namedBuilder.charOffset,
usedAsClassFileUri: namedBuilder.fileUri)!;
}
if (mixin is ClassBuilder) {
scope = mixin.scope.computeMixinScope();
for (Builder builder in scope.localMembers) {
MemberBuilder memberBuilder = builder as MemberBuilder;
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;
}
}
}
}
}
void extend(Map<Name, ClassMember>? superClassMembers) {
if (superClassMembers == null) return;
for (MapEntry<Name, ClassMember> entry in superClassMembers.entries) {
Name name = entry.key;
ClassMember superClassMember = entry.value;
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 (MapEntry<Name, ClassMember> entry in superInterfaceMembers.entries) {
Name name = entry.key;
ClassMember superInterfaceMember = entry.value;
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.
} else {
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++) {
ClassMembersNode? interfaceNode = _membersBuilder
.getNodeFromTypeBuilder(directInterfaceBuilders[i]);
if (interfaceNode != null) {
hasInterfaces = true;
implement(interfaceNode.interfaceMemberMap ??
interfaceNode.classMemberMap);
implement(interfaceNode.interfaceSetterMap ??
interfaceNode.classSetterMap);
}
}
}
}
/// 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,
{required ClassMember aliasForTesting}) {
if (classBuilder is SourceClassBuilder) {
assert(
inheritedMember.classBuilder != classBuilder,
"Only inherited members can implement by inheritance: "
"${inheritedMember}");
inheritedImplementsMap[inheritedMember] = overrides;
// ignore: unnecessary_null_comparison
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;
ClassMember? tupleMixedInMember = tuple.mixedInMember;
if (tupleMixedInMember != null &&
!tupleMixedInMember.isStatic &&
!tupleMixedInMember.isDuplicate &&
!tupleMixedInMember.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 = tupleMixedInMember;
} 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 != tupleMixedInMember.isProperty) {
reportInheritanceConflict(definingGetable, tupleMixedInMember);
} else {
mixedInGetable = tupleMixedInMember;
}
}
}
ClassMember? mixedInSetable;
ClassMember? tupleMixedInSetter = tuple.mixedInSetter;
if (tupleMixedInSetter != null &&
!tupleMixedInSetter.isStatic &&
!tupleMixedInSetter.isDuplicate &&
!tupleMixedInSetter.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 = tupleMixedInSetter;
} else if (!definingSetable.isDuplicate) {
if (definingSetable.isStatic ||
definingSetable.isProperty != tupleMixedInSetter.isProperty) {
reportInheritanceConflict(definingSetable, tupleMixedInSetter);
} else {
mixedInSetable = tupleMixedInSetter;
}
}
}
ClassMember? extendedGetable;
ClassMember? tupleExtendedMember = tuple.extendedMember;
if (tupleExtendedMember != null &&
!tupleExtendedMember.isStatic &&
!tupleExtendedMember.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 = tupleExtendedMember;
} else if (!definingGetable.isDuplicate) {
if (definingGetable.isStatic ||
definingGetable.isProperty != tupleExtendedMember.isProperty) {
/// class Super {
/// method() {}
/// }
/// class Class extends Super {
/// static method() {}
/// }
///
/// or
///
/// class Super {
/// method() {}
/// }
/// class Class extends Super {
/// get getter => 0;
/// }
reportInheritanceConflict(definingGetable, tupleExtendedMember);
} else {
extendedGetable = tupleExtendedMember;
}
}
}
ClassMember? extendedSetable;
ClassMember? tupleExtendedSetter = tuple.extendedSetter;
if (tupleExtendedSetter != null &&
!tupleExtendedSetter.isStatic &&
!tupleExtendedSetter.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 = tupleExtendedSetter;
} else if (!definingSetable.isDuplicate) {
if (definingSetable.isStatic ||
definingSetable.isProperty != tupleExtendedSetter.isProperty) {
reportInheritanceConflict(definingSetable, tupleExtendedSetter);
} else {
extendedSetable = tupleExtendedSetter;
}
}
}
// 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;
List<ClassMember>? tupleImplementedMembers = tuple.implementedMembers;
if (tupleImplementedMembers != null &&
// Skip implemented members if we already have a duplicate.
!(definingGetable != null && definingGetable.isDuplicate)) {
for (int i = 0; i < tupleImplementedMembers.length; i++) {
ClassMember? implementedGetable = tupleImplementedMembers[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 ??= tupleImplementedMembers.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 = tupleImplementedMembers;
} else if (implementedGetables.isEmpty) {
// No members were included.
implementedGetables = null;
}
}
List<ClassMember>? implementedSetables;
List<ClassMember>? tupleImplementedSetters = tuple.implementedSetters;
if (tupleImplementedSetters != null &&
// Skip implemented setters if we already have a duplicate.
!(definingSetable != null && definingSetable.isDuplicate)) {
for (int i = 0; i < tupleImplementedSetters.length; i++) {
ClassMember? implementedSetable = tupleImplementedSetters[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 ??= tupleImplementedSetters.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 = tupleImplementedSetters;
} 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(
{required ClassMember definingMember,
required ClassMember? declaredMember,
required ClassMember? mixedInMember,
required ClassMember? extendedMember,
required List<ClassMember>? implementedMembers,
required Map<Name, ClassMember> classMemberMap,
required 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);
_membersBuilder.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);
_membersBuilder.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);
_membersBuilder.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);
_membersBuilder.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);
_membersBuilder.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);
_membersBuilder.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 recognize 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);
_membersBuilder.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);
_membersBuilder.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);
_membersBuilder.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);
_membersBuilder.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);
_membersBuilder.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`.
_membersBuilder.registerGetterSetterCheck(
classBuilder as SourceClassBuilder,
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);
_membersBuilder.registerDelayedTypeComputation(computation);
/// Declared members must be checked to validly override the
/// overridden members.
_membersBuilder.registerOverrideCheck(
classBuilder as SourceClassBuilder, classMember, overriddenMembers);
});
mixinApplicationOverridesMap.forEach(
(ClassMember classMember, Set<ClassMember> overriddenMembers) {
/// Declared mixed in members must be checked to validly override the
/// overridden members.
_membersBuilder.registerOverrideCheck(
classBuilder as SourceClassBuilder, classMember, overriddenMembers);
});
inheritedImplementsMap.forEach(
(ClassMember classMember, Set<ClassMember> overriddenMembers) {
/// Concrete members must be checked to validly override the overridden
/// members in concrete classes.
_membersBuilder.registerOverrideCheck(
classBuilder as SourceClassBuilder, 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;
}
// ignore: unnecessary_null_comparison
if (abstractMembers != null && !classBuilder.isAbstract) {
if (!hasNoSuchMethod) {
reportMissingMembers(abstractMembers);
} else {
installNsmHandlers();
}
}
return new ClassMembersNode(
classBuilder,
classMemberMap,
classSetterMap,
interfaceMemberMap,
interfaceSetterMap,
hasNoSuchMethod,
dataForTesting);
}