| // Copyright (c) 2015, 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:async_helper/async_helper.dart'; |
| import 'package:compiler/src/commandline_options.dart'; |
| import 'package:compiler/src/common_elements.dart'; |
| import 'package:compiler/src/compiler.dart'; |
| import 'package:compiler/src/elements/entities.dart'; |
| import 'package:compiler/src/js_backend/no_such_method_registry.dart'; |
| import 'package:compiler/src/world.dart'; |
| import 'package:expect/expect.dart'; |
| import 'memory_compiler.dart'; |
| |
| class NoSuchMethodInfo { |
| final String className; |
| final String superClassName; |
| final bool hasThrowingSyntax; |
| final bool hasForwardingSyntax; |
| final bool isThrowing; |
| final bool isDefault; |
| final bool isOther; |
| final bool isNotApplicable; |
| final bool isComplexNoReturn; |
| final bool isComplexReturn; |
| |
| const NoSuchMethodInfo(this.className, |
| {this.superClassName, |
| this.hasThrowingSyntax: false, |
| this.hasForwardingSyntax: false, |
| this.isThrowing: false, |
| this.isDefault: false, |
| this.isOther: false, |
| this.isNotApplicable: false, |
| this.isComplexNoReturn: false, |
| this.isComplexReturn: false}); |
| } |
| |
| class NoSuchMethodTest { |
| final String code; |
| final List<NoSuchMethodInfo> methods; |
| final bool isNoSuchMethodUsed; |
| |
| const NoSuchMethodTest(this.code, this.methods, |
| {this.isNoSuchMethodUsed: false}); |
| } |
| |
| const List<NoSuchMethodTest> TESTS = const <NoSuchMethodTest>[ |
| const NoSuchMethodTest(""" |
| class A { |
| foo() => 3; |
| noSuchMethod(x) => super.noSuchMethod(x); |
| } |
| main() { |
| print(new A().foo()); |
| } |
| """, const <NoSuchMethodInfo>[ |
| const NoSuchMethodInfo('A', hasForwardingSyntax: true, isDefault: true), |
| ]), |
| const NoSuchMethodTest(""" |
| class A extends B { |
| foo() => 3; |
| noSuchMethod(x) => super.noSuchMethod(x); |
| } |
| class B {} |
| main() { |
| print(new A().foo()); |
| } |
| """, const <NoSuchMethodInfo>[ |
| const NoSuchMethodInfo('A', hasForwardingSyntax: true, isDefault: true), |
| ]), |
| const NoSuchMethodTest(""" |
| class A extends B { |
| foo() => 3; |
| noSuchMethod(x) { |
| return super.noSuchMethod(x); |
| } |
| } |
| class B {} |
| main() { |
| print(new A().foo()); |
| } |
| """, const <NoSuchMethodInfo>[ |
| const NoSuchMethodInfo('A', hasForwardingSyntax: true, isDefault: true), |
| ]), |
| const NoSuchMethodTest(""" |
| class A extends B { |
| foo() => 3; |
| noSuchMethod(x) => super.noSuchMethod(x); |
| } |
| class B { |
| noSuchMethod(x) => super.noSuchMethod(x); |
| } |
| main() { |
| print(new A().foo()); |
| } |
| """, const <NoSuchMethodInfo>[ |
| const NoSuchMethodInfo('A', |
| superClassName: 'B', hasForwardingSyntax: true, isDefault: true), |
| const NoSuchMethodInfo('B', hasForwardingSyntax: true, isDefault: true), |
| ]), |
| const NoSuchMethodTest(""" |
| class A extends B { |
| foo() => 3; |
| noSuchMethod(x) => super.noSuchMethod(x); |
| } |
| class B { |
| noSuchMethod(x) => throw 'foo'; |
| } |
| main() { |
| print(new A().foo()); |
| } |
| """, const <NoSuchMethodInfo>[ |
| const NoSuchMethodInfo('A', |
| superClassName: 'B', hasForwardingSyntax: true, isThrowing: true), |
| const NoSuchMethodInfo('B', hasThrowingSyntax: true, isThrowing: true), |
| ], isNoSuchMethodUsed: true), |
| const NoSuchMethodTest(""" |
| class A { |
| noSuchMethod(x) => 3; |
| } |
| main() { |
| print(new A().foo()); |
| } |
| """, const <NoSuchMethodInfo>[ |
| const NoSuchMethodInfo('A', isOther: true, isComplexReturn: true), |
| ], isNoSuchMethodUsed: true), |
| const NoSuchMethodTest(""" |
| class A { |
| noSuchMethod(x, [y]) => super.noSuchMethod(x); |
| } |
| main() { |
| print(new A().foo()); |
| } |
| """, const <NoSuchMethodInfo>[ |
| const NoSuchMethodInfo('A', hasForwardingSyntax: true, isDefault: true), |
| ]), |
| const NoSuchMethodTest(""" |
| class A { |
| noSuchMethod(x, [y]) => super.noSuchMethod(x, y); |
| } |
| main() { |
| print(new A().foo()); |
| } |
| """, const <NoSuchMethodInfo>[ |
| const NoSuchMethodInfo('A', isOther: true, isComplexNoReturn: true), |
| ], isNoSuchMethodUsed: true), |
| const NoSuchMethodTest(""" |
| class A { |
| noSuchMethod(x, y) => super.noSuchMethod(x); |
| } |
| main() { |
| print(new A().foo()); |
| } |
| """, const <NoSuchMethodInfo>[ |
| const NoSuchMethodInfo('A', |
| hasForwardingSyntax: true, isNotApplicable: true), |
| ]), |
| const NoSuchMethodTest(""" |
| class A { |
| noSuchMethod(Invocation x) { |
| throw new UnsupportedError(); |
| } |
| } |
| main() { |
| print(new A().foo()); |
| } |
| """, const <NoSuchMethodInfo>[ |
| const NoSuchMethodInfo('A', hasThrowingSyntax: true, isThrowing: true), |
| ], isNoSuchMethodUsed: true), |
| const NoSuchMethodTest(""" |
| class A { |
| noSuchMethod(Invocation x) { |
| print('foo'); |
| throw 'foo'; |
| } |
| } |
| main() { |
| print(new A().foo()); |
| } |
| """, const <NoSuchMethodInfo>[ |
| const NoSuchMethodInfo('A', isOther: true, isComplexNoReturn: true), |
| ], isNoSuchMethodUsed: true), |
| const NoSuchMethodTest(""" |
| class A { |
| noSuchMethod(Invocation x) { |
| return toString(); |
| } |
| } |
| main() { |
| print(new A().foo()); |
| } |
| """, const <NoSuchMethodInfo>[ |
| const NoSuchMethodInfo('A', isOther: true, isComplexReturn: true), |
| ], isNoSuchMethodUsed: true), |
| const NoSuchMethodTest(""" |
| class A { |
| noSuchMethod(x) => super.noSuchMethod(x) as dynamic; |
| } |
| main() { |
| print(new A().foo()); |
| } |
| """, const <NoSuchMethodInfo>[ |
| const NoSuchMethodInfo('A', hasForwardingSyntax: true, isDefault: true), |
| ]), |
| ]; |
| |
| main() { |
| runTests({bool useKernel}) async { |
| for (NoSuchMethodTest test in TESTS) { |
| print('---- testing -------------------------------------------------'); |
| print(test.code); |
| CompilationResult result = await runCompiler( |
| memorySourceFiles: {'main.dart': test.code}, |
| options: useKernel ? [] : [Flags.useOldFrontend]); |
| Compiler compiler = result.compiler; |
| checkTest(compiler, test); |
| } |
| } |
| |
| asyncTest(() async { |
| print('--test from ast---------------------------------------------------'); |
| await runTests(useKernel: false); |
| print('--test from kernel------------------------------------------------'); |
| await runTests(useKernel: true); |
| }); |
| } |
| |
| checkTest(Compiler compiler, NoSuchMethodTest test) { |
| ElementEnvironment frontendEnvironment = |
| compiler.frontendStrategy.elementEnvironment; |
| NoSuchMethodRegistryImpl registry = compiler.backend.noSuchMethodRegistry; |
| NoSuchMethodResolver resolver = registry.internalResolverForTesting; |
| FunctionEntity ObjectNSM = frontendEnvironment.lookupClassMember( |
| compiler.frontendStrategy.commonElements.objectClass, 'noSuchMethod'); |
| ClosedWorld backendClosedWorld = compiler.backendClosedWorldForTesting; |
| ElementEnvironment backendEnvironment = backendClosedWorld.elementEnvironment; |
| NoSuchMethodDataImpl data = backendClosedWorld.noSuchMethodData; |
| |
| // Test [NoSuchMethodResolver] results for each method. |
| for (NoSuchMethodInfo info in test.methods) { |
| ClassEntity cls = frontendEnvironment.lookupClass( |
| frontendEnvironment.mainLibrary, info.className); |
| Expect.isNotNull(cls, "Class ${info.className} not found."); |
| FunctionEntity noSuchMethod = |
| frontendEnvironment.lookupClassMember(cls, 'noSuchMethod'); |
| Expect.isNotNull(noSuchMethod, "noSuchMethod not found in $cls."); |
| |
| if (info.superClassName == null) { |
| Expect.equals(ObjectNSM, resolver.getSuperNoSuchMethod(noSuchMethod)); |
| } else { |
| ClassEntity superclass = frontendEnvironment.lookupClass( |
| frontendEnvironment.mainLibrary, info.superClassName); |
| Expect.isNotNull( |
| superclass, "Superclass ${info.superClassName} not found."); |
| FunctionEntity superNoSuchMethod = |
| frontendEnvironment.lookupClassMember(superclass, 'noSuchMethod'); |
| Expect.isNotNull( |
| superNoSuchMethod, "noSuchMethod not found in $superclass."); |
| Expect.equals( |
| superNoSuchMethod, |
| resolver.getSuperNoSuchMethod(noSuchMethod), |
| "Unexpected super noSuchMethod for $noSuchMethod."); |
| } |
| |
| Expect.equals( |
| info.hasForwardingSyntax, |
| resolver.hasForwardingSyntax(noSuchMethod), |
| "Unexpected hasForwardSyntax result on $noSuchMethod."); |
| Expect.equals( |
| info.hasThrowingSyntax, |
| resolver.hasThrowingSyntax(noSuchMethod), |
| "Unexpected hasThrowingSyntax result on $noSuchMethod."); |
| } |
| |
| // Test [NoSuchMethodRegistry] results for each method. These are based on |
| // the [NoSuchMethodResolver] results which are therefore tested for all |
| // methods first. |
| for (NoSuchMethodInfo info in test.methods) { |
| ClassEntity frontendClass = frontendEnvironment.lookupClass( |
| frontendEnvironment.mainLibrary, info.className); |
| Expect.isNotNull(frontendClass, "Class ${info.className} not found."); |
| FunctionEntity frontendNoSuchMethod = |
| frontendEnvironment.lookupClassMember(frontendClass, 'noSuchMethod'); |
| Expect.isNotNull( |
| frontendNoSuchMethod, "noSuchMethod not found in $frontendClass."); |
| |
| Expect.equals( |
| info.isDefault, |
| registry.defaultImpls.contains(frontendNoSuchMethod), |
| "Unexpected isDefault result on $frontendNoSuchMethod."); |
| Expect.equals( |
| info.isThrowing, |
| registry.throwingImpls.contains(frontendNoSuchMethod), |
| "Unexpected isThrowing result on $frontendNoSuchMethod."); |
| Expect.equals( |
| info.isOther, |
| registry.otherImpls.contains(frontendNoSuchMethod), |
| "Unexpected isOther result on $frontendNoSuchMethod."); |
| Expect.equals( |
| info.isNotApplicable, |
| registry.notApplicableImpls.contains(frontendNoSuchMethod), |
| "Unexpected isNotApplicable result on $frontendNoSuchMethod."); |
| |
| ClassEntity backendClass = backendEnvironment.lookupClass( |
| backendEnvironment.mainLibrary, info.className); |
| Expect.isNotNull(backendClass, "Class ${info.className} not found."); |
| FunctionEntity backendNoSuchMethod = |
| backendEnvironment.lookupClassMember(backendClass, 'noSuchMethod'); |
| Expect.isNotNull( |
| backendNoSuchMethod, "noSuchMethod not found in $backendClass."); |
| |
| Expect.equals( |
| info.isComplexNoReturn, |
| data.complexNoReturnImpls.contains(backendNoSuchMethod), |
| "Unexpected isComplexNoReturn result on $backendNoSuchMethod."); |
| Expect.equals( |
| info.isComplexReturn, |
| data.complexReturningImpls.contains(backendNoSuchMethod), |
| "Unexpected isComplexReturn result on $backendNoSuchMethod."); |
| } |
| |
| Expect.equals( |
| test.isNoSuchMethodUsed, |
| backendClosedWorld.backendUsage.isNoSuchMethodUsed, |
| "Unexpected isNoSuchMethodUsed result."); |
| } |