blob: 2d396f7d9c07c80ff562167311ced70cbc6d7fbf [file] [log] [blame]
// Copyright (c) 2020, 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.desugar;
import com.android.tools.r8.DesugarTestConfiguration;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
import com.google.common.collect.ImmutableList;
import java.util.List;
import java.util.concurrent.Callable;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class DesugarInnerClassesInInterfaces extends TestBase {
private final List<String> EXPECTED_RESULT_WITHOUT_DESUGARING =
ImmutableList.of(
WithAnonymousInner.class.getName(), "true", WithLocalInner.class.getName(), "true");
private final List<String> EXPECTED_RESULT_WITH_DESUGARING =
ImmutableList.of(
WithAnonymousInner.class.getName() + InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX,
"true",
WithLocalInner.class.getName() + InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX,
"true");
private final List<String> EXPECTED_RESULT_WITH_DESUGARING_B168697955 =
ImmutableList.of(
WithAnonymousInner.class.getName() + InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX,
"false",
WithLocalInner.class.getName() + InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX,
"false");
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
private final TestParameters parameters;
public DesugarInnerClassesInInterfaces(TestParameters parameters) {
this.parameters = parameters;
}
@Test
public void testDesugar() throws Exception {
testForDesugaring(parameters)
.addInnerClasses(DesugarInnerClassesInInterfaces.class)
.run(parameters.getRuntime(), TestClass.class)
.applyIf(
DesugarTestConfiguration::isNotDesugared,
r -> r.assertSuccessWithOutputLines(EXPECTED_RESULT_WITHOUT_DESUGARING))
.applyIf(
DesugarTestConfiguration::isDesugared,
r ->
r.assertSuccessWithOutputLines(
parameters
.getApiLevel()
.isGreaterThanOrEqualTo(apiLevelWithDefaultInterfaceMethodsSupport())
? EXPECTED_RESULT_WITHOUT_DESUGARING
: EXPECTED_RESULT_WITH_DESUGARING));
}
@Test
public void testR8Compat() throws Exception {
testForR8Compat(parameters.getBackend())
.addInnerClasses(DesugarInnerClassesInInterfaces.class)
.setMinApi(parameters.getApiLevel())
.addKeepAllClassesRule()
.addKeepAttributeInnerClassesAndEnclosingMethod()
.noHorizontalClassMergingOfSynthetics()
.compile()
.run(parameters.getRuntime(), TestClass.class)
.applyIf(
parameters.canUseDefaultAndStaticInterfaceMethods(),
result -> result.assertSuccessWithOutputLines(EXPECTED_RESULT_WITHOUT_DESUGARING),
// The static method which is moved to the companion class is inlined and causing
// this different output. The rule "-keep class * { *; }" does not keep the static
// method from being inlined after it has moved. Turning off inlining produces the
// expected result. The inlining cause the getEnclosingClass() to return null.
// See b/168697955.
result ->
result.assertSuccessWithOutputLines(EXPECTED_RESULT_WITH_DESUGARING_B168697955));
}
@Test
public void testR8_B168697955() throws Exception {
testForR8(parameters.getBackend())
.addInnerClasses(DesugarInnerClassesInInterfaces.class)
.setMinApi(parameters.getApiLevel())
.addKeepAllClassesRule()
.addKeepAttributeInnerClassesAndEnclosingMethod()
// With inlining turned off we get the expected result.
.addOptionsModification(options -> options.enableInlining = false)
.compile()
.run(parameters.getRuntime(), TestClass.class)
.applyIf(
parameters.canUseDefaultAndStaticInterfaceMethods(),
result -> result.assertSuccessWithOutputLines(EXPECTED_RESULT_WITHOUT_DESUGARING),
// TODO(b/187377562): We remove the attribute due to not pinning the moved methods.
result -> result.assertFailureWithErrorThatThrows(NullPointerException.class));
}
@Test
public void testR8Full() throws Exception {
testForR8(parameters.getBackend())
.addInnerClasses(DesugarInnerClassesInInterfaces.class)
.setMinApi(parameters.getApiLevel())
.addKeepAllClassesRule()
.addKeepAttributeInnerClassesAndEnclosingMethod()
.compile()
.run(parameters.getRuntime(), TestClass.class)
.applyIf(
parameters.canUseDefaultAndStaticInterfaceMethods(),
result -> result.assertSuccessWithOutputLines(EXPECTED_RESULT_WITHOUT_DESUGARING),
// TODO(b/187377562): We remove the attribute due to not pinning the moved methods.
result -> result.assertFailureWithErrorThatThrows(NullPointerException.class));
}
interface WithAnonymousInner {
static Callable<Class<?>> staticOuter() {
return new Callable<Class<?>>() {
@Override
public Class<?> call() {
return getClass().getEnclosingClass();
}
};
}
default Callable<Class<?>> defaultOuter() {
return new Callable<Class<?>>() {
@Override
public Class<?> call() {
return getClass().getEnclosingClass();
}
};
}
}
interface WithLocalInner {
static Callable<Class<?>> staticOuter() {
class Local implements Callable<Class<?>> {
@Override
public Class<?> call() {
return getClass().getEnclosingClass();
}
}
return new Local();
}
default Callable<Class<?>> defaultOuter() {
class Local implements Callable<Class<?>> {
@Override
public Class<?> call() {
return getClass().getEnclosingClass();
}
}
return new Local();
}
}
public static class TestClass {
public static void main(String[] args) throws Exception {
System.out.println(new WithAnonymousInner() {}.defaultOuter().call().getName());
System.out.println(
new WithAnonymousInner() {}.defaultOuter()
.call()
.equals(WithAnonymousInner.staticOuter().call()));
System.out.println(new WithLocalInner() {}.defaultOuter().call().getName());
System.out.println(
new WithLocalInner() {}.defaultOuter()
.call()
.equals(WithLocalInner.staticOuter().call()));
}
}
}