Fix optimization of Objects.requireNonNullElse
Fixes: 197057070
Change-Id: I5c2a14fd56f13fa88e091a494c106cd082d3a6d8
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectsMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectsMethodOptimizer.java
index 4b7cffb..1e518d6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectsMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectsMethodOptimizer.java
@@ -177,11 +177,14 @@
instructionIterator.removeOrReplaceByDebugLocalRead();
} else if (inValue.isAlwaysNull(appView)) {
if (singleTarget.getReference() == objectsMethods.requireNonNullElse) {
- // Optimize Objects.requireNonNullElse(null, defaultObj) into defaultObj.
- if (invoke.hasOutValue()) {
- invoke.outValue().replaceUsers(invoke.getLastArgument(), affectedValues);
+ // Optimize Objects.requireNonNullElse(null, defaultObj) into defaultObj if defaultObj
+ // is never null.
+ if (invoke.getLastArgument().isNeverNull()) {
+ if (invoke.hasOutValue()) {
+ invoke.outValue().replaceUsers(invoke.getLastArgument(), affectedValues);
+ }
+ instructionIterator.removeOrReplaceByDebugLocalRead();
}
- instructionIterator.removeOrReplaceByDebugLocalRead();
} else if (singleTarget.getReference() == objectsMethods.requireNonNullElseGet) {
// Don't optimize Objects.requireNonNullElseGet. The result of calling supplier.get() still
// needs a null-check, so two invokes will be needed.
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/library/ObjectsRequireNonNullElseTest.java b/src/test/java/com/android/tools/r8/ir/optimize/library/ObjectsRequireNonNullElseTest.java
index 39acb1e..bde8237 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/library/ObjectsRequireNonNullElseTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/library/ObjectsRequireNonNullElseTest.java
@@ -36,6 +36,16 @@
}
@Test
+ public void testD8() throws Exception {
+ testForD8(parameters.getBackend())
+ .addProgramClassFileData(getProgramClassFileData())
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Foo", "Bar", "Expected NPE");
+ }
+
+ @Test
public void test() throws Exception {
testForR8(parameters.getBackend())
.addProgramClassFileData(getProgramClassFileData())
@@ -62,7 +72,7 @@
testNullArgumentMethodSubject, not(invokesMethodWithName("requireNonNullElse")));
})
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines("Foo", "Bar");
+ .assertSuccessWithOutputLines("Foo", "Bar", "Expected NPE");
}
private byte[] getProgramClassFileData() throws IOException {
@@ -77,6 +87,12 @@
public static void main(String[] args) {
testNonNullArgument();
testNullArgument();
+ try {
+ testNullArgumentAndNullDefaultValue();
+ System.out.println("Unexpected");
+ } catch (NullPointerException e) {
+ System.out.println("Expected NPE");
+ }
}
@NeverInline
@@ -88,6 +104,11 @@
static void testNullArgument() {
System.out.println(Mock.requireNonNullElse(null, "Bar"));
}
+
+ @NeverInline
+ static void testNullArgumentAndNullDefaultValue() {
+ System.out.println(Mock.requireNonNullElse(null, null));
+ }
}
// References to this class are rewritten to java.util.Objects by transformation.