blob: 155c5313eadc06ad051f78be6f905589633914a3 [file] [log] [blame]
// Copyright (c) 2014, 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 members_test;
import 'package:expect/expect.dart';
import "package:async_helper/async_helper.dart";
import 'type_test_helper.dart';
import '../../../sdk/lib/_internal/compiler/implementation/dart_types.dart';
import "../../../sdk/lib/_internal/compiler/implementation/elements/elements.dart"
show Element, ClassElement, MemberSignature, Name, PublicName;
import "../../../sdk/lib/_internal/compiler/implementation/resolution/class_members.dart"
show SyntheticMember, ErroneousMember;
void main() {
testClassMembers();
testInterfaceMembers();
testClassVsInterfaceMembers();
testMixinMembers();
}
MemberSignature getMember(InterfaceType cls, String name,
{bool isSetter: false,
int checkType: CHECK_INTERFACE}) {
Name memberName =
new Name(name, cls.element.getLibrary(), isSetter: isSetter);
MemberSignature member = checkType == CHECK_CLASS
? cls.element.lookupClassMember(memberName)
: cls.element.lookupInterfaceMember(memberName);
if (member != null) {
Expect.equals(memberName, member.name);
}
return member;
}
/// Check interface member only.
const int CHECK_INTERFACE = 0;
/// Check class member only.
const int CHECK_CLASS = 1;
/// Check that there is no class member for the interface member.
const int NO_CLASS_MEMBER = 2;
/// Check that the interface member is also a class member.
const int ALSO_CLASS_MEMBER = 3;
/**
* Checks [member] or interface member [name] of the declaration of [cls].
*
* If [inheritFrom] is set, the member from [cls] must be identical to the
* member from [inheritedFrom].
*
* Otherwise, the properties of member are checked against the values of
* [isStatic], [isSetter], [isGetter], [declarer], [type] and
* [functionType].
*
* If [synthesizedFrom] or [erroneousFrom] is not `null`, the member is checked
* to be synthesized for the corresponding members found on the type is
* [synthesizedFrom] or or [erroneousFrom], respectively.
* Otherwise, if [declarer] is `null`, the declarer is checked to be [cls], and
* if [declarer] is not `null`, the declarer is checked to be [declarer].
* If [type] is `null` it is checked that the type of the member is also the
* member type, otherwise the type is checked to be [type].
*
* If [isClassMember] is `true` it is checked that the member is also a class
* member.
*/
MemberSignature checkMember(InterfaceType cls,
String name,
{bool isStatic: false,
bool isSetter: false,
bool isGetter: false,
InterfaceType declarer,
DartType type,
FunctionType functionType,
InterfaceType inheritedFrom,
List<InterfaceType> synthesizedFrom,
List<InterfaceType> erroneousFrom,
int checkType: ALSO_CLASS_MEMBER}) {
String memberKind = checkType == CHECK_CLASS ? 'class' : 'interface';
MemberSignature member =
getMember(cls, name, isSetter: isSetter, checkType: checkType);
Expect.isNotNull(member, "No $memberKind member '$name' in $cls.");
Name memberName = member.name;
if (checkType == ALSO_CLASS_MEMBER) {
MemberSignature classMember = cls.element.lookupClassMember(memberName);
Expect.isNotNull(classMember, "No class member '$memberName' in $cls.");
Expect.equals(member, classMember);
} else if (checkType == NO_CLASS_MEMBER) {
Expect.isNull(cls.element.lookupClassMember(memberName));
}
if (inheritedFrom != null) {
MemberSignature inherited = checkType == CHECK_CLASS
? inheritedFrom.element.lookupClassMember(memberName)
: inheritedFrom.element.lookupInterfaceMember(memberName);
Expect.isNotNull(inherited,
"No $memberKind member '$memberName' in $inheritedFrom.");
Expect.equals(inherited.inheritFrom(inheritedFrom), member);
} else {
if (erroneousFrom != null || synthesizedFrom != null) {
Expect.notEquals(checkType, CHECK_CLASS,
"Arguments 'erroneousFrom' and 'synthesizedFrom' only apply "
"to interface members.");
if (synthesizedFrom != null) {
Expect.isTrue(member is SyntheticMember,
"Member '$member' is not synthesized.");
} else {
Expect.isTrue(member is ErroneousMember,
"Member '$member' is not erroneous.");
}
Set<MemberSignature> members = new Set<MemberSignature>();
List from = synthesizedFrom != null ? synthesizedFrom : erroneousFrom;
for (InterfaceType type in from) {
MemberSignature inheritedMember =
type.element.lookupInterfaceMember(memberName);
Expect.isNotNull(inheritedMember);
members.add(inheritedMember.inheritFrom(type));
}
Expect.setEquals(members, member.declarations);
} else if (declarer != null) {
Expect.equals(declarer, member.declarer,
"Unexpected declarer '${member.declarer}' of $memberKind member "
"'$member'. Expected '${declarer}'.");
} else {
Expect.equals(cls.element, member.element.getEnclosingClass());
Expect.equals(cls, member.declarer);
}
Expect.equals(isSetter, member.isSetter);
Expect.equals(isGetter, member.isGetter);
if (type != null) {
Expect.equals(type, member.type,
"Unexpected type of $memberKind member '$member'.");
}
if (functionType != null) {
if (type == null) {
Expect.equals(member.type, member.functionType,
"Unexpected type of $memberKind member '$member'.");
}
Expect.equals(functionType, member.functionType,
"Unexpected member type of $memberKind member '$member'.");
}
}
return member;
}
void checkMemberCount(InterfaceType cls, int expectedCount,
{bool interfaceMembers: true}) {
int count = 0;
if (interfaceMembers) {
cls.element.forEachInterfaceMember((_) => count++);
} else {
cls.element.forEachClassMember((_) => count++);
}
Expect.equals(expectedCount, count);
}
void testClassMembers() {
asyncTest(() => TypeEnvironment.create(r"""
abstract class A {
int field;
final finalField = 0;
static var staticField;
int get getter => 0;
get abstractGetter;
void set setter(int _) {}
set abstractSetter(_);
method() {}
abstractMethod();
static staticMethod() {}
}
class B<T> {
T field;
void method(T t) {}
static staticMethod() {}
toString([T t]) {}
}
class C<S> extends B<S> {}
class D extends C<int> {}
class E extends D {}
""", useMockCompiler: false).then((env) {
InterfaceType bool_ = env['bool'];
InterfaceType String_ = env['String'];
InterfaceType num_ = env['num'];
InterfaceType int_ = env['int'];
InterfaceType dynamic_ = env['dynamic'];
VoidType void_ = env['void'];
InterfaceType Type_ = env['Type'];
InterfaceType Invocation_ = env['Invocation'];
InterfaceType Object_ = env['Object'];
checkMemberCount(Object_, 5 /*declared*/, interfaceMembers: true);
checkMemberCount(Object_, 5 /*declared*/, interfaceMembers: false);
checkMember(Object_, '==',
functionType: env.functionType(bool_, [dynamic_]));
checkMember(Object_, 'hashCode',
isGetter: true,
type: int_, functionType: env.functionType(int_, []));
checkMember(Object_, 'noSuchMethod',
functionType: env.functionType(dynamic_, [Invocation_]));
checkMember(Object_, 'runtimeType',
isGetter: true,
type: Type_, functionType: env.functionType(Type_, []));
checkMember(Object_, 'toString',
functionType: env.functionType(String_, []));
InterfaceType A = env['A'];
checkMemberCount(A, 5 /*inherited*/ + 9 /*non-static declared*/,
interfaceMembers: true);
checkMemberCount(A, 5 /*inherited*/ + 9 /*non-abstract declared*/,
interfaceMembers: false);
checkMember(A, '==', inheritedFrom: Object_);
checkMember(A, 'hashCode', inheritedFrom: Object_);
checkMember(A, 'noSuchMethod', inheritedFrom: Object_);
checkMember(A, 'runtimeType', inheritedFrom: Object_);
checkMember(A, 'toString', inheritedFrom: Object_);
checkMember(A, 'field', isGetter: true,
type: int_, functionType: env.functionType(int_, []));
checkMember(A, 'field', isSetter: true,
type: int_, functionType: env.functionType(void_, [int_]));
checkMember(A, 'finalField', isGetter: true,
type: dynamic_, functionType: env.functionType(dynamic_, []));
checkMember(A, 'staticField', isGetter: true, isStatic: true,
checkType: CHECK_CLASS,
type: dynamic_, functionType: env.functionType(dynamic_, []));
checkMember(A, 'staticField', isSetter: true, isStatic: true,
checkType: CHECK_CLASS, type: dynamic_,
functionType: env.functionType(void_, [dynamic_]));
checkMember(A, 'getter', isGetter: true,
type: int_, functionType: env.functionType(int_, []));
checkMember(A, 'abstractGetter', isGetter: true,
checkType: NO_CLASS_MEMBER,
type: dynamic_, functionType: env.functionType(dynamic_, []));
checkMember(A, 'setter', isSetter: true,
type: int_, functionType: env.functionType(void_, [int_]));
checkMember(A, 'abstractSetter', isSetter: true,
checkType: NO_CLASS_MEMBER, type: dynamic_,
functionType: env.functionType(dynamic_, [dynamic_]));
checkMember(A, 'method', functionType: env.functionType(dynamic_, []));
checkMember(A, 'abstractMethod',
checkType: NO_CLASS_MEMBER,
functionType: env.functionType(dynamic_, []));
checkMember(A, 'staticMethod',
checkType: CHECK_CLASS,
isStatic: true, functionType: env.functionType(dynamic_, []));
ClassElement B = env.getElement('B');
InterfaceType B_this = B.thisType;
TypeVariableType B_T = B_this.typeArguments.head;
checkMemberCount(B_this, 4 /*inherited*/ + 4 /*non-static declared*/,
interfaceMembers: true);
checkMemberCount(B_this, 4 /*inherited*/ + 5 /*declared*/,
interfaceMembers: false);
checkMember(B_this, '==', inheritedFrom: Object_);
checkMember(B_this, 'hashCode', inheritedFrom: Object_);
checkMember(B_this, 'noSuchMethod', inheritedFrom: Object_);
checkMember(B_this, 'runtimeType', inheritedFrom: Object_);
checkMember(B_this, 'field', isGetter: true,
type: B_T, functionType: env.functionType(B_T, []));
checkMember(B_this, 'field', isSetter: true,
type: B_T, functionType: env.functionType(void_, [B_T]));
checkMember(B_this, 'method', functionType: env.functionType(void_, [B_T]));
checkMember(B_this, 'staticMethod',
checkType: CHECK_CLASS,
isStatic: true, functionType: env.functionType(dynamic_, []));
checkMember(B_this, 'toString',
functionType: env.functionType(dynamic_, [],
optionalParameters: [B_T]));
ClassElement C = env.getElement('C');
InterfaceType C_this = C.thisType;
TypeVariableType C_S = C_this.typeArguments.head;
checkMemberCount(C_this, 8 /*inherited*/, interfaceMembers: true);
checkMemberCount(C_this, 8 /*inherited*/, interfaceMembers: false);
InterfaceType B_S = instantiate(B, [C_S]);
checkMember(C_this, '==', inheritedFrom: Object_);
checkMember(C_this, 'hashCode', inheritedFrom: Object_);
checkMember(C_this, 'noSuchMethod', inheritedFrom: Object_);
checkMember(C_this, 'runtimeType', inheritedFrom: Object_);
checkMember(C_this, 'field', isGetter: true,
declarer: B_S,
type: C_S, functionType: env.functionType(C_S, []));
checkMember(C_this, 'field', isSetter: true,
declarer: B_S,
type: C_S, functionType: env.functionType(void_, [C_S]));
checkMember(C_this, 'method',
declarer: B_S,
functionType: env.functionType(void_, [C_S]));
checkMember(C_this, 'toString',
declarer: B_S,
functionType: env.functionType(dynamic_, [],
optionalParameters: [C_S]));
InterfaceType D = env['D'];
checkMemberCount(D, 8 /*inherited*/, interfaceMembers: true);
checkMemberCount(D, 8 /*inherited*/, interfaceMembers: false);
InterfaceType B_int = instantiate(B, [int_]);
checkMember(D, '==', inheritedFrom: Object_);
checkMember(D, 'hashCode', inheritedFrom: Object_);
checkMember(D, 'noSuchMethod', inheritedFrom: Object_);
checkMember(D, 'runtimeType', inheritedFrom: Object_);
checkMember(D, 'field', isGetter: true,
declarer: B_int,
type: int_, functionType: env.functionType(int_, []));
checkMember(D, 'field', isSetter: true,
declarer: B_int,
type: int_, functionType: env.functionType(void_, [int_]));
checkMember(D, 'method',
declarer: B_int,
functionType: env.functionType(void_, [int_]));
checkMember(D, 'toString',
declarer: B_int,
functionType: env.functionType(dynamic_, [],
optionalParameters: [int_]));
InterfaceType E = env['E'];
checkMemberCount(E, 8 /*inherited*/, interfaceMembers: true);
checkMemberCount(E, 8 /*inherited*/, interfaceMembers: false);
checkMember(E, '==', inheritedFrom: Object_);
checkMember(E, 'hashCode', inheritedFrom: Object_);
checkMember(E, 'noSuchMethod', inheritedFrom: Object_);
checkMember(E, 'runtimeType', inheritedFrom: Object_);
checkMember(E, 'field', isGetter: true,
declarer: B_int,
type: int_, functionType: env.functionType(int_, []));
checkMember(E, 'field', isSetter: true,
declarer: B_int,
type: int_, functionType: env.functionType(void_, [int_]));
checkMember(E, 'method',
declarer: B_int,
functionType: env.functionType(void_, [int_]));
checkMember(E, 'toString',
declarer: B_int,
functionType: env.functionType(dynamic_, [],
optionalParameters: [int_]));
}));
}
void testInterfaceMembers() {
asyncTest(() => TypeEnvironment.create(r"""
abstract class A {
num method1();
void method2();
void method3();
void method4();
method5(a);
method6(a);
method7(a);
method8(a, b);
method9(a, b, c);
method10(a, {b, c});
method11(a, {b, c});
num get getter1;
num get getter2;
void set setter1(num _);
void set setter2(num _);
void set setter3(num _);
get getterAndMethod;
}
abstract class B {
int method1();
int method2();
num method3();
num method4();
method5([a]);
method6([a, b]);
method7(a, [b]);
method8([a]);
method9(a, [b]);
method10(a, {c, d});
method11(a, b, {c, d});
num get getter1;
int get getter2;
void set setter1(num _);
set setter2(num _);
void set setter3(int _);
getterAndMethod();
}
abstract class C {
int method3();
num method4();
}
abstract class D implements A, B, C {}
""").then((env) {
InterfaceType dynamic_ = env['dynamic'];
VoidType void_ = env['void'];
InterfaceType num_ = env['num'];
InterfaceType int_ = env['int'];
InterfaceType A = env['A'];
InterfaceType B = env['B'];
InterfaceType C = env['C'];
InterfaceType D = env['D'];
// A: num method1()
// B: int method1()
// D: dynamic method1() -- synthesized from A and B.
checkMember(D, 'method1',
synthesizedFrom: [A, B],
functionType: env.functionType(dynamic_ , []),
checkType: NO_CLASS_MEMBER);
// A: void method2()
// B: int method2()
// D: int method2() -- inherited from B
checkMember(D, 'method2', inheritedFrom: B, checkType: NO_CLASS_MEMBER);
// A: void method3()
// B: num method3()
// C: int method3()
// D: dynamic method3() -- synthesized from A, B, and C.
checkMember(D, 'method3',
synthesizedFrom: [A, B, C],
functionType: env.functionType(dynamic_ , []),
checkType: NO_CLASS_MEMBER);
// A: void method4()
// B: num method4()
// C: num method4()
// D: num method4() -- synthesized from B and C.
checkMember(D, 'method4',
synthesizedFrom: [B, C],
functionType: env.functionType(num_, []),
checkType: NO_CLASS_MEMBER);
// A: method5(a)
// B: method5([a])
// D: method5([a]) -- inherited from B
checkMember(D, 'method5', inheritedFrom: B, checkType: NO_CLASS_MEMBER);
// A: method6(a)
// B: method6([a, b])
// D: method6([a, b]) -- inherited from B
checkMember(D, 'method6', inheritedFrom: B, checkType: NO_CLASS_MEMBER);
// A: method7(a)
// B: method7(a, [b])
// D: method7(a, [b]) -- inherited from B
checkMember(D, 'method7', inheritedFrom: B, checkType: NO_CLASS_MEMBER);
// A: method8(a, b)
// B: method8([a])
// D: method8([a, b]) -- synthesized from A and B.
checkMember(D, 'method8',
synthesizedFrom: [A, B],
functionType: env.functionType(
dynamic_, [], optionalParameters: [dynamic_, dynamic_]),
checkType: NO_CLASS_MEMBER);
// A: method9(a, b, c)
// B: method9(a, [b])
// D: method9(a, [b, c]) -- synthesized from A and B.
checkMember(D, 'method9',
synthesizedFrom: [A, B],
functionType: env.functionType(
dynamic_, [dynamic_], optionalParameters: [dynamic_, dynamic_]),
checkType: NO_CLASS_MEMBER);
// A: method10(a, {b, c})
// B: method10(a, {c, d})
// D: method10(a, {b, c, d}) -- synthesized from A and B.
checkMember(D, 'method10',
synthesizedFrom: [A, B],
functionType: env.functionType(dynamic_, [dynamic_],
namedParameters: {'b': dynamic_,
'c': dynamic_,
'd': dynamic_}),
checkType: NO_CLASS_MEMBER);
// A: method11(a, {b, c})
// B: method11(a, b, {c, d})
// D: method11(a, [b], {c, d}) -- synthesized from A and B.
// TODO(johnniwinther): Change to check synthesized member when function
// types with both optional and named parameters are supported.
Expect.isNull(getMember(D, 'method11'));
/*checkMember(D, 'method11',
synthesizedFrom: [A, B],
functionType: env.functionType(dynamic_, [dynamic_],
optionalParameters: [dynamic_],
namedParameters: {'c': dynamic_,
'd': dynamic_,}),
checkType: NO_CLASS_MEMBER);*/
// A: num get getter1
// B: num get getter1
// D: num get getter1 -- synthesized from A and B.
checkMember(D, 'getter1',
isGetter: true,
synthesizedFrom: [A, B], type: num_,
functionType: env.functionType(num_ , []),
checkType: NO_CLASS_MEMBER);
// A: num get getter2
// B: int get getter2
// D: dynamic get getter2 -- synthesized from A and B.
checkMember(D, 'getter2',
isGetter: true,
synthesizedFrom: [A, B], type: dynamic_,
functionType: env.functionType(dynamic_ , []),
checkType: NO_CLASS_MEMBER);
// A: void set setter1(num _)
// B: void set setter1(num _)
// D: void set setter1(num _) -- synthesized from A and B.
checkMember(D, 'setter1',
isSetter: true,
synthesizedFrom: [A, B], type: num_,
functionType: env.functionType(void_ , [num_]),
checkType: NO_CLASS_MEMBER);
// A: void set setter2(num _)
// B: set setter2(num _)
// D: dynamic set setter2(dynamic _) -- synthesized from A and B.
checkMember(D, 'setter2',
isSetter: true,
synthesizedFrom: [A, B], type: dynamic_,
functionType: env.functionType(dynamic_ , [dynamic_]),
checkType: NO_CLASS_MEMBER);
// A: void set setter3(num _)
// B: void set setter3(int _)
// D: dynamic set setter3(dynamic _) -- synthesized from A and B.
checkMember(D, 'setter3',
isSetter: true,
synthesizedFrom: [A, B], type: dynamic_,
functionType: env.functionType(dynamic_ , [dynamic_]),
checkType: NO_CLASS_MEMBER);
// A: get getterAndMethod
// B: getterAndMethod()
// D: nothing inherited
checkMember(D, 'getterAndMethod',
erroneousFrom: [A, B],
checkType: NO_CLASS_MEMBER);
}));
}
void testClassVsInterfaceMembers() {
asyncTest(() => TypeEnvironment.create(r"""
class A {
method1() {}
method2() {}
}
abstract class B {
method1();
method2(a);
}
abstract class C extends A implements B {}
""").then((env) {
InterfaceType dynamic_ = env['dynamic'];
VoidType void_ = env['void'];
InterfaceType num_ = env['num'];
InterfaceType int_ = env['int'];
InterfaceType A = env['A'];
InterfaceType B = env['B'];
InterfaceType C = env['C'];
// A: method1()
// B: method1()
// C class: method1() -- inherited from A.
// C interface: dynamic method1() -- synthesized from A and B.
MemberSignature interfaceMember =
checkMember(C, 'method1', checkType: CHECK_INTERFACE,
synthesizedFrom: [A, B],
functionType: env.functionType(dynamic_ , []));
MemberSignature classMember =
checkMember(C, 'method1', checkType: CHECK_CLASS, inheritedFrom: A);
Expect.notEquals(interfaceMember, classMember);
// A: method2()
// B: method2(a)
// C class: method2() -- inherited from A.
// C interface: dynamic method2([a]) -- synthesized from A and B.
interfaceMember =
checkMember(C, 'method2', checkType: CHECK_INTERFACE,
synthesizedFrom: [A, B],
functionType: env.functionType(dynamic_ , [],
optionalParameters: [dynamic_]));
classMember =
checkMember(C, 'method2', checkType: CHECK_CLASS, inheritedFrom: A);
Expect.notEquals(interfaceMember, classMember);
}));
}
void testMixinMembers() {
asyncTest(() => TypeEnvironment.create(r"""
class A<T> {
method1() {}
method2() {}
method3(T a) {}
method4(T a) {}
}
abstract class B<S> {
method1();
method2(a);
method3(S a) {}
}
abstract class C<U, V> extends Object with A<U> implements B<V> {}
""").then((env) {
InterfaceType dynamic_ = env['dynamic'];
VoidType void_ = env['void'];
InterfaceType num_ = env['num'];
InterfaceType int_ = env['int'];
ClassElement A = env.getElement('A');
ClassElement B = env.getElement('B');
ClassElement C = env.getElement('C');
InterfaceType C_this = C.thisType;
TypeVariableType C_U = C_this.typeArguments.head;
TypeVariableType C_V = C_this.typeArguments.tail.head;
InterfaceType A_U = instantiate(A, [C_U]);
InterfaceType B_V = instantiate(B, [C_V]);
// A: method1()
// B: method1()
// C class: method1() -- inherited from A.
// C interface: dynamic method1() -- synthesized from A and B.
MemberSignature interfaceMember =
checkMember(C_this, 'method1', checkType: CHECK_INTERFACE,
synthesizedFrom: [A_U, B_V],
functionType: env.functionType(dynamic_ , []));
MemberSignature classMember =
checkMember(C_this, 'method1', checkType: CHECK_CLASS,
inheritedFrom: A_U);
Expect.notEquals(interfaceMember, classMember);
// A: method2()
// B: method2(a)
// C class: method2() -- inherited from A.
// C interface: dynamic method2([a]) -- synthesized from A and B.
interfaceMember =
checkMember(C_this, 'method2', checkType: CHECK_INTERFACE,
synthesizedFrom: [A_U, B_V],
functionType: env.functionType(dynamic_ , [],
optionalParameters: [dynamic_]));
classMember =
checkMember(C_this, 'method2', checkType: CHECK_CLASS,
inheritedFrom: A_U);
Expect.notEquals(interfaceMember, classMember);
// A: method3(U a)
// B: method3(V a)
// C class: method3(U a) -- inherited from A.
// C interface: dynamic method3(a) -- synthesized from A and B.
interfaceMember =
checkMember(C_this, 'method3', checkType: CHECK_INTERFACE,
synthesizedFrom: [A_U, B_V],
functionType: env.functionType(dynamic_ , [dynamic_]));
classMember =
checkMember(C_this, 'method3', checkType: CHECK_CLASS,
inheritedFrom: A_U);
Expect.notEquals(interfaceMember, classMember);
// A: method4(U a)
// B: --
// C class: method4(U a) -- inherited from A.
// C interface: method4(U a) -- inherited from A.
checkMember(C_this, 'method4', checkType: ALSO_CLASS_MEMBER,
inheritedFrom: A_U);
}));
}