| // Copyright (c) 2012, 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. |
| |
| library dart2js.resolution.compute_members; |
| |
| import '../common.dart'; |
| import '../common/names.dart' show Identifiers, Names; |
| import '../common/resolution.dart' show Resolution; |
| import '../dart_types.dart'; |
| import '../elements/elements.dart' |
| show |
| ClassElement, |
| Element, |
| LibraryElement, |
| Member, |
| MemberElement, |
| MemberSignature, |
| MixinApplicationElement, |
| Name; |
| import '../util/util.dart'; |
| |
| part 'member_impl.dart'; |
| |
| abstract class MembersCreator { |
| final ClassElement cls; |
| final Resolution resolution; |
| |
| final Iterable<String> computedMemberNames; |
| final Map<Name, Member> classMembers; |
| |
| Map<dynamic /* Member | Element */, Set<MessageKind>> reportedMessages = |
| new Map<dynamic, Set<MessageKind>>(); |
| |
| MembersCreator( |
| this.resolution, this.cls, this.computedMemberNames, this.classMembers) { |
| assert(invariant(cls, cls.isDeclaration, |
| message: "Members may only be computed on declarations.")); |
| } |
| |
| DiagnosticReporter get reporter => resolution.reporter; |
| |
| void reportMessage(var marker, MessageKind kind, report()) { |
| Set<MessageKind> messages = |
| reportedMessages.putIfAbsent(marker, () => new Set<MessageKind>()); |
| if (messages.add(kind)) { |
| report(); |
| } |
| } |
| |
| bool shouldSkipMember(MemberSignature member) { |
| return member == null || shouldSkipName(member.name.text); |
| } |
| |
| bool shouldSkipName(String name) { |
| return computedMemberNames != null && |
| // 'call' is implicitly contained in [computedMemberNames]. |
| (name == Identifiers.call || computedMemberNames.contains(name)); |
| } |
| |
| /// Compute all members of [cls] with the given names. |
| void computeMembersByName(String name, Setlet<Name> names) { |
| computeMembers(name, names); |
| } |
| |
| /// Compute all members of [cls] and checked that [cls] implements its |
| /// interface unless it is abstract or declares a `noSuchMethod` method. |
| void computeAllMembers() { |
| computeMembers(null, null); |
| if (!cls.isAbstract) { |
| Member member = classMembers[Names.noSuchMethod_]; |
| if (member != null && |
| !resolution.target.isDefaultNoSuchMethod(member.element)) { |
| return; |
| } |
| // Check for unimplemented members on concrete classes that neither have |
| // a `@proxy` annotation nor declare a `noSuchMethod` method. |
| checkInterfaceImplementation(); |
| } |
| } |
| |
| /// Compute declared and inherited members of [cls] and return a map of the |
| /// declared members. |
| /// |
| /// If [name] and [names] are not null, the computation is restricted to |
| /// members with these names. |
| Map<Name, Member> computeMembers(String name, Setlet<Name> names); |
| |
| /// Compute the members of the super type(s) of [cls] and store them in |
| /// [classMembers]. |
| /// |
| /// If [name] and [names] are not null, the computation is restricted to |
| /// members with these names. |
| void computeSuperMembers(String name, Setlet<Name> names); |
| |
| /// Compute the members of the super class of [cls] and store them in |
| /// [classMembers]. |
| /// |
| /// If [name] and [names] are not null, the computation is restricted to |
| /// members with these names. |
| void computeSuperClassMembers(String name, Setlet<Name> names) { |
| InterfaceType supertype = cls.supertype; |
| if (supertype == null) return; |
| ClassElement superclass = supertype.element; |
| |
| // Inherit class and interface members from superclass. |
| void inheritClassMember(DeclaredMember member) { |
| if (shouldSkipMember(member)) return; |
| if (!member.isStatic) { |
| DeclaredMember inherited = member.inheritFrom(supertype); |
| classMembers[member.name] = inherited; |
| } |
| } |
| |
| if (names != null) { |
| _computeClassMember(resolution, superclass, name, names); |
| for (Name memberName in names) { |
| inheritClassMember(superclass.lookupClassMember(memberName)); |
| } |
| } else { |
| computeAllClassMembers(resolution, superclass); |
| superclass.forEachClassMember(inheritClassMember); |
| } |
| } |
| |
| /// Compute the members declared or directly mixed in [cls]. |
| /// |
| /// If [name] and [names] are not null, the computation is restricted to |
| /// members with these names. |
| Map<Name, Member> computeClassMembers(String nameText, Setlet<Name> names) { |
| Map<Name, Member> declaredMembers = new Map<Name, Member>(); |
| |
| if (cls.isMixinApplication) { |
| MixinApplicationElement mixinApplication = cls; |
| if (mixinApplication.mixin != null) { |
| // Only mix in class members when the mixin type is not malformed. |
| |
| void inheritMixinMember(DeclaredMember member) { |
| if (shouldSkipMember(member)) return; |
| Name name = member.name; |
| if (!member.isAbstract && !member.isStatic) { |
| // Abstract and static members are not mixed in. |
| DeclaredMember mixedInMember = |
| member.inheritFrom(mixinApplication.mixinType); |
| DeclaredMember inherited = classMembers[name]; |
| classMembers[name] = mixedInMember; |
| checkValidOverride(mixedInMember, inherited); |
| } |
| } |
| |
| if (names != null) { |
| _computeClassMember( |
| resolution, mixinApplication.mixin, nameText, names); |
| for (Name memberName in names) { |
| inheritMixinMember( |
| mixinApplication.mixin.lookupClassMember(memberName)); |
| } |
| } else { |
| computeAllClassMembers(resolution, mixinApplication.mixin); |
| mixinApplication.mixin.forEachClassMember(inheritMixinMember); |
| } |
| } |
| } else { |
| LibraryElement library = cls.library; |
| InterfaceType thisType = cls.thisType; |
| |
| void createMember(MemberElement element) { |
| if (element.isConstructor) return; |
| String elementName = element.name; |
| if (shouldSkipName(elementName)) return; |
| if (nameText != null && elementName != nameText) return; |
| |
| void addDeclaredMember( |
| Name name, DartType type, FunctionType functionType) { |
| DeclaredMember inherited = classMembers[name]; |
| DeclaredMember declared; |
| if (element.isAbstract) { |
| declared = new DeclaredAbstractMember( |
| name, element, thisType, type, functionType, inherited); |
| } else { |
| declared = |
| new DeclaredMember(name, element, thisType, type, functionType); |
| } |
| declaredMembers[name] = declared; |
| classMembers[name] = declared; |
| checkValidOverride(declared, inherited); |
| } |
| |
| Name name = new Name(element.name, library); |
| if (element.isField) { |
| DartType type = element.computeType(resolution); |
| addDeclaredMember(name, type, new FunctionType.synthesized(type)); |
| if (!element.isConst && !element.isFinal) { |
| addDeclaredMember( |
| name.setter, |
| type, |
| new FunctionType.synthesized( |
| const VoidType(), <DartType>[type])); |
| } |
| } else if (element.isGetter) { |
| FunctionType functionType = element.computeType(resolution); |
| DartType type = functionType.returnType; |
| addDeclaredMember(name, type, functionType); |
| } else if (element.isSetter) { |
| FunctionType functionType = element.computeType(resolution); |
| DartType type; |
| if (!functionType.parameterTypes.isEmpty) { |
| type = functionType.parameterTypes.first; |
| } else { |
| type = const DynamicType(); |
| } |
| name = name.setter; |
| addDeclaredMember(name, type, functionType); |
| } else { |
| assert(invariant(element, element.isFunction)); |
| FunctionType type = element.computeType(resolution); |
| addDeclaredMember(name, type, type); |
| } |
| } |
| |
| cls.forEachLocalMember(createMember); |
| if (cls.isPatched) { |
| cls.implementation.forEachLocalMember((Element element) { |
| if (element.isDeclaration) { |
| createMember(element); |
| } |
| }); |
| } |
| } |
| |
| return declaredMembers; |
| } |
| |
| /// Checks that [classMember] is a valid implementation for [interfaceMember]. |
| void checkInterfaceMember( |
| Name name, MemberSignature interfaceMember, Member classMember) { |
| if (classMember != null) { |
| // TODO(johnniwinther): Check that the class member is a valid override |
| // of the interface member. |
| return; |
| } |
| if (interfaceMember is DeclaredMember && |
| interfaceMember.declarer.element == cls) { |
| // Abstract method declared in [cls]. |
| MessageKind kind = MessageKind.ABSTRACT_METHOD; |
| if (interfaceMember.isSetter) { |
| kind = MessageKind.ABSTRACT_SETTER; |
| } else if (interfaceMember.isGetter) { |
| kind = MessageKind.ABSTRACT_GETTER; |
| } |
| reportMessage(interfaceMember.element, MessageKind.ABSTRACT_METHOD, () { |
| reporter.reportWarningMessage(interfaceMember.element, kind, |
| {'class': cls.name, 'name': name.text}); |
| }); |
| } else { |
| reportWarning(MessageKind singleKind, MessageKind multipleKind, |
| MessageKind explicitlyDeclaredKind, |
| [MessageKind implicitlyDeclaredKind]) { |
| Member inherited = interfaceMember.declarations.first; |
| reportMessage(interfaceMember, MessageKind.UNIMPLEMENTED_METHOD, () { |
| DiagnosticMessage warning = reporter.createMessage( |
| cls, |
| interfaceMember.declarations.length == 1 |
| ? singleKind |
| : multipleKind, |
| { |
| 'class': cls.name, |
| 'name': name.text, |
| 'method': interfaceMember, |
| 'declarer': inherited.declarer |
| }); |
| List<DiagnosticMessage> infos = <DiagnosticMessage>[]; |
| for (Member inherited in interfaceMember.declarations) { |
| infos.add(reporter.createMessage( |
| inherited.element, |
| inherited.isDeclaredByField |
| ? implicitlyDeclaredKind |
| : explicitlyDeclaredKind, |
| {'class': inherited.declarer.name, 'name': name.text})); |
| } |
| reporter.reportWarning(warning, infos); |
| }); |
| } |
| |
| if (interfaceMember.isSetter) { |
| reportWarning( |
| MessageKind.UNIMPLEMENTED_SETTER_ONE, |
| MessageKind.UNIMPLEMENTED_SETTER, |
| MessageKind.UNIMPLEMENTED_EXPLICIT_SETTER, |
| MessageKind.UNIMPLEMENTED_IMPLICIT_SETTER); |
| } else if (interfaceMember.isGetter) { |
| reportWarning( |
| MessageKind.UNIMPLEMENTED_GETTER_ONE, |
| MessageKind.UNIMPLEMENTED_GETTER, |
| MessageKind.UNIMPLEMENTED_EXPLICIT_GETTER, |
| MessageKind.UNIMPLEMENTED_IMPLICIT_GETTER); |
| } else if (interfaceMember.isMethod) { |
| reportWarning( |
| MessageKind.UNIMPLEMENTED_METHOD_ONE, |
| MessageKind.UNIMPLEMENTED_METHOD, |
| MessageKind.UNIMPLEMENTED_METHOD_CONT); |
| } |
| } |
| // TODO(johnniwinther): If [cls] is not abstract, check that for all |
| // interface members, there is a class member whose type is a subtype of |
| // the interface member. |
| } |
| |
| /// Checks that [cls], if it implements Function, has defined call(). |
| void checkImplementsFunctionWithCall() { |
| assert(!cls.isAbstract); |
| |
| ClassElement functionClass = resolution.coreClasses.functionClass; |
| functionClass.ensureResolved(resolution); |
| if (cls.asInstanceOf(functionClass) == null) return; |
| if (cls.lookupMember(Identifiers.call) != null) return; |
| // TODO(johnniwinther): Make separate methods for backend exceptions. |
| // Avoid warnings on backend implementation classes for closures. |
| if (resolution.target.isTargetSpecificLibrary(cls.library)) return; |
| |
| reportMessage(functionClass, MessageKind.UNIMPLEMENTED_METHOD, () { |
| reporter.reportWarningMessage(cls, MessageKind.UNIMPLEMENTED_METHOD_ONE, { |
| 'class': cls.name, |
| 'name': Identifiers.call, |
| 'method': Identifiers.call, |
| 'declarer': functionClass.name |
| }); |
| }); |
| } |
| |
| /// Checks that a class member exists for every interface member. |
| void checkInterfaceImplementation(); |
| |
| /// Check that [declared] is a valid override of [superMember]. |
| void checkValidOverride(Member declared, MemberSignature superMember) { |
| if (superMember == null) { |
| // No override. |
| if (!declared.isStatic) { |
| ClassElement superclass = cls.superclass; |
| while (superclass != null) { |
| Member superMember = superclass.lookupClassMember(declared.name); |
| if (superMember != null && superMember.isStatic) { |
| reportMessage(superMember, MessageKind.INSTANCE_STATIC_SAME_NAME, |
| () { |
| reporter.reportWarning( |
| reporter.createMessage( |
| declared.element, MessageKind.INSTANCE_STATIC_SAME_NAME, { |
| 'memberName': declared.name, |
| 'className': superclass.name |
| }), |
| <DiagnosticMessage>[ |
| reporter.createMessage(superMember.element, |
| MessageKind.INSTANCE_STATIC_SAME_NAME_CONT), |
| ]); |
| }); |
| break; |
| } |
| superclass = superclass.superclass; |
| } |
| } |
| } else { |
| assert(declared.name == superMember.name); |
| |
| if (declared.isStatic) { |
| for (Member inherited in superMember.declarations) { |
| if (cls == inherited.declarer.element) { |
| // An error should already have been reported. |
| assert(invariant( |
| declared.element, resolution.reporter.hasReportedError)); |
| continue; |
| } |
| |
| reportMessage(inherited.element, MessageKind.NO_STATIC_OVERRIDE, () { |
| reportErrorWithContext( |
| declared.element, |
| MessageKind.NO_STATIC_OVERRIDE, |
| inherited.element, |
| MessageKind.NO_STATIC_OVERRIDE_CONT); |
| }); |
| } |
| } |
| |
| DartType declaredType = declared.functionType; |
| for (Member inherited in superMember.declarations) { |
| if (inherited.element == declared.element) { |
| // TODO(ahe): For some reason, "call" elements are repeated in |
| // superMember.declarations. Investigate why. |
| } else if (cls == inherited.declarer.element) { |
| // An error should already have been reported. |
| assert( |
| invariant(declared.element, resolution.reporter.hasReportedError, |
| message: "Member $inherited inherited from its " |
| "declaring class: ${cls}.")); |
| continue; |
| } |
| |
| void reportError(MessageKind errorKind, MessageKind infoKind) { |
| reportMessage(inherited.element, MessageKind.INVALID_OVERRIDE_METHOD, |
| () { |
| reporter.reportError( |
| reporter.createMessage(declared.element, errorKind, { |
| 'name': declared.name.text, |
| 'class': cls.thisType, |
| 'inheritedClass': inherited.declarer |
| }), |
| <DiagnosticMessage>[ |
| reporter.createMessage(inherited.element, infoKind, { |
| 'name': declared.name.text, |
| 'class': inherited.declarer |
| }), |
| ]); |
| }); |
| } |
| |
| if (declared.isDeclaredByField && inherited.isMethod) { |
| reportError(MessageKind.CANNOT_OVERRIDE_METHOD_WITH_FIELD, |
| MessageKind.CANNOT_OVERRIDE_METHOD_WITH_FIELD_CONT); |
| } else if (declared.isMethod && inherited.isDeclaredByField) { |
| reportError(MessageKind.CANNOT_OVERRIDE_FIELD_WITH_METHOD, |
| MessageKind.CANNOT_OVERRIDE_FIELD_WITH_METHOD_CONT); |
| } else if (declared.isGetter && inherited.isMethod) { |
| reportError(MessageKind.CANNOT_OVERRIDE_METHOD_WITH_GETTER, |
| MessageKind.CANNOT_OVERRIDE_METHOD_WITH_GETTER_CONT); |
| } else if (declared.isMethod && inherited.isGetter) { |
| reportError(MessageKind.CANNOT_OVERRIDE_GETTER_WITH_METHOD, |
| MessageKind.CANNOT_OVERRIDE_GETTER_WITH_METHOD_CONT); |
| } else { |
| DartType inheritedType = inherited.functionType; |
| if (!resolution.types.isSubtype(declaredType, inheritedType)) { |
| void reportWarning( |
| var marker, MessageKind warningKind, MessageKind infoKind) { |
| reportMessage(marker, MessageKind.INVALID_OVERRIDE_METHOD, () { |
| reporter.reportWarning( |
| reporter.createMessage(declared.element, warningKind, { |
| 'declaredType': declared.type, |
| 'name': declared.name.text, |
| 'class': cls.thisType, |
| 'inheritedType': inherited.type, |
| 'inheritedClass': inherited.declarer |
| }), |
| <DiagnosticMessage>[ |
| reporter.createMessage(inherited.element, infoKind, { |
| 'name': declared.name.text, |
| 'class': inherited.declarer |
| }), |
| ]); |
| }); |
| } |
| |
| if (declared.isDeclaredByField) { |
| if (inherited.isDeclaredByField) { |
| reportWarning( |
| inherited.element, |
| MessageKind.INVALID_OVERRIDE_FIELD, |
| MessageKind.INVALID_OVERRIDDEN_FIELD); |
| } else if (inherited.isGetter) { |
| reportWarning( |
| inherited, |
| MessageKind.INVALID_OVERRIDE_GETTER_WITH_FIELD, |
| MessageKind.INVALID_OVERRIDDEN_GETTER); |
| } else if (inherited.isSetter) { |
| reportWarning( |
| inherited, |
| MessageKind.INVALID_OVERRIDE_SETTER_WITH_FIELD, |
| MessageKind.INVALID_OVERRIDDEN_SETTER); |
| } |
| } else if (declared.isGetter) { |
| if (inherited.isDeclaredByField) { |
| reportWarning( |
| inherited, |
| MessageKind.INVALID_OVERRIDE_FIELD_WITH_GETTER, |
| MessageKind.INVALID_OVERRIDDEN_FIELD); |
| } else { |
| reportWarning(inherited, MessageKind.INVALID_OVERRIDE_GETTER, |
| MessageKind.INVALID_OVERRIDDEN_GETTER); |
| } |
| } else if (declared.isSetter) { |
| if (inherited.isDeclaredByField) { |
| reportWarning( |
| inherited, |
| MessageKind.INVALID_OVERRIDE_FIELD_WITH_SETTER, |
| MessageKind.INVALID_OVERRIDDEN_FIELD); |
| } else { |
| reportWarning(inherited, MessageKind.INVALID_OVERRIDE_SETTER, |
| MessageKind.INVALID_OVERRIDDEN_SETTER); |
| } |
| } else { |
| reportWarning(inherited, MessageKind.INVALID_OVERRIDE_METHOD, |
| MessageKind.INVALID_OVERRIDDEN_METHOD); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| void reportErrorWithContext( |
| Element errorneousElement, |
| MessageKind errorMessage, |
| Element contextElement, |
| MessageKind contextMessage) { |
| reporter.reportError( |
| reporter.createMessage(errorneousElement, errorMessage, { |
| 'memberName': contextElement.name, |
| 'className': contextElement.enclosingClass.name |
| }), |
| <DiagnosticMessage>[ |
| reporter.createMessage(contextElement, contextMessage), |
| ]); |
| } |
| |
| /// Compute all class and interface names by the [name] in [cls]. |
| static void computeClassMembersByName( |
| Resolution resolution, ClassMemberMixin cls, String name) { |
| if (cls.isMemberComputed(name)) return; |
| LibraryElement library = cls.library; |
| _computeClassMember( |
| resolution, |
| cls, |
| name, |
| new Setlet<Name>() |
| ..add(new Name(name, library)) |
| ..add(new Name(name, library, isSetter: true))); |
| } |
| |
| static void _computeClassMember(Resolution resolution, ClassMemberMixin cls, |
| String name, Setlet<Name> names) { |
| cls.computeClassMember(resolution, name, names); |
| } |
| |
| /// Compute all class and interface names in [cls]. |
| static void computeAllClassMembers( |
| Resolution resolution, ClassMemberMixin cls) { |
| cls.computeAllClassMembers(resolution); |
| } |
| } |
| |
| /// Class member creator for classes where the interface members are known to |
| /// be a subset of the class members. |
| class ClassMembersCreator extends MembersCreator { |
| ClassMembersCreator(Resolution resolution, ClassElement cls, |
| Iterable<String> computedMemberNames, Map<Name, Member> classMembers) |
| : super(resolution, cls, computedMemberNames, classMembers); |
| |
| Map<Name, Member> computeMembers(String name, Setlet<Name> names) { |
| computeSuperMembers(name, names); |
| return computeClassMembers(name, names); |
| } |
| |
| void computeSuperMembers(String name, Setlet<Name> names) { |
| computeSuperClassMembers(name, names); |
| } |
| |
| void checkInterfaceImplementation() { |
| LibraryElement library = cls.library; |
| classMembers.forEach((Name name, Member classMember) { |
| if (!name.isAccessibleFrom(library)) return; |
| checkInterfaceMember(name, classMember, classMember.implementation); |
| }); |
| } |
| } |
| |
| /// Class Member creator for classes where the interface members might be |
| /// different from the class members. |
| class InterfaceMembersCreator extends MembersCreator { |
| final Map<Name, MemberSignature> interfaceMembers; |
| |
| InterfaceMembersCreator( |
| Resolution resolution, |
| ClassElement cls, |
| Iterable<String> computedMemberNames, |
| Map<Name, Member> classMembers, |
| Map<Name, MemberSignature> this.interfaceMembers) |
| : super(resolution, cls, computedMemberNames, classMembers); |
| |
| Map<Name, Member> computeMembers(String name, Setlet<Name> names) { |
| Map<Name, Setlet<Member>> inheritedInterfaceMembers = |
| computeSuperMembers(name, names); |
| Map<Name, Member> declaredMembers = computeClassMembers(name, names); |
| computeInterfaceMembers(inheritedInterfaceMembers, declaredMembers); |
| return declaredMembers; |
| } |
| |
| /// Compute the members of the super type(s) of [cls]. The class members are |
| /// stored if the [classMembers] map and the inherited interface members are |
| /// returned. |
| /// |
| /// If [name] and [names] are not null, the computation is restricted to |
| /// members with these names. |
| Map<Name, Setlet<Member>> computeSuperMembers( |
| String name, Setlet<Name> names) { |
| computeSuperClassMembers(name, names); |
| return computeSuperInterfaceMembers(name, names); |
| } |
| |
| Map<Name, Setlet<Member>> computeSuperInterfaceMembers( |
| String name, Setlet<Name> names) { |
| InterfaceType supertype = cls.supertype; |
| assert(invariant(cls, supertype != null, |
| message: "Interface members computed for $cls.")); |
| ClassElement superclass = supertype.element; |
| |
| Map<Name, Setlet<Member>> inheritedInterfaceMembers = |
| new Map<Name, Setlet<Member>>(); |
| |
| void inheritInterfaceMember( |
| InterfaceType supertype, MemberSignature member) { |
| if (shouldSkipMember(member)) return; |
| Setlet<Member> members = inheritedInterfaceMembers.putIfAbsent( |
| member.name, () => new Setlet<Member>()); |
| for (DeclaredMember declaredMember in member.declarations) { |
| members.add(declaredMember.inheritFrom(supertype)); |
| } |
| } |
| |
| void inheritInterfaceMembers(InterfaceType supertype) { |
| supertype.element.forEachInterfaceMember((MemberSignature member) { |
| inheritInterfaceMember(supertype, member); |
| }); |
| } |
| |
| if (names != null) { |
| for (Name memberName in names) { |
| inheritInterfaceMember( |
| supertype, superclass.lookupInterfaceMember(memberName)); |
| } |
| } else { |
| inheritInterfaceMembers(supertype); |
| } |
| |
| // Inherit interface members from superinterfaces. |
| for (Link<DartType> link = cls.interfaces; |
| !link.isEmpty; |
| link = link.tail) { |
| InterfaceType superinterface = link.head; |
| if (names != null) { |
| MembersCreator._computeClassMember( |
| resolution, superinterface.element, name, names); |
| for (Name memberName in names) { |
| inheritInterfaceMember(superinterface, |
| superinterface.element.lookupInterfaceMember(memberName)); |
| } |
| } else { |
| MembersCreator.computeAllClassMembers( |
| resolution, superinterface.element); |
| inheritInterfaceMembers(superinterface); |
| } |
| } |
| |
| return inheritedInterfaceMembers; |
| } |
| |
| /// Checks that a class member exists for every interface member. |
| void checkInterfaceImplementation() { |
| LibraryElement library = cls.library; |
| checkImplementsFunctionWithCall(); |
| interfaceMembers.forEach((Name name, MemberSignature interfaceMember) { |
| if (!name.isAccessibleFrom(library)) return; |
| Member classMember = classMembers[name]; |
| if (classMember != null) classMember = classMember.implementation; |
| checkInterfaceMember(name, interfaceMember, classMember); |
| }); |
| } |
| |
| /// Compute the interface members of [cls] given the set of inherited |
| /// interface members [inheritedInterfaceMembers] and declared members |
| /// [declaredMembers]. The computed members are stored in [interfaceMembers]. |
| void computeInterfaceMembers( |
| Map<Name, Setlet<Member>> inheritedInterfaceMembers, |
| Map<Name, Member> declaredMembers) { |
| InterfaceType thisType = cls.thisType; |
| // Compute the interface members by overriding the inherited members with |
| // a declared member or by computing a single, possibly synthesized, |
| // inherited member. |
| inheritedInterfaceMembers |
| .forEach((Name name, Setlet<Member> inheritedMembers) { |
| Member declared = declaredMembers[name]; |
| if (declared != null) { |
| // Check that [declaredMember] is a valid override |
| for (Member inherited in inheritedMembers) { |
| checkValidOverride(declared, inherited); |
| } |
| if (!declared.isStatic) { |
| interfaceMembers[name] = declared; |
| } |
| } else if (inheritedMembers.length == 1) { |
| interfaceMembers[name] = inheritedMembers.single; |
| } else { |
| bool someAreGetters = false; |
| bool allAreGetters = true; |
| Map<DartType, Setlet<Member>> subtypesOfAllInherited = |
| new Map<DartType, Setlet<Member>>(); |
| outer: |
| for (Member inherited in inheritedMembers) { |
| if (inherited.isGetter) { |
| someAreGetters = true; |
| if (!allAreGetters) break outer; |
| } else { |
| allAreGetters = false; |
| if (someAreGetters) break outer; |
| } |
| for (MemberSignature other in inheritedMembers) { |
| if (!resolution.types |
| .isSubtype(inherited.functionType, other.functionType)) { |
| continue outer; |
| } |
| } |
| subtypesOfAllInherited |
| .putIfAbsent(inherited.functionType, () => new Setlet<Member>()) |
| .add(inherited); |
| } |
| if (someAreGetters && !allAreGetters) { |
| DiagnosticMessage warning = reporter.createMessage( |
| cls, |
| MessageKind.INHERIT_GETTER_AND_METHOD, |
| {'class': thisType, 'name': name.text}); |
| List<DiagnosticMessage> infos = <DiagnosticMessage>[]; |
| for (Member inherited in inheritedMembers) { |
| MessageKind kind; |
| if (inherited.isMethod) { |
| kind = MessageKind.INHERITED_METHOD; |
| } else { |
| assert(invariant(cls, inherited.isGetter, |
| message: 'Conflicting member is neither a method nor a ' |
| 'getter.')); |
| if (inherited.isDeclaredByField) { |
| kind = MessageKind.INHERITED_IMPLICIT_GETTER; |
| } else { |
| kind = MessageKind.INHERITED_EXPLICIT_GETTER; |
| } |
| } |
| infos.add(reporter.createMessage(inherited.element, kind, |
| {'class': inherited.declarer, 'name': name.text})); |
| } |
| reporter.reportWarning(warning, infos); |
| interfaceMembers[name] = new ErroneousMember(inheritedMembers); |
| } else if (subtypesOfAllInherited.length == 1) { |
| // All signatures have the same type. |
| Setlet<Member> members = subtypesOfAllInherited.values.first; |
| MemberSignature inherited = members.first; |
| if (members.length != 1) { |
| // Multiple signatures with the same type => return a |
| // synthesized signature. |
| inherited = new SyntheticMember( |
| members, inherited.type, inherited.functionType); |
| } |
| interfaceMembers[name] = inherited; |
| } else { |
| _inheritedSynthesizedMember(name, inheritedMembers); |
| } |
| } |
| }); |
| |
| // Add the non-overriding instance methods to the interface members. |
| declaredMembers.forEach((Name name, Member member) { |
| if (!member.isStatic) { |
| interfaceMembers.putIfAbsent(name, () => member); |
| } |
| }); |
| } |
| |
| /// Create and inherit a synthesized member for [inheritedMembers]. |
| void _inheritedSynthesizedMember(Name name, Setlet<Member> inheritedMembers) { |
| // Multiple signatures with different types => create the synthesized |
| // version. |
| int minRequiredParameters; |
| int maxPositionalParameters; |
| Set<String> names = new Set<String>(); |
| for (MemberSignature member in inheritedMembers) { |
| int requiredParameters = 0; |
| int optionalParameters = 0; |
| if (member.isSetter) { |
| requiredParameters = 1; |
| } |
| if (member.type.isFunctionType) { |
| FunctionType type = member.type; |
| type.namedParameters.forEach((String name) => names.add(name)); |
| requiredParameters = type.parameterTypes.length; |
| optionalParameters = type.optionalParameterTypes.length; |
| } |
| int positionalParameters = requiredParameters + optionalParameters; |
| if (minRequiredParameters == null || |
| minRequiredParameters > requiredParameters) { |
| minRequiredParameters = requiredParameters; |
| } |
| if (maxPositionalParameters == null || |
| maxPositionalParameters < positionalParameters) { |
| maxPositionalParameters = positionalParameters; |
| } |
| } |
| int optionalParameters = maxPositionalParameters - minRequiredParameters; |
| // TODO(johnniwinther): Support function types with both optional |
| // and named parameters? |
| if (optionalParameters == 0 || names.isEmpty) { |
| DartType dynamic = const DynamicType(); |
| List<DartType> requiredParameterTypes = |
| new List.filled(minRequiredParameters, dynamic); |
| List<DartType> optionalParameterTypes = |
| new List.filled(optionalParameters, dynamic); |
| List<String> namedParameters = names.toList() |
| ..sort((a, b) => a.compareTo(b)); |
| List<DartType> namedParameterTypes = |
| new List.filled(namedParameters.length, dynamic); |
| FunctionType memberType = new FunctionType.synthesized( |
| const DynamicType(), |
| requiredParameterTypes, |
| optionalParameterTypes, |
| namedParameters, |
| namedParameterTypes); |
| DartType type = memberType; |
| if (inheritedMembers.first.isGetter || inheritedMembers.first.isSetter) { |
| type = const DynamicType(); |
| } |
| interfaceMembers[name] = |
| new SyntheticMember(inheritedMembers, type, memberType); |
| } |
| } |
| } |
| |
| abstract class ClassMemberMixin implements ClassElement { |
| /// When [classMembers] and [interfaceMembers] have not been fully computed |
| /// [computedMembersNames] holds the names for which members have already been |
| /// computed. |
| /// |
| /// If [computedMemberNames], [classMembers] and [interfaceMembers] are `null` |
| /// no members have been computed, if only [computedMemberNames] is `null` all |
| /// members have been computed. A non-null [computedMemberNames] implicitly |
| /// includes `call`. |
| Iterable<String> computedMemberNames; |
| |
| bool _interfaceMembersAreClassMembers; |
| |
| /// Compute value of the [_interfaceMembersAreClassMembers] for this class |
| /// and its superclasses. |
| void _computeInterfaceMembersAreClassMembers(Resolution resolution) { |
| if (_interfaceMembersAreClassMembers == null) { |
| ensureResolved(resolution); |
| ClassMemberMixin superclass = this.superclass; |
| if (superclass != null) { |
| superclass._computeInterfaceMembersAreClassMembers(resolution); |
| } |
| if ((superclass != null && |
| (!superclass.interfaceMembersAreClassMembers || |
| superclass.isMixinApplication)) || |
| !interfaces.isEmpty) { |
| _interfaceMembersAreClassMembers = false; |
| } else { |
| _interfaceMembersAreClassMembers = true; |
| } |
| } |
| } |
| |
| /// If `true` interface members are the non-static class member. |
| bool get interfaceMembersAreClassMembers => _interfaceMembersAreClassMembers; |
| |
| Map<Name, Member> classMembers; |
| Map<Name, MemberSignature> interfaceMembers; |
| |
| /// Creates the necessary maps and [MembersCreator] for compute members of |
| /// this class. |
| MembersCreator _prepareCreator(Resolution resolution) { |
| if (classMembers == null) { |
| _computeInterfaceMembersAreClassMembers(resolution); |
| classMembers = new Map<Name, Member>(); |
| if (!interfaceMembersAreClassMembers) { |
| interfaceMembers = new Map<Name, MemberSignature>(); |
| } |
| } |
| return interfaceMembersAreClassMembers |
| ? new ClassMembersCreator( |
| resolution, this, computedMemberNames, classMembers) |
| : new InterfaceMembersCreator(resolution, this, computedMemberNames, |
| classMembers, interfaceMembers); |
| } |
| |
| static Iterable<String> _EMPTY_MEMBERS_NAMES = const <String>[]; |
| |
| /// Compute the members by the name [name] for this class. [names] collects |
| /// the set of possible variations of [name], including getter, setter and |
| /// and private names. |
| void computeClassMember( |
| Resolution resolution, String name, Setlet<Name> names) { |
| // TODO(johnniwinther): Should we assert that the class has been resolved |
| // instead? |
| ensureResolved(resolution); |
| if (isMemberComputed(name)) return; |
| if (Name.isPrivateName(name)) { |
| names |
| ..add(new Name(name, library)) |
| ..add(new Name(name, library, isSetter: true)); |
| } |
| MembersCreator creator = _prepareCreator(resolution); |
| creator.computeMembersByName(name, names); |
| if (computedMemberNames == null) { |
| computedMemberNames = _EMPTY_MEMBERS_NAMES; |
| } |
| if (name != Identifiers.call) { |
| Setlet<String> set; |
| if (identical(computedMemberNames, _EMPTY_MEMBERS_NAMES)) { |
| computedMemberNames = set = new Setlet<String>(); |
| } else { |
| set = computedMemberNames; |
| } |
| set.add(name); |
| } |
| } |
| |
| void computeAllClassMembers(Resolution resolution) { |
| // TODO(johnniwinther): Should we assert that the class has been resolved |
| // instead? |
| ensureResolved(resolution); |
| if (areAllMembersComputed()) return; |
| MembersCreator creator = _prepareCreator(resolution); |
| creator.computeAllMembers(); |
| computedMemberNames = null; |
| assert(invariant(this, areAllMembersComputed())); |
| } |
| |
| bool areAllMembersComputed() { |
| return computedMemberNames == null && classMembers != null; |
| } |
| |
| bool isMemberComputed(String name) { |
| if (computedMemberNames == null) { |
| return classMembers != null; |
| } else { |
| return name == Identifiers.call || computedMemberNames.contains(name); |
| } |
| } |
| |
| Member lookupClassMember(Name name) { |
| assert(invariant(this, isMemberComputed(name.text), |
| message: "Member ${name} has not been computed for $this.")); |
| return classMembers[name]; |
| } |
| |
| void forEachClassMember(f(Member member)) { |
| assert(invariant(this, areAllMembersComputed(), |
| message: "Members have not been fully computed for $this.")); |
| classMembers.forEach((_, member) => f(member)); |
| } |
| |
| MemberSignature lookupInterfaceMember(Name name) { |
| assert(invariant(this, isMemberComputed(name.text), |
| message: "Member ${name.text} has not been computed for $this.")); |
| if (interfaceMembersAreClassMembers) { |
| Member member = classMembers[name]; |
| if (member != null && member.isStatic) return null; |
| return member; |
| } |
| return interfaceMembers[name]; |
| } |
| |
| void forEachInterfaceMember(f(MemberSignature member)) { |
| assert(invariant(this, areAllMembersComputed(), |
| message: "Members have not been fully computed for $this.")); |
| if (interfaceMembersAreClassMembers) { |
| classMembers.forEach((_, member) { |
| if (!member.isStatic) f(member); |
| }); |
| } else { |
| interfaceMembers.forEach((_, member) => f(member)); |
| } |
| } |
| } |