| // 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:front_end/src/incremental/combine.dart'; |
| import 'package:kernel/ast.dart'; |
| import 'package:kernel/core_types.dart'; |
| import 'package:kernel/testing/mock_sdk_program.dart'; |
| import 'package:test/test.dart'; |
| import 'package:test_reflective_loader/test_reflective_loader.dart'; |
| |
| main() { |
| defineReflectiveSuite(() { |
| defineReflectiveTests(CombineTest); |
| }); |
| } |
| |
| @reflectiveTest |
| class CombineTest { |
| Program sdk; |
| CoreTypes coreTypes; |
| |
| Supertype get objectSuper => coreTypes.objectClass.asThisSupertype; |
| |
| void setUp() { |
| sdk = createMockSdkProgram(); |
| coreTypes = new CoreTypes(sdk); |
| } |
| |
| void test_class_mergeLibrary_appendClass() { |
| var libraryA1 = _newLibrary('a'); |
| libraryA1.addClass(new Class(name: 'A', supertype: objectSuper)); |
| |
| var libraryA2 = _newLibrary('a'); |
| libraryA2.addClass(new Class(name: 'B', supertype: objectSuper)); |
| |
| var outline1 = _newOutline([libraryA1]); |
| var outline2 = _newOutline([libraryA2]); |
| |
| _runCombineTest([outline1, outline2], (result) { |
| var libraryA = _getLibrary(result.program, 'a'); |
| |
| var classA = _getClass(libraryA, 'A'); |
| expect(classA.members, isEmpty); |
| |
| var classB = _getClass(libraryA, 'B'); |
| expect(classB.members, isEmpty); |
| }); |
| } |
| |
| /// We test two cases of class declarations: |
| /// * When a class to merge is first time declared in the first library; |
| /// * When a class to merge is first time declared in the second library. |
| /// |
| /// With two cases of constructor declarations: |
| /// * Already defined, so references to it should be rewritten. |
| /// * First defined in this outline, so references to it can be kept as is. |
| /// |
| /// For each case we validate [DirectMethodInvocation], [MethodInvocation], |
| /// and [SuperMethodInvocation]. |
| void test_class_procedure_constructor() { |
| var nodeToName = <NamedNode, String>{}; |
| |
| var library1 = _newLibrary('test'); |
| var constructorA11 = _newConstructor('a1'); |
| var classA1 = new Class( |
| name: 'A', supertype: objectSuper, constructors: [constructorA11]); |
| library1.addClass(classA1); |
| nodeToName[classA1] = 'A1'; |
| nodeToName[constructorA11] = 'A11'; |
| |
| var library2 = _newLibrary('test'); |
| var constructorA12 = _newConstructor('a1'); |
| var constructorA22 = _newConstructor('a2'); |
| var constructorB11 = _newConstructor('b1'); |
| var classA2 = new Class( |
| name: 'A', |
| supertype: objectSuper, |
| constructors: [constructorA12, constructorA22]); |
| library2.addClass(classA2); |
| var classB1 = new Class( |
| name: 'B', supertype: objectSuper, constructors: [constructorB11]); |
| library2.addClass(classB1); |
| // Use 'A.a1' and 'A.a2' to validate later how they are rewritten. |
| library2.addProcedure(_newExpressionsProcedure([ |
| new ConstructorInvocation(constructorA12, new Arguments.empty()), |
| new ConstructorInvocation(constructorA22, new Arguments.empty()), |
| ], name: 'main2')); |
| library2.addClass(new Class( |
| name: 'S1', |
| supertype: classA2.asThisSupertype, |
| constructors: [ |
| new Constructor(new FunctionNode(new EmptyStatement()), |
| name: new Name('c1'), |
| initializers: [ |
| new SuperInitializer(constructorA12, new Arguments.empty()), |
| new SuperInitializer(constructorA22, new Arguments.empty()), |
| ]), |
| new Constructor(new FunctionNode(new EmptyStatement()), |
| name: new Name('c2'), |
| initializers: [ |
| new RedirectingInitializer( |
| constructorA12, new Arguments.empty()), |
| new RedirectingInitializer( |
| constructorA22, new Arguments.empty()), |
| ]), |
| ])); |
| nodeToName[classA2] = 'A2'; |
| nodeToName[constructorA12] = 'A12'; |
| nodeToName[constructorA22] = 'A22'; |
| nodeToName[constructorB11] = 'B11'; |
| nodeToName[classB1] = 'B1'; |
| nodeToName[constructorB11] = 'B11'; |
| |
| var library3 = _newLibrary('test'); |
| var constructorB12 = _newConstructor('b1'); |
| var constructorB22 = _newConstructor('b2'); |
| var classB2 = new Class( |
| name: 'B', |
| supertype: objectSuper, |
| constructors: [constructorB12, constructorB22]); |
| library3.addClass(classB2); |
| library3.addProcedure(_newExpressionsProcedure([ |
| new ConstructorInvocation(constructorB12, new Arguments.empty()), |
| new ConstructorInvocation(constructorB22, new Arguments.empty()), |
| ], name: 'main3')); |
| library3.addClass(new Class( |
| name: 'S2', |
| supertype: classB2.asThisSupertype, |
| constructors: [ |
| new Constructor(new FunctionNode(new EmptyStatement()), |
| name: new Name('c1'), |
| initializers: [ |
| new SuperInitializer(constructorB12, new Arguments.empty()), |
| new SuperInitializer(constructorB22, new Arguments.empty()), |
| ]), |
| new Constructor(new FunctionNode(new EmptyStatement()), |
| name: new Name('c2'), |
| initializers: [ |
| new RedirectingInitializer( |
| constructorB12, new Arguments.empty()), |
| new RedirectingInitializer( |
| constructorB22, new Arguments.empty()), |
| ]), |
| ])); |
| nodeToName[classB2] = 'B2'; |
| nodeToName[constructorB12] = 'B12'; |
| nodeToName[constructorB22] = 'B22'; |
| |
| var outline1 = _newOutline([library1]); |
| var outline2 = _newOutline([library2]); |
| var outline3 = _newOutline([library3]); |
| |
| expect(_getLibraryText(library1, nodeToName), r''' |
| class A[A1] { |
| constructor a1[A11](); |
| } |
| '''); |
| expect(_getLibraryText(library2, nodeToName), r''' |
| class A[A2] { |
| constructor a1[A12](); |
| constructor a2[A22](); |
| } |
| class B[B1] { |
| constructor b1[B11](); |
| } |
| class S1 extends A[A2] { |
| constructor c1() : |
| super[A12](), |
| super[A22](); |
| constructor c2() : |
| redirect[A12](), |
| redirect[A22](); |
| } |
| main2() { |
| ConstructorInvocation[A12](); |
| ConstructorInvocation[A22](); |
| } |
| '''); |
| expect(_getLibraryText(library3, nodeToName), r''' |
| class B[B2] { |
| constructor b1[B12](); |
| constructor b2[B22](); |
| } |
| class S2 extends B[B2] { |
| constructor c1() : |
| super[B12](), |
| super[B22](); |
| constructor c2() : |
| redirect[B12](), |
| redirect[B22](); |
| } |
| main3() { |
| ConstructorInvocation[B12](); |
| ConstructorInvocation[B22](); |
| } |
| '''); |
| |
| _runCombineTest([outline1, outline2, outline3], (result) { |
| var library = _getLibrary(result.program, 'test'); |
| expect(_getLibraryText(library, nodeToName), r''' |
| class A[A1] { |
| constructor a1[A11](); |
| constructor a2[A22](); |
| } |
| class B[B1] { |
| constructor b1[B11](); |
| constructor b2[B22](); |
| } |
| class S1 extends A[A1] { |
| constructor c1() : |
| super[A11](), |
| super[A22](); |
| constructor c2() : |
| redirect[A11](), |
| redirect[A22](); |
| } |
| class S2 extends B[B1] { |
| constructor c1() : |
| super[B11](), |
| super[B22](); |
| constructor c2() : |
| redirect[B11](), |
| redirect[B22](); |
| } |
| main2() { |
| ConstructorInvocation[A11](); |
| ConstructorInvocation[A22](); |
| } |
| main3() { |
| ConstructorInvocation[B11](); |
| ConstructorInvocation[B22](); |
| } |
| '''); |
| }); |
| } |
| |
| /// We test two cases of class declarations: |
| /// * When a class to merge is first time declared in the first library; |
| /// * When a class to merge is first time declared in the second library. |
| /// |
| /// With two cases of field declarations: |
| /// * Already defined, so references to it should be rewritten. |
| /// * First defined in this outline, so references to it can be kept as is. |
| /// |
| /// For each case we validate [DirectPropertyGet], [DirectPropertySet], |
| /// [PropertyGet], [PropertySet], [SuperPropertyGet], and [SuperPropertySet]. |
| void test_class_procedure_field() { |
| var nodeToName = <NamedNode, String>{}; |
| |
| var library1 = _newLibrary('test'); |
| var fieldA11 = _newField('a1'); |
| var classA1 = |
| new Class(name: 'A', supertype: objectSuper, fields: [fieldA11]); |
| library1.addClass(classA1); |
| nodeToName[classA1] = 'A1'; |
| nodeToName[fieldA11] = 'a11'; |
| |
| var library2 = _newLibrary('test'); |
| var fieldA12 = _newField('a1'); |
| var fieldA22 = _newField('a2'); |
| var fieldB11 = _newField('b1'); |
| var classA2 = new Class( |
| name: 'A', supertype: objectSuper, fields: [fieldA12, fieldA22]); |
| library2.addClass(classA2); |
| var classB1 = |
| new Class(name: 'B', supertype: objectSuper, fields: [fieldB11]); |
| library2.addClass(classB1); |
| // Use 'A.a1' and 'A.a2' to validate later how they are rewritten. |
| library2.addProcedure(_newExpressionsProcedure([ |
| new DirectPropertyGet(null, fieldA12), |
| new PropertyGet(null, null, fieldA12), |
| new DirectPropertySet(null, fieldA12, null), |
| new PropertySet(null, null, null, fieldA12), |
| new DirectPropertyGet(null, fieldA22), |
| new PropertyGet(null, null, fieldA22), |
| new DirectPropertySet(null, fieldA22, null), |
| new PropertySet(null, null, null, fieldA22), |
| ], name: 'main2')); |
| library2.addClass( |
| new Class(name: 'S1', supertype: classA2.asThisSupertype, procedures: [ |
| _newExpressionsProcedure([ |
| new SuperPropertyGet(null, fieldA12), |
| new SuperPropertySet(null, null, fieldA12), |
| new SuperPropertyGet(null, fieldA22), |
| new SuperPropertySet(null, null, fieldA22), |
| ], name: 'foo') |
| ])); |
| nodeToName[classA2] = 'A2'; |
| nodeToName[classB1] = 'B1'; |
| nodeToName[fieldA12] = 'a12'; |
| nodeToName[fieldA22] = 'a22'; |
| nodeToName[fieldB11] = 'b11'; |
| |
| var library3 = _newLibrary('test'); |
| var fieldB12 = _newField('b1'); |
| var fieldB22 = _newField('b2'); |
| var classB2 = new Class( |
| name: 'B', supertype: objectSuper, fields: [fieldB12, fieldB22]); |
| library3.addClass(classB2); |
| library3.addProcedure(_newExpressionsProcedure([ |
| new DirectPropertyGet(null, fieldB12), |
| new PropertyGet(null, null, fieldB12), |
| new DirectPropertyGet(null, fieldB22), |
| new PropertyGet(null, null, fieldB22), |
| ], name: 'main3')); |
| library3.addClass( |
| new Class(name: 'S2', supertype: classB2.asThisSupertype, procedures: [ |
| _newExpressionsProcedure([ |
| new SuperPropertyGet(null, fieldB12), |
| new SuperPropertySet(null, null, fieldB12), |
| new SuperPropertyGet(null, fieldB22), |
| new SuperPropertySet(null, null, fieldB22), |
| ], name: 'foo') |
| ])); |
| nodeToName[classB2] = 'B2'; |
| nodeToName[fieldB12] = 'b12'; |
| nodeToName[fieldB22] = 'b22'; |
| |
| var outline1 = _newOutline([library1]); |
| var outline2 = _newOutline([library2]); |
| var outline3 = _newOutline([library3]); |
| |
| expect(_getLibraryText(library1, nodeToName), r''' |
| class A[A1] { |
| var a1[a11]; |
| } |
| '''); |
| expect(_getLibraryText(library2, nodeToName), r''' |
| class A[A2] { |
| var a1[a12]; |
| var a2[a22]; |
| } |
| class B[B1] { |
| var b1[b11]; |
| } |
| class S1 extends A[A2] { |
| foo() { |
| SuperPropertyGet[a12](); |
| SuperPropertySet[a12](); |
| SuperPropertyGet[a22](); |
| SuperPropertySet[a22](); |
| } |
| } |
| main2() { |
| DirectPropertyGet[a12](); |
| PropertyGet[a12](); |
| DirectPropertySet[a12](); |
| PropertySet[a12](); |
| DirectPropertyGet[a22](); |
| PropertyGet[a22](); |
| DirectPropertySet[a22](); |
| PropertySet[a22](); |
| } |
| '''); |
| expect(_getLibraryText(library3, nodeToName), r''' |
| class B[B2] { |
| var b1[b12]; |
| var b2[b22]; |
| } |
| class S2 extends B[B2] { |
| foo() { |
| SuperPropertyGet[b12](); |
| SuperPropertySet[b12](); |
| SuperPropertyGet[b22](); |
| SuperPropertySet[b22](); |
| } |
| } |
| main3() { |
| DirectPropertyGet[b12](); |
| PropertyGet[b12](); |
| DirectPropertyGet[b22](); |
| PropertyGet[b22](); |
| } |
| '''); |
| |
| _runCombineTest([outline1, outline2, outline3], (result) { |
| var library = _getLibrary(result.program, 'test'); |
| expect(_getLibraryText(library, nodeToName), r''' |
| class A[A1] { |
| var a1[a11]; |
| var a2[a22]; |
| } |
| class B[B1] { |
| var b1[b11]; |
| var b2[b22]; |
| } |
| class S1 extends A[A1] { |
| foo() { |
| SuperPropertyGet[a11](); |
| SuperPropertySet[a11](); |
| SuperPropertyGet[a22](); |
| SuperPropertySet[a22](); |
| } |
| } |
| class S2 extends B[B1] { |
| foo() { |
| SuperPropertyGet[b11](); |
| SuperPropertySet[b11](); |
| SuperPropertyGet[b22](); |
| SuperPropertySet[b22](); |
| } |
| } |
| main2() { |
| DirectPropertyGet[a11](); |
| PropertyGet[a11](); |
| DirectPropertySet[a11](); |
| PropertySet[a11](); |
| DirectPropertyGet[a22](); |
| PropertyGet[a22](); |
| DirectPropertySet[a22](); |
| PropertySet[a22](); |
| } |
| main3() { |
| DirectPropertyGet[b11](); |
| PropertyGet[b11](); |
| DirectPropertyGet[b22](); |
| PropertyGet[b22](); |
| } |
| '''); |
| }); |
| } |
| |
| /// We test two cases of class declarations: |
| /// * When a class to merge is first time declared in the first library; |
| /// * When a class to merge is first time declared in the second library. |
| /// |
| /// With two cases of setter declarations: |
| /// * Already defined, so references to it should be rewritten. |
| /// * First defined in this outline, so references to it can be kept as is. |
| /// |
| /// For each case we validate [DirectPropertyGet], [PropertyGet], |
| /// and [SuperPropertyGet]. |
| void test_class_procedure_getter() { |
| var nodeToName = <NamedNode, String>{}; |
| |
| var library1 = _newLibrary('test'); |
| var procedureA11 = _newGetter('a1'); |
| var classA1 = new Class( |
| name: 'A', supertype: objectSuper, procedures: [procedureA11]); |
| library1.addClass(classA1); |
| nodeToName[classA1] = 'A1'; |
| nodeToName[procedureA11] = 'a11'; |
| |
| var library2 = _newLibrary('test'); |
| var procedureA12 = _newGetter('a1'); |
| var procedureA22 = _newGetter('a2'); |
| var procedureB11 = _newGetter('b1'); |
| var classA2 = new Class( |
| name: 'A', |
| supertype: objectSuper, |
| procedures: [procedureA12, procedureA22]); |
| library2.addClass(classA2); |
| var classB1 = new Class( |
| name: 'B', supertype: objectSuper, procedures: [procedureB11]); |
| library2.addClass(classB1); |
| // Use 'A.a1' and 'A.a2' to validate later how they are rewritten. |
| library2.addProcedure(_newExpressionsProcedure([ |
| new DirectPropertyGet(null, procedureA12), |
| new PropertyGet(null, null, procedureA12), |
| new DirectPropertyGet(null, procedureA22), |
| new PropertyGet(null, null, procedureA22), |
| ], name: 'main2')); |
| library2.addClass( |
| new Class(name: 'S1', supertype: classA2.asThisSupertype, procedures: [ |
| _newExpressionsProcedure([ |
| new SuperPropertyGet(null, procedureA12), |
| new SuperPropertyGet(null, procedureA22), |
| ], name: 'foo') |
| ])); |
| nodeToName[classA2] = 'A2'; |
| nodeToName[classB1] = 'B1'; |
| nodeToName[procedureA12] = 'a12'; |
| nodeToName[procedureA22] = 'a22'; |
| nodeToName[procedureB11] = 'b11'; |
| |
| var library3 = _newLibrary('test'); |
| var procedureB12 = _newGetter('b1'); |
| var procedureB22 = _newGetter('b2'); |
| var classB2 = new Class( |
| name: 'B', |
| supertype: objectSuper, |
| procedures: [procedureB12, procedureB22]); |
| library3.addClass(classB2); |
| library3.addProcedure(_newExpressionsProcedure([ |
| new DirectPropertyGet(null, procedureB12), |
| new PropertyGet(null, null, procedureB12), |
| ], name: 'main3')); |
| library3.addClass( |
| new Class(name: 'S2', supertype: classB2.asThisSupertype, procedures: [ |
| _newExpressionsProcedure([ |
| new SuperPropertyGet(null, procedureB12), |
| new SuperPropertyGet(null, procedureB22), |
| ], name: 'foo') |
| ])); |
| nodeToName[classB2] = 'B2'; |
| nodeToName[procedureB12] = 'b12'; |
| nodeToName[procedureB22] = 'b22'; |
| |
| var outline1 = _newOutline([library1]); |
| var outline2 = _newOutline([library2]); |
| var outline3 = _newOutline([library3]); |
| |
| expect(_getLibraryText(library1, nodeToName), r''' |
| class A[A1] { |
| get a1[a11] => 0; |
| } |
| '''); |
| expect(_getLibraryText(library2, nodeToName), r''' |
| class A[A2] { |
| get a1[a12] => 0; |
| get a2[a22] => 0; |
| } |
| class B[B1] { |
| get b1[b11] => 0; |
| } |
| class S1 extends A[A2] { |
| foo() { |
| SuperPropertyGet[a12](); |
| SuperPropertyGet[a22](); |
| } |
| } |
| main2() { |
| DirectPropertyGet[a12](); |
| PropertyGet[a12](); |
| DirectPropertyGet[a22](); |
| PropertyGet[a22](); |
| } |
| '''); |
| expect(_getLibraryText(library3, nodeToName), r''' |
| class B[B2] { |
| get b1[b12] => 0; |
| get b2[b22] => 0; |
| } |
| class S2 extends B[B2] { |
| foo() { |
| SuperPropertyGet[b12](); |
| SuperPropertyGet[b22](); |
| } |
| } |
| main3() { |
| DirectPropertyGet[b12](); |
| PropertyGet[b12](); |
| } |
| '''); |
| |
| _runCombineTest([outline1, outline2, outline3], (result) { |
| var library = _getLibrary(result.program, 'test'); |
| expect(_getLibraryText(library, nodeToName), r''' |
| class A[A1] { |
| get a1[a11] => 0; |
| get a2[a22] => 0; |
| } |
| class B[B1] { |
| get b1[b11] => 0; |
| get b2[b22] => 0; |
| } |
| class S1 extends A[A1] { |
| foo() { |
| SuperPropertyGet[a11](); |
| SuperPropertyGet[a22](); |
| } |
| } |
| class S2 extends B[B1] { |
| foo() { |
| SuperPropertyGet[b11](); |
| SuperPropertyGet[b22](); |
| } |
| } |
| main2() { |
| DirectPropertyGet[a11](); |
| PropertyGet[a11](); |
| DirectPropertyGet[a22](); |
| PropertyGet[a22](); |
| } |
| main3() { |
| DirectPropertyGet[b11](); |
| PropertyGet[b11](); |
| } |
| '''); |
| }); |
| } |
| |
| /// We test two cases of class declarations: |
| /// * When a class to merge is first time declared in the first library; |
| /// * When a class to merge is first time declared in the second library. |
| /// |
| /// With two cases of method declarations: |
| /// * Already defined, so references to it should be rewritten. |
| /// * First defined in this outline, so references to it can be kept as is. |
| /// |
| /// For each case we validate [DirectMethodInvocation], [MethodInvocation], |
| /// and [SuperMethodInvocation]. |
| void test_class_procedure_method() { |
| var nodeToName = <NamedNode, String>{}; |
| |
| var library1 = _newLibrary('test'); |
| var procedureA11 = _newMethod('a1'); |
| var classA1 = new Class( |
| name: 'A', supertype: objectSuper, procedures: [procedureA11]); |
| library1.addClass(classA1); |
| nodeToName[classA1] = 'A1'; |
| nodeToName[procedureA11] = 'a11'; |
| |
| var library2 = _newLibrary('test'); |
| var procedureA12 = _newMethod('a1'); |
| var procedureA22 = _newMethod('a2'); |
| var procedureB11 = _newMethod('b1'); |
| var classA2 = new Class( |
| name: 'A', |
| supertype: objectSuper, |
| procedures: [procedureA12, procedureA22]); |
| library2.addClass(classA2); |
| var classB1 = new Class( |
| name: 'B', supertype: objectSuper, procedures: [procedureB11]); |
| library2.addClass(classB1); |
| // Use 'A.a1' and 'A.a2' to validate later how they are rewritten. |
| library2.addProcedure(_newExpressionsProcedure([ |
| new DirectMethodInvocation(null, procedureA12, new Arguments.empty()), |
| new MethodInvocation(null, null, new Arguments.empty(), procedureA12), |
| new DirectMethodInvocation(null, procedureA22, new Arguments.empty()), |
| new MethodInvocation(null, null, new Arguments.empty(), procedureA22), |
| ], name: 'main2')); |
| library2.addClass( |
| new Class(name: 'S1', supertype: classA2.asThisSupertype, procedures: [ |
| _newExpressionsProcedure([ |
| new SuperMethodInvocation(null, null, procedureA12), |
| new SuperMethodInvocation(null, null, procedureA22), |
| ], name: 'foo') |
| ])); |
| nodeToName[classA2] = 'A2'; |
| nodeToName[classB1] = 'B1'; |
| nodeToName[procedureA12] = 'a12'; |
| nodeToName[procedureA22] = 'a22'; |
| nodeToName[procedureB11] = 'b11'; |
| |
| var library3 = _newLibrary('test'); |
| var procedureB12 = _newMethod('b1'); |
| var procedureB22 = _newMethod('b2'); |
| var classB2 = new Class( |
| name: 'B', |
| supertype: objectSuper, |
| procedures: [procedureB12, procedureB22]); |
| library3.addClass(classB2); |
| library3.addProcedure(_newExpressionsProcedure([ |
| new DirectMethodInvocation(null, procedureB12, new Arguments.empty()), |
| new MethodInvocation(null, null, new Arguments.empty(), procedureB12), |
| ], name: 'main3')); |
| library3.addClass( |
| new Class(name: 'S2', supertype: classB2.asThisSupertype, procedures: [ |
| _newExpressionsProcedure([ |
| new SuperMethodInvocation(null, null, procedureB12), |
| new SuperMethodInvocation(null, null, procedureB22), |
| ], name: 'foo') |
| ])); |
| nodeToName[classB2] = 'B2'; |
| nodeToName[procedureB12] = 'b12'; |
| nodeToName[procedureB22] = 'b22'; |
| |
| var outline1 = _newOutline([library1]); |
| var outline2 = _newOutline([library2]); |
| var outline3 = _newOutline([library3]); |
| |
| expect(_getLibraryText(library1, nodeToName), r''' |
| class A[A1] { |
| a1[a11](); |
| } |
| '''); |
| expect(_getLibraryText(library2, nodeToName), r''' |
| class A[A2] { |
| a1[a12](); |
| a2[a22](); |
| } |
| class B[B1] { |
| b1[b11](); |
| } |
| class S1 extends A[A2] { |
| foo() { |
| SuperMethodInvocation[a12](); |
| SuperMethodInvocation[a22](); |
| } |
| } |
| main2() { |
| DirectMethodInvocation[a12](); |
| MethodInvocation[a12](); |
| DirectMethodInvocation[a22](); |
| MethodInvocation[a22](); |
| } |
| '''); |
| expect(_getLibraryText(library3, nodeToName), r''' |
| class B[B2] { |
| b1[b12](); |
| b2[b22](); |
| } |
| class S2 extends B[B2] { |
| foo() { |
| SuperMethodInvocation[b12](); |
| SuperMethodInvocation[b22](); |
| } |
| } |
| main3() { |
| DirectMethodInvocation[b12](); |
| MethodInvocation[b12](); |
| } |
| '''); |
| |
| _runCombineTest([outline1, outline2, outline3], (result) { |
| var library = _getLibrary(result.program, 'test'); |
| expect(_getLibraryText(library, nodeToName), r''' |
| class A[A1] { |
| a1[a11](); |
| a2[a22](); |
| } |
| class B[B1] { |
| b1[b11](); |
| b2[b22](); |
| } |
| class S1 extends A[A1] { |
| foo() { |
| SuperMethodInvocation[a11](); |
| SuperMethodInvocation[a22](); |
| } |
| } |
| class S2 extends B[B1] { |
| foo() { |
| SuperMethodInvocation[b11](); |
| SuperMethodInvocation[b22](); |
| } |
| } |
| main2() { |
| DirectMethodInvocation[a11](); |
| MethodInvocation[a11](); |
| DirectMethodInvocation[a22](); |
| MethodInvocation[a22](); |
| } |
| main3() { |
| DirectMethodInvocation[b11](); |
| MethodInvocation[b11](); |
| } |
| '''); |
| }); |
| } |
| |
| /// We test two cases of class declarations: |
| /// * When a class to merge is first time declared in the first library; |
| /// * When a class to merge is first time declared in the second library. |
| /// |
| /// With two cases of setter declarations: |
| /// * Already defined, so references to it should be rewritten. |
| /// * First defined in this outline, so references to it can be kept as is. |
| /// |
| /// For each case we validate [DirectPropertySet], [PropertySet], |
| /// and [SuperPropertySet]. |
| void test_class_procedure_setter() { |
| var nodeToName = <NamedNode, String>{}; |
| |
| var library1 = _newLibrary('test'); |
| var procedureA11 = _newSetter('a1'); |
| var classA1 = new Class( |
| name: 'A', supertype: objectSuper, procedures: [procedureA11]); |
| library1.addClass(classA1); |
| nodeToName[classA1] = 'A1'; |
| nodeToName[procedureA11] = 'a11'; |
| |
| var library2 = _newLibrary('test'); |
| var procedureA12 = _newSetter('a1'); |
| var procedureA22 = _newSetter('a2'); |
| var procedureB11 = _newSetter('b1'); |
| var classA2 = new Class( |
| name: 'A', |
| supertype: objectSuper, |
| procedures: [procedureA12, procedureA22]); |
| library2.addClass(classA2); |
| var classB1 = new Class( |
| name: 'B', supertype: objectSuper, procedures: [procedureB11]); |
| library2.addClass(classB1); |
| // Use 'A.a1' and 'A.a2' to validate later how they are rewritten. |
| library2.addProcedure(_newExpressionsProcedure([ |
| new DirectPropertySet(null, procedureA12, new IntLiteral(0)), |
| new PropertySet(null, null, new IntLiteral(0), procedureA12), |
| new DirectPropertySet(null, procedureA22, new IntLiteral(0)), |
| new PropertySet(null, null, new IntLiteral(0), procedureA22), |
| ], name: 'main2')); |
| library2.addClass( |
| new Class(name: 'S1', supertype: classA2.asThisSupertype, procedures: [ |
| _newExpressionsProcedure([ |
| new SuperPropertySet(null, new IntLiteral(0), procedureA12), |
| new SuperPropertySet(null, new IntLiteral(0), procedureA22), |
| ], name: 'foo') |
| ])); |
| nodeToName[classA2] = 'A2'; |
| nodeToName[classB1] = 'B1'; |
| nodeToName[procedureA12] = 'a12'; |
| nodeToName[procedureA22] = 'a22'; |
| nodeToName[procedureB11] = 'b11'; |
| |
| var library3 = _newLibrary('test'); |
| var procedureB12 = _newSetter('b1'); |
| var procedureB22 = _newSetter('b2'); |
| var classB2 = new Class( |
| name: 'B', |
| supertype: objectSuper, |
| procedures: [procedureB12, procedureB22]); |
| library3.addClass(classB2); |
| library3.addProcedure(_newExpressionsProcedure([ |
| new DirectPropertySet(null, procedureB12, new IntLiteral(0)), |
| new PropertySet(null, null, new IntLiteral(0), procedureB12), |
| ], name: 'main3')); |
| library3.addClass( |
| new Class(name: 'S2', supertype: classB2.asThisSupertype, procedures: [ |
| _newExpressionsProcedure([ |
| new SuperPropertySet(null, new IntLiteral(0), procedureB12), |
| new SuperPropertySet(null, new IntLiteral(0), procedureB22), |
| ], name: 'foo') |
| ])); |
| nodeToName[classB2] = 'B2'; |
| nodeToName[procedureB12] = 'b12'; |
| nodeToName[procedureB22] = 'b22'; |
| |
| var outline1 = _newOutline([library1]); |
| var outline2 = _newOutline([library2]); |
| var outline3 = _newOutline([library3]); |
| |
| expect(_getLibraryText(library1, nodeToName), r''' |
| class A[A1] { |
| set a1[a11](); |
| } |
| '''); |
| expect(_getLibraryText(library2, nodeToName), r''' |
| class A[A2] { |
| set a1[a12](); |
| set a2[a22](); |
| } |
| class B[B1] { |
| set b1[b11](); |
| } |
| class S1 extends A[A2] { |
| foo() { |
| SuperPropertySet[a12](); |
| SuperPropertySet[a22](); |
| } |
| } |
| main2() { |
| DirectPropertySet[a12](); |
| PropertySet[a12](); |
| DirectPropertySet[a22](); |
| PropertySet[a22](); |
| } |
| '''); |
| expect(_getLibraryText(library3, nodeToName), r''' |
| class B[B2] { |
| set b1[b12](); |
| set b2[b22](); |
| } |
| class S2 extends B[B2] { |
| foo() { |
| SuperPropertySet[b12](); |
| SuperPropertySet[b22](); |
| } |
| } |
| main3() { |
| DirectPropertySet[b12](); |
| PropertySet[b12](); |
| } |
| '''); |
| |
| _runCombineTest([outline1, outline2, outline3], (result) { |
| var library = _getLibrary(result.program, 'test'); |
| expect(_getLibraryText(library, nodeToName), r''' |
| class A[A1] { |
| set a1[a11](); |
| set a2[a22](); |
| } |
| class B[B1] { |
| set b1[b11](); |
| set b2[b22](); |
| } |
| class S1 extends A[A1] { |
| foo() { |
| SuperPropertySet[a11](); |
| SuperPropertySet[a22](); |
| } |
| } |
| class S2 extends B[B1] { |
| foo() { |
| SuperPropertySet[b11](); |
| SuperPropertySet[b22](); |
| } |
| } |
| main2() { |
| DirectPropertySet[a11](); |
| PropertySet[a11](); |
| DirectPropertySet[a22](); |
| PropertySet[a22](); |
| } |
| main3() { |
| DirectPropertySet[b11](); |
| PropertySet[b11](); |
| } |
| '''); |
| }); |
| } |
| |
| void test_class_typeParameter_updateReference() { |
| var nodeToName = <TreeNode, String>{}; |
| |
| var library1 = _newLibrary('test'); |
| var typeParameterT1 = _newTypeParameter('T'); |
| var fieldA11 = |
| _newField('a1', type: new TypeParameterType(typeParameterT1)); |
| var classA1 = new Class( |
| name: 'A', |
| typeParameters: [typeParameterT1], |
| supertype: objectSuper, |
| fields: [fieldA11]); |
| library1.addClass(classA1); |
| nodeToName[typeParameterT1] = 'T1'; |
| nodeToName[classA1] = 'A1'; |
| nodeToName[fieldA11] = 'a11'; |
| |
| var library2 = _newLibrary('test'); |
| var typeParameterT2 = _newTypeParameter('T'); |
| var fieldA12 = |
| _newField('a1', type: new TypeParameterType(typeParameterT2)); |
| var fieldA22 = |
| _newField('a2', type: new TypeParameterType(typeParameterT2)); |
| var classA2 = new Class( |
| name: 'A', |
| typeParameters: [typeParameterT2], |
| supertype: objectSuper, |
| fields: [fieldA12, fieldA22]); |
| nodeToName[typeParameterT2] = 'T2'; |
| library2.addClass(classA2); |
| nodeToName[classA2] = 'A2'; |
| nodeToName[fieldA12] = 'a12'; |
| nodeToName[fieldA22] = 'a22'; |
| |
| var outline1 = _newOutline([library1]); |
| var outline2 = _newOutline([library2]); |
| |
| expect(_getLibraryText(library1, nodeToName), r''' |
| class A[A1]<T[T1]> { |
| T[T1] a1[a11]; |
| } |
| '''); |
| expect(_getLibraryText(library2, nodeToName), r''' |
| class A[A2]<T[T2]> { |
| T[T2] a1[a12]; |
| T[T2] a2[a22]; |
| } |
| '''); |
| |
| _runCombineTest([outline1, outline2], (result) { |
| var library = _getLibrary(result.program, 'test'); |
| expect(_getLibraryText(library, nodeToName), r''' |
| class A[A1]<T[T1]> { |
| T[T1] a1[a11]; |
| T[T1] a2[a22]; |
| } |
| '''); |
| }); |
| } |
| |
| void test_class_updateReferences() { |
| var nodeToName = <TreeNode, String>{}; |
| |
| var library1 = _newLibrary('test'); |
| var classA1 = new Class(name: 'A', supertype: objectSuper); |
| library1.addClass(classA1); |
| nodeToName[classA1] = 'A1'; |
| |
| var library2 = _newLibrary('test'); |
| var classA2 = new Class(name: 'A', supertype: objectSuper); |
| var classB1 = new Class(name: 'B1', supertype: new Supertype(classA2, [])); |
| var classB2 = new Class( |
| name: 'B2', |
| supertype: objectSuper, |
| implementedTypes: [new Supertype(classA2, [])]); |
| var classB3 = new Class( |
| name: 'B3', |
| supertype: objectSuper, |
| mixedInType: new Supertype(classA2, [])); |
| var typeParameterT1 = new TypeParameter('T', new InterfaceType(classA2)); |
| var classB4 = new Class( |
| name: 'B4', supertype: objectSuper, typeParameters: [typeParameterT1]); |
| library2.addClass(classA2); |
| library2.addClass(classB1); |
| library2.addClass(classB2); |
| library2.addClass(classB3); |
| library2.addClass(classB4); |
| nodeToName[classA2] = 'A2'; |
| nodeToName[classB1] = 'B1'; |
| nodeToName[classB2] = 'B2'; |
| nodeToName[classB3] = 'B3'; |
| nodeToName[classB4] = 'B4'; |
| nodeToName[typeParameterT1] = 'T'; |
| |
| var outline1 = _newOutline([library1]); |
| var outline2 = _newOutline([library2]); |
| |
| expect(_getLibraryText(library1, nodeToName), r''' |
| class A[A1] {} |
| '''); |
| expect(_getLibraryText(library2, nodeToName), r''' |
| class A[A2] {} |
| class B1[B1] extends A[A2] {} |
| class B2[B2] implements A[A2] {} |
| class B3[B3] with A[A2] {} |
| class B4[B4]<T[T] extends A[A2]> {} |
| '''); |
| |
| _runCombineTest([outline1, outline2], (result) { |
| var library = _getLibrary(result.program, 'test'); |
| expect(_getLibraryText(library, nodeToName), r''' |
| class A[A1] {} |
| class B1[B1] extends A[A1] {} |
| class B2[B2] implements A[A1] {} |
| class B3[B3] with A[A1] {} |
| class B4[B4]<T[T] extends A[A1]> {} |
| '''); |
| }); |
| } |
| |
| void test_field() { |
| var libraryA1 = _newLibrary('a'); |
| libraryA1.addField(_newField('A')); |
| |
| var libraryA2 = _newLibrary('a'); |
| libraryA2.addField(_newField('B')); |
| |
| var outline1 = _newOutline([libraryA1]); |
| var outline2 = _newOutline([libraryA2]); |
| |
| _runCombineTest([outline1, outline2], (result) { |
| var libraryA = _getLibrary(result.program, 'a'); |
| _getField(libraryA, 'A'); |
| _getField(libraryA, 'B'); |
| }); |
| } |
| |
| void test_field_skipDuplicate() { |
| var libraryA1 = _newLibrary('a'); |
| libraryA1.addField(_newField('A')); |
| libraryA1.addField(_newField('B')); |
| |
| var libraryA2 = _newLibrary('a'); |
| libraryA2.addField(_newField('A')); |
| libraryA2.addField(_newField('C')); |
| |
| var outline1 = _newOutline([libraryA1]); |
| var outline2 = _newOutline([libraryA2]); |
| |
| _runCombineTest([outline1, outline2], (result) { |
| var libraryA = _getLibrary(result.program, 'a'); |
| _getField(libraryA, 'A'); |
| _getField(libraryA, 'B'); |
| _getField(libraryA, 'C'); |
| }); |
| } |
| |
| void test_field_updateReferences() { |
| var libraryA1 = _newLibrary('a'); |
| var fieldA1A = _newField('A'); |
| libraryA1.addField(fieldA1A); |
| |
| var libraryA2 = _newLibrary('a'); |
| var fieldA2A = _newField('A'); |
| libraryA2.addField(fieldA2A); |
| |
| var libraryB = _newLibrary('b'); |
| libraryB.addProcedure(_newExpressionsProcedure([ |
| new StaticGet(fieldA2A), |
| new StaticSet(fieldA2A, new IntLiteral(0)), |
| ])); |
| |
| var outline1 = _newOutline([libraryA1]); |
| var outline2 = _newOutline([libraryA2, libraryB]); |
| |
| _runCombineTest([outline1, outline2], (result) { |
| var libraryA = _getLibrary(result.program, 'a'); |
| _getField(libraryA, 'A'); |
| |
| var libraryB = _getLibrary(result.program, 'b'); |
| var main = _getProcedure(libraryB, 'main', '@methods'); |
| expect((_getProcedureExpression(main, 0) as StaticGet).targetReference, |
| same(fieldA1A.reference)); |
| expect((_getProcedureExpression(main, 1) as StaticSet).targetReference, |
| same(fieldA1A.reference)); |
| }); |
| } |
| |
| void test_library_additionalExports() { |
| Map<String, Reference> addLibraryDeclarations(Library library) { |
| var A = new Class(name: 'A'); |
| var B = _newField('B'); |
| var C = _newMethod('C'); |
| var D = _newGetter('D'); |
| var E = _newSetter('E'); |
| library.addClass(A); |
| library.addField(B); |
| library.addProcedure(C); |
| library.addProcedure(D); |
| library.addProcedure(E); |
| return { |
| 'A': A.reference, |
| 'B': B.reference, |
| 'C': C.reference, |
| 'D': D.reference, |
| 'E': E.reference |
| }; |
| } |
| |
| var libraryA1 = _newLibrary('a'); |
| var declarations = addLibraryDeclarations(libraryA1); |
| |
| var libraryA2 = _newLibrary('a'); |
| var declarations2 = addLibraryDeclarations(libraryA2); |
| |
| var libraryB = _newLibrary('b'); |
| libraryB.additionalExports.addAll(declarations2.values); |
| |
| var outline1 = _newOutline([libraryA1]); |
| var outline2 = _newOutline([libraryA2, libraryB]); |
| |
| _runCombineTest([outline1, outline2], (result) { |
| var libraryA = _getLibrary(result.program, 'a'); |
| var libraryB = _getLibrary(result.program, 'b'); |
| expect(libraryB.additionalExports, hasLength(declarations.length)); |
| for (var declaration in _getLibraryDeclarations(libraryA)) { |
| String name = declaration.canonicalName.name; |
| Reference reference = declaration.reference; |
| expect(declarations[name], same(reference)); |
| expect(libraryB.additionalExports, contains(reference)); |
| } |
| }); |
| } |
| |
| void test_library_additionalExports_externalVersionFirst() { |
| // As if outline for "a" uses "b" (which is external). |
| // And we first see the external "b", without addition exports. |
| var libraryA = _newLibrary('a'); |
| var libraryB1 = _newLibrary('b'); |
| |
| // As if outline for "b" exports C1 and C2 from "c". |
| // So, we see "b" with exports only as the second library. |
| var libraryB2 = _newLibrary('b'); |
| var libraryC = _newLibrary('c'); |
| var C1 = new Class(name: 'C1'); |
| var C2 = new Class(name: 'C2'); |
| libraryC.addClass(C1); |
| libraryC.addClass(C2); |
| libraryB2.additionalExports.add(C1.reference); |
| libraryB2.additionalExports.add(C2.reference); |
| |
| var outline1 = _newOutline([libraryA, libraryB1]); |
| var outline2 = _newOutline([libraryB2, libraryC]); |
| |
| _runCombineTest([outline1, outline2], (result) { |
| var libraryB = _getLibrary(result.program, 'b'); |
| List<Reference> exports = libraryB.additionalExports; |
| expect(exports, unorderedEquals([C1.reference, C2.reference])); |
| }); |
| } |
| |
| void test_library_replaceReference() { |
| var libraryA1 = _newLibrary('a'); |
| |
| var libraryA2 = _newLibrary('a'); |
| |
| var libraryB = _newLibrary('b'); |
| libraryB.dependencies.add(new LibraryDependency.import(libraryA2)); |
| |
| var outline1 = _newOutline([libraryA1]); |
| var outline2 = _newOutline([libraryA2, libraryB]); |
| |
| _runCombineTest([outline1, outline2], (result) { |
| var libraryA = _getLibrary(result.program, 'a'); |
| |
| var libraryB = _getLibrary(result.program, 'b'); |
| expect(libraryB.dependencies, hasLength(1)); |
| expect(libraryB.dependencies[0].targetLibrary, libraryA); |
| }); |
| } |
| |
| void test_procedure_getter() { |
| var libraryA1 = _newLibrary('a'); |
| libraryA1.addProcedure(_newGetter('A')); |
| |
| var libraryA2 = _newLibrary('a'); |
| libraryA2.addProcedure(_newGetter('B')); |
| |
| var outline1 = _newOutline([libraryA1]); |
| var outline2 = _newOutline([libraryA2]); |
| |
| _runCombineTest([outline1, outline2], (result) { |
| var libraryA = _getLibrary(result.program, 'a'); |
| _getProcedure(libraryA, 'A', '@getters'); |
| _getProcedure(libraryA, 'B', '@getters'); |
| }); |
| } |
| |
| void test_procedure_getter_skipDuplicate() { |
| var libraryA1 = _newLibrary('a'); |
| libraryA1.addProcedure(_newGetter('A')); |
| libraryA1.addProcedure(_newGetter('B')); |
| |
| var libraryA2 = _newLibrary('a'); |
| libraryA2.addProcedure(_newGetter('A')); |
| libraryA2.addProcedure(_newGetter('C')); |
| |
| var outline1 = _newOutline([libraryA1]); |
| var outline2 = _newOutline([libraryA2]); |
| |
| _runCombineTest([outline1, outline2], (result) { |
| var libraryA = _getLibrary(result.program, 'a'); |
| _getProcedure(libraryA, 'A', '@getters'); |
| _getProcedure(libraryA, 'B', '@getters'); |
| _getProcedure(libraryA, 'C', '@getters'); |
| }); |
| } |
| |
| void test_procedure_getter_updateReferences() { |
| var libraryA1 = _newLibrary('a'); |
| var procedureA1A = _newGetter('A'); |
| libraryA1.addProcedure(procedureA1A); |
| |
| var libraryA2 = _newLibrary('a'); |
| var procedureA2A = _newGetter('A'); |
| libraryA2.addProcedure(procedureA2A); |
| |
| var libraryB = _newLibrary('b'); |
| libraryB.addProcedure(_newExpressionsProcedure([ |
| new StaticGet(procedureA2A), |
| ])); |
| |
| var outline1 = _newOutline([libraryA1]); |
| var outline2 = _newOutline([libraryA2, libraryB]); |
| |
| _runCombineTest([outline1, outline2], (result) { |
| var libraryA = _getLibrary(result.program, 'a'); |
| _getProcedure(libraryA, 'A', '@getters'); |
| |
| var libraryB = _getLibrary(result.program, 'b'); |
| var main = _getProcedure(libraryB, 'main', '@methods'); |
| expect((_getProcedureExpression(main, 0) as StaticGet).targetReference, |
| same(procedureA1A.reference)); |
| }); |
| } |
| |
| void test_procedure_method() { |
| var libraryA1 = _newLibrary('a'); |
| libraryA1.addProcedure(_newMethod('A')); |
| |
| var libraryA2 = _newLibrary('a'); |
| libraryA2.addProcedure(_newMethod('B')); |
| |
| var outline1 = _newOutline([libraryA1]); |
| var outline2 = _newOutline([libraryA2]); |
| |
| _runCombineTest([outline1, outline2], (result) { |
| var libraryA = _getLibrary(result.program, 'a'); |
| _getProcedure(libraryA, 'A', '@methods'); |
| _getProcedure(libraryA, 'B', '@methods'); |
| }); |
| } |
| |
| void test_procedure_method_private_updateReferences() { |
| var libraryA1 = _newLibrary('a'); |
| var procedureA1A = _newMethod('_A', libraryForPrivate: libraryA1); |
| libraryA1.addProcedure(procedureA1A); |
| |
| var libraryA2 = _newLibrary('a'); |
| var procedureA2A = _newMethod('_A', libraryForPrivate: libraryA2); |
| libraryA2.addProcedure(procedureA2A); |
| |
| var libraryB = _newLibrary('b'); |
| libraryB.addProcedure(_newExpressionsProcedure([ |
| new StaticInvocation(procedureA2A, new Arguments.empty()), |
| ])); |
| |
| var outline1 = _newOutline([libraryA1]); |
| var outline2 = _newOutline([libraryA2, libraryB]); |
| |
| _runCombineTest([outline1, outline2], (result) { |
| var libraryA = _getLibrary(result.program, 'a'); |
| _getProcedure(libraryA, '_A', '@methods'); |
| |
| var libraryB = _getLibrary(result.program, 'b'); |
| var main = _getProcedure(libraryB, 'main', '@methods'); |
| expect( |
| (_getProcedureExpression(main, 0) as StaticInvocation) |
| .targetReference, |
| same(procedureA1A.reference)); |
| }); |
| } |
| |
| void test_procedure_method_skipDuplicate() { |
| var libraryA1 = _newLibrary('a'); |
| libraryA1.addProcedure(_newMethod('A')); |
| libraryA1.addProcedure(_newMethod('B')); |
| |
| var libraryA2 = _newLibrary('a'); |
| libraryA2.addProcedure(_newMethod('A')); |
| libraryA2.addProcedure(_newMethod('C')); |
| |
| var outline1 = _newOutline([libraryA1]); |
| var outline2 = _newOutline([libraryA2]); |
| |
| _runCombineTest([outline1, outline2], (result) { |
| var libraryA = _getLibrary(result.program, 'a'); |
| _getProcedure(libraryA, 'A', '@methods'); |
| _getProcedure(libraryA, 'B', '@methods'); |
| _getProcedure(libraryA, 'C', '@methods'); |
| }); |
| } |
| |
| void test_procedure_method_updateReferences() { |
| var libraryA1 = _newLibrary('a'); |
| var procedureA1A = _newMethod('A'); |
| libraryA1.addProcedure(procedureA1A); |
| |
| var libraryA2 = _newLibrary('a'); |
| var procedureA2A = _newMethod('A'); |
| libraryA2.addProcedure(procedureA2A); |
| |
| var libraryB = _newLibrary('b'); |
| libraryB.addProcedure(_newExpressionsProcedure([ |
| new StaticInvocation(procedureA2A, new Arguments.empty()), |
| ])); |
| |
| var outline1 = _newOutline([libraryA1]); |
| var outline2 = _newOutline([libraryA2, libraryB]); |
| |
| _runCombineTest([outline1, outline2], (result) { |
| var libraryA = _getLibrary(result.program, 'a'); |
| _getProcedure(libraryA, 'A', '@methods'); |
| |
| var libraryB = _getLibrary(result.program, 'b'); |
| var main = _getProcedure(libraryB, 'main', '@methods'); |
| expect( |
| (_getProcedureExpression(main, 0) as StaticInvocation) |
| .targetReference, |
| same(procedureA1A.reference)); |
| }); |
| } |
| |
| void test_procedure_setter() { |
| var libraryA1 = _newLibrary('a'); |
| libraryA1.addProcedure(_newSetter('A')); |
| |
| var libraryA2 = _newLibrary('a'); |
| libraryA2.addProcedure(_newSetter('B')); |
| |
| var outline1 = _newOutline([libraryA1]); |
| var outline2 = _newOutline([libraryA2]); |
| |
| _runCombineTest([outline1, outline2], (result) { |
| var libraryA = _getLibrary(result.program, 'a'); |
| _getProcedure(libraryA, 'A', '@setters'); |
| _getProcedure(libraryA, 'B', '@setters'); |
| }); |
| } |
| |
| void test_procedure_setter_skipDuplicate() { |
| var libraryA1 = _newLibrary('a'); |
| libraryA1.addProcedure(_newSetter('A')); |
| libraryA1.addProcedure(_newSetter('B')); |
| |
| var libraryA2 = _newLibrary('a'); |
| libraryA2.addProcedure(_newSetter('A')); |
| libraryA2.addProcedure(_newSetter('C')); |
| |
| var outline1 = _newOutline([libraryA1]); |
| var outline2 = _newOutline([libraryA2]); |
| |
| _runCombineTest([outline1, outline2], (result) { |
| var libraryA = _getLibrary(result.program, 'a'); |
| _getProcedure(libraryA, 'A', '@setters'); |
| _getProcedure(libraryA, 'B', '@setters'); |
| _getProcedure(libraryA, 'C', '@setters'); |
| }); |
| } |
| |
| void test_procedure_setter_updateReferences() { |
| var libraryA1 = _newLibrary('a'); |
| var procedureA1A = _newSetter('A'); |
| libraryA1.addProcedure(procedureA1A); |
| |
| var libraryA2 = _newLibrary('a'); |
| var procedureA2A = _newSetter('A'); |
| libraryA2.addProcedure(procedureA2A); |
| |
| var libraryB = _newLibrary('b'); |
| libraryB.addProcedure(_newExpressionsProcedure([ |
| new StaticSet(procedureA2A, new IntLiteral(0)), |
| ])); |
| |
| var outline1 = _newOutline([libraryA1]); |
| var outline2 = _newOutline([libraryA2, libraryB]); |
| |
| _runCombineTest([outline1, outline2], (result) { |
| var libraryA = _getLibrary(result.program, 'a'); |
| _getProcedure(libraryA, 'A', '@setters'); |
| |
| var libraryB = _getLibrary(result.program, 'b'); |
| var main = _getProcedure(libraryB, 'main', '@methods'); |
| expect((_getProcedureExpression(main, 0) as StaticSet).targetReference, |
| same(procedureA1A.reference)); |
| }); |
| } |
| |
| void test_undo_twice() { |
| var libraryA1 = _newLibrary('a'); |
| libraryA1.addField(_newField('A')); |
| |
| var libraryA2 = _newLibrary('a'); |
| libraryA2.addField(_newField('B')); |
| |
| var outline1 = _newOutline([libraryA1]); |
| var outline2 = _newOutline([libraryA2]); |
| |
| var result = combine([outline1, outline2]); |
| result.undo(); |
| expect(() => result.undo(), throwsStateError); |
| } |
| |
| /// Get a single [Class] with the given [name]. |
| /// Throw if there is not exactly one. |
| Class _getClass(Library library, String name) { |
| var results = library.classes.where((class_) => class_.name == name); |
| expect(results, hasLength(1), reason: 'Expected only one: $name'); |
| Class result = results.first; |
| expect(result.parent, library); |
| expect(result.canonicalName.parent, library.canonicalName); |
| return result; |
| } |
| |
| /// Get a single [Field] with the given [name]. |
| /// Throw if there is not exactly one. |
| Field _getField(NamedNode parent, String name) { |
| List<Field> fields; |
| if (parent is Library) { |
| fields = parent.fields; |
| } else if (parent is Class) { |
| fields = parent.fields; |
| } else { |
| throw new ArgumentError('Only Library or Class expected'); |
| } |
| |
| var results = fields.where((field) => field.name.name == name); |
| expect(results, hasLength(1), reason: 'Expected only one: $name'); |
| Field result = results.first; |
| expect(result.parent, parent); |
| var parentName = parent.canonicalName.getChild('@fields'); |
| expect(result.canonicalName.parent, parentName); |
| return result; |
| } |
| |
| /// Get a single [Library] with the given [name]. |
| /// Throw if there is not exactly one. |
| Library _getLibrary(Program program, String name) { |
| var results = program.libraries.where((library) => library.name == name); |
| expect(results, hasLength(1), reason: 'Expected only one: $name'); |
| var result = results.first; |
| expect(result.parent, program); |
| expect(result.canonicalName.parent, program.root); |
| return result; |
| } |
| |
| /// Get a single [Procedure] with the given [name]. |
| /// Throw if there is not exactly one. |
| Procedure _getProcedure(NamedNode parent, String name, String prefixName) { |
| Library enclosingLibrary; |
| List<Procedure> procedures; |
| if (parent is Library) { |
| enclosingLibrary = parent; |
| procedures = parent.procedures; |
| } else if (parent is Class) { |
| enclosingLibrary = parent.enclosingLibrary; |
| procedures = parent.procedures; |
| } else { |
| throw new ArgumentError('Only Library or Class expected'); |
| } |
| |
| Iterable<Procedure> results = |
| procedures.where((procedure) => procedure.name.name == name); |
| expect(results, hasLength(1), reason: 'Expected only one: $name'); |
| Procedure result = results.first; |
| expect(result.parent, parent); |
| |
| var parentName = parent.canonicalName.getChild(prefixName); |
| if (name.startsWith('_')) { |
| parentName = parentName.getChildFromUri(enclosingLibrary.importUri); |
| } |
| expect(result.canonicalName.parent, parentName); |
| |
| return result; |
| } |
| |
| /// Return the [Expression] in the [index]th statement of the [procedure]'s |
| /// block body. |
| Expression _getProcedureExpression(Procedure procedure, int index) { |
| Block mainBlock = procedure.function.body; |
| ExpressionStatement statement = mainBlock.statements[index]; |
| return statement.expression; |
| } |
| |
| Constructor _newConstructor(String name, {Statement body}) { |
| body ??= new EmptyStatement(); |
| return new Constructor(new FunctionNode(body), name: new Name(name)); |
| } |
| |
| Procedure _newExpressionsProcedure(List<Expression> expressions, |
| {String name: 'main'}) { |
| var statements = |
| expressions.map((e) => new ExpressionStatement(e)).toList(); |
| return new Procedure(new Name(name), ProcedureKind.Method, |
| new FunctionNode(new Block(statements))); |
| } |
| |
| Field _newField(String name, {DartType type}) { |
| type ??= const DynamicType(); |
| return new Field(new Name(name), type: type); |
| } |
| |
| Procedure _newGetter(String name) { |
| return new Procedure(new Name(name), ProcedureKind.Getter, |
| new FunctionNode(new ExpressionStatement(new IntLiteral((0))))); |
| } |
| |
| Library _newLibrary(String name) { |
| var uri = Uri.parse('org-dartlang:///$name.dart'); |
| return new Library(uri, name: name); |
| } |
| |
| Procedure _newMethod(String name, |
| {Statement body, Library libraryForPrivate}) { |
| body ??= new EmptyStatement(); |
| return new Procedure(new Name(name, libraryForPrivate), |
| ProcedureKind.Method, new FunctionNode(body)); |
| } |
| |
| Program _newOutline(List<Library> libraries) { |
| var outline = new Program(libraries: libraries); |
| outline.computeCanonicalNames(); |
| return outline; |
| } |
| |
| Procedure _newSetter(String name) { |
| return new Procedure( |
| new Name(name), |
| ProcedureKind.Setter, |
| new FunctionNode(new EmptyStatement(), |
| positionalParameters: [new VariableDeclaration('_')])); |
| } |
| |
| TypeParameter _newTypeParameter(String name) { |
| var bound = new InterfaceType(coreTypes.objectClass); |
| return new TypeParameter(name, bound); |
| } |
| |
| void _runCombineTest( |
| List<Program> outlines, void checkResult(CombineResult result)) { |
| // Store the original state. |
| var states = <Program, _OutlineState>{}; |
| for (var outline in outlines) { |
| states[outline] = new _OutlineState(outline); |
| } |
| |
| // Combine the outlines and check the result. |
| var result = combine(outlines); |
| checkResult(result); |
| |
| // Undo and verify that the state is the same as the original. |
| result.undo(); |
| states.forEach((outline, state) { |
| state.verifySame(); |
| }); |
| } |
| |
| /// Produces all declarations of the [library]. |
| static Iterable<NamedNode> _getLibraryDeclarations(Library library) sync* { |
| yield* library.classes; |
| yield* library.procedures; |
| yield* library.fields; |
| } |
| |
| /// Return the text presentation of the [library] that is not a normal Kernel |
| /// AST text, but includes portions that we want to test - declarations |
| /// and references. The map [nodeToName] must have entries for all |
| /// referenced nodes, other declarations are optional. |
| static String _getLibraryText( |
| Library library, Map<TreeNode, String> nodeToName) { |
| var buffer = new StringBuffer(); |
| |
| String getNodeName(TreeNode node, {bool required: false}) { |
| String name = nodeToName[node]; |
| if (name != null) { |
| return '[$name]'; |
| } else { |
| if (required) { |
| fail('The name is required for (${node.runtimeType}) $node'); |
| } |
| return ''; |
| } |
| } |
| |
| void writeType(DartType type) { |
| if (type is InterfaceType) { |
| String name = getNodeName(type.classNode); |
| buffer.write('${type.classNode.name}$name'); |
| } else if (type is TypeParameterType) { |
| String name = getNodeName(type.parameter); |
| buffer.write('${type.parameter.name}$name'); |
| } else { |
| throw new UnimplementedError('(${type.runtimeType}) $type'); |
| } |
| } |
| |
| void writeStatement(Statement node, String indent) { |
| if (node is ExpressionStatement) { |
| Expression expression = node.expression; |
| String prefix = expression.runtimeType.toString(); |
| Member target; |
| if (expression is ConstructorInvocation) { |
| target = expression.target; |
| } else if (expression is DirectMethodInvocation) { |
| target = expression.target; |
| } else if (expression is DirectPropertyGet) { |
| target = expression.target; |
| } else if (expression is DirectPropertySet) { |
| target = expression.target; |
| } else if (expression is MethodInvocation) { |
| target = expression.interfaceTarget; |
| } else if (expression is PropertyGet) { |
| target = expression.interfaceTarget; |
| } else if (expression is PropertySet) { |
| target = expression.interfaceTarget; |
| } else if (expression is SuperMethodInvocation) { |
| target = expression.interfaceTarget; |
| } else if (expression is SuperPropertyGet) { |
| target = expression.interfaceTarget; |
| } else if (expression is SuperPropertySet) { |
| target = expression.interfaceTarget; |
| } else { |
| var type = expression.runtimeType; |
| fail('Unsupported expression: $type'); |
| } |
| String name = getNodeName(target, required: true); |
| buffer.writeln('$indent$prefix$name();'); |
| } else { |
| fail('Unsupported statement: (${node.runtimeType}) $node'); |
| } |
| } |
| |
| void writeBody(Statement body, String indent) { |
| if (body is EmptyStatement) { |
| buffer.writeln(';'); |
| } else if (body is Block) { |
| buffer.write(' {'); |
| if (body.statements.isNotEmpty) { |
| buffer.writeln(); |
| for (var statement in body.statements) { |
| writeStatement(statement, '$indent '); |
| } |
| buffer.writeln('$indent}'); |
| } else { |
| buffer.writeln('}'); |
| } |
| } else if (body is ExpressionStatement) { |
| buffer.write(' => '); |
| Expression expression = body.expression; |
| if (expression is IntLiteral) { |
| buffer.write(expression); |
| } else { |
| fail('Not implemented ${expression.runtimeType}'); |
| } |
| buffer.writeln(';'); |
| } else { |
| fail('Not implemented ${body.runtimeType}'); |
| } |
| } |
| |
| void writeField(Field node, String indent) { |
| buffer.write(indent); |
| String name = getNodeName(node, required: true); |
| if (node.type is DynamicType) { |
| buffer.write('var'); |
| } else { |
| writeType(node.type); |
| } |
| buffer.writeln(' ${node.name}$name;'); |
| } |
| |
| void writeInitializer(Initializer node, String indent) { |
| String kind; |
| Constructor target; |
| if (node is RedirectingInitializer) { |
| kind = 'redirect'; |
| target = node.target; |
| } else if (node is SuperInitializer) { |
| kind = 'super'; |
| target = node.target; |
| } else { |
| fail('Not implemented ${node.runtimeType}'); |
| } |
| String name = getNodeName(target, required: true); |
| buffer.write('${indent}${kind}$name()'); |
| } |
| |
| void writeConstructor(Constructor node, String indent) { |
| String name = getNodeName(node); |
| buffer.write('${indent}constructor ${node.name}$name()'); |
| List<Initializer> initializers = node.initializers; |
| if (initializers.isNotEmpty) { |
| buffer.writeln(' :'); |
| for (int i = 0; i < initializers.length; i++) { |
| Initializer initializer = initializers[i]; |
| writeInitializer(initializer, ' '); |
| if (i != initializers.length - 1) { |
| buffer.writeln(','); |
| } |
| } |
| } |
| writeBody(node.function.body, indent); |
| } |
| |
| void writeProcedure(NamedNode parent, Procedure node, String indent) { |
| String prefixName; |
| String kindStr; |
| ProcedureKind kind = node.kind; |
| if (kind == ProcedureKind.Method) { |
| prefixName = '@methods'; |
| kindStr = ''; |
| } else if (kind == ProcedureKind.Getter) { |
| prefixName = '@getters'; |
| kindStr = 'get '; |
| } else if (kind == ProcedureKind.Setter) { |
| prefixName = '@setters'; |
| kindStr = 'set '; |
| } else { |
| fail('Unsupported kind: $kind'); |
| } |
| |
| // Verify canonical names linkage. |
| var parentName = parent.canonicalName.getChild(prefixName); |
| expect(node.canonicalName.parent, parentName); |
| |
| String nodeName = getNodeName(node); |
| buffer.write('$indent$kindStr${node.name}$nodeName'); |
| if (kind != ProcedureKind.Getter) { |
| buffer.write('()'); |
| } |
| writeBody(node.function.body, indent); |
| } |
| |
| void writeSupertype(Supertype supertype) { |
| expect(supertype.typeArguments, isEmpty); |
| var clazz = supertype.classNode; |
| String name = getNodeName(clazz, required: true); |
| buffer.write('${clazz.name}$name'); |
| } |
| |
| void writeClass(Class node) { |
| String nodeName = getNodeName(node); |
| buffer.write('class ${node.name}$nodeName'); |
| |
| if (node.typeParameters.isNotEmpty) { |
| buffer.write('<'); |
| for (var i = 0; i < node.typeParameters.length; i++) { |
| if (i != 0) { |
| buffer.write(', '); |
| } |
| TypeParameter typeParameter = node.typeParameters[i]; |
| String name = getNodeName(typeParameter, required: true); |
| buffer.write('${typeParameter.name}$name'); |
| if (typeParameter.bound != null) { |
| var bound = typeParameter.bound as InterfaceType; |
| if (bound.classNode.name != 'Object') { |
| buffer.write(' extends '); |
| writeType(typeParameter.bound); |
| } |
| } |
| } |
| buffer.write('>'); |
| } |
| |
| { |
| var superType = node.supertype; |
| var superClassName = superType.classNode.name; |
| if (superClassName != 'Object') { |
| buffer.write(' extends '); |
| writeSupertype(superType); |
| } |
| |
| if (node.implementedTypes.isNotEmpty) { |
| buffer.write(' implements '); |
| for (var i = 0; i < node.implementedTypes.length; i++) { |
| if (i != 0) { |
| buffer.write(', '); |
| } |
| writeSupertype(node.implementedTypes[i]); |
| } |
| } |
| |
| Supertype mixedInType = node.mixedInType; |
| if (mixedInType != null) { |
| buffer.write(' with '); |
| writeSupertype(mixedInType); |
| } |
| } |
| |
| buffer.write(' {'); |
| |
| if (!node.members.isEmpty) { |
| buffer.writeln(); |
| for (var field in node.fields) { |
| writeField(field, ' '); |
| } |
| for (var constructor in node.constructors) { |
| writeConstructor(constructor, ' '); |
| } |
| for (var procedure in node.procedures) { |
| writeProcedure(node, procedure, ' '); |
| } |
| } |
| buffer.writeln('}'); |
| } |
| |
| for (var node in library.fields) { |
| writeField(node, ''); |
| } |
| |
| for (var node in library.classes) { |
| writeClass(node); |
| } |
| |
| for (var node in library.procedures) { |
| writeProcedure(library, node, ''); |
| } |
| |
| return buffer.toString(); |
| } |
| } |
| |
| /// The original state of an outline, and code that validates that after some |
| /// manipulations (e.g. combine and undo) the state stays the same. |
| class _OutlineState { |
| final Program outline; |
| final initialCollector = new _StateCollector(); |
| |
| _OutlineState(this.outline) { |
| outline.accept(initialCollector); |
| } |
| |
| void verifySame() { |
| var collector = new _StateCollector(); |
| outline.accept(collector); |
| expect(collector.nodes, initialCollector.nodes); |
| expect(collector.references, initialCollector.references); |
| expect(collector.typeParameters, initialCollector.typeParameters); |
| initialCollector.libraryParents.forEach((library, outline) { |
| expect(library.canonicalName.parent, outline.root); |
| expect(library.parent, outline); |
| }); |
| initialCollector.nodeParents.forEach((child, parent) { |
| expect(child.parent, parent); |
| if (child is Member) { |
| var qualifier = CanonicalName.getMemberQualifier(child); |
| var parentName = parent.canonicalName.getChild(qualifier); |
| if (child.name.isPrivate) { |
| var libraryUri = child.enclosingLibrary.importUri; |
| parentName = parentName.getChildFromUri(libraryUri); |
| } |
| expect(child.canonicalName.parent, parentName); |
| } else { |
| expect(child.canonicalName.parent, parent.canonicalName); |
| } |
| }); |
| } |
| } |
| |
| class _StateCollector extends RecursiveVisitor { |
| final List<Node> nodes = []; |
| final Map<NamedNode, NamedNode> nodeParents = {}; |
| final Map<Library, Program> libraryParents = {}; |
| final List<Reference> references = []; |
| final List<TypeParameter> typeParameters = []; |
| |
| @override |
| void defaultMemberReference(Member node) { |
| references.add(node.reference); |
| } |
| |
| @override |
| void defaultNode(Node node) { |
| nodes.add(node); |
| if (node is Library) { |
| libraryParents[node] = node.parent as Program; |
| } else if (node is NamedNode) { |
| nodeParents[node] = node.parent as NamedNode; |
| } |
| super.defaultNode(node); |
| } |
| |
| @override |
| void visitClassReference(Class node) { |
| references.add(node.reference); |
| } |
| |
| @override |
| visitLibrary(Library node) { |
| references.addAll(node.additionalExports); |
| super.visitLibrary(node); |
| } |
| |
| @override |
| visitLibraryDependency(LibraryDependency node) { |
| references.add(node.importedLibraryReference); |
| super.visitLibraryDependency(node); |
| } |
| |
| @override |
| void visitTypedefReference(Typedef node) { |
| references.add(node.reference); |
| } |
| |
| @override |
| void visitTypeParameterType(TypeParameterType node) { |
| typeParameters.add(node.parameter); |
| } |
| } |