27f5e7f9ee
This method is no longer needed, since the front end iterates through members directly to find cross overrides. Change-Id: Ibc5180962a097c4d6194dbf4cbd1a0dea15050c2 Reviewed-on: https://dart-review.googlesource.com/13582 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Commit-Queue: Paul Berry <paulberry@google.com>
1370 lines
46 KiB
Dart
1370 lines
46 KiB
Dart
// Copyright (c) 2017, 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.
|
|
|
|
import 'package:kernel/ast.dart';
|
|
import 'package:kernel/class_hierarchy.dart';
|
|
import 'package:kernel/core_types.dart';
|
|
import 'package:kernel/src/incremental_class_hierarchy.dart';
|
|
import 'package:kernel/testing/mock_sdk_program.dart';
|
|
import 'package:kernel/text/ast_to_text.dart';
|
|
import 'package:kernel/type_algebra.dart';
|
|
import 'package:test/test.dart';
|
|
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
|
|
|
main() {
|
|
defineReflectiveSuite(() {
|
|
defineReflectiveTests(ClosedWorldClassHierarchyTest);
|
|
defineReflectiveTests(IncrementalClassHierarchyTest);
|
|
});
|
|
}
|
|
|
|
@reflectiveTest
|
|
class ClosedWorldClassHierarchyTest extends _ClassHierarchyTest {
|
|
ClassHierarchy createClassHierarchy(Program program) {
|
|
return new ClosedWorldClassHierarchy(program);
|
|
}
|
|
|
|
void test_applyChanges() {
|
|
var a = addClass(new Class(name: 'A', supertype: objectSuper));
|
|
addClass(new Class(name: 'B', supertype: a.asThisSupertype));
|
|
|
|
_assertTestLibraryText('''
|
|
class A {}
|
|
class B extends self::A {}
|
|
''');
|
|
|
|
// No updated classes, the same hierarchy.
|
|
expect(hierarchy.applyChanges([]), same(hierarchy));
|
|
|
|
// Has updated classes, a new hierarchy.
|
|
var newHierarchy = hierarchy.applyChanges([a]);
|
|
expect(newHierarchy, isNot(same(hierarchy)));
|
|
expect(newHierarchy, new isInstanceOf<ClosedWorldClassHierarchy>());
|
|
}
|
|
|
|
void test_getSingleTargetForInterfaceInvocation() {
|
|
var methodInA = newEmptyMethod('foo', isAbstract: true);
|
|
var methodInB = newEmptyMethod('foo');
|
|
var methodInD = newEmptyMethod('foo');
|
|
var methodInE = newEmptyMethod('foo');
|
|
|
|
var a = addClass(
|
|
new Class(name: 'A', supertype: objectSuper, procedures: [methodInA]));
|
|
var b = addClass(new Class(
|
|
name: 'B',
|
|
isAbstract: true,
|
|
supertype: objectSuper,
|
|
procedures: [methodInB]));
|
|
var c = addClass(new Class(
|
|
name: 'C',
|
|
supertype: b.asThisSupertype,
|
|
implementedTypes: [a.asThisSupertype]));
|
|
addClass(new Class(
|
|
name: 'D', supertype: b.asThisSupertype, procedures: [methodInD]));
|
|
addClass(new Class(
|
|
name: 'E',
|
|
isAbstract: true,
|
|
supertype: objectSuper,
|
|
implementedTypes: [c.asThisSupertype],
|
|
procedures: [methodInE]));
|
|
|
|
_assertTestLibraryText('''
|
|
class A {
|
|
abstract method foo() → void;
|
|
}
|
|
abstract class B {
|
|
method foo() → void {}
|
|
}
|
|
class C extends self::B implements self::A {}
|
|
class D extends self::B {
|
|
method foo() → void {}
|
|
}
|
|
abstract class E implements self::C {
|
|
method foo() → void {}
|
|
}
|
|
''');
|
|
|
|
ClosedWorldClassHierarchy cwch = hierarchy as ClosedWorldClassHierarchy;
|
|
|
|
expect(cwch.getSingleTargetForInterfaceInvocation(methodInA), methodInB);
|
|
expect(cwch.getSingleTargetForInterfaceInvocation(methodInB),
|
|
null); // B::foo and D::foo
|
|
expect(cwch.getSingleTargetForInterfaceInvocation(methodInD), methodInD);
|
|
expect(cwch.getSingleTargetForInterfaceInvocation(methodInE),
|
|
null); // no concrete subtypes
|
|
}
|
|
}
|
|
|
|
@reflectiveTest
|
|
class IncrementalClassHierarchyTest extends _ClassHierarchyTest {
|
|
ClassHierarchy createClassHierarchy(Program program) {
|
|
return new IncrementalClassHierarchy();
|
|
}
|
|
|
|
void test_applyChanges() {
|
|
var a = addClass(new Class(name: 'A', supertype: objectSuper));
|
|
addClass(new Class(name: 'B', supertype: a.asThisSupertype));
|
|
|
|
_assertTestLibraryText('''
|
|
class A {}
|
|
class B extends self::A {}
|
|
''');
|
|
|
|
// No updated classes, the same hierarchy.
|
|
expect(hierarchy.applyChanges([]), same(hierarchy));
|
|
|
|
// Has updated classes, a new hierarchy.
|
|
var newHierarchy = hierarchy.applyChanges([a]);
|
|
expect(newHierarchy, isNot(same(hierarchy)));
|
|
expect(newHierarchy, new isInstanceOf<IncrementalClassHierarchy>());
|
|
}
|
|
}
|
|
|
|
abstract class _ClassHierarchyTest {
|
|
Program program;
|
|
CoreTypes coreTypes;
|
|
|
|
/// The test library.
|
|
Library library;
|
|
|
|
ClassHierarchy _hierarchy;
|
|
|
|
/// Return the new or existing instance of [ClassHierarchy].
|
|
ClassHierarchy get hierarchy {
|
|
return _hierarchy ??= createClassHierarchy(program);
|
|
}
|
|
|
|
Class get objectClass => coreTypes.objectClass;
|
|
|
|
Supertype get objectSuper => coreTypes.objectClass.asThisSupertype;
|
|
|
|
Class addClass(Class c) {
|
|
if (_hierarchy != null) {
|
|
fail('The classs hierarchy has already been created.');
|
|
}
|
|
library.addClass(c);
|
|
return c;
|
|
}
|
|
|
|
/// Add a new generic class with the given [name] and [typeParameterNames].
|
|
/// The [TypeParameterType]s corresponding to [typeParameterNames] are
|
|
/// passed to optional [extends_] and [implements_] callbacks.
|
|
Class addGenericClass(String name, List<String> typeParameterNames,
|
|
{Supertype extends_(List<DartType> typeParameterTypes),
|
|
List<Supertype> implements_(List<DartType> typeParameterTypes)}) {
|
|
var typeParameters = typeParameterNames
|
|
.map((name) => new TypeParameter(name, objectClass.rawType))
|
|
.toList();
|
|
var typeParameterTypes = typeParameters
|
|
.map((parameter) => new TypeParameterType(parameter))
|
|
.toList();
|
|
var supertype =
|
|
extends_ != null ? extends_(typeParameterTypes) : objectSuper;
|
|
var implementedTypes =
|
|
implements_ != null ? implements_(typeParameterTypes) : [];
|
|
return addClass(new Class(
|
|
name: name,
|
|
typeParameters: typeParameters,
|
|
supertype: supertype,
|
|
implementedTypes: implementedTypes));
|
|
}
|
|
|
|
/// Add a new class with the given [name] that extends `Object` and
|
|
/// [implements_] the given classes.
|
|
Class addImplementsClass(String name, List<Class> implements_) {
|
|
return addClass(new Class(
|
|
name: name,
|
|
supertype: objectSuper,
|
|
implementedTypes: implements_.map((c) => c.asThisSupertype).toList()));
|
|
}
|
|
|
|
ClassHierarchy createClassHierarchy(Program program);
|
|
|
|
Procedure newEmptyGetter(String name,
|
|
{DartType returnType: const DynamicType(), bool isAbstract: false}) {
|
|
var body =
|
|
isAbstract ? null : new Block([new ReturnStatement(new NullLiteral())]);
|
|
return new Procedure(new Name(name), ProcedureKind.Getter,
|
|
new FunctionNode(body, returnType: returnType));
|
|
}
|
|
|
|
Procedure newEmptyMethod(String name, {bool isAbstract: false}) {
|
|
var body = isAbstract ? null : new Block([]);
|
|
return new Procedure(new Name(name), ProcedureKind.Method,
|
|
new FunctionNode(body, returnType: const VoidType()),
|
|
isAbstract: isAbstract);
|
|
}
|
|
|
|
Procedure newEmptySetter(String name,
|
|
{bool isAbstract: false, DartType type: const DynamicType()}) {
|
|
var body = isAbstract ? null : new Block([]);
|
|
return new Procedure(
|
|
new Name(name),
|
|
ProcedureKind.Setter,
|
|
new FunctionNode(body,
|
|
returnType: const VoidType(),
|
|
positionalParameters: [new VariableDeclaration('_', type: type)]));
|
|
}
|
|
|
|
void setUp() {
|
|
// Start with mock SDK libraries.
|
|
program = createMockSdkProgram();
|
|
coreTypes = new CoreTypes(program);
|
|
|
|
// Add the test library.
|
|
library = new Library(Uri.parse('org-dartlang:///test.dart'), name: 'test');
|
|
library.parent = program;
|
|
program.libraries.add(library);
|
|
}
|
|
|
|
/// 2. A non-abstract member is inherited from a superclass, and in the
|
|
/// context of this class, it overrides an abstract member inheritable through
|
|
/// one of its superinterfaces.
|
|
void test_forEachOverridePair_supertypeOverridesInterface() {
|
|
var a = addClass(new Class(
|
|
name: 'A',
|
|
supertype: objectSuper,
|
|
procedures: [newEmptyMethod('foo'), newEmptyMethod('bar')]));
|
|
var b = addClass(new Class(
|
|
name: 'B',
|
|
supertype: a.asThisSupertype,
|
|
procedures: [newEmptyMethod('foo', isAbstract: true)]));
|
|
var c = addClass(new Class(
|
|
name: 'C',
|
|
supertype: a.asThisSupertype,
|
|
implementedTypes: [b.asThisSupertype]));
|
|
var d = addClass(new Class(name: 'D', supertype: objectSuper));
|
|
var e = addClass(new Class(
|
|
name: 'E',
|
|
supertype: d.asThisSupertype,
|
|
mixedInType: a.asThisSupertype,
|
|
implementedTypes: [b.asThisSupertype]));
|
|
|
|
_assertTestLibraryText('''
|
|
class A {
|
|
method foo() → void {}
|
|
method bar() → void {}
|
|
}
|
|
class B extends self::A {
|
|
abstract method foo() → void;
|
|
}
|
|
class C extends self::A implements self::B {}
|
|
class D {}
|
|
class E = self::D with self::A implements self::B {}
|
|
''');
|
|
|
|
_assertOverridePairs(c, ['test::A::foo overrides test::B::foo']);
|
|
_assertOverridePairs(e, ['test::A::foo overrides test::B::foo']);
|
|
}
|
|
|
|
/// An abstract member declared in the class is overridden by a member in
|
|
/// one of the interfaces.
|
|
void test_forEachOverridePair_supertypeOverridesThis() {
|
|
var a = addClass(new Class(
|
|
name: 'A',
|
|
supertype: objectSuper,
|
|
procedures: [newEmptyMethod('foo')]));
|
|
var b = addClass(new Class(
|
|
name: 'B',
|
|
supertype: a.asThisSupertype,
|
|
procedures: [newEmptyMethod('foo', isAbstract: true)]));
|
|
var c = addClass(new Class(
|
|
name: 'C',
|
|
supertype: a.asThisSupertype,
|
|
procedures: [newEmptyMethod('foo', isAbstract: true)],
|
|
isAbstract: true));
|
|
var d = addClass(new Class(name: 'D', supertype: b.asThisSupertype));
|
|
var e = addClass(new Class(name: 'E', supertype: c.asThisSupertype));
|
|
|
|
_assertTestLibraryText('''
|
|
class A {
|
|
method foo() → void {}
|
|
}
|
|
class B extends self::A {
|
|
abstract method foo() → void;
|
|
}
|
|
abstract class C extends self::A {
|
|
abstract method foo() → void;
|
|
}
|
|
class D extends self::B {}
|
|
class E extends self::C {}
|
|
''');
|
|
|
|
_assertOverridePairs(b, [
|
|
'test::A::foo overrides test::B::foo',
|
|
'test::B::foo overrides test::A::foo'
|
|
]);
|
|
_assertOverridePairs(c, ['test::C::foo overrides test::A::foo']);
|
|
_assertOverridePairs(d, ['test::A::foo overrides test::B::foo']);
|
|
_assertOverridePairs(e, ['test::A::foo overrides test::C::foo']);
|
|
}
|
|
|
|
/// 3. A non-abstract member is inherited from a superclass, and it overrides
|
|
/// an abstract member declared in this class.
|
|
void test_forEachOverridePair_supertypeOverridesThisAbstract() {
|
|
var a = addClass(new Class(
|
|
name: 'A',
|
|
supertype: objectSuper,
|
|
procedures: [newEmptyMethod('foo'), newEmptyMethod('bar')]));
|
|
var b = addClass(new Class(
|
|
name: 'B',
|
|
supertype: a.asThisSupertype,
|
|
procedures: [newEmptyMethod('foo', isAbstract: true)]));
|
|
|
|
_assertTestLibraryText('''
|
|
class A {
|
|
method foo() → void {}
|
|
method bar() → void {}
|
|
}
|
|
class B extends self::A {
|
|
abstract method foo() → void;
|
|
}
|
|
''');
|
|
|
|
// The documentation says:
|
|
// It is possible for two methods to override one another in both directions.
|
|
_assertOverridePairs(b, [
|
|
'test::A::foo overrides test::B::foo',
|
|
'test::B::foo overrides test::A::foo'
|
|
]);
|
|
}
|
|
|
|
/// 1. A member declared in the class overrides a member inheritable through
|
|
/// one of the supertypes of the class.
|
|
void test_forEachOverridePair_thisOverridesSupertype() {
|
|
var a = addClass(new Class(
|
|
name: 'A',
|
|
supertype: objectSuper,
|
|
procedures: [newEmptyMethod('foo'), newEmptyMethod('bar')]));
|
|
var b = addClass(new Class(
|
|
name: 'B',
|
|
supertype: a.asThisSupertype,
|
|
procedures: [newEmptyMethod('foo')]));
|
|
var c = addClass(new Class(
|
|
name: 'C',
|
|
supertype: b.asThisSupertype,
|
|
procedures: [newEmptyMethod('bar')]));
|
|
|
|
_assertTestLibraryText('''
|
|
class A {
|
|
method foo() → void {}
|
|
method bar() → void {}
|
|
}
|
|
class B extends self::A {
|
|
method foo() → void {}
|
|
}
|
|
class C extends self::B {
|
|
method bar() → void {}
|
|
}
|
|
''');
|
|
|
|
_assertOverridePairs(b, ['test::B::foo overrides test::A::foo']);
|
|
_assertOverridePairs(c, ['test::C::bar overrides test::A::bar']);
|
|
}
|
|
|
|
/// 1. A member declared in the class overrides a member inheritable through
|
|
/// one of the supertypes of the class.
|
|
void test_forEachOverridePair_thisOverridesSupertype_setter() {
|
|
var a = addClass(new Class(
|
|
name: 'A',
|
|
supertype: objectSuper,
|
|
procedures: [newEmptySetter('foo'), newEmptySetter('bar')]));
|
|
var b = addClass(new Class(
|
|
name: 'B',
|
|
supertype: a.asThisSupertype,
|
|
procedures: [newEmptySetter('foo')]));
|
|
var c = addClass(new Class(
|
|
name: 'C',
|
|
supertype: b.asThisSupertype,
|
|
procedures: [newEmptySetter('bar')]));
|
|
|
|
_assertTestLibraryText('''
|
|
class A {
|
|
set foo(dynamic _) → void {}
|
|
set bar(dynamic _) → void {}
|
|
}
|
|
class B extends self::A {
|
|
set foo(dynamic _) → void {}
|
|
}
|
|
class C extends self::B {
|
|
set bar(dynamic _) → void {}
|
|
}
|
|
''');
|
|
|
|
_assertOverridePairs(b, ['test::B::foo= overrides test::A::foo=']);
|
|
_assertOverridePairs(c, ['test::C::bar= overrides test::A::bar=']);
|
|
}
|
|
|
|
void test_getClassAsInstanceOf_generic_extends() {
|
|
var int = coreTypes.intClass.rawType;
|
|
var bool = coreTypes.boolClass.rawType;
|
|
|
|
var a = addGenericClass('A', ['T', 'U']);
|
|
|
|
var bT = new TypeParameter('T', objectClass.rawType);
|
|
var bTT = new TypeParameterType(bT);
|
|
var b = addClass(new Class(
|
|
name: 'B',
|
|
typeParameters: [bT],
|
|
supertype: new Supertype(a, [bTT, bool])));
|
|
|
|
var c = addClass(new Class(name: 'C', supertype: new Supertype(b, [int])));
|
|
|
|
_assertTestLibraryText('''
|
|
class A<T, U> {}
|
|
class B<T> extends self::A<self::B::T, core::bool> {}
|
|
class C extends self::B<core::int> {}
|
|
''');
|
|
|
|
expect(hierarchy.getClassAsInstanceOf(a, objectClass), objectSuper);
|
|
expect(hierarchy.getClassAsInstanceOf(a, a), a.asThisSupertype);
|
|
expect(hierarchy.getClassAsInstanceOf(b, a), new Supertype(a, [bTT, bool]));
|
|
expect(hierarchy.getClassAsInstanceOf(c, b), new Supertype(b, [int]));
|
|
expect(hierarchy.getClassAsInstanceOf(c, a), new Supertype(a, [int, bool]));
|
|
}
|
|
|
|
void test_getClassAsInstanceOf_generic_implements() {
|
|
var int = coreTypes.intClass.rawType;
|
|
var bool = coreTypes.boolClass.rawType;
|
|
|
|
var a = addGenericClass('A', ['T', 'U']);
|
|
|
|
var bT = new TypeParameter('T', objectClass.rawType);
|
|
var bTT = new TypeParameterType(bT);
|
|
var b = addClass(new Class(
|
|
name: 'B',
|
|
typeParameters: [bT],
|
|
supertype: objectSuper,
|
|
implementedTypes: [
|
|
new Supertype(a, [bTT, bool])
|
|
]));
|
|
|
|
var c = addClass(
|
|
new Class(name: 'C', supertype: objectSuper, implementedTypes: [
|
|
new Supertype(b, [int])
|
|
]));
|
|
|
|
_assertTestLibraryText('''
|
|
class A<T, U> {}
|
|
class B<T> implements self::A<self::B::T, core::bool> {}
|
|
class C implements self::B<core::int> {}
|
|
''');
|
|
|
|
expect(hierarchy.getClassAsInstanceOf(a, objectClass), objectSuper);
|
|
expect(hierarchy.getClassAsInstanceOf(a, a), a.asThisSupertype);
|
|
expect(hierarchy.getClassAsInstanceOf(b, a), new Supertype(a, [bTT, bool]));
|
|
expect(hierarchy.getClassAsInstanceOf(c, b), new Supertype(b, [int]));
|
|
expect(hierarchy.getClassAsInstanceOf(c, a), new Supertype(a, [int, bool]));
|
|
}
|
|
|
|
void test_getClassAsInstanceOf_generic_with() {
|
|
var int = coreTypes.intClass.rawType;
|
|
var bool = coreTypes.boolClass.rawType;
|
|
|
|
var a = addGenericClass('A', ['T', 'U']);
|
|
|
|
var bT = new TypeParameter('T', objectClass.rawType);
|
|
var bTT = new TypeParameterType(bT);
|
|
var b = addClass(new Class(
|
|
name: 'B',
|
|
typeParameters: [bT],
|
|
supertype: objectSuper,
|
|
mixedInType: new Supertype(a, [bTT, bool])));
|
|
|
|
var c = addClass(new Class(
|
|
name: 'C',
|
|
supertype: objectSuper,
|
|
mixedInType: new Supertype(b, [int])));
|
|
|
|
_assertTestLibraryText('''
|
|
class A<T, U> {}
|
|
class B<T> = core::Object with self::A<self::B::T, core::bool> {}
|
|
class C = core::Object with self::B<core::int> {}
|
|
''');
|
|
|
|
expect(hierarchy.getClassAsInstanceOf(a, objectClass), objectSuper);
|
|
expect(hierarchy.getClassAsInstanceOf(a, a), a.asThisSupertype);
|
|
expect(hierarchy.getClassAsInstanceOf(b, a), new Supertype(a, [bTT, bool]));
|
|
expect(hierarchy.getClassAsInstanceOf(c, b), new Supertype(b, [int]));
|
|
expect(hierarchy.getClassAsInstanceOf(c, a), new Supertype(a, [int, bool]));
|
|
}
|
|
|
|
void test_getClassAsInstanceOf_notGeneric_extends() {
|
|
var a = addClass(new Class(name: 'A', supertype: objectSuper));
|
|
var b = addClass(new Class(name: 'B', supertype: a.asThisSupertype));
|
|
var c = addClass(new Class(name: 'C', supertype: b.asThisSupertype));
|
|
var z = addClass(new Class(name: 'Z', supertype: objectSuper));
|
|
|
|
_assertTestLibraryText('''
|
|
class A {}
|
|
class B extends self::A {}
|
|
class C extends self::B {}
|
|
class Z {}
|
|
''');
|
|
|
|
expect(hierarchy.getClassAsInstanceOf(a, objectClass), objectSuper);
|
|
expect(hierarchy.getClassAsInstanceOf(a, a), a.asThisSupertype);
|
|
expect(hierarchy.getClassAsInstanceOf(b, a), a.asThisSupertype);
|
|
expect(hierarchy.getClassAsInstanceOf(c, a), a.asThisSupertype);
|
|
expect(hierarchy.getClassAsInstanceOf(c, b), b.asThisSupertype);
|
|
expect(hierarchy.getClassAsInstanceOf(z, a), null);
|
|
expect(hierarchy.getClassAsInstanceOf(z, objectClass), objectSuper);
|
|
}
|
|
|
|
void test_getClassAsInstanceOf_notGeneric_implements() {
|
|
var a = addClass(new Class(name: 'A', supertype: objectSuper));
|
|
var b = addClass(new Class(name: 'B', supertype: objectSuper));
|
|
var c = addClass(new Class(
|
|
name: 'C',
|
|
supertype: objectSuper,
|
|
implementedTypes: [a.asThisSupertype]));
|
|
var d = addClass(new Class(
|
|
name: 'D',
|
|
supertype: objectSuper,
|
|
implementedTypes: [c.asThisSupertype]));
|
|
var e = addClass(new Class(
|
|
name: 'D',
|
|
supertype: a.asThisSupertype,
|
|
implementedTypes: [b.asThisSupertype]));
|
|
var z = addClass(new Class(name: 'Z', supertype: objectSuper));
|
|
|
|
_assertTestLibraryText('''
|
|
class A {}
|
|
class B {}
|
|
class C implements self::A {}
|
|
class D implements self::C {}
|
|
class D extends self::A implements self::B {}
|
|
class Z {}
|
|
''');
|
|
|
|
expect(hierarchy.getClassAsInstanceOf(c, a), a.asThisSupertype);
|
|
expect(hierarchy.getClassAsInstanceOf(d, a), a.asThisSupertype);
|
|
expect(hierarchy.getClassAsInstanceOf(d, c), c.asThisSupertype);
|
|
expect(hierarchy.getClassAsInstanceOf(e, a), a.asThisSupertype);
|
|
expect(hierarchy.getClassAsInstanceOf(e, b), b.asThisSupertype);
|
|
expect(hierarchy.getClassAsInstanceOf(z, a), null);
|
|
}
|
|
|
|
void test_getClassAsInstanceOf_notGeneric_with() {
|
|
var a = addClass(new Class(name: 'A', supertype: objectSuper));
|
|
var b = addClass(new Class(
|
|
name: 'B', supertype: objectSuper, mixedInType: a.asThisSupertype));
|
|
var z = addClass(new Class(name: 'Z', supertype: objectSuper));
|
|
|
|
_assertTestLibraryText('''
|
|
class A {}
|
|
class B = core::Object with self::A {}
|
|
class Z {}
|
|
''');
|
|
|
|
expect(hierarchy.getClassAsInstanceOf(b, objectClass), objectSuper);
|
|
expect(hierarchy.getClassAsInstanceOf(b, a), a.asThisSupertype);
|
|
expect(hierarchy.getClassAsInstanceOf(z, a), null);
|
|
}
|
|
|
|
void test_getClassDepth() {
|
|
var base = addClass(new Class(name: 'base', supertype: objectSuper));
|
|
var extends_ =
|
|
addClass(new Class(name: 'extends_', supertype: base.asThisSupertype));
|
|
var with_ = addClass(new Class(
|
|
name: 'with_',
|
|
supertype: objectSuper,
|
|
mixedInType: base.asThisSupertype));
|
|
var implements_ = addClass(new Class(
|
|
name: 'implements_',
|
|
supertype: objectSuper,
|
|
implementedTypes: [base.asThisSupertype]));
|
|
|
|
_assertTestLibraryText('''
|
|
class base {}
|
|
class extends_ extends self::base {}
|
|
class with_ = core::Object with self::base {}
|
|
class implements_ implements self::base {}
|
|
''');
|
|
|
|
expect(hierarchy.getClassDepth(objectClass), 0);
|
|
expect(hierarchy.getClassDepth(base), 1);
|
|
expect(hierarchy.getClassDepth(extends_), 2);
|
|
expect(hierarchy.getClassDepth(with_), 2);
|
|
expect(hierarchy.getClassDepth(implements_), 2);
|
|
}
|
|
|
|
/// Copy of the tests/language/least_upper_bound_expansive_test.dart test.
|
|
void test_getClassicLeastUpperBound_expansive() {
|
|
var int = coreTypes.intClass.rawType;
|
|
var string = coreTypes.stringClass.rawType;
|
|
|
|
// class N<T> {}
|
|
var NT = new TypeParameter('T', objectClass.rawType);
|
|
var N = addClass(
|
|
new Class(name: 'N', typeParameters: [NT], supertype: objectSuper));
|
|
|
|
// class C1<T> extends N<N<C1<T>>> {}
|
|
Class C1;
|
|
{
|
|
var T = new TypeParameter('T', objectClass.rawType);
|
|
C1 = addClass(
|
|
new Class(name: 'C1', typeParameters: [T], supertype: objectSuper));
|
|
DartType C1_T = Substitution
|
|
.fromMap({T: new TypeParameterType(T)}).substituteType(C1.thisType);
|
|
DartType N_C1_T =
|
|
Substitution.fromMap({NT: C1_T}).substituteType(N.thisType);
|
|
Supertype N_N_C1_T = Substitution
|
|
.fromMap({NT: N_C1_T}).substituteSupertype(N.asThisSupertype);
|
|
C1.supertype = N_N_C1_T;
|
|
}
|
|
|
|
// class C2<T> extends N<N<C2<N<C2<T>>>>> {}
|
|
Class C2;
|
|
{
|
|
var T = new TypeParameter('T', objectClass.rawType);
|
|
C2 = addClass(
|
|
new Class(name: 'C2', typeParameters: [T], supertype: objectSuper));
|
|
DartType C2_T = Substitution
|
|
.fromMap({T: new TypeParameterType(T)}).substituteType(C2.thisType);
|
|
DartType N_C2_T =
|
|
Substitution.fromMap({NT: C2_T}).substituteType(N.thisType);
|
|
DartType C2_N_C2_T =
|
|
Substitution.fromMap({T: N_C2_T}).substituteType(C2.thisType);
|
|
DartType N_C2_N_C2_T =
|
|
Substitution.fromMap({NT: C2_N_C2_T}).substituteType(N.thisType);
|
|
Supertype N_N_C2_N_C2_T = Substitution
|
|
.fromMap({NT: N_C2_N_C2_T}).substituteSupertype(N.asThisSupertype);
|
|
C2.supertype = N_N_C2_N_C2_T;
|
|
}
|
|
|
|
_assertTestLibraryText('''
|
|
class N<T> {}
|
|
class C1<T> extends self::N<self::N<self::C1<self::C1::T>>> {}
|
|
class C2<T> extends self::N<self::N<self::C2<self::N<self::C2<self::C2::T>>>>> {}
|
|
''');
|
|
|
|
// The least upper bound of C1<int> and N<C1<String>> is Object since the
|
|
// supertypes are
|
|
// {C1<int>, N<N<C1<int>>>, Object} for C1<int> and
|
|
// {N<C1<String>>, Object} for N<C1<String>> and
|
|
// Object is the most specific type in the intersection of the supertypes.
|
|
expect(
|
|
hierarchy.getClassicLeastUpperBound(
|
|
new InterfaceType(C1, [int]),
|
|
new InterfaceType(N, [
|
|
new InterfaceType(C1, [string])
|
|
])),
|
|
objectClass.thisType);
|
|
|
|
// The least upper bound of C2<int> and N<C2<String>> is Object since the
|
|
// supertypes are
|
|
// {C2<int>, N<N<C2<N<C2<int>>>>>, Object} for C2<int> and
|
|
// {N<C2<String>>, Object} for N<C2<String>> and
|
|
// Object is the most specific type in the intersection of the supertypes.
|
|
expect(
|
|
hierarchy.getClassicLeastUpperBound(
|
|
new InterfaceType(C2, [int]),
|
|
new InterfaceType(N, [
|
|
new InterfaceType(C2, [string])
|
|
])),
|
|
objectClass.thisType);
|
|
}
|
|
|
|
void test_getClassicLeastUpperBound_generic() {
|
|
var int = coreTypes.intClass.rawType;
|
|
var double = coreTypes.doubleClass.rawType;
|
|
var bool = coreTypes.boolClass.rawType;
|
|
|
|
var a = addGenericClass('A', []);
|
|
var b =
|
|
addGenericClass('B', ['T'], implements_: (_) => [a.asThisSupertype]);
|
|
var c =
|
|
addGenericClass('C', ['U'], implements_: (_) => [a.asThisSupertype]);
|
|
var d = addGenericClass('D', ['T', 'U'], implements_: (typeParameterTypes) {
|
|
var t = typeParameterTypes[0];
|
|
var u = typeParameterTypes[1];
|
|
return [
|
|
new Supertype(b, [t]),
|
|
new Supertype(c, [u])
|
|
];
|
|
});
|
|
var e = addGenericClass('E', [],
|
|
implements_: (_) => [
|
|
new Supertype(d, [int, double])
|
|
]);
|
|
var f = addGenericClass('F', [],
|
|
implements_: (_) => [
|
|
new Supertype(d, [int, bool])
|
|
]);
|
|
|
|
_assertTestLibraryText('''
|
|
class A {}
|
|
class B<T> implements self::A {}
|
|
class C<U> implements self::A {}
|
|
class D<T, U> implements self::B<self::D::T>, self::C<self::D::U> {}
|
|
class E implements self::D<core::int, core::double> {}
|
|
class F implements self::D<core::int, core::bool> {}
|
|
''');
|
|
|
|
expect(
|
|
hierarchy.getClassicLeastUpperBound(new InterfaceType(d, [int, double]),
|
|
new InterfaceType(d, [int, double])),
|
|
new InterfaceType(d, [int, double]));
|
|
expect(
|
|
hierarchy.getClassicLeastUpperBound(new InterfaceType(d, [int, double]),
|
|
new InterfaceType(d, [int, bool])),
|
|
new InterfaceType(b, [int]));
|
|
expect(
|
|
hierarchy.getClassicLeastUpperBound(new InterfaceType(d, [int, double]),
|
|
new InterfaceType(d, [bool, double])),
|
|
new InterfaceType(c, [double]));
|
|
expect(
|
|
hierarchy.getClassicLeastUpperBound(new InterfaceType(d, [int, double]),
|
|
new InterfaceType(d, [bool, int])),
|
|
a.rawType);
|
|
expect(hierarchy.getClassicLeastUpperBound(e.rawType, f.rawType),
|
|
new InterfaceType(b, [int]));
|
|
}
|
|
|
|
void test_getClassicLeastUpperBound_nonGeneric() {
|
|
var a = addImplementsClass('A', []);
|
|
var b = addImplementsClass('B', []);
|
|
var c = addImplementsClass('C', [a]);
|
|
var d = addImplementsClass('D', [a]);
|
|
var e = addImplementsClass('E', [a]);
|
|
var f = addImplementsClass('F', [c, d]);
|
|
var g = addImplementsClass('G', [c, d]);
|
|
var h = addImplementsClass('H', [c, d, e]);
|
|
var i = addImplementsClass('I', [c, d, e]);
|
|
|
|
_assertTestLibraryText('''
|
|
class A {}
|
|
class B {}
|
|
class C implements self::A {}
|
|
class D implements self::A {}
|
|
class E implements self::A {}
|
|
class F implements self::C, self::D {}
|
|
class G implements self::C, self::D {}
|
|
class H implements self::C, self::D, self::E {}
|
|
class I implements self::C, self::D, self::E {}
|
|
''');
|
|
|
|
expect(hierarchy.getClassicLeastUpperBound(a.rawType, b.rawType),
|
|
objectClass.rawType);
|
|
expect(hierarchy.getClassicLeastUpperBound(a.rawType, objectClass.rawType),
|
|
objectClass.rawType);
|
|
expect(hierarchy.getClassicLeastUpperBound(objectClass.rawType, b.rawType),
|
|
objectClass.rawType);
|
|
expect(
|
|
hierarchy.getClassicLeastUpperBound(c.rawType, d.rawType), a.rawType);
|
|
expect(
|
|
hierarchy.getClassicLeastUpperBound(c.rawType, a.rawType), a.rawType);
|
|
expect(
|
|
hierarchy.getClassicLeastUpperBound(a.rawType, d.rawType), a.rawType);
|
|
expect(
|
|
hierarchy.getClassicLeastUpperBound(f.rawType, g.rawType), a.rawType);
|
|
expect(
|
|
hierarchy.getClassicLeastUpperBound(h.rawType, i.rawType), a.rawType);
|
|
}
|
|
|
|
void test_getDeclaredMembers() {
|
|
var method = newEmptyMethod('method');
|
|
var getter = newEmptyGetter('getter');
|
|
var setter = newEmptySetter('setter');
|
|
var abstractMethod = newEmptyMethod('abstractMethod', isAbstract: true);
|
|
var abstractGetter = newEmptyGetter('abstractGetter', isAbstract: true);
|
|
var abstractSetter = newEmptySetter('abstractSetter', isAbstract: true);
|
|
var nonFinalField = new Field(new Name('nonFinalField'));
|
|
var finalField = new Field(new Name('finalField'), isFinal: true);
|
|
var a = addClass(new Class(
|
|
isAbstract: true,
|
|
name: 'A',
|
|
supertype: objectSuper,
|
|
fields: [
|
|
nonFinalField,
|
|
finalField
|
|
],
|
|
procedures: [
|
|
method,
|
|
getter,
|
|
setter,
|
|
abstractMethod,
|
|
abstractGetter,
|
|
abstractSetter
|
|
]));
|
|
var b = addClass(
|
|
new Class(isAbstract: true, name: 'B', supertype: a.asThisSupertype));
|
|
|
|
_assertTestLibraryText('''
|
|
abstract class A {
|
|
field dynamic nonFinalField;
|
|
final field dynamic finalField;
|
|
method method() → void {}
|
|
get getter() → dynamic {
|
|
return null;
|
|
}
|
|
set setter(dynamic _) → void {}
|
|
abstract method abstractMethod() → void;
|
|
get abstractGetter() → dynamic;
|
|
set abstractSetter(dynamic _) → void;
|
|
}
|
|
abstract class B extends self::A {}
|
|
''');
|
|
|
|
expect(
|
|
hierarchy.getDeclaredMembers(a),
|
|
unorderedEquals([
|
|
method,
|
|
getter,
|
|
abstractMethod,
|
|
abstractGetter,
|
|
nonFinalField,
|
|
finalField
|
|
]));
|
|
expect(hierarchy.getDeclaredMembers(a, setters: true),
|
|
unorderedEquals([setter, abstractSetter, nonFinalField]));
|
|
expect(hierarchy.getDeclaredMembers(b), isEmpty);
|
|
expect(hierarchy.getDeclaredMembers(b, setters: true), isEmpty);
|
|
}
|
|
|
|
void test_getDispatchTarget() {
|
|
var aMethod = newEmptyMethod('aMethod');
|
|
var aSetter = newEmptySetter('aSetter');
|
|
var bMethod = newEmptyMethod('bMethod');
|
|
var bSetter = newEmptySetter('bSetter');
|
|
var a = addClass(new Class(
|
|
name: 'A', supertype: objectSuper, procedures: [aMethod, aSetter]));
|
|
var b = addClass(new Class(
|
|
name: 'B',
|
|
supertype: a.asThisSupertype,
|
|
procedures: [bMethod, bSetter]));
|
|
var c = addClass(new Class(name: 'C', supertype: b.asThisSupertype));
|
|
|
|
_assertTestLibraryText('''
|
|
class A {
|
|
method aMethod() → void {}
|
|
set aSetter(dynamic _) → void {}
|
|
}
|
|
class B extends self::A {
|
|
method bMethod() → void {}
|
|
set bSetter(dynamic _) → void {}
|
|
}
|
|
class C extends self::B {}
|
|
''');
|
|
|
|
var aMethodName = new Name('aMethod');
|
|
var aSetterName = new Name('aSetter');
|
|
var bMethodName = new Name('bMethod');
|
|
var bSetterName = new Name('bSetter');
|
|
expect(hierarchy.getDispatchTarget(a, aMethodName), aMethod);
|
|
expect(hierarchy.getDispatchTarget(a, bMethodName), isNull);
|
|
expect(hierarchy.getDispatchTarget(a, aSetterName, setter: true), aSetter);
|
|
expect(hierarchy.getDispatchTarget(a, bSetterName, setter: true), isNull);
|
|
expect(hierarchy.getDispatchTarget(b, aMethodName), aMethod);
|
|
expect(hierarchy.getDispatchTarget(b, bMethodName), bMethod);
|
|
expect(hierarchy.getDispatchTarget(b, aSetterName, setter: true), aSetter);
|
|
expect(hierarchy.getDispatchTarget(b, bSetterName, setter: true), bSetter);
|
|
expect(hierarchy.getDispatchTarget(c, aMethodName), aMethod);
|
|
expect(hierarchy.getDispatchTarget(c, bMethodName), bMethod);
|
|
expect(hierarchy.getDispatchTarget(c, aSetterName, setter: true), aSetter);
|
|
expect(hierarchy.getDispatchTarget(c, bSetterName, setter: true), bSetter);
|
|
}
|
|
|
|
void test_getDispatchTarget_abstract() {
|
|
var aFoo = newEmptyMethod('foo', isAbstract: true);
|
|
var aBar = newEmptyMethod('bar');
|
|
var bFoo = newEmptyMethod('foo');
|
|
var bBar = newEmptyMethod('bar', isAbstract: true);
|
|
var a = addClass(new Class(
|
|
isAbstract: true,
|
|
name: 'A',
|
|
supertype: objectSuper,
|
|
procedures: [aFoo, aBar]));
|
|
var b = addClass(new Class(
|
|
isAbstract: true,
|
|
name: 'B',
|
|
supertype: a.asThisSupertype,
|
|
procedures: [bFoo, bBar]));
|
|
var c = addClass(new Class(name: 'C', supertype: b.asThisSupertype));
|
|
|
|
_assertTestLibraryText('''
|
|
abstract class A {
|
|
abstract method foo() → void;
|
|
method bar() → void {}
|
|
}
|
|
abstract class B extends self::A {
|
|
method foo() → void {}
|
|
abstract method bar() → void;
|
|
}
|
|
class C extends self::B {}
|
|
''');
|
|
|
|
expect(hierarchy.getDispatchTarget(a, new Name('foo')), isNull);
|
|
expect(hierarchy.getDispatchTarget(b, new Name('foo')), bFoo);
|
|
expect(hierarchy.getDispatchTarget(c, new Name('foo')), bFoo);
|
|
|
|
expect(hierarchy.getDispatchTarget(a, new Name('bar')), aBar);
|
|
expect(hierarchy.getDispatchTarget(b, new Name('bar')), aBar);
|
|
expect(hierarchy.getDispatchTarget(c, new Name('bar')), aBar);
|
|
}
|
|
|
|
void test_getInterfaceMember_extends() {
|
|
var aMethod = newEmptyMethod('aMethod');
|
|
var aSetter = newEmptySetter('aSetter');
|
|
var bMethod = newEmptyMethod('bMethod');
|
|
var bSetter = newEmptySetter('bSetter');
|
|
var a = addClass(new Class(
|
|
name: 'A', supertype: objectSuper, procedures: [aMethod, aSetter]));
|
|
var b = addClass(new Class(
|
|
name: 'B',
|
|
supertype: a.asThisSupertype,
|
|
procedures: [bMethod, bSetter]));
|
|
var c = addClass(new Class(name: 'C', supertype: b.asThisSupertype));
|
|
|
|
_assertTestLibraryText('''
|
|
class A {
|
|
method aMethod() → void {}
|
|
set aSetter(dynamic _) → void {}
|
|
}
|
|
class B extends self::A {
|
|
method bMethod() → void {}
|
|
set bSetter(dynamic _) → void {}
|
|
}
|
|
class C extends self::B {}
|
|
''');
|
|
|
|
var aMethodName = new Name('aMethod');
|
|
var aSetterName = new Name('aSetter');
|
|
var bMethodName = new Name('bMethod');
|
|
var bSetterName = new Name('bSetter');
|
|
expect(hierarchy.getInterfaceMember(a, aMethodName), aMethod);
|
|
expect(hierarchy.getInterfaceMember(a, bMethodName), isNull);
|
|
expect(hierarchy.getInterfaceMember(a, aSetterName, setter: true), aSetter);
|
|
expect(hierarchy.getInterfaceMember(a, bSetterName, setter: true), isNull);
|
|
expect(hierarchy.getInterfaceMember(b, aMethodName), aMethod);
|
|
expect(hierarchy.getInterfaceMember(b, bMethodName), bMethod);
|
|
expect(hierarchy.getInterfaceMember(b, aSetterName, setter: true), aSetter);
|
|
expect(hierarchy.getInterfaceMember(b, bSetterName, setter: true), bSetter);
|
|
expect(hierarchy.getInterfaceMember(c, aMethodName), aMethod);
|
|
expect(hierarchy.getInterfaceMember(c, bMethodName), bMethod);
|
|
expect(hierarchy.getInterfaceMember(c, aSetterName, setter: true), aSetter);
|
|
expect(hierarchy.getInterfaceMember(c, bSetterName, setter: true), bSetter);
|
|
}
|
|
|
|
void test_getInterfaceMember_implements() {
|
|
var aMethod = newEmptyMethod('aMethod');
|
|
var aSetter = newEmptySetter('aSetter');
|
|
var bMethod = newEmptyMethod('bMethod');
|
|
var bSetter = newEmptySetter('bSetter');
|
|
var a = addClass(new Class(
|
|
name: 'A', supertype: objectSuper, procedures: [aMethod, aSetter]));
|
|
var b = addClass(new Class(
|
|
name: 'B',
|
|
supertype: objectSuper,
|
|
implementedTypes: [a.asThisSupertype],
|
|
procedures: [bMethod, bSetter]));
|
|
var c = addClass(new Class(
|
|
name: 'C',
|
|
supertype: objectSuper,
|
|
implementedTypes: [b.asThisSupertype]));
|
|
|
|
_assertTestLibraryText('''
|
|
class A {
|
|
method aMethod() → void {}
|
|
set aSetter(dynamic _) → void {}
|
|
}
|
|
class B implements self::A {
|
|
method bMethod() → void {}
|
|
set bSetter(dynamic _) → void {}
|
|
}
|
|
class C implements self::B {}
|
|
''');
|
|
|
|
var aMethodName = new Name('aMethod');
|
|
var aSetterName = new Name('aSetter');
|
|
var bMethodName = new Name('bMethod');
|
|
var bSetterName = new Name('bSetter');
|
|
expect(hierarchy.getInterfaceMember(a, aMethodName), aMethod);
|
|
expect(hierarchy.getInterfaceMember(a, bMethodName), isNull);
|
|
expect(hierarchy.getInterfaceMember(a, aSetterName, setter: true), aSetter);
|
|
expect(hierarchy.getInterfaceMember(a, bSetterName, setter: true), isNull);
|
|
expect(hierarchy.getInterfaceMember(b, aMethodName), aMethod);
|
|
expect(hierarchy.getInterfaceMember(b, bMethodName), bMethod);
|
|
expect(hierarchy.getInterfaceMember(b, aSetterName, setter: true), aSetter);
|
|
expect(hierarchy.getInterfaceMember(b, bSetterName, setter: true), bSetter);
|
|
expect(hierarchy.getInterfaceMember(c, aMethodName), aMethod);
|
|
expect(hierarchy.getInterfaceMember(c, bMethodName), bMethod);
|
|
expect(hierarchy.getInterfaceMember(c, aSetterName, setter: true), aSetter);
|
|
expect(hierarchy.getInterfaceMember(c, bSetterName, setter: true), bSetter);
|
|
}
|
|
|
|
void test_getInterfaceMembers_in_class() {
|
|
var method = newEmptyMethod('method');
|
|
var getter = newEmptyGetter('getter');
|
|
var setter = newEmptySetter('setter');
|
|
var abstractMethod = newEmptyMethod('abstractMethod', isAbstract: true);
|
|
var abstractGetter = newEmptyGetter('abstractGetter', isAbstract: true);
|
|
var abstractSetter = newEmptySetter('abstractSetter', isAbstract: true);
|
|
var nonFinalField = new Field(new Name('nonFinalField'));
|
|
var finalField = new Field(new Name('finalField'), isFinal: true);
|
|
var a = addClass(new Class(
|
|
isAbstract: true,
|
|
name: 'A',
|
|
supertype: objectSuper,
|
|
fields: [
|
|
nonFinalField,
|
|
finalField
|
|
],
|
|
procedures: [
|
|
method,
|
|
getter,
|
|
setter,
|
|
abstractMethod,
|
|
abstractGetter,
|
|
abstractSetter
|
|
]));
|
|
|
|
_assertTestLibraryText('''
|
|
abstract class A {
|
|
field dynamic nonFinalField;
|
|
final field dynamic finalField;
|
|
method method() → void {}
|
|
get getter() → dynamic {
|
|
return null;
|
|
}
|
|
set setter(dynamic _) → void {}
|
|
abstract method abstractMethod() → void;
|
|
get abstractGetter() → dynamic;
|
|
set abstractSetter(dynamic _) → void;
|
|
}
|
|
''');
|
|
|
|
expect(
|
|
hierarchy.getInterfaceMembers(a),
|
|
unorderedEquals([
|
|
method,
|
|
getter,
|
|
abstractMethod,
|
|
abstractGetter,
|
|
nonFinalField,
|
|
finalField
|
|
]));
|
|
expect(hierarchy.getInterfaceMembers(a, setters: true),
|
|
unorderedEquals([setter, abstractSetter, nonFinalField]));
|
|
}
|
|
|
|
void test_getInterfaceMembers_inherited_or_mixed_in() {
|
|
var method = newEmptyMethod('method');
|
|
var getter = newEmptyGetter('getter');
|
|
var setter = newEmptySetter('setter');
|
|
var abstractMethod = newEmptyMethod('abstractMethod', isAbstract: true);
|
|
var abstractGetter = newEmptyGetter('abstractGetter', isAbstract: true);
|
|
var abstractSetter = newEmptySetter('abstractSetter', isAbstract: true);
|
|
var nonFinalField = new Field(new Name('nonFinalField'));
|
|
var finalField = new Field(new Name('finalField'), isFinal: true);
|
|
|
|
var a = addClass(new Class(name: 'A', supertype: objectSuper, fields: [
|
|
nonFinalField,
|
|
finalField
|
|
], procedures: [
|
|
method,
|
|
getter,
|
|
setter,
|
|
abstractMethod,
|
|
abstractGetter,
|
|
abstractSetter
|
|
]));
|
|
var b = addClass(new Class(name: 'B', supertype: a.asThisSupertype));
|
|
var c = addClass(new Class(
|
|
isAbstract: true,
|
|
name: 'C',
|
|
supertype: objectSuper,
|
|
implementedTypes: [a.asThisSupertype]));
|
|
var d = addClass(new Class(
|
|
name: 'D', supertype: objectSuper, mixedInType: a.asThisSupertype));
|
|
|
|
_assertTestLibraryText('''
|
|
class A {
|
|
field dynamic nonFinalField;
|
|
final field dynamic finalField;
|
|
method method() → void {}
|
|
get getter() → dynamic {
|
|
return null;
|
|
}
|
|
set setter(dynamic _) → void {}
|
|
abstract method abstractMethod() → void;
|
|
get abstractGetter() → dynamic;
|
|
set abstractSetter(dynamic _) → void;
|
|
}
|
|
class B extends self::A {}
|
|
abstract class C implements self::A {}
|
|
class D = core::Object with self::A {}
|
|
''');
|
|
|
|
var expectedGetters = [
|
|
method,
|
|
getter,
|
|
abstractMethod,
|
|
abstractGetter,
|
|
nonFinalField,
|
|
finalField
|
|
];
|
|
expect(hierarchy.getInterfaceMembers(b), unorderedEquals(expectedGetters));
|
|
var expectedSetters = [setter, abstractSetter, nonFinalField];
|
|
expect(hierarchy.getInterfaceMembers(b, setters: true),
|
|
unorderedEquals(expectedSetters));
|
|
expect(hierarchy.getInterfaceMembers(c), unorderedEquals(expectedGetters));
|
|
expect(hierarchy.getInterfaceMembers(c, setters: true),
|
|
unorderedEquals(expectedSetters));
|
|
expect(hierarchy.getInterfaceMembers(d), unorderedEquals(expectedGetters));
|
|
expect(hierarchy.getInterfaceMembers(d, setters: true),
|
|
unorderedEquals(expectedSetters));
|
|
}
|
|
|
|
void test_getInterfaceMembers_multiple() {
|
|
var method_a = newEmptyMethod('method');
|
|
var getter_a = newEmptyGetter('getter');
|
|
var setter_a = newEmptySetter('setter');
|
|
var nonFinalField_a = new Field(new Name('nonFinalField'));
|
|
var finalField_a = new Field(new Name('finalField'), isFinal: true);
|
|
var method_b = newEmptyMethod('method');
|
|
var getter_b = newEmptyGetter('getter');
|
|
var setter_b = newEmptySetter('setter');
|
|
var nonFinalField_b = new Field(new Name('nonFinalField'));
|
|
var finalField_b = new Field(new Name('finalField'), isFinal: true);
|
|
|
|
var a = addClass(new Class(
|
|
name: 'A',
|
|
supertype: objectSuper,
|
|
fields: [nonFinalField_a, finalField_a],
|
|
procedures: [method_a, getter_a, setter_a]));
|
|
var b = addClass(new Class(
|
|
name: 'B',
|
|
supertype: objectSuper,
|
|
fields: [nonFinalField_b, finalField_b],
|
|
procedures: [method_b, getter_b, setter_b]));
|
|
var c = addClass(new Class(
|
|
isAbstract: true,
|
|
name: 'C',
|
|
supertype: objectSuper,
|
|
implementedTypes: [a.asThisSupertype, b.asThisSupertype]));
|
|
|
|
_assertTestLibraryText('''
|
|
class A {
|
|
field dynamic nonFinalField;
|
|
final field dynamic finalField;
|
|
method method() → void {}
|
|
get getter() → dynamic {
|
|
return null;
|
|
}
|
|
set setter(dynamic _) → void {}
|
|
}
|
|
class B {
|
|
field dynamic nonFinalField;
|
|
final field dynamic finalField;
|
|
method method() → void {}
|
|
get getter() → dynamic {
|
|
return null;
|
|
}
|
|
set setter(dynamic _) → void {}
|
|
}
|
|
abstract class C implements self::A, self::B {}
|
|
''');
|
|
|
|
expect(
|
|
hierarchy.getInterfaceMembers(c),
|
|
unorderedEquals([
|
|
method_a,
|
|
getter_a,
|
|
nonFinalField_a,
|
|
finalField_a,
|
|
method_b,
|
|
getter_b,
|
|
nonFinalField_b,
|
|
finalField_b
|
|
]));
|
|
expect(
|
|
hierarchy.getInterfaceMembers(c, setters: true),
|
|
unorderedEquals(
|
|
[setter_a, nonFinalField_a, setter_b, nonFinalField_b]));
|
|
}
|
|
|
|
void test_getInterfaceMembers_shadowed() {
|
|
var method_a = newEmptyMethod('method');
|
|
var nonShadowedMethod_a = newEmptyMethod('nonShadowedMethod');
|
|
var getter_a = newEmptyGetter('getter');
|
|
var setter_a = newEmptySetter('setter');
|
|
var nonShadowedSetter_a = newEmptySetter('nonShadowedSetter');
|
|
var nonFinalField_a = new Field(new Name('nonFinalField'));
|
|
var finalField_a = new Field(new Name('finalField'), isFinal: true);
|
|
var method_b = newEmptyMethod('method');
|
|
var getter_b = newEmptyGetter('getter');
|
|
var setter_b = newEmptySetter('setter');
|
|
var nonFinalField_b = new Field(new Name('nonFinalField'));
|
|
var finalField_b = new Field(new Name('finalField'), isFinal: true);
|
|
|
|
var a = addClass(new Class(name: 'A', supertype: objectSuper, fields: [
|
|
nonFinalField_a,
|
|
finalField_a
|
|
], procedures: [
|
|
method_a,
|
|
nonShadowedMethod_a,
|
|
getter_a,
|
|
setter_a,
|
|
nonShadowedSetter_a
|
|
]));
|
|
var b = addClass(new Class(
|
|
name: 'B',
|
|
supertype: a.asThisSupertype,
|
|
fields: [nonFinalField_b, finalField_b],
|
|
procedures: [method_b, getter_b, setter_b]));
|
|
|
|
_assertTestLibraryText('''
|
|
class A {
|
|
field dynamic nonFinalField;
|
|
final field dynamic finalField;
|
|
method method() → void {}
|
|
method nonShadowedMethod() → void {}
|
|
get getter() → dynamic {
|
|
return null;
|
|
}
|
|
set setter(dynamic _) → void {}
|
|
set nonShadowedSetter(dynamic _) → void {}
|
|
}
|
|
class B extends self::A {
|
|
field dynamic nonFinalField;
|
|
final field dynamic finalField;
|
|
method method() → void {}
|
|
get getter() → dynamic {
|
|
return null;
|
|
}
|
|
set setter(dynamic _) → void {}
|
|
}
|
|
''');
|
|
|
|
expect(
|
|
hierarchy.getInterfaceMembers(b),
|
|
unorderedEquals([
|
|
nonShadowedMethod_a,
|
|
method_b,
|
|
getter_b,
|
|
nonFinalField_b,
|
|
finalField_b
|
|
]));
|
|
expect(hierarchy.getInterfaceMembers(b, setters: true),
|
|
unorderedEquals([nonShadowedSetter_a, setter_b, nonFinalField_b]));
|
|
}
|
|
|
|
void test_getOrderedClasses() {
|
|
var a = addClass(new Class(name: 'A', supertype: objectSuper));
|
|
var b = addClass(new Class(name: 'B', supertype: a.asThisSupertype));
|
|
var c = addClass(new Class(name: 'C', supertype: b.asThisSupertype));
|
|
|
|
void assertOrderOfClasses(List<Class> unordered, List<Class> expected) {
|
|
var ordered = hierarchy.getOrderedClasses(unordered);
|
|
expect(ordered, expected);
|
|
}
|
|
|
|
assertOrderOfClasses([a, b, c], [a, b, c]);
|
|
assertOrderOfClasses([b, a, c], [a, b, c]);
|
|
assertOrderOfClasses([a, c, b], [a, b, c]);
|
|
assertOrderOfClasses([b, c, a], [a, b, c]);
|
|
assertOrderOfClasses([c, a, b], [a, b, c]);
|
|
assertOrderOfClasses([c, b, a], [a, b, c]);
|
|
assertOrderOfClasses([c, b], [b, c]);
|
|
}
|
|
|
|
void test_getRankedSuperclasses() {
|
|
var a = addImplementsClass('A', []);
|
|
var b = addImplementsClass('B', [a]);
|
|
var c = addImplementsClass('C', [a]);
|
|
var d = addImplementsClass('D', [c]);
|
|
var e = addImplementsClass('E', [b, d]);
|
|
|
|
_assertTestLibraryText('''
|
|
class A {}
|
|
class B implements self::A {}
|
|
class C implements self::A {}
|
|
class D implements self::C {}
|
|
class E implements self::B, self::D {}
|
|
''');
|
|
|
|
expect(hierarchy.getRankedSuperclasses(a), [a, objectClass]);
|
|
expect(hierarchy.getRankedSuperclasses(b), [b, a, objectClass]);
|
|
expect(hierarchy.getRankedSuperclasses(c), [c, a, objectClass]);
|
|
expect(hierarchy.getRankedSuperclasses(d), [d, c, a, objectClass]);
|
|
if (hierarchy.getClassIndex(b) < hierarchy.getClassIndex(c)) {
|
|
expect(hierarchy.getRankedSuperclasses(e), [e, d, b, c, a, objectClass]);
|
|
} else {
|
|
expect(hierarchy.getRankedSuperclasses(e), [e, d, c, b, a, objectClass]);
|
|
}
|
|
}
|
|
|
|
void test_getTypeAsInstanceOf_generic_extends() {
|
|
var int = coreTypes.intClass.rawType;
|
|
var bool = coreTypes.boolClass.rawType;
|
|
|
|
var a = addGenericClass('A', ['T', 'U']);
|
|
|
|
var bT = new TypeParameter('T', objectClass.rawType);
|
|
var bTT = new TypeParameterType(bT);
|
|
var b = addClass(new Class(
|
|
name: 'B',
|
|
typeParameters: [bT],
|
|
supertype: new Supertype(a, [bTT, bool])));
|
|
|
|
_assertTestLibraryText('''
|
|
class A<T, U> {}
|
|
class B<T> extends self::A<self::B::T, core::bool> {}
|
|
''');
|
|
|
|
var b_int = new InterfaceType(b, [int]);
|
|
expect(hierarchy.getTypeAsInstanceOf(b_int, a),
|
|
new InterfaceType(a, [int, bool]));
|
|
expect(hierarchy.getTypeAsInstanceOf(b_int, objectClass),
|
|
new InterfaceType(objectClass));
|
|
}
|
|
|
|
void _assertOverridePairs(Class class_, List<String> expected) {
|
|
List<String> overrideDescriptions = [];
|
|
void callback(
|
|
Member declaredMember, Member interfaceMember, bool isSetter) {
|
|
var suffix = isSetter ? '=' : '';
|
|
String declaredName = '$declaredMember$suffix';
|
|
String interfaceName = '$interfaceMember$suffix';
|
|
var desc = '$declaredName overrides $interfaceName';
|
|
overrideDescriptions.add(desc);
|
|
}
|
|
|
|
hierarchy.forEachOverridePair(class_, callback);
|
|
expect(overrideDescriptions, unorderedEquals(expected));
|
|
}
|
|
|
|
/// Assert that the test [library] has the [expectedText] presentation.
|
|
/// The presentation is close, but not identical to the normal Kernel one.
|
|
void _assertTestLibraryText(String expectedText) {
|
|
StringBuffer sb = new StringBuffer();
|
|
Printer printer = new Printer(sb);
|
|
printer.writeLibraryFile(library);
|
|
|
|
String actualText = sb.toString();
|
|
|
|
// Clean up the text a bit.
|
|
const oftenUsedPrefix = '''
|
|
library test;
|
|
import self as self;
|
|
import "dart:core" as core;
|
|
|
|
''';
|
|
if (actualText.startsWith(oftenUsedPrefix)) {
|
|
actualText = actualText.substring(oftenUsedPrefix.length);
|
|
}
|
|
actualText = actualText.replaceAll('{\n}', '{}');
|
|
actualText = actualText.replaceAll(' extends core::Object', '');
|
|
|
|
if (actualText != expectedText) {
|
|
print('-------- Actual --------');
|
|
print(actualText + '------------------------');
|
|
}
|
|
|
|
expect(actualText, expectedText);
|
|
}
|
|
}
|