Don't optimize getClass on kept classes.
This also adds a test for a know type issue:
Bug: 154792347
Change-Id: If227e53ae97a8a87fdb31632e1f5073755a35a78
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
index 64a8c86..3b427cb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
@@ -119,8 +119,7 @@
return null;
}
// Only consider effectively final class. Exception: new Base().getClass().
- if (appView.appInfo().hasSubtypes(baseType)
- && appView.appInfo().isInstantiatedIndirectly(clazz)
+ if (!clazz.isEffectivelyFinal(appView)
&& (in.isPhi() || !in.definition.isCreatingInstanceOrArray())) {
return null;
}
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 743a898..f172bb12f 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -1449,6 +1449,27 @@
return buildOnDexRuntime(parameters, Arrays.asList(paths));
}
+ public Path buildOnDexRuntime(TestParameters parameters, Class<?>... classes)
+ throws IOException, CompilationFailedException {
+ if (parameters.isDexRuntime()) {
+ return testForD8()
+ .addProgramClasses(classes)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .writeToZip();
+ }
+ Path path = temp.newFolder().toPath().resolve("classes.jar");
+ ArchiveConsumer consumer = new ArchiveConsumer(path);
+ for (Class clazz : classes) {
+ consumer.accept(
+ ByteDataView.of(ToolHelper.getClassAsBytes(clazz)),
+ DescriptorUtils.javaTypeToDescriptor(clazz.getTypeName()),
+ null);
+ }
+ consumer.finished(null);
+ return path;
+ }
+
public static String binaryName(Class<?> clazz) {
return DescriptorUtils.getBinaryNameFromJavaType(typeName(clazz));
}
diff --git a/src/test/java/com/android/tools/r8/cf/MissingClassJoinsToObjectTest.java b/src/test/java/com/android/tools/r8/cf/MissingClassJoinsToObjectTest.java
new file mode 100644
index 0000000..81f1d09
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/MissingClassJoinsToObjectTest.java
@@ -0,0 +1,87 @@
+// 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.cf;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MissingClassJoinsToObjectTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("A::foo");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+ }
+
+ public MissingClassJoinsToObjectTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private List<Path> getRuntimeClasspath() throws Exception {
+ return buildOnDexRuntime(parameters, ToolHelper.getClassFileForTestClass(B.class));
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(TestClass.class, A.class, B.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ R8TestRunResult result =
+ testForR8(parameters.getBackend())
+ .enableInliningAnnotations()
+ .addProgramClasses(TestClass.class, A.class)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .addRunClasspathFiles(getRuntimeClasspath())
+ .run(parameters.getRuntime(), TestClass.class);
+ if (parameters.isCfRuntime()) {
+ // TODO(b/154792347): The analysis of types in the presence of undefined is incomplete.
+ result.assertFailureWithErrorThatThrows(VerifyError.class);
+ } else {
+ result.assertSuccessWithOutput(EXPECTED);
+ }
+ }
+
+ static class A {
+ @NeverInline
+ public void foo() {
+ System.out.println("A::foo");
+ }
+ }
+
+ // Missing at compile time.
+ static class B extends A {
+ // Intentionally empty.
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ // Due to the missing class B, the join is assigned Object.
+ A join = args.length == 0 ? new A() : new B();
+ // The call to Object::foo fails.
+ join.foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassBaseAndSubTest.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassBaseAndSubTest.java
new file mode 100644
index 0000000..b5daa52
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassBaseAndSubTest.java
@@ -0,0 +1,76 @@
+// 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.ir.optimize.reflection;
+
+import static org.junit.Assert.assertFalse;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class GetClassBaseAndSubTest extends TestBase {
+
+ static final String EXPECTED =
+ StringUtils.lines("class " + Base.class.getTypeName(), "class " + Sub.class.getTypeName());
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public GetClassBaseAndSubTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ testForRuntime(parameters)
+ .addInnerClasses(GetClassBaseAndSubTest.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .noMinification()
+ .addInnerClasses(GetClassBaseAndSubTest.class)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(
+ inspector ->
+ assertFalse(
+ inspector
+ .clazz(TestClass.class)
+ .mainMethod()
+ .streamInstructions()
+ .anyMatch(InstructionSubject::isConstClass)));
+ }
+
+ static class Base {}
+
+ static class Sub extends Base {}
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ Base baseWithBase = System.currentTimeMillis() > 0 ? new Base() : new Sub();
+ // Cannot be rewritten to const-class.
+ System.out.println(baseWithBase.getClass());
+ Base baseWithSub = System.currentTimeMillis() > 0 ? new Sub() : new Base();
+ // Cannot be rewritten to const-class.
+ System.out.println(baseWithSub.getClass());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassOnKeptClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassOnKeptClassTest.java
new file mode 100644
index 0000000..6e0e9e3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassOnKeptClassTest.java
@@ -0,0 +1,97 @@
+// 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.ir.optimize.reflection;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.concurrent.Callable;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class GetClassOnKeptClassTest extends TestBase {
+
+ static final String EXPECTED =
+ StringUtils.lines(
+ "class " + KeptClass.class.getTypeName(),
+ "class " + KeptClass.class.getTypeName(),
+ "class " + UnknownClass.class.getTypeName(),
+ "class " + UnknownClass.class.getTypeName());
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+ }
+
+ public GetClassOnKeptClassTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(KeptClass.class, UnknownClass.class, TestClass.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .enableInliningAnnotations()
+ .addProgramClasses(KeptClass.class, TestClass.class)
+ .addKeepMainRule(TestClass.class)
+ .addKeepClassRules(KeptClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .addRunClasspathFiles(buildOnDexRuntime(parameters, UnknownClass.class))
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ static class KeptClass implements Callable<Class<?>> {
+ @NeverInline
+ static Class<?> getClassMethod(KeptClass instance) {
+ // Nullable argument. Should not be rewritten to const-class to preserve NPE.
+ return instance.getClass();
+ }
+
+ @NeverInline
+ @Override
+ public Class<?> call() {
+ // Non-null `this` pointer.
+ return getClass();
+ }
+ }
+
+ static class UnknownClass extends KeptClass {
+ // Empty subtype of KeptClass.
+ }
+
+ static class TestClass {
+
+ static KeptClass getInstance(int i) throws Exception {
+ return i == 0
+ ? new KeptClass()
+ : (KeptClass)
+ Class.forName(TestClass.class.getName().replace("TestClass", "UnknownClass"))
+ .getDeclaredConstructor()
+ .newInstance();
+ }
+
+ public static void main(String[] args) throws Exception {
+ for (int i = 0; i < 2; i++) {
+ KeptClass instance = getInstance(args.length + i);
+ System.out.println(instance.call());
+ System.out.println(KeptClass.getClassMethod(instance));
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
index c2fa7fb..7807a9f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
@@ -9,30 +9,34 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
-import com.android.tools.r8.D8TestRunResult;
+import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.ForceInline;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.TestRunResult;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
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;
-class GetClassTestMain implements Callable<Class<?>> {
+@RunWith(Parameterized.class)
+public class GetClassTest extends ReflectionOptimizerTestBase {
+
static class Base {}
+
static class Sub extends Base {}
+
static class EffectivelyFinal {}
static class Reflection implements Callable<Class<?>> {
+
@ForceInline
@Override
public Class<?> call() {
@@ -40,96 +44,106 @@
}
}
- @NeverInline
- static Class<?> getMainClass(GetClassTestMain instance) {
- // Nullable argument. Should not be rewritten to const-class to preserve NPE.
- return instance.getClass();
+ static class GetClassTestMain implements Callable<Class<?>> {
+
+ @NeverInline
+ static Class<?> getMainClass(GetClassTestMain instance) {
+ // Nullable argument. Should not be rewritten to const-class to preserve NPE.
+ return instance.getClass();
+ }
+
+ @NeverInline
+ @Override
+ public Class<?> call() {
+ // Non-null `this` pointer.
+ return getClass();
+ }
}
- @NeverInline
- @Override
- public Class<?> call() {
- // Non-null `this` pointer.
- return getClass();
- }
+ static class Main {
- public static void main(String[] args) {
- {
- Base base = new Base();
- // Not applicable in debug mode.
- System.out.println(base.getClass());
- // Can be rewritten to const-class always.
- System.out.println(new Base().getClass());
- }
+ public static void main(String[] args) {
+ {
+ Base base = new Base();
+ // Not applicable in debug mode.
+ System.out.println(base.getClass());
+ // Can be rewritten to const-class always.
+ System.out.println(new Base().getClass());
+ }
- {
- Base sub = new Sub();
- // Not applicable in debug mode.
- System.out.println(sub.getClass());
- }
+ {
+ Base sub = new Sub();
+ // Not applicable in debug mode.
+ System.out.println(sub.getClass());
+ }
- {
- Base[] subs = new Sub[1];
- // Not applicable in debug mode.
- System.out.println(subs.getClass());
- }
+ {
+ Base[] subs = new Sub[1];
+ // Not applicable in debug mode.
+ System.out.println(subs.getClass());
+ }
- {
- EffectivelyFinal ef = new EffectivelyFinal();
- // Not applicable in debug mode.
- System.out.println(ef.getClass());
- }
+ {
+ EffectivelyFinal ef = new EffectivelyFinal();
+ // Not applicable in debug mode.
+ System.out.println(ef.getClass());
+ }
- try {
- // To not be recognized as un-instantiated class.
- GetClassTestMain instance = new GetClassTestMain();
- System.out.println(instance.call());
- System.out.println(getMainClass(instance));
-
- System.out.println(getMainClass(null));
- throw new AssertionError("Should preserve NPE.");
- } catch (NullPointerException e) {
- // Expected
- }
-
- {
- Reflection r = new Reflection();
- // Not applicable in debug mode.
- System.out.println(r.getClass());
try {
- // Can be rewritten to const-class after inlining.
- System.out.println(r.call());
- } catch (Throwable e) {
- throw new AssertionError("Not expected any exceptions.");
+ // To not be recognized as un-instantiated class.
+ GetClassTestMain instance = new GetClassTestMain();
+ System.out.println(instance.call());
+ System.out.println(GetClassTestMain.getMainClass(instance));
+
+ System.out.println(GetClassTestMain.getMainClass(null));
+ throw new AssertionError("Should preserve NPE.");
+ } catch (NullPointerException e) {
+ // Expected
+ }
+
+ {
+ Reflection r = new Reflection();
+ // Not applicable in debug mode.
+ System.out.println(r.getClass());
+ try {
+ // Can be rewritten to const-class after inlining.
+ System.out.println(r.call());
+ } catch (Throwable e) {
+ throw new AssertionError("Not expected any exceptions.");
+ }
}
}
}
-}
-@RunWith(Parameterized.class)
-public class GetClassTest extends ReflectionOptimizerTestBase {
- private static final String JAVA_OUTPUT = StringUtils.lines(
- "class com.android.tools.r8.ir.optimize.reflection.GetClassTestMain$Base",
- "class com.android.tools.r8.ir.optimize.reflection.GetClassTestMain$Base",
- "class com.android.tools.r8.ir.optimize.reflection.GetClassTestMain$Sub",
- "class [Lcom.android.tools.r8.ir.optimize.reflection.GetClassTestMain$Sub;",
- "class com.android.tools.r8.ir.optimize.reflection.GetClassTestMain$EffectivelyFinal",
- "class com.android.tools.r8.ir.optimize.reflection.GetClassTestMain",
- "class com.android.tools.r8.ir.optimize.reflection.GetClassTestMain",
- "class com.android.tools.r8.ir.optimize.reflection.GetClassTestMain$Reflection",
- "class com.android.tools.r8.ir.optimize.reflection.GetClassTestMain$Reflection"
- );
- private static final Class<?> MAIN = GetClassTestMain.class;
+ private static final String JAVA_OUTPUT =
+ StringUtils.lines(
+ ListUtils.map(
+ ImmutableList.of(
+ Base.class.getTypeName(),
+ Base.class.getTypeName(),
+ Sub.class.getTypeName(),
+ "[L" + Sub.class.getTypeName() + ";",
+ EffectivelyFinal.class.getTypeName(),
+ GetClassTestMain.class.getTypeName(),
+ GetClassTestMain.class.getTypeName(),
+ Reflection.class.getTypeName(),
+ Reflection.class.getTypeName()),
+ l -> "class " + l));
- @Parameterized.Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+ private static final Class<?> MAIN = Main.class;
+
+ @Parameterized.Parameters(name = "{0}, mode:{1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(), CompilationMode.values());
}
private final TestParameters parameters;
+ private final CompilationMode mode;
- public GetClassTest(TestParameters parameters) {
+ public GetClassTest(TestParameters parameters, CompilationMode mode) {
this.parameters = parameters;
+ this.mode = mode;
}
private void configure(InternalOptions options) {
@@ -140,109 +154,75 @@
}
@Test
- public void testJVMOutput() throws Exception {
- assumeTrue("Only run JVM reference on CF runtimes", parameters.isCfRuntime());
+ public void testJVM() throws Exception {
+ assumeTrue(
+ "Only run JVM reference on CF runtimes",
+ parameters.isCfRuntime() && mode == CompilationMode.DEBUG);
testForJvm()
- .addTestClasspath()
+ .addInnerClasses(GetClassTest.class)
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutput(JAVA_OUTPUT);
}
private void test(
- TestRunResult<?> result, boolean isR8, boolean isRelease) throws Exception {
- CodeInspector codeInspector = result.inspector();
+ CodeInspector codeInspector,
+ boolean expectCallPresent,
+ int expectedGetClassCount,
+ int expectedConstClassCount) {
ClassSubject mainClass = codeInspector.clazz(MAIN);
MethodSubject mainMethod = mainClass.mainMethod();
assertThat(mainMethod, isPresent());
- int expectedCount = isR8 ? (isRelease ? 0 : 5) : 6;
- assertEquals(expectedCount, countGetClass(mainMethod));
- expectedCount = isR8 ? (isRelease ? (parameters.isCfRuntime() ? 8 : 6) : 1) : 0;
- assertEquals(expectedCount, countConstClass(mainMethod));
+ assertEquals(expectedGetClassCount, countGetClass(mainMethod));
+ assertEquals(expectedConstClassCount, countConstClass(mainMethod));
- boolean expectToBeOptimized = isR8 && isRelease;
-
- MethodSubject getMainClass = mainClass.uniqueMethodWithName("getMainClass");
+ ClassSubject getterClass = codeInspector.clazz(GetClassTestMain.class);
+ MethodSubject getMainClass = getterClass.uniqueMethodWithName("getMainClass");
assertThat(getMainClass, isPresent());
// Because of nullable argument, getClass() should remain.
assertEquals(1, countGetClass(getMainClass));
assertEquals(0, countConstClass(getMainClass));
- MethodSubject call = mainClass.method("java.lang.Class", "call", ImmutableList.of());
- if (isR8 && isRelease) {
+ MethodSubject call = getterClass.method("java.lang.Class", "call", ImmutableList.of());
+ if (!expectCallPresent) {
assertThat(call, not(isPresent()));
} else {
assertThat(call, isPresent());
// Because of local, only R8 release mode can rewrite getClass() to const-class.
- assertEquals(expectToBeOptimized ? 0 : 1, countGetClass(call));
- assertEquals(expectToBeOptimized ? 1 : 0, countConstClass(call));
+ assertEquals(1, countGetClass(call));
+ assertEquals(0, countConstClass(call));
}
}
@Test
public void testD8() throws Exception {
assumeTrue("Only run D8 for Dex backend", parameters.isDexRuntime());
-
- // D8 debug.
- D8TestRunResult result =
- testForD8()
- .debug()
- .addProgramClassesAndInnerClasses(MAIN)
- .addOptionsModification(this::configure)
- .setMinApi(parameters.getApiLevel())
- .run(parameters.getRuntime(), MAIN)
- .assertSuccessWithOutput(JAVA_OUTPUT);
- test(result, false, false);
-
- // D8 release.
- result =
- testForD8()
- .release()
- .addProgramClassesAndInnerClasses(MAIN)
- .addOptionsModification(this::configure)
- .setMinApi(parameters.getApiLevel())
- .run(parameters.getRuntime(), MAIN)
- .assertSuccessWithOutput(JAVA_OUTPUT);
- test(result, false, true);
+ testForD8()
+ .setMode(mode)
+ .addInnerClasses(GetClassTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), MAIN)
+ .assertSuccessWithOutput(JAVA_OUTPUT)
+ .inspect(inspector -> test(inspector, true, 6, 0));
}
@Test
public void testR8() throws Exception {
- // R8 debug, no minification.
- R8TestRunResult result =
- testForR8(parameters.getBackend())
- .debug()
- .addProgramClassesAndInnerClasses(MAIN)
- .enableInliningAnnotations()
- .addKeepMainRule(MAIN)
- .noMinification()
- .addOptionsModification(this::configure)
- .setMinApi(parameters.getApiLevel())
- .run(parameters.getRuntime(), MAIN);
- test(result, true, false);
-
- // R8 release, no minification.
- result =
- testForR8(parameters.getBackend())
- .addProgramClassesAndInnerClasses(MAIN)
- .enableInliningAnnotations()
- .addKeepMainRule(MAIN)
- .noMinification()
- .addOptionsModification(this::configure)
- .setMinApi(parameters.getApiLevel())
- .run(parameters.getRuntime(), MAIN)
- .assertSuccessWithOutput(JAVA_OUTPUT);
- test(result, true, true);
-
- // R8 release, minification.
- result =
- testForR8(parameters.getBackend())
- .addProgramClassesAndInnerClasses(MAIN)
- .enableInliningAnnotations()
- .addKeepMainRule(MAIN)
- .addOptionsModification(this::configure)
- .setMinApi(parameters.getApiLevel())
- // We are not checking output because it can't be matched due to minification. Just run.
- .run(parameters.getRuntime(), MAIN);
- test(result, true, true);
+ boolean isRelease = mode == CompilationMode.RELEASE;
+ boolean expectCallPresent = !isRelease;
+ int expectedGetClassCount = isRelease ? 0 : 5;
+ int expectedConstClassCount = isRelease ? (parameters.isCfRuntime() ? 8 : 6) : 1;
+ testForR8(parameters.getBackend())
+ .setMode(mode)
+ .addInnerClasses(GetClassTest.class)
+ .enableInliningAnnotations()
+ .addKeepMainRule(MAIN)
+ .noMinification()
+ .addOptionsModification(this::configure)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), MAIN)
+ .assertSuccessWithOutput(JAVA_OUTPUT)
+ .inspect(
+ inspector ->
+ test(inspector, expectCallPresent, expectedGetClassCount, expectedConstClassCount));
}
}