| // 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:expect/legacy/minitest.dart"; // ignore: deprecated_member_use | |
| import "package:kernel/ast.dart"; | |
| import "package:kernel/class_hierarchy.dart"; | |
| import "package:kernel/core_types.dart"; | |
| import "package:kernel/testing/mock_sdk_component.dart"; | |
| import "package:kernel/text/ast_to_text.dart"; | |
| import "package:kernel/src/text_util.dart"; | |
| void main() { | |
| new ClosedWorldClassHierarchyTest().test_applyTreeChanges(); | |
| new ClosedWorldClassHierarchyTest().test_applyMemberChanges(); | |
| new ClosedWorldClassHierarchyTest() | |
| .test_getSingleTargetForInterfaceInvocation(); | |
| new ClosedWorldClassHierarchyTest().test_getSubtypesOf(); | |
| new ClosedWorldClassHierarchyTest() | |
| .test_forEachOverridePair_supertypeOverridesInterface(); | |
| new ClosedWorldClassHierarchyTest() | |
| .test_forEachOverridePair_supertypeOverridesThis(); | |
| new ClosedWorldClassHierarchyTest() | |
| .test_forEachOverridePair_supertypeOverridesThisAbstract(); | |
| new ClosedWorldClassHierarchyTest() | |
| .test_forEachOverridePair_thisOverridesSupertype(); | |
| new ClosedWorldClassHierarchyTest() | |
| .test_forEachOverridePair_thisOverridesSupertype_setter(); | |
| new ClosedWorldClassHierarchyTest() | |
| .test_getClassAsInstanceOf_generic_extends(); | |
| new ClosedWorldClassHierarchyTest() | |
| .test_getClassAsInstanceOf_generic_implements(); | |
| new ClosedWorldClassHierarchyTest().test_getClassAsInstanceOf_generic_with(); | |
| new ClosedWorldClassHierarchyTest() | |
| .test_getClassAsInstanceOf_notGeneric_extends(); | |
| new ClosedWorldClassHierarchyTest() | |
| .test_getClassAsInstanceOf_notGeneric_implements(); | |
| new ClosedWorldClassHierarchyTest() | |
| .test_getClassAsInstanceOf_notGeneric_with(); | |
| new ClosedWorldClassHierarchyTest().test_getDeclaredMembers(); | |
| new ClosedWorldClassHierarchyTest().test_getDispatchTarget(); | |
| new ClosedWorldClassHierarchyTest().test_getDispatchTarget_abstract(); | |
| new ClosedWorldClassHierarchyTest().test_getInterfaceMember_extends(); | |
| new ClosedWorldClassHierarchyTest().test_getInterfaceMember_implements(); | |
| new ClosedWorldClassHierarchyTest().test_getInterfaceMembers_in_class(); | |
| new ClosedWorldClassHierarchyTest() | |
| .test_getInterfaceMembers_inherited_or_mixed_in(); | |
| new ClosedWorldClassHierarchyTest().test_getInterfaceMembers_multiple(); | |
| new ClosedWorldClassHierarchyTest().test_getInterfaceMembers_shadowed(); | |
| new ClosedWorldClassHierarchyTest().test_getOrderedClasses(); | |
| new ClosedWorldClassHierarchyTest() | |
| .test_getTypeAsInstanceOf_generic_extends(); | |
| } | |
| class ClosedWorldClassHierarchyTest { | |
| final Component component = createMockSdkComponent(); | |
| late CoreTypes coreTypes; | |
| late Library library; | |
| ClassHierarchy? _hierarchy; | |
| new() { | |
| coreTypes = new CoreTypes(component); | |
| Uri uri = Uri.parse('org-dartlang:///test.dart'); | |
| library = new Library(uri, fileUri: uri, name: 'test'); | |
| library.parent = component; | |
| component.libraries.add(library); | |
| } | |
| ClassHierarchy createClassHierarchy(Component component) { | |
| return new ClassHierarchy(component, coreTypes); | |
| } | |
| void test_applyTreeChanges() { | |
| Class a = addClass( | |
| new Class(name: 'A', supertype: objectSuper, fileUri: library.fileUri), | |
| ); | |
| _assertLibraryText(library, ''' | |
| class A {} | |
| '''); | |
| Uri uriB = Uri.parse('org-dartlang:///test_b.dart'); | |
| Class b = new Class( | |
| name: 'B', | |
| supertype: a.asThisSupertype, | |
| fileUri: library.fileUri, | |
| ); | |
| Library libWithB = new Library(uriB, fileUri: uriB, name: 'test_b'); | |
| libWithB.parent = component; | |
| component.libraries.add(libWithB); | |
| libWithB.addClass(b); | |
| _assertLibraryText(libWithB, ''' | |
| library test_b; | |
| import self as self; | |
| import "test.dart" as test; | |
| class B extends test::A { // from org-dartlang:///test.dart | |
| } | |
| '''); | |
| // No updated classes, the same hierarchy. | |
| expect(hierarchy.applyTreeChanges([], [], []), same(hierarchy)); | |
| // Has updated classes, still the same hierarchy (instance). Can answer | |
| // queries about the new classes. | |
| var c = new Class( | |
| name: 'C', | |
| supertype: a.asThisSupertype, | |
| fileUri: library.fileUri, | |
| ); | |
| Uri uriC = Uri.parse('org-dartlang:///test2.dart'); | |
| Library libWithC = new Library(uriC, fileUri: uriC, name: 'test2'); | |
| libWithC.parent = component; | |
| component.libraries.add(libWithC); | |
| libWithC.addClass(c); | |
| expect( | |
| hierarchy.applyTreeChanges([libWithB], [libWithC], []), | |
| same(hierarchy), | |
| ); | |
| expect(hierarchy.isSubclassOf(a, c), false); | |
| expect(hierarchy.isSubclassOf(c, a), true); | |
| // Remove C - after this C should be unknown. | |
| expect(hierarchy.applyTreeChanges([libWithC], [], []), same(hierarchy)); | |
| expectThrows(() => hierarchy.isSubclassOf(a, c)); | |
| expectThrows(() => hierarchy.isSubclassOf(c, a)); | |
| } | |
| void expectThrows(Function() f) { | |
| bool hasThrown = false; | |
| try { | |
| f(); | |
| } catch (e) { | |
| hasThrown = true; | |
| } | |
| if (!hasThrown) { | |
| throw "Expected the function to throw an exception."; | |
| } | |
| } | |
| void test_applyMemberChanges() { | |
| var methodA1 = newEmptyMethod('memberA1'); | |
| var methodA2 = newEmptyMethod('memberA2'); | |
| var methodA3 = newEmptyMethod('memberA3'); | |
| var methodB1 = newEmptyMethod('memberB1'); | |
| var a = addClass( | |
| new Class( | |
| name: 'A', | |
| supertype: objectSuper, | |
| procedures: [methodA1, methodA2], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var b = addClass( | |
| new Class( | |
| name: 'B', | |
| supertype: a.asThisSupertype, | |
| procedures: [methodB1], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| _assertTestLibraryText(''' | |
| class A { | |
| method memberA1() → void {} | |
| method memberA2() → void {} | |
| } | |
| class B extends self::A { | |
| method memberB1() → void {} | |
| } | |
| '''); | |
| // No changes: B has memberA1, memberA2 and memberB1; | |
| // A has memberA1 and memberA2 | |
| expect( | |
| hierarchy.getDispatchTargets(b), | |
| unorderedEquals([methodA1, methodA2, methodB1]), | |
| ); | |
| expect( | |
| hierarchy.getDispatchTargets(a), | |
| unorderedEquals([methodA1, methodA2]), | |
| ); | |
| // Add a member to A, but only update A. | |
| a.addProcedure(methodA3); | |
| hierarchy.applyMemberChanges([a]); | |
| expect( | |
| hierarchy.getDispatchTargets(b), | |
| unorderedEquals([methodA1, methodA2, methodB1]), | |
| ); | |
| expect( | |
| hierarchy.getDispatchTargets(a), | |
| unorderedEquals([methodA1, methodA2, methodA3]), | |
| ); | |
| // Apply member changes again, this time telling the hierarchy to find | |
| // descendants. | |
| hierarchy.applyMemberChanges([a], findDescendants: true); | |
| expect( | |
| hierarchy.getDispatchTargets(b), | |
| unorderedEquals([methodA1, methodA2, methodA3, methodB1]), | |
| ); | |
| expect( | |
| hierarchy.getDispatchTargets(a), | |
| unorderedEquals([methodA1, methodA2, methodA3]), | |
| ); | |
| } | |
| 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], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var b = addClass( | |
| new Class( | |
| name: 'B', | |
| isAbstract: true, | |
| supertype: objectSuper, | |
| procedures: [methodInB], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var c = addClass( | |
| new Class( | |
| name: 'C', | |
| supertype: b.asThisSupertype, | |
| implementedTypes: [a.asThisSupertype], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| addClass( | |
| new Class( | |
| name: 'D', | |
| supertype: b.asThisSupertype, | |
| procedures: [methodInD], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| addClass( | |
| new Class( | |
| name: 'E', | |
| isAbstract: true, | |
| supertype: objectSuper, | |
| implementedTypes: [c.asThisSupertype], | |
| procedures: [methodInE], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| _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; | |
| ClassHierarchySubtypes cwchst = cwch.computeSubtypesInformation(); | |
| expect(cwchst.getSingleTargetForInterfaceInvocation(methodInA), methodInB); | |
| expect( | |
| cwchst.getSingleTargetForInterfaceInvocation(methodInB), | |
| null, | |
| ); // B::foo and D::foo | |
| expect(cwchst.getSingleTargetForInterfaceInvocation(methodInD), methodInD); | |
| expect( | |
| cwchst.getSingleTargetForInterfaceInvocation(methodInE), | |
| null, | |
| ); // no concrete subtypes | |
| } | |
| void test_getSubtypesOf() { | |
| var a = addClass( | |
| new Class(name: 'A', supertype: objectSuper, fileUri: library.fileUri), | |
| ); | |
| var b = addClass( | |
| new Class(name: 'B', supertype: objectSuper, fileUri: library.fileUri), | |
| ); | |
| var c = addClass( | |
| new Class(name: 'C', supertype: objectSuper, fileUri: library.fileUri), | |
| ); | |
| var d = addClass( | |
| new Class( | |
| name: 'D', | |
| supertype: a.asThisSupertype, | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var e = addClass( | |
| new Class( | |
| name: 'E', | |
| supertype: b.asThisSupertype, | |
| implementedTypes: [c.asThisSupertype], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var f = addClass( | |
| new Class( | |
| name: 'F', | |
| supertype: e.asThisSupertype, | |
| implementedTypes: [a.asThisSupertype], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var g = addClass( | |
| new Class(name: 'G', supertype: objectSuper, fileUri: library.fileUri), | |
| ); | |
| var h = addClass( | |
| new Class( | |
| name: 'H', | |
| supertype: g.asThisSupertype, | |
| implementedTypes: [c.asThisSupertype, a.asThisSupertype], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| _assertTestLibraryText(''' | |
| class A {} | |
| class B {} | |
| class C {} | |
| class D extends self::A {} | |
| class E extends self::B implements self::C {} | |
| class F extends self::E implements self::A {} | |
| class G {} | |
| class H extends self::G implements self::C, self::A {} | |
| '''); | |
| ClosedWorldClassHierarchy cwch = hierarchy as ClosedWorldClassHierarchy; | |
| ClassHierarchySubtypes cwchst = cwch.computeSubtypesInformation(); | |
| expect(cwchst.getSubtypesOf(a), unorderedEquals([a, d, f, h])); | |
| expect(cwchst.getSubtypesOf(b), unorderedEquals([b, e, f])); | |
| expect(cwchst.getSubtypesOf(c), unorderedEquals([c, e, f, h])); | |
| expect(cwchst.getSubtypesOf(d), unorderedEquals([d])); | |
| expect(cwchst.getSubtypesOf(e), unorderedEquals([e, f])); | |
| expect(cwchst.getSubtypesOf(f), unorderedEquals([f])); | |
| expect(cwchst.getSubtypesOf(g), unorderedEquals([g, h])); | |
| expect(cwchst.getSubtypesOf(h), unorderedEquals([h])); | |
| // Update hierarchy. | |
| expect( | |
| hierarchy.applyTreeChanges([library], [library], []), | |
| same(hierarchy), | |
| ); | |
| // The existing ClassHierarchySubtypes should be invalidated. | |
| expect((cwchst as dynamic).invalidated, true); | |
| // Asking for it again gives one that works. | |
| cwchst = cwch.computeSubtypesInformation(); | |
| expect(cwchst.getSubtypesOf(a), unorderedEquals([a, d, f, h])); | |
| expect(cwchst.getSubtypesOf(b), unorderedEquals([b, e, f])); | |
| expect(cwchst.getSubtypesOf(c), unorderedEquals([c, e, f, h])); | |
| expect(cwchst.getSubtypesOf(d), unorderedEquals([d])); | |
| expect(cwchst.getSubtypesOf(e), unorderedEquals([e, f])); | |
| expect(cwchst.getSubtypesOf(f), unorderedEquals([f])); | |
| expect(cwchst.getSubtypesOf(g), unorderedEquals([g, h])); | |
| expect(cwchst.getSubtypesOf(h), unorderedEquals([h])); | |
| } | |
| /// Return the new or existing instance of [ClassHierarchy]. | |
| ClassHierarchy get hierarchy { | |
| return _hierarchy ??= createClassHierarchy(component); | |
| } | |
| Class get objectClass => coreTypes.objectClass; | |
| Supertype get objectSuper => coreTypes.objectClass.asThisSupertype; | |
| Class addClass(Class c) { | |
| if (_hierarchy != null) { | |
| fail('The class 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 Function(List<DartType> typeParameterTypes)? extends_, | |
| List<Supertype> Function(List<DartType> typeParameterTypes)? implements_, | |
| }) { | |
| var typeParameters = typeParameterNames | |
| .map( | |
| (name) => new TypeParameter( | |
| name, | |
| coreTypes.objectNullableRawType, | |
| const DynamicType(), | |
| ), | |
| ) | |
| .toList(); | |
| var typeParameterTypes = typeParameters | |
| .map( | |
| (parameter) => | |
| new TypeParameterType(parameter, Nullability.undetermined), | |
| ) | |
| .toList(); | |
| var supertype = extends_ != null | |
| ? extends_(typeParameterTypes) | |
| : objectSuper; | |
| var implementedTypes = implements_ != null | |
| ? implements_(typeParameterTypes) | |
| : <Supertype>[]; | |
| return addClass( | |
| new Class( | |
| name: name, | |
| typeParameters: new TypeParameterList.from(typeParameters), | |
| supertype: supertype, | |
| implementedTypes: implementedTypes, | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| } | |
| 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), | |
| fileUri: library.fileUri, | |
| ); | |
| } | |
| 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, | |
| fileUri: library.fileUri, | |
| ); | |
| } | |
| 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 PositionalParameterList( | |
| new PositionalParameter(parameterName: '_', type: type), | |
| ), | |
| ), | |
| fileUri: library.fileUri, | |
| ); | |
| } | |
| /// 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')], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var b = addClass( | |
| new Class( | |
| name: 'B', | |
| supertype: a.asThisSupertype, | |
| procedures: [newEmptyMethod('foo', isAbstract: true)], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var c = addClass( | |
| new Class( | |
| name: 'C', | |
| supertype: a.asThisSupertype, | |
| implementedTypes: [b.asThisSupertype], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var d = addClass( | |
| new Class(name: 'D', supertype: objectSuper, fileUri: library.fileUri), | |
| ); | |
| var e = addClass( | |
| new Class( | |
| name: 'E', | |
| supertype: d.asThisSupertype, | |
| mixedInType: a.asThisSupertype, | |
| implementedTypes: [b.asThisSupertype], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| _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, []); | |
| _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')], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var b = addClass( | |
| new Class( | |
| name: 'B', | |
| supertype: a.asThisSupertype, | |
| procedures: [newEmptyMethod('foo', isAbstract: true)], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var c = addClass( | |
| new Class( | |
| name: 'C', | |
| supertype: a.asThisSupertype, | |
| procedures: [newEmptyMethod('foo', isAbstract: true)], | |
| isAbstract: true, | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var d = addClass( | |
| new Class( | |
| name: 'D', | |
| supertype: b.asThisSupertype, | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var e = addClass( | |
| new Class( | |
| name: 'E', | |
| supertype: c.asThisSupertype, | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| _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::B.foo overrides test::A.foo']); | |
| _assertOverridePairs(c, ['test::C.foo overrides test::A.foo']); | |
| _assertOverridePairs(d, []); | |
| _assertOverridePairs(e, []); | |
| } | |
| /// 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')], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var b = addClass( | |
| new Class( | |
| name: 'B', | |
| supertype: a.asThisSupertype, | |
| procedures: [newEmptyMethod('foo', isAbstract: true)], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| _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::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')], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var b = addClass( | |
| new Class( | |
| name: 'B', | |
| supertype: a.asThisSupertype, | |
| procedures: [newEmptyMethod('foo')], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var c = addClass( | |
| new Class( | |
| name: 'C', | |
| supertype: b.asThisSupertype, | |
| procedures: [newEmptyMethod('bar')], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| _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')], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var b = addClass( | |
| new Class( | |
| name: 'B', | |
| supertype: a.asThisSupertype, | |
| procedures: [newEmptySetter('foo')], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var c = addClass( | |
| new Class( | |
| name: 'C', | |
| supertype: b.asThisSupertype, | |
| procedures: [newEmptySetter('bar')], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| _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.intNonNullableRawType; | |
| var bool = coreTypes.boolNonNullableRawType; | |
| var a = addGenericClass('A', ['T', 'U']); | |
| var bT = new TypeParameter( | |
| 'T', | |
| coreTypes.objectNullableRawType, | |
| const DynamicType(), | |
| ); | |
| var bTT = new TypeParameterType(bT, Nullability.undetermined); | |
| var b = addClass( | |
| new Class( | |
| name: 'B', | |
| typeParameters: new TypeParameterList(bT), | |
| supertype: new Supertype(a, new DartTypeList(bTT, bool)), | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var c = addClass( | |
| new Class( | |
| name: 'C', | |
| supertype: new Supertype(b, new DartTypeList(int)), | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| _assertTestLibraryText(''' | |
| class A<T? = dynamic, U? = dynamic> {} | |
| class B<T? = dynamic> 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, new DartTypeList(bTT, bool)), | |
| ); | |
| expect( | |
| hierarchy.getClassAsInstanceOf(c, b), | |
| new Supertype(b, new DartTypeList(int)), | |
| ); | |
| expect( | |
| hierarchy.getClassAsInstanceOf(c, a), | |
| new Supertype(a, new DartTypeList(int, bool)), | |
| ); | |
| } | |
| void test_getClassAsInstanceOf_generic_implements() { | |
| var int = coreTypes.intNonNullableRawType; | |
| var bool = coreTypes.boolNonNullableRawType; | |
| var a = addGenericClass('A', ['T', 'U']); | |
| var bT = new TypeParameter( | |
| 'T', | |
| coreTypes.objectNullableRawType, | |
| const DynamicType(), | |
| ); | |
| var bTT = new TypeParameterType(bT, Nullability.undetermined); | |
| var b = addClass( | |
| new Class( | |
| name: 'B', | |
| typeParameters: new TypeParameterList(bT), | |
| supertype: objectSuper, | |
| implementedTypes: [new Supertype(a, new DartTypeList(bTT, bool))], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var c = addClass( | |
| new Class( | |
| name: 'C', | |
| supertype: objectSuper, | |
| implementedTypes: [new Supertype(b, new DartTypeList(int))], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| _assertTestLibraryText(''' | |
| class A<T? = dynamic, U? = dynamic> {} | |
| class B<T? = dynamic> 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, new DartTypeList(bTT, bool)), | |
| ); | |
| expect( | |
| hierarchy.getClassAsInstanceOf(c, b), | |
| new Supertype(b, new DartTypeList(int)), | |
| ); | |
| expect( | |
| hierarchy.getClassAsInstanceOf(c, a), | |
| new Supertype(a, new DartTypeList(int, bool)), | |
| ); | |
| } | |
| void test_getClassAsInstanceOf_generic_with() { | |
| var int = coreTypes.intNonNullableRawType; | |
| var bool = coreTypes.boolNonNullableRawType; | |
| var a = addGenericClass('A', ['T', 'U']); | |
| var bT = new TypeParameter( | |
| 'T', | |
| coreTypes.objectNullableRawType, | |
| const DynamicType(), | |
| ); | |
| var bTT = new TypeParameterType(bT, Nullability.undetermined); | |
| var b = addClass( | |
| new Class( | |
| name: 'B', | |
| typeParameters: new TypeParameterList(bT), | |
| supertype: objectSuper, | |
| mixedInType: new Supertype(a, new DartTypeList(bTT, bool)), | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var c = addClass( | |
| new Class( | |
| name: 'C', | |
| supertype: objectSuper, | |
| mixedInType: new Supertype(b, new DartTypeList(int)), | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| _assertTestLibraryText(''' | |
| class A<T? = dynamic, U? = dynamic> {} | |
| class B<T? = dynamic> = 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, new DartTypeList(bTT, bool)), | |
| ); | |
| expect( | |
| hierarchy.getClassAsInstanceOf(c, b), | |
| new Supertype(b, new DartTypeList(int)), | |
| ); | |
| expect( | |
| hierarchy.getClassAsInstanceOf(c, a), | |
| new Supertype(a, new DartTypeList(int, bool)), | |
| ); | |
| } | |
| void test_getClassAsInstanceOf_notGeneric_extends() { | |
| var a = addClass( | |
| new Class(name: 'A', supertype: objectSuper, fileUri: library.fileUri), | |
| ); | |
| var b = addClass( | |
| new Class( | |
| name: 'B', | |
| supertype: a.asThisSupertype, | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var c = addClass( | |
| new Class( | |
| name: 'C', | |
| supertype: b.asThisSupertype, | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var z = addClass( | |
| new Class(name: 'Z', supertype: objectSuper, fileUri: library.fileUri), | |
| ); | |
| _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, fileUri: library.fileUri), | |
| ); | |
| var b = addClass( | |
| new Class(name: 'B', supertype: objectSuper, fileUri: library.fileUri), | |
| ); | |
| var c = addClass( | |
| new Class( | |
| name: 'C', | |
| supertype: objectSuper, | |
| implementedTypes: [a.asThisSupertype], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var d = addClass( | |
| new Class( | |
| name: 'D', | |
| supertype: objectSuper, | |
| implementedTypes: [c.asThisSupertype], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var e = addClass( | |
| new Class( | |
| name: 'D', | |
| supertype: a.asThisSupertype, | |
| implementedTypes: [b.asThisSupertype], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var z = addClass( | |
| new Class(name: 'Z', supertype: objectSuper, fileUri: library.fileUri), | |
| ); | |
| _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, fileUri: library.fileUri), | |
| ); | |
| var b = addClass( | |
| new Class( | |
| name: 'B', | |
| supertype: objectSuper, | |
| mixedInType: a.asThisSupertype, | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var z = addClass( | |
| new Class(name: 'Z', supertype: objectSuper, fileUri: library.fileUri), | |
| ); | |
| _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_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.mutable( | |
| new Name('nonFinalField'), | |
| fileUri: library.fileUri, | |
| ); | |
| var finalField = new Field.immutable( | |
| new Name('finalField'), | |
| isFinal: true, | |
| fileUri: library.fileUri, | |
| ); | |
| var a = addClass( | |
| new Class( | |
| isAbstract: true, | |
| name: 'A', | |
| supertype: objectSuper, | |
| fields: [nonFinalField, finalField], | |
| procedures: [ | |
| method, | |
| getter, | |
| setter, | |
| abstractMethod, | |
| abstractGetter, | |
| abstractSetter, | |
| ], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var b = addClass( | |
| new Class( | |
| isAbstract: true, | |
| name: 'B', | |
| supertype: a.asThisSupertype, | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| _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, isTrue); | |
| expect(hierarchy.getDeclaredMembers(b, setters: true).isEmpty, isTrue); | |
| } | |
| 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], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var b = addClass( | |
| new Class( | |
| name: 'B', | |
| supertype: a.asThisSupertype, | |
| procedures: [bMethod, bSetter], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var c = addClass( | |
| new Class( | |
| name: 'C', | |
| supertype: b.asThisSupertype, | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| _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], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var b = addClass( | |
| new Class( | |
| isAbstract: true, | |
| name: 'B', | |
| supertype: a.asThisSupertype, | |
| procedures: [bFoo, bBar], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var c = addClass( | |
| new Class( | |
| name: 'C', | |
| supertype: b.asThisSupertype, | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| _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], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var b = addClass( | |
| new Class( | |
| name: 'B', | |
| supertype: a.asThisSupertype, | |
| procedures: [bMethod, bSetter], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var c = addClass( | |
| new Class( | |
| name: 'C', | |
| supertype: b.asThisSupertype, | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| _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], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var b = addClass( | |
| new Class( | |
| name: 'B', | |
| supertype: objectSuper, | |
| implementedTypes: [a.asThisSupertype], | |
| procedures: [bMethod, bSetter], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var c = addClass( | |
| new Class( | |
| name: 'C', | |
| supertype: objectSuper, | |
| implementedTypes: [b.asThisSupertype], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| _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.mutable( | |
| new Name('nonFinalField'), | |
| fileUri: library.fileUri, | |
| ); | |
| var finalField = new Field.immutable( | |
| new Name('finalField'), | |
| isFinal: true, | |
| fileUri: library.fileUri, | |
| ); | |
| var a = addClass( | |
| new Class( | |
| isAbstract: true, | |
| name: 'A', | |
| supertype: objectSuper, | |
| fields: [nonFinalField, finalField], | |
| procedures: [ | |
| method, | |
| getter, | |
| setter, | |
| abstractMethod, | |
| abstractGetter, | |
| abstractSetter, | |
| ], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| _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.mutable( | |
| new Name('nonFinalField'), | |
| fileUri: library.fileUri, | |
| ); | |
| var finalField = new Field.immutable( | |
| new Name('finalField'), | |
| isFinal: true, | |
| fileUri: library.fileUri, | |
| ); | |
| var a = addClass( | |
| new Class( | |
| name: 'A', | |
| supertype: objectSuper, | |
| fields: [nonFinalField, finalField], | |
| procedures: [ | |
| method, | |
| getter, | |
| setter, | |
| abstractMethod, | |
| abstractGetter, | |
| abstractSetter, | |
| ], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var b = addClass( | |
| new Class( | |
| name: 'B', | |
| supertype: a.asThisSupertype, | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var c = addClass( | |
| new Class( | |
| isAbstract: true, | |
| name: 'C', | |
| supertype: objectSuper, | |
| implementedTypes: [a.asThisSupertype], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var d = addClass( | |
| new Class( | |
| name: 'D', | |
| supertype: objectSuper, | |
| mixedInType: a.asThisSupertype, | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| _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.mutable( | |
| new Name('nonFinalField'), | |
| fileUri: library.fileUri, | |
| ); | |
| var finalField_a = new Field.immutable( | |
| new Name('finalField'), | |
| isFinal: true, | |
| fileUri: library.fileUri, | |
| ); | |
| var method_b = newEmptyMethod('method'); | |
| var getter_b = newEmptyGetter('getter'); | |
| var setter_b = newEmptySetter('setter'); | |
| var nonFinalField_b = new Field.mutable( | |
| new Name('nonFinalField'), | |
| fileUri: library.fileUri, | |
| ); | |
| var finalField_b = new Field.immutable( | |
| new Name('finalField'), | |
| isFinal: true, | |
| fileUri: library.fileUri, | |
| ); | |
| var a = addClass( | |
| new Class( | |
| name: 'A', | |
| supertype: objectSuper, | |
| fields: [nonFinalField_a, finalField_a], | |
| procedures: [method_a, getter_a, setter_a], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var b = addClass( | |
| new Class( | |
| name: 'B', | |
| supertype: objectSuper, | |
| fields: [nonFinalField_b, finalField_b], | |
| procedures: [method_b, getter_b, setter_b], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var c = addClass( | |
| new Class( | |
| isAbstract: true, | |
| name: 'C', | |
| supertype: objectSuper, | |
| implementedTypes: [a.asThisSupertype, b.asThisSupertype], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| _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.mutable( | |
| new Name('nonFinalField'), | |
| fileUri: library.fileUri, | |
| ); | |
| var finalField_a = new Field.immutable( | |
| new Name('finalField'), | |
| isFinal: true, | |
| fileUri: library.fileUri, | |
| ); | |
| var method_b = newEmptyMethod('method'); | |
| var getter_b = newEmptyGetter('getter'); | |
| var setter_b = newEmptySetter('setter'); | |
| var nonFinalField_b = new Field.mutable( | |
| new Name('nonFinalField'), | |
| fileUri: library.fileUri, | |
| ); | |
| var finalField_b = new Field.immutable( | |
| new Name('finalField'), | |
| isFinal: true, | |
| fileUri: library.fileUri, | |
| ); | |
| 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, | |
| ], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var b = addClass( | |
| new Class( | |
| name: 'B', | |
| supertype: a.asThisSupertype, | |
| fields: [nonFinalField_b, finalField_b], | |
| procedures: [method_b, getter_b, setter_b], | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| _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, fileUri: library.fileUri), | |
| ); | |
| var b = addClass( | |
| new Class( | |
| name: 'B', | |
| supertype: a.asThisSupertype, | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| var c = addClass( | |
| new Class( | |
| name: 'C', | |
| supertype: b.asThisSupertype, | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| 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_getTypeAsInstanceOf_generic_extends() { | |
| var int = coreTypes.intNonNullableRawType; | |
| var bool = coreTypes.boolNonNullableRawType; | |
| var a = addGenericClass('A', ['T', 'U']); | |
| var bT = new TypeParameter( | |
| 'T', | |
| coreTypes.objectNullableRawType, | |
| const DynamicType(), | |
| ); | |
| var bTT = new TypeParameterType(bT, Nullability.undetermined); | |
| var b = addClass( | |
| new Class( | |
| name: 'B', | |
| typeParameters: new TypeParameterList(bT), | |
| supertype: new Supertype(a, new DartTypeList(bTT, bool)), | |
| fileUri: library.fileUri, | |
| ), | |
| ); | |
| _assertTestLibraryText(''' | |
| class A<T? = dynamic, U? = dynamic> {} | |
| class B<T? = dynamic> extends self::A<self::B::T%, core::bool> {} | |
| '''); | |
| var b_int = new InterfaceType( | |
| b, | |
| Nullability.nonNullable, | |
| new DartTypeList(int), | |
| ); | |
| expect( | |
| hierarchy.getInterfaceTypeAsInstanceOfClass(b_int, a), | |
| new InterfaceType( | |
| a, | |
| Nullability.nonNullable, | |
| new DartTypeList(int, bool), | |
| ), | |
| ); | |
| expect( | |
| hierarchy.getInterfaceTypeAsInstanceOfClass(b_int, objectClass), | |
| new InterfaceType(objectClass, Nullability.nonNullable), | |
| ); | |
| } | |
| void _assertOverridePairs(Class class_, List<String> expected) { | |
| List<String> overrideDescriptions = []; | |
| void callback( | |
| Member declaredMember, | |
| Member interfaceMember, | |
| bool isSetter, | |
| ) { | |
| var suffix = isSetter ? '=' : ''; | |
| String declaredMemberName = qualifiedMemberNameToString( | |
| declaredMember, | |
| includeLibraryName: true, | |
| ); | |
| String declaredName = '${declaredMemberName}$suffix'; | |
| String interfaceMemberName = qualifiedMemberNameToString( | |
| interfaceMember, | |
| includeLibraryName: true, | |
| ); | |
| String interfaceName = '${interfaceMemberName}$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) { | |
| _assertLibraryText(library, expectedText); | |
| } | |
| void _assertLibraryText(Library lib, String expectedText) { | |
| StringBuffer sb = new StringBuffer(); | |
| Printer printer = new Printer(sb); | |
| printer.writeLibraryFile(lib); | |
| 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); | |
| } | |
| } |