blob: e7cc9c9c9bc6189b278cb98eea7233cfc9739437 [file] [log] [blame]
// Copyright (c) 2019, 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.enclosingmethod;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.EnclosingMethodAttribute;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
interface A {
default int def() {
return new C() {
@Override
int number() {
Class<?> clazz = getClass();
System.out.println(clazz.getEnclosingClass());
System.out.println(clazz.getEnclosingMethod());
return 42;
}
}.getNumber();
}
}
abstract class C {
abstract int number();
public int getNumber() {
return number();
}
}
class TestClass implements A {
public static void main(String[] args) throws NoClassDefFoundError {
System.out.println(new TestClass().def());
}
}
@RunWith(Parameterized.class)
public class EnclosingMethodRewriteTest extends TestBase {
private static final Class<?> MAIN = TestClass.class;
private final TestParameters parameters;
private final String[] EXPECTED =
new String[] {
"interface " + A.class.getTypeName(),
"public default int " + A.class.getTypeName() + ".def()",
"42"
};
private final String[] EXPECTED_CC =
new String[] {
"class " + A.class.getTypeName() + "$-CC",
"public static int " + A.class.getTypeName() + "$-CC.a(" + A.class.getTypeName() + ")",
"42"
};
private final String[] EXPECTED_NOUGAT =
new String[] {
"interface " + A.class.getTypeName(), "public int " + A.class.getTypeName() + ".def()", "42"
};
private final String[] EXPECTED_FULL = new String[] {"null", "null", "42"};
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public EnclosingMethodRewriteTest(TestParameters parameters) {
this.parameters = parameters;
}
@Test
public void testJVMOutput() throws Exception {
assumeTrue("Only run JVM reference on CF runtimes", parameters.isCfRuntime());
testForJvm()
.addTestClasspath()
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines(EXPECTED);
}
@Test
public void testR8Compat() throws Exception {
testForR8Compat(parameters.getBackend())
.addProgramClassesAndInnerClasses(A.class)
.addProgramClasses(C.class, MAIN)
.addKeepAllClassesRule()
.addKeepAttributeInnerClassesAndEnclosingMethod()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(inspector -> inspect(inspector, true))
.run(parameters.getRuntime(), MAIN)
.applyIf(
parameters.canUseDefaultAndStaticInterfaceMethods(),
result -> {
if (!parameters.isCfRuntime()
&& parameters.getApiLevel().isEqualTo(AndroidApiLevel.N)) {
result.assertSuccessWithOutputLines(EXPECTED_NOUGAT);
} else {
result.assertSuccessWithOutputLines(EXPECTED);
}
},
result -> result.assertSuccessWithOutputLines(EXPECTED_CC));
}
@Test
public void testR8Full() throws Exception {
testForR8(parameters.getBackend())
.addProgramClassesAndInnerClasses(A.class)
.addProgramClasses(C.class, MAIN)
.addKeepAllClassesRule()
.addKeepAttributeInnerClassesAndEnclosingMethod()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(
inspector -> inspect(inspector, parameters.canUseDefaultAndStaticInterfaceMethods()))
.run(parameters.getRuntime(), MAIN)
.applyIf(
parameters.canUseDefaultAndStaticInterfaceMethods(),
result -> {
if (!parameters.isCfRuntime()
&& parameters.getApiLevel().isEqualTo(AndroidApiLevel.N)) {
result.assertSuccessWithOutputLines(EXPECTED_NOUGAT);
} else {
result.assertSuccessWithOutputLines(EXPECTED);
}
},
result -> result.assertSuccessWithOutputLines(EXPECTED_FULL));
}
private void inspect(CodeInspector inspector, boolean hasEnclosingMethod) {
ClassSubject cImplSubject = inspector.clazz(A.class.getTypeName() + "$1");
assertThat(cImplSubject, isPresent());
ClassSubject enclosingClassSubject =
parameters.canUseDefaultAndStaticInterfaceMethods()
? inspector.clazz(A.class.getTypeName())
: inspector.clazz(A.class.getTypeName() + "$-CC");
assertThat(enclosingClassSubject, isPresent());
EnclosingMethodAttribute enclosingMethodAttribute =
cImplSubject.getDexProgramClass().getEnclosingMethodAttribute();
if (hasEnclosingMethod) {
assertEquals(
enclosingClassSubject.getDexProgramClass().getType(),
enclosingMethodAttribute.getEnclosingMethod().getHolderType());
} else {
assertNull(enclosingMethodAttribute);
}
}
}