blob: 68ff7c5bb428b702ceb5c4f78b6e40569a5f36b0 [file] [log] [blame]
// 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.");
}