| // 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 resolution.compute_members; |
| |
| import '../elements/elements.dart' |
| show Element, |
| Name, |
| PublicName, |
| Member, |
| MemberSignature, |
| LibraryElement, |
| ClassElement, |
| MixinApplicationElement; |
| import '../elements/modelx.dart' |
| show BaseClassElementX; |
| import '../dart_types.dart'; |
| import '../dart2jslib.dart' |
| show Compiler, |
| MessageKind, |
| invariant; |
| import '../util/util.dart'; |
| |
| part 'member_impl.dart'; |
| |
| class MembersCreator { |
| final ClassElement cls; |
| final Compiler compiler; |
| |
| Map<Name, Member> classMembers = new Map<Name, Member>(); |
| Map<Name, MemberSignature> interfaceMembers = |
| new Map<Name, MemberSignature>(); |
| |
| Map<dynamic/* Member | Element */, Set<MessageKind>> reportedMessages = |
| new Map<dynamic, Set<MessageKind>>(); |
| |
| MembersCreator(this.compiler, this.cls) { |
| assert(invariant(cls, cls.isDeclaration, |
| message: "Members may only be computed on declarations.")); |
| } |
| |
| void reportMessage(var marker, MessageKind kind, report()) { |
| Set<MessageKind> messages = |
| reportedMessages.putIfAbsent(marker, |
| () => new Set<MessageKind>()); |
| if (messages.add(kind)) { |
| report(); |
| } |
| } |
| |
| void computeMembers() { |
| Map<Name, Set<Member>> inheritedInterfaceMembers = |
| _computeSuperMembers(); |
| Map<Name, Member> declaredMembers = _computeClassMembers(); |
| _computeInterfaceMembers(inheritedInterfaceMembers, declaredMembers); |
| |
| if (!cls.modifiers.isAbstract() && |
| !declaredMembers.containsKey(const PublicName('noSuchMethod'))) { |
| // Check for unimplemented members on concrete classes that neither have |
| // a `@proxy` annotation nor declare a `noSuchMethod` method. |
| checkInterfaceImplementation(); |
| } |
| } |
| |
| Map<Name, Set<Member>> _computeSuperMembers() { |
| Map<Name, Set<Member>> inheritedInterfaceMembers = |
| new Map<Name, Set<Member>>(); |
| |
| void inheritInterfaceMembers(InterfaceType supertype) { |
| supertype.element.forEachInterfaceMember((MemberSignature member) { |
| Set<Member> members = |
| inheritedInterfaceMembers.putIfAbsent( |
| member.name, () => new Set<Member>()); |
| for (DeclaredMember declaredMember in member.declarations) { |
| members.add(declaredMember.inheritFrom(supertype)); |
| } |
| }); |
| } |
| |
| // Inherit class and interface members from superclass. |
| InterfaceType superclass = cls.supertype; |
| if (superclass != null) { |
| computeClassMembers(compiler, superclass.element); |
| superclass.element.forEachClassMember((DeclaredMember member) { |
| if (!member.isStatic) { |
| DeclaredMember inherited = member.inheritFrom(superclass); |
| classMembers[member.name] = inherited; |
| } |
| }); |
| inheritInterfaceMembers(superclass); |
| } |
| |
| // Inherit interface members from superinterfaces. |
| for (Link<DartType> link = cls.interfaces; |
| !link.isEmpty; |
| link = link.tail) { |
| InterfaceType superinterface = link.head; |
| computeClassMembers(compiler, superinterface.element); |
| inheritInterfaceMembers(superinterface); |
| } |
| |
| return inheritedInterfaceMembers; |
| } |
| |
| Map<Name, Member> _computeClassMembers() { |
| Map<Name, Member> declaredMembers = new Map<Name, Member>(); |
| |
| void overrideMember(DeclaredMember declared) { |
| DeclaredMember inherited = classMembers[declared.name]; |
| classMembers[declared.name] = declared; |
| checkValidOverride(declared, inherited); |
| } |
| |
| if (cls.isMixinApplication) { |
| MixinApplicationElement mixinApplication = cls; |
| if (mixinApplication.mixin != null) { |
| // Only mix in class members when the mixin type is not malformed. |
| computeClassMembers(compiler, mixinApplication.mixin); |
| |
| mixinApplication.mixin.forEachClassMember((DeclaredMember member) { |
| if (!member.isStatic) { |
| // Abstract and static members are not mixed in. |
| DeclaredMember mixedInMember = |
| member.inheritFrom(mixinApplication.mixinType); |
| overrideMember(mixedInMember); |
| } |
| }); |
| } |
| } else { |
| LibraryElement library = cls.getLibrary(); |
| InterfaceType thisType = cls.thisType; |
| |
| void createMember(Element element) { |
| if (element.isConstructor()) return; |
| |
| Name name = new Name(element.name, library); |
| if (element.isField()) { |
| DartType type = element.computeType(compiler); |
| declaredMembers[name] = new DeclaredMember( |
| name, element, thisType, type, |
| new FunctionType(compiler.functionClass, type)); |
| if (!element.modifiers.isConst() && |
| !element.modifiers.isFinal()) { |
| name = name.setter; |
| declaredMembers[name] = new DeclaredMember( |
| name, element, thisType, type, |
| new FunctionType(compiler.functionClass, |
| compiler.types.voidType, |
| const Link<DartType>().prepend(type))); |
| } |
| } else if (element.isGetter()) { |
| FunctionType functionType = element.computeType(compiler); |
| DartType type = functionType.returnType; |
| declaredMembers[name] = |
| new DeclaredMember(name, element, thisType, type, functionType); |
| } else if (element.isSetter()) { |
| FunctionType functionType = element.computeType(compiler); |
| DartType type; |
| if (!functionType.parameterTypes.isEmpty) { |
| type = functionType.parameterTypes.head; |
| } else { |
| type = compiler.types.dynamicType; |
| } |
| name = name.setter; |
| declaredMembers[name] = new DeclaredMember( |
| name, element, thisType, type, functionType); |
| } else { |
| assert(invariant(element, element.isFunction())); |
| FunctionType type = element.computeType(compiler); |
| declaredMembers[name] = new DeclaredMember( |
| name, element, thisType, type, type); |
| } |
| } |
| |
| cls.forEachLocalMember(createMember); |
| if (cls.isPatched) { |
| cls.implementation.forEachLocalMember((Element element) { |
| if (element.isDeclaration) { |
| createMember(element); |
| } |
| }); |
| } |
| } |
| |
| declaredMembers.values.forEach((Member member) { |
| if (!member.element.isAbstract) { |
| overrideMember(member); |
| } |
| }); |
| |
| return declaredMembers; |
| } |
| |
| void _computeInterfaceMembers( |
| Map<Name, Set<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, Set<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 { |
| bool someAreGetters = false; |
| bool allAreGetters = true; |
| Map<DartType, Set<Member>> subtypesOfAllInherited = |
| new Map<DartType, Set<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 (!compiler.types.isSubtype(inherited.functionType, |
| other.functionType)) { |
| continue outer; |
| } |
| } |
| subtypesOfAllInherited.putIfAbsent(inherited.functionType, |
| () => new Set<Member>()).add(inherited); |
| } |
| if (someAreGetters && !allAreGetters) { |
| compiler.reportWarning(cls, |
| MessageKind.INHERIT_GETTER_AND_METHOD, |
| {'class': thisType, 'name': name.text }); |
| 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; |
| } |
| } |
| compiler.reportInfo(inherited.element, kind, |
| {'class': inherited.declarer, 'name': name.text }); |
| } |
| interfaceMembers[name] = new ErroneousMember(inheritedMembers); |
| } else if (subtypesOfAllInherited.length == 1) { |
| // All signatures have the same type. |
| Set<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, |
| Set<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.kind == TypeKind.FUNCTION) { |
| FunctionType type = member.type; |
| type.namedParameters.forEach( |
| (String name) => names.add(name)); |
| requiredParameters = type.parameterTypes.slowLength(); |
| optionalParameters = type.optionalParameterTypes.slowLength(); |
| } |
| 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) { |
| Link<DartType> requiredParameterTypes = const Link<DartType>(); |
| while (--minRequiredParameters >= 0) { |
| requiredParameterTypes = |
| requiredParameterTypes.prepend(compiler.types.dynamicType); |
| } |
| Link<DartType> optionalParameterTypes = const Link<DartType>(); |
| while (--optionalParameters >= 0) { |
| optionalParameterTypes = |
| optionalParameterTypes.prepend(compiler.types.dynamicType); |
| } |
| Link<String> namedParameters = const Link<String>(); |
| Link<DartType> namedParameterTypes = const Link<DartType>(); |
| List<String> namesReversed = |
| names.toList()..sort((a, b) => -a.compareTo(b)); |
| for (String name in namesReversed) { |
| namedParameters = namedParameters.prepend(name); |
| namedParameterTypes = |
| namedParameterTypes.prepend(compiler.types.dynamicType); |
| } |
| FunctionType memberType = new FunctionType( |
| compiler.functionClass, |
| compiler.types.dynamicType, |
| requiredParameterTypes, |
| optionalParameterTypes, |
| namedParameters, namedParameterTypes); |
| DartType type = memberType; |
| if (inheritedMembers.first.isGetter || |
| inheritedMembers.first.isSetter) { |
| type = compiler.types.dynamicType; |
| } |
| interfaceMembers[name] = new SyntheticMember( |
| inheritedMembers, type, memberType); |
| } |
| } |
| |
| /// Checks that a class member exists for every interface member. |
| void checkInterfaceImplementation() { |
| LibraryElement library = cls.getLibrary(); |
| |
| interfaceMembers.forEach((Name name, MemberSignature interfaceMember) { |
| if (!name.isAccessibleFrom(library)) return; |
| Member classMember = classMembers[name]; |
| if (classMember != null) 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, () { |
| compiler.reportWarning( |
| 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, () { |
| compiler.reportWarning(cls, |
| interfaceMember.declarations.length == 1 |
| ? singleKind : multipleKind, |
| {'class': cls.name, |
| 'name': name.text, |
| 'method': interfaceMember, |
| 'declarer': inherited.declarer}); |
| for (Member inherited in interfaceMember.declarations) { |
| compiler.reportInfo(inherited.element, |
| inherited.isDeclaredByField ? |
| implicitlyDeclaredKind : explicitlyDeclaredKind, |
| {'class': inherited.declarer.name, |
| 'name': name.text}); |
| } |
| }); |
| } |
| 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. |
| }); |
| } |
| |
| 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, |
| () { |
| compiler.reportWarning( |
| declared.element, |
| MessageKind.INSTANCE_STATIC_SAME_NAME, |
| {'memberName': declared.name, |
| 'className': superclass.name}); |
| compiler.reportInfo(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) { |
| 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) { |
| |
| void reportError(MessageKind errorKind, MessageKind infoKind) { |
| reportMessage( |
| inherited.element, MessageKind.INVALID_OVERRIDE_METHOD, () { |
| compiler.reportError(declared.element, errorKind, |
| {'name': declared.name.text, |
| 'class': cls.thisType, |
| 'inheritedClass': inherited.declarer}); |
| compiler.reportInfo(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 (!compiler.types.isSubtype(declaredType, inheritedType)) { |
| void reportWarning(var marker, |
| MessageKind warningKind, |
| MessageKind infoKind) { |
| reportMessage(marker, MessageKind.INVALID_OVERRIDE_METHOD, () { |
| compiler.reportWarning(declared.element, warningKind, |
| {'declaredType': declared.type, |
| 'name': declared.name.text, |
| 'class': cls.thisType, |
| 'inheritedType': inherited.type, |
| 'inheritedClass': inherited.declarer}); |
| compiler.reportInfo(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) { |
| compiler.reportError( |
| errorneousElement, |
| errorMessage, |
| {'memberName': contextElement.name, |
| 'className': contextElement.getEnclosingClass().name}); |
| compiler.reportInfo(contextElement, contextMessage); |
| } |
| |
| static void computeClassMembers(Compiler compiler, BaseClassElementX cls) { |
| if (cls.classMembers != null) return; |
| MembersCreator creator = new MembersCreator(compiler, cls); |
| creator.computeMembers(); |
| cls.classMembers = creator.classMembers; |
| cls.interfaceMembers = creator.interfaceMembers; |
| } |
| } |