|  | // 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/common/elements.dart'; | 
|  | import 'package:compiler/src/common/names.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/js_model/elements.dart'; | 
|  | import 'package:compiler/src/js_model/js_world.dart' show JClosedWorld; | 
|  | import 'package:expect/expect.dart'; | 
|  | import 'package:compiler/src/util/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 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.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(""" | 
|  | abstract class I { | 
|  | foo(); | 
|  | } | 
|  | class A implements I { | 
|  | noSuchMethod(x) => super.noSuchMethod(x); | 
|  | } | 
|  | main() { | 
|  | print(new A().foo()); | 
|  | } | 
|  | """, const <NoSuchMethodInfo>[ | 
|  | const NoSuchMethodInfo('A', hasForwardingSyntax: true, isDefault: true), | 
|  | ]), | 
|  | const NoSuchMethodTest(""" | 
|  | abstract class I { | 
|  | foo(); | 
|  | } | 
|  | class A extends B implements I { | 
|  | noSuchMethod(x) => super.noSuchMethod(x); | 
|  | } | 
|  | class B {} | 
|  | main() { | 
|  | print(new A().foo()); | 
|  | } | 
|  | """, const <NoSuchMethodInfo>[ | 
|  | const NoSuchMethodInfo('A', hasForwardingSyntax: true, isDefault: true), | 
|  | ]), | 
|  | const NoSuchMethodTest(""" | 
|  | abstract class I { | 
|  | foo(); | 
|  | } | 
|  | class A extends B implements I { | 
|  | noSuchMethod(x) { | 
|  | return super.noSuchMethod(x); | 
|  | } | 
|  | } | 
|  | class B {} | 
|  | main() { | 
|  | print(new A().foo()); | 
|  | } | 
|  | """, const <NoSuchMethodInfo>[ | 
|  | const NoSuchMethodInfo('A', hasForwardingSyntax: true, isDefault: true), | 
|  | ]), | 
|  | const NoSuchMethodTest(""" | 
|  | abstract class I { | 
|  | foo(); | 
|  | } | 
|  | class A extends B implements I { | 
|  | 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(""" | 
|  | abstract class I { | 
|  | foo(); | 
|  | } | 
|  | class A extends B implements I { | 
|  | 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() as dynamic).foo()); | 
|  | } | 
|  | """, const <NoSuchMethodInfo>[ | 
|  | const NoSuchMethodInfo('A', isOther: true, isComplexReturn: true), | 
|  | ], isNoSuchMethodUsed: true), | 
|  | const NoSuchMethodTest(""" | 
|  | abstract class I { | 
|  | foo(); | 
|  | } | 
|  | class A implements I { | 
|  | noSuchMethod(x, [y]) => super.noSuchMethod(x); | 
|  | } | 
|  | main() { | 
|  | print(new A().foo()); | 
|  | } | 
|  | """, const <NoSuchMethodInfo>[ | 
|  | const NoSuchMethodInfo('A', hasForwardingSyntax: true, isDefault: true), | 
|  | ]), | 
|  | const NoSuchMethodTest(""" | 
|  | abstract class I { | 
|  | foo(); | 
|  | } | 
|  | class A implements I { | 
|  | noSuchMethod(x, [y]) => super.noSuchMethod(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) + y; | 
|  | } | 
|  | main() { | 
|  | print((new A() as dynamic).foo()); | 
|  | } | 
|  | """, const <NoSuchMethodInfo>[ | 
|  | const NoSuchMethodInfo('A', isOther: true, isComplexNoReturn: true), | 
|  | ], isNoSuchMethodUsed: true), | 
|  | const NoSuchMethodTest(""" | 
|  | class A { | 
|  | noSuchMethod(Invocation x) { | 
|  | throw UnsupportedError(''); | 
|  | } | 
|  | } | 
|  | main() { | 
|  | print((new A() as dynamic).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() as dynamic).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() as dynamic).foo()); | 
|  | } | 
|  | """, const <NoSuchMethodInfo>[ | 
|  | const NoSuchMethodInfo('A', isOther: true, isComplexReturn: true), | 
|  | ], isNoSuchMethodUsed: true), | 
|  | const NoSuchMethodTest(""" | 
|  | abstract class I { | 
|  | foo(); | 
|  | } | 
|  | class A implements I { | 
|  | noSuchMethod(x) => super.noSuchMethod(x) as dynamic; | 
|  | } | 
|  | main() { | 
|  | print(new A().foo()); | 
|  | } | 
|  | """, const <NoSuchMethodInfo>[ | 
|  | const NoSuchMethodInfo('A', hasForwardingSyntax: true, isDefault: true), | 
|  | ]), | 
|  | const NoSuchMethodTest(""" | 
|  | abstract class I { | 
|  | foo(); | 
|  | } | 
|  | class A implements I { | 
|  | noSuchMethod(x) => super.noSuchMethod(x) as int; | 
|  | } | 
|  | main() { | 
|  | print(new A().foo()); | 
|  | } | 
|  | """, const <NoSuchMethodInfo>[ | 
|  | const NoSuchMethodInfo('A', isOther: true, isComplexNoReturn: true), | 
|  | ], isNoSuchMethodUsed: true), | 
|  | ]; | 
|  |  | 
|  | main() { | 
|  | runTests() async { | 
|  | for (NoSuchMethodTest test in TESTS) { | 
|  | print('---- testing -------------------------------------------------'); | 
|  | print(test.code); | 
|  | CompilationResult result = | 
|  | await runCompiler(memorySourceFiles: {'main.dart': test.code}); | 
|  | Expect.isTrue(result.isSuccess); | 
|  | Compiler compiler = result.compiler!; | 
|  | checkTest(compiler, test); | 
|  | } | 
|  | } | 
|  |  | 
|  | asyncTest(() async { | 
|  | print('--test from kernel------------------------------------------------'); | 
|  | await runTests(); | 
|  | }); | 
|  | } | 
|  |  | 
|  | checkTest(Compiler compiler, NoSuchMethodTest test) { | 
|  | ElementEnvironment frontendEnvironment = | 
|  | compiler.frontendStrategy.elementEnvironment; | 
|  | NoSuchMethodRegistry registry = | 
|  | compiler.frontendStrategy.noSuchMethodRegistry; | 
|  | var resolver = registry.internalResolverForTesting; | 
|  | final ObjectNSM = frontendEnvironment.lookupClassMember( | 
|  | compiler.frontendStrategy.commonElements.objectClass, | 
|  | Names.noSuchMethod_) as FunctionEntity; | 
|  | JClosedWorld backendClosedWorld = compiler.backendClosedWorldForTesting!; | 
|  | ElementEnvironment backendEnvironment = backendClosedWorld.elementEnvironment; | 
|  | NoSuchMethodData 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."); | 
|  | final noSuchMethod = frontendEnvironment.lookupClassMember( | 
|  | cls!, Names.noSuchMethod_) as FunctionEntity?; | 
|  | 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."); | 
|  | final superNoSuchMethod = frontendEnvironment.lookupClassMember( | 
|  | superclass!, Names.noSuchMethod_) as FunctionEntity?; | 
|  | 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 as JFunction), | 
|  | "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."); | 
|  | final frontendNoSuchMethod = frontendEnvironment.lookupClassMember( | 
|  | frontendClass!, Names.noSuchMethod_) as FunctionEntity?; | 
|  | 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."); | 
|  |  | 
|  | ClassEntity? backendClass = backendEnvironment.lookupClass( | 
|  | backendEnvironment.mainLibrary!, info.className); | 
|  | Expect.isNotNull(backendClass, "Class ${info.className} not found."); | 
|  | final backendNoSuchMethod = backendEnvironment.lookupClassMember( | 
|  | backendClass!, Names.noSuchMethod_) as FunctionEntity?; | 
|  | 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."); | 
|  | } |