Merge "Guard check-cast elimination when in-value is null constant"
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 7758d83..fbdb9f3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -2112,6 +2112,16 @@
return false;
}
+ // If the in-value is `null` and the cast-type is a float-array type, then trivial check-cast
+ // elimination may lead to verification errors. See b/123269162.
+ if (options.canHaveArtCheckCastVerifierBug()) {
+ if (inValue.getTypeLattice().isNullType()
+ && castType.isArrayType()
+ && castType.toBaseType(dexItemFactory).isFloatType()) {
+ return false;
+ }
+ }
+
// We might see chains of casts on subtypes. It suffices to cast to the lowest subtype,
// as that will fail if a cast on a supertype would have failed.
Predicate<Instruction> isCheckcastToSubtype =
@@ -2156,6 +2166,9 @@
private boolean isTypeInaccessibleInCurrentContext(DexType type, DexEncodedMethod context) {
DexType baseType = type.toBaseType(appInfo.dexItemFactory);
+ if (baseType.isPrimitiveType()) {
+ return false;
+ }
DexClass clazz = definitionFor(baseType);
if (clazz == null) {
// Conservatively say yes.
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 3015132..3997982 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -898,4 +898,12 @@
public boolean canHaveExceptionTypeBug() {
return minApiLevel < AndroidApiLevel.Q.getLevel();
}
+
+ // Art 4.0.4 fails with a verification error when a null-literal is being passed directly to an
+ // aget instruction. We therefore need to be careful when performing trivial check-cast
+ // elimination of check-cast instructions where the value being cast is the constant null.
+ // See b/123269162.
+ public boolean canHaveArtCheckCastVerifierBug() {
+ return minApiLevel < AndroidApiLevel.J.getLevel();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/checkcast/TrivialArrayCheckCastTest.java b/src/test/java/com/android/tools/r8/ir/optimize/checkcast/TrivialArrayCheckCastTest.java
new file mode 100644
index 0000000..8cf8b26
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/checkcast/TrivialArrayCheckCastTest.java
@@ -0,0 +1,188 @@
+// 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.ir.optimize.checkcast;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+
+/** Regression test for b/123269162. */
+public class TrivialArrayCheckCastTest extends TestBase {
+
+ @Test
+ public void test() throws Exception {
+ String expectedOutput =
+ StringUtils.lines(
+ "Caught NullPointerException", "Caught NullPointerException",
+ "Caught NullPointerException", "Caught NullPointerException",
+ "Caught NullPointerException", "Caught NullPointerException",
+ "Caught NullPointerException", "Caught NullPointerException",
+ "Caught NullPointerException", "Caught NullPointerException");
+
+ testForJvm().addTestClasspath().run(TestClass.class).assertSuccessWithOutput(expectedOutput);
+
+ InternalOptions options = new InternalOptions();
+ options.minApiLevel = AndroidApiLevel.I_MR1.getLevel();
+ assert options.canHaveArtCheckCastVerifierBug();
+
+ testForR8(Backend.DEX)
+ .addInnerClasses(TrivialArrayCheckCastTest.class)
+ .addKeepMainRule(TestClass.class)
+ .enableInliningAnnotations()
+ .setMinApi(AndroidApiLevel.I_MR1)
+ .run(TestClass.class)
+ .assertSuccessWithOutput(expectedOutput);
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ testBooleanArray();
+ testByteArray();
+ testCharArray();
+ testDoubleArray();
+ testFloatArray();
+ testFloatArrayNested();
+ testIntArray();
+ testLongArray();
+ testObjectArray();
+ testShortArray();
+ }
+
+ @NeverInline
+ private static void testBooleanArray() {
+ boolean[] array = (boolean[]) null;
+ try {
+ boolean value = array[42];
+ System.out.println("Read value: " + value);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ System.out.println("Caught ArrayIndexOutOfBoundsException");
+ } catch (NullPointerException e) {
+ System.out.println("Caught NullPointerException");
+ }
+ }
+
+ @NeverInline
+ private static void testByteArray() {
+ byte[] array = (byte[]) null;
+ try {
+ byte value = array[42];
+ System.out.println("Read value: " + value);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ System.out.println("Caught ArrayIndexOutOfBoundsException");
+ } catch (NullPointerException e) {
+ System.out.println("Caught NullPointerException");
+ }
+ }
+
+ @NeverInline
+ private static void testCharArray() {
+ char[] array = (char[]) null;
+ try {
+ char value = array[42];
+ System.out.println("Read value: " + value);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ System.out.println("Caught ArrayIndexOutOfBoundsException");
+ } catch (NullPointerException e) {
+ System.out.println("Caught NullPointerException");
+ }
+ }
+
+ @NeverInline
+ private static void testDoubleArray() {
+ double[] array = (double[]) null;
+ try {
+ double value = array[42];
+ System.out.println("Read value: " + value);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ System.out.println("Caught ArrayIndexOutOfBoundsException");
+ } catch (NullPointerException e) {
+ System.out.println("Caught NullPointerException");
+ }
+ }
+
+ @NeverInline
+ private static void testFloatArray() {
+ float[] array = (float[]) null;
+ try {
+ float value = array[42];
+ System.out.println("Read value: " + value);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ System.out.println("Caught ArrayIndexOutOfBoundsException");
+ } catch (NullPointerException e) {
+ System.out.println("Caught NullPointerException");
+ }
+ }
+
+ @NeverInline
+ private static void testFloatArrayNested() {
+ float[][] nestedArray = (float[][]) null;
+ try {
+ float[] array = nestedArray[42];
+ float value = array[42];
+ System.out.println("Read value: " + value);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ System.out.println("Caught ArrayIndexOutOfBoundsException");
+ } catch (NullPointerException e) {
+ System.out.println("Caught NullPointerException");
+ }
+ }
+
+ @NeverInline
+ private static void testIntArray() {
+ int[] array = (int[]) null;
+ try {
+ int value = array[42];
+ System.out.println("Read value: " + value);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ System.out.println("Caught ArrayIndexOutOfBoundsException");
+ } catch (NullPointerException e) {
+ System.out.println("Caught NullPointerException");
+ }
+ }
+
+ @NeverInline
+ private static void testLongArray() {
+ long[] array = (long[]) null;
+ try {
+ long value = array[42];
+ System.out.println("Read value: " + value);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ System.out.println("Caught ArrayIndexOutOfBoundsException");
+ } catch (NullPointerException e) {
+ System.out.println("Caught NullPointerException");
+ }
+ }
+
+ @NeverInline
+ private static void testObjectArray() {
+ Object[] array = (Object[]) null;
+ try {
+ Object value = array[42];
+ System.out.println("Read value: " + value);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ System.out.println("Caught ArrayIndexOutOfBoundsException");
+ } catch (NullPointerException e) {
+ System.out.println("Caught NullPointerException");
+ }
+ }
+
+ @NeverInline
+ private static void testShortArray() {
+ short[] array = (short[]) null;
+ try {
+ short value = array[42];
+ System.out.println("Read value: " + value);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ System.out.println("Caught ArrayIndexOutOfBoundsException");
+ } catch (NullPointerException e) {
+ System.out.println("Caught NullPointerException");
+ }
+ }
+ }
+}