Add additional tests for not synthesizing null check
Bug: 206427323
Bug: 215339687
Bug: 214377135
Change-Id: I55ebcc7b5272f682270e31e3d9ece9e6892fc43f
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineConditionTest.java b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineConditionTest.java
new file mode 100644
index 0000000..15466f7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineConditionTest.java
@@ -0,0 +1,61 @@
+// Copyright (c) 2022, 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.naming.retrace;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.hamcrest.CoreMatchers;
+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 RetraceInlineConditionTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Throwable {
+ R8TestCompileResult compileResult =
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .compile()
+ .inspectProguardMap(
+ map -> {
+ // TODO(b/215339687): We should not have a rewriteFrame in the mapping file since
+ // an explicit null check should be inserted.
+ assertThat(map, CoreMatchers.containsString("com.android.tools.r8.rewriteFrame"));
+ });
+ }
+
+ static class Foo {
+
+ void inlinable(boolean loop) {
+ while (loop) {}
+ String string = toString();
+ System.out.println(string);
+ }
+ }
+
+ static class Main {
+ public static void main(String[] args) {
+ Foo foo = (args.length == 0 ? null : new Foo());
+ foo.inlinable(args.length == 0);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckInlinedTest.java b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckInlinedTest.java
new file mode 100644
index 0000000..6143522
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckInlinedTest.java
@@ -0,0 +1,116 @@
+// Copyright (c) 2022, 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.naming.retrace;
+
+import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.List;
+import org.junit.Before;
+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 RetraceInlineeWithNullCheckInlinedTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameter(1)
+ public boolean throwReceiverNpe;
+
+ @Parameters(name = "{0}, throwReceiverNpe: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(),
+ BooleanUtils.values());
+ }
+
+ public StackTrace expectedStackTrace;
+
+ @Before
+ public void setup() throws Exception {
+ // Get the expected stack trace by running on the JVM.
+ expectedStackTrace =
+ testForRuntime(parameters)
+ .addProgramClasses(Caller.class, Foo.class)
+ .run(parameters.getRuntime(), Caller.class, getArgs())
+ .getStackTrace();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Caller.class)
+ .addKeepAttributeLineNumberTable()
+ .addKeepAttributeSourceFile()
+ .setMinApi(parameters.getApiLevel())
+ .enableInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
+ .run(parameters.getRuntime(), Caller.class, getArgs())
+ .assertFailureWithErrorThatThrows(NullPointerException.class)
+ .inspectStackTrace(
+ (stackTrace, inspector) -> {
+ ClassSubject callerClass = inspector.clazz(Caller.class);
+ assertThat(callerClass, isPresent());
+ MethodSubject staticized = callerClass.uniqueMethodWithName("outerCaller");
+ assertThat(staticized, isPresentAndRenamed());
+ // TODO(b/214377135): The stack traces should be the same (when 206427323) is
+ // resolved.
+ assertThat(stackTrace, notIf(isSame(expectedStackTrace), throwReceiverNpe));
+ });
+ }
+
+ private String[] getArgs() {
+ return throwReceiverNpe ? new String[] {"Foo"} : new String[0];
+ }
+
+ static class Foo {
+ @NeverInline
+ @NoMethodStaticizing
+ Object notInlinable() {
+ System.out.println("Hello, world!");
+ throw new NullPointerException("Foo");
+ }
+
+ Object inlinable() {
+ return notInlinable();
+ }
+ }
+
+ static class Caller {
+
+ static void caller(Foo f) {
+ Object inlinable = f.inlinable();
+ System.out.println(inlinable);
+ }
+
+ @NeverInline
+ public static void outerCaller(Foo f) {
+ // caller should be inlined here as is. When inlinable is inlined into caller, it is done so
+ // without synthesizing a null check since the call notInlineable will throw an NPE if not
+ // staticized. We have this outer caller to check that stack traces still work correctly
+ // when the bit on throwing NPE is inlined.
+ caller(f);
+ }
+
+ public static void main(String[] args) {
+ outerCaller(args.length == 0 ? new Foo() : null);
+ }
+ }
+}