blob: 99939993dfe9571369034484524a224e8b4f0e7d [file] [log] [blame]
// Copyright (c) 2018, the R8 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.
package com.android.tools.r8.memberrebinding;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class IndirectSuperInterfaceTest extends TestBase {
private static final List<Class<?>> CLASSES =
ImmutableList.of(
InterfaceA.class, InterfaceASub.class, A.class,
InterfaceB.class, InterfaceBSub.class, B.class,
InterfaceC.class, InterfaceCSub.class, C.class,
InterfaceD.class, InterfaceDSub.class, D.class,
TestClass.class);
// Test A: class A extends an empty class that implements a non-empty interface.
interface InterfaceA {
@NeverInline
default String method() {
return "InterfaceA::method";
}
}
static class InterfaceASub implements InterfaceA {
// Intentionally empty.
}
static class A extends InterfaceASub {
@Override
public String method() {
return "A::method -> " + super.method();
}
}
// Test B: class B implements an empty interface that extends a non-empty interface.
interface InterfaceB {
@NeverInline
default String method() {
return "InterfaceB::method";
}
}
interface InterfaceBSub extends InterfaceB {
// Intentionally empty.
}
static class B implements InterfaceBSub {
@Override
public String method() {
return "B::method -> " + InterfaceBSub.super.method();
}
}
// Test C: class C extends a non-empty class that implements a non-empty interface.
interface InterfaceC {
@NeverInline
default String method() {
return "InterfaceC::method";
}
}
static class InterfaceCSub implements InterfaceC {
// This method is intentionally not annotated with @NeverInline. If we were to inline this
// method we would risk introducing a super-invocation to InterfaceC.method() in C.method,
// which would lead to an IncompatibleClassChangeError on the JVM.
// (See also Art978_virtual_interfaceTest.)
@Override
public String method() {
return InterfaceC.super.method();
}
}
static class C extends InterfaceCSub {
@Override
public String method() {
return "C::method -> " + super.method();
}
}
// Test D: class D implements a non-empty empty interface that extends a non-empty interface.
interface InterfaceD {
@NeverInline
default String method() {
return "InterfaceD::method";
}
}
interface InterfaceDSub extends InterfaceD {
// This method is intentionally not annotated with @NeverInline. If we were to inline this
// method we would risk introducing a super-invocation to InterfaceC.method() in C.method,
// which would lead to an IncompatibleClassChangeError on the JVM.
// (See also Art978_virtual_interfaceTest.)
@Override
default String method() {
return InterfaceD.super.method();
}
}
static class D implements InterfaceDSub {
@Override
public String method() {
return "D::method -> " + InterfaceDSub.super.method();
}
}
static class TestClass {
public static void main(String[] args) {
System.out.println(new A().method());
System.out.println(new B().method());
System.out.println(new C().method());
System.out.print(new D().method());
}
}
private final Backend backend;
@Parameters(name = "{0}")
public static Backend[] setup() {
return ToolHelper.getBackends();
}
public IndirectSuperInterfaceTest(Backend backend) {
this.backend = backend;
}
@Test
public void test() throws Exception {
String expected = StringUtils.joinLines(
"A::method -> InterfaceA::method",
"B::method -> InterfaceB::method",
"C::method -> InterfaceC::method",
"D::method -> InterfaceD::method");
testForJvm()
.addTestClasspath()
.run(TestClass.class)
.assertSuccessWithOutput(expected);
testForR8(backend)
.addProgramClasses(CLASSES)
.addKeepPackageRules(TestClass.class.getPackage())
.addKeepMainRule(TestClass.class)
.enableInliningAnnotations()
.run(TestClass.class)
.assertSuccessWithOutput(expected);
}
}