Reproduction of instanceof on null values
On JVM the accessibility of the type parameter is not considered
when the stack argument is the null value.
On DEX an IAE is thrown. The D8 workaround for b/288376353 will align
the behavior with JVM in the instances where the SSA value is statically
known to be the null value.
Bug: b/288273207
Change-Id: Ic09c6ebceb089a7f40d3ecf5a1d7d70637ba3993
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 19e91a5..5674c90 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -764,11 +764,12 @@
// by the program at runtime.
.put("005-annotations", TestCondition.match(TestCondition.D8_COMPILER))
// On Art 4.4.4 we have fewer refs than expected (except for d8 when compiled with dx).
- .put("072-precise-gc",
+ .put(
+ "072-precise-gc",
TestCondition.match(
- TestCondition.R8_COMPILER,
- TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
- .put("072-precise-gc",
+ TestCondition.R8_COMPILER, TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
+ .put(
+ "072-precise-gc",
TestCondition.match(
TestCondition.tools(DexTool.NONE),
TestCondition.D8_COMPILER,
@@ -779,16 +780,23 @@
.put("099-vmdebug", TestCondition.any())
// This test relies on output on stderr, which we currently do not collect.
.put("143-string-value", TestCondition.any())
- .put("800-smali",
+ .put(
+ "800-smali",
TestCondition.match(
TestCondition.D8_COMPILER,
TestCondition.runtimes(DexVm.Version.V5_1_1, DexVm.Version.V6_0_1)))
// Triggers regression test in 6.0.1 when using R8/D8 in debug mode.
- .put("474-fp-sub-neg",
+ .put(
+ "474-fp-sub-neg",
TestCondition.match(
TestCondition.tools(DexTool.NONE),
TestCondition.D8_NOT_AFTER_R8CF_COMPILER,
TestCondition.runtimes(DexVm.Version.V6_0_1)))
+ .put(
+ "536-checker-needs-access-check",
+ // DEX VMs incorrectly check access before checking null-value in instanceof.
+ // See IllegalAccessDeadInstanceOfTest and b/288376353.
+ TestCondition.anyDexVm())
.build();
private static final TestCondition beforeAndroidN =
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/instanceofremoval/illegalaccess/IllegalAccessDeadInstanceOfTest.java b/src/test/java/com/android/tools/r8/ir/optimize/instanceofremoval/illegalaccess/IllegalAccessDeadInstanceOfTest.java
new file mode 100644
index 0000000..68b19e4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/instanceofremoval/illegalaccess/IllegalAccessDeadInstanceOfTest.java
@@ -0,0 +1,70 @@
+// 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.ir.optimize.instanceofremoval.illegalaccess;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ir.optimize.instanceofremoval.illegalaccess.pkg.A;
+import java.io.IOException;
+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;
+
+@RunWith(Parameterized.class)
+public class IllegalAccessDeadInstanceOfTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ public IllegalAccessDeadInstanceOfTest() {}
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(Main.class)
+ .addProgramClassFileData(getTransformedA())
+ .run(this.parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("false");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class)
+ .addProgramClassFileData(getTransformedA())
+ .addKeepMainRule(Main.class)
+ .setMinApi(this.parameters)
+ .run(this.parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("false");
+ }
+
+ private static byte[] getTransformedA() throws IOException {
+ return transformer(A.class).setClassAccessFlags(0).transform();
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(check());
+ }
+
+ private static boolean check() {
+ Object o = null;
+ // A is transformed to be a non-public class in a different package.
+ if (o instanceof A) {
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/instanceofremoval/illegalaccess/pkg/A.java b/src/test/java/com/android/tools/r8/ir/optimize/instanceofremoval/illegalaccess/pkg/A.java
new file mode 100644
index 0000000..989006a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/instanceofremoval/illegalaccess/pkg/A.java
@@ -0,0 +1,7 @@
+// 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.ir.optimize.instanceofremoval.illegalaccess.pkg;
+
+public /* will be private */ class A {}
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index 2e3df74..5c8821d 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -187,6 +187,22 @@
return classReference;
}
+ public ClassFileTransformer setClassAccessFlags(int accessFlags) {
+ return addClassTransformer(
+ new ClassTransformer() {
+ @Override
+ public void visit(
+ int version,
+ int access,
+ String name,
+ String signature,
+ String superName,
+ String[] interfaces) {
+ super.visit(version, accessFlags, name, signature, superName, interfaces);
+ }
+ });
+ }
+
/** Unconditionally replace the implements clause of a class. */
public ClassFileTransformer setImplements(Class<?>... interfaces) {
return setImplementsClassDescriptors(