Add tests for rewriting subtypes into FilledNewArray
Bug: b/283715197
Change-Id: I24d847474f604ce7f0b95b3592a2c57faf1b19fe
diff --git a/src/test/java/com/android/tools/r8/TestParameters.java b/src/test/java/com/android/tools/r8/TestParameters.java
index 7e64f85..904d325 100644
--- a/src/test/java/com/android/tools/r8/TestParameters.java
+++ b/src/test/java/com/android/tools/r8/TestParameters.java
@@ -112,6 +112,10 @@
return false;
}
+ public boolean canUseFilledNewArrayOnNonStringObjects() {
+ return isDexRuntime() && getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N);
+ }
+
public boolean runtimeWithClassValue() {
assert isCfRuntime() || isDexRuntime();
return isCfRuntime() || getDexRuntimeVersion().isNewerThanOrEqual(DexVm.Version.V14_0_0);
diff --git a/src/test/java/com/android/tools/r8/rewrite/arrays/FilledArrayDataRemoveCheckCastTest.java b/src/test/java/com/android/tools/r8/rewrite/arrays/FilledArrayDataRemoveCheckCastTest.java
new file mode 100644
index 0000000..1f4e6fb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/arrays/FilledArrayDataRemoveCheckCastTest.java
@@ -0,0 +1,122 @@
+// Copyright (c) 2023, 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.rewrite.arrays;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.Optional;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * This is a reproduction of b/283715197. It actually does not reproduce on our headless VMs, so we
+ * cannot assert an error - only the existences of the instruction that causes verification error.
+ */
+@RunWith(Parameterized.class)
+public class FilledArrayDataRemoveCheckCastTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters)
+ .addKeepClassAndMembersRules(Main.class)
+ .addKeepClassAndMembersRules(Base.class)
+ .addKeepClassRulesWithAllowObfuscation(Sub1.class, Sub2.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello", "World", "Hello", "World")
+ .inspect(
+ inspector -> {
+ ClassSubject mainClass = inspector.clazz(Main.class);
+ assertThat(mainClass, isPresent());
+ MethodSubject iterateSubClasses =
+ mainClass.uniqueMethodWithOriginalName("iterateSubClasses");
+ assertThat(iterateSubClasses, isPresent());
+ Optional<InstructionSubject> filledNewArrayInIterateSubClasses =
+ iterateSubClasses
+ .streamInstructions()
+ .filter(InstructionSubject::isFilledNewArray)
+ .findFirst();
+ MethodSubject iterateBaseClasses =
+ mainClass.uniqueMethodWithOriginalName("iterateBaseClasses");
+ assertThat(iterateBaseClasses, isPresent());
+ Optional<InstructionSubject> filledNewArrayInIterateBaseClasses =
+ iterateBaseClasses
+ .streamInstructions()
+ .filter(InstructionSubject::isFilledNewArray)
+ .findFirst();
+ assertEquals(
+ parameters.canUseFilledNewArrayOnNonStringObjects(),
+ filledNewArrayInIterateBaseClasses.isPresent());
+ // TODO(b/283715197): It should not be present.
+ assertEquals(
+ parameters.canUseFilledNewArrayOnNonStringObjects(),
+ filledNewArrayInIterateSubClasses.isPresent());
+ });
+ }
+
+ public abstract static class Base {
+
+ public abstract String toString();
+ }
+
+ public static class Sub1 extends Base {
+
+ @Override
+ public String toString() {
+ return "Hello";
+ }
+ }
+
+ public static class Sub2 extends Base {
+
+ @Override
+ public String toString() {
+ return "World";
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ iterateBaseClasses(new Sub1(), new Sub2());
+ iterateSubClasses();
+ }
+
+ public static void iterateBaseClasses(Base b1, Base b2) {
+ Base[] arr = new Base[] {b1, b2};
+ iterate(arr);
+ }
+
+ public static void iterateSubClasses() {
+ Base[] arr = new Base[] {new Sub1(), new Sub2()};
+ iterate(arr);
+ }
+
+ public static void iterate(Object[] arr) {
+ for (Object b : arr) {
+ System.out.println(b.toString());
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
index 23684e1..45d6202 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
@@ -380,6 +380,11 @@
}
@Override
+ public boolean isFilledNewArray() {
+ return false;
+ }
+
+ @Override
public boolean isNewArray() {
return instruction instanceof CfNewArray;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
index 16a41b1..38421a2 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
@@ -49,6 +49,7 @@
import com.android.tools.r8.dex.code.DexDivIntLit8;
import com.android.tools.r8.dex.code.DexDivLong;
import com.android.tools.r8.dex.code.DexDivLong2Addr;
+import com.android.tools.r8.dex.code.DexFilledNewArray;
import com.android.tools.r8.dex.code.DexGoto;
import com.android.tools.r8.dex.code.DexIfEq;
import com.android.tools.r8.dex.code.DexIfEqz;
@@ -624,6 +625,11 @@
}
@Override
+ public boolean isFilledNewArray() {
+ return instruction instanceof DexFilledNewArray;
+ }
+
+ @Override
public int size() {
return instruction.getSize();
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
index 7fd166e..6d1bd94 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
@@ -158,6 +158,8 @@
boolean isMonitorExit();
+ boolean isFilledNewArray();
+
int size();
InstructionOffsetSubject getOffset(MethodSubject methodSubject);