Model Objects.requireNonNull() methods
Bug: 148893977
Change-Id: I66a820713d25d94862c245c35d07ccc2e83ccc3e
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 70d4425..c9a31fb 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -38,6 +38,7 @@
import com.android.tools.r8.ir.optimize.UninstantiatedTypeOptimization;
import com.android.tools.r8.ir.optimize.UnusedArgumentsCollector;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
+import com.android.tools.r8.ir.optimize.library.LibraryOptimizationInfoInitializer;
import com.android.tools.r8.jar.CfApplicationWriter;
import com.android.tools.r8.kotlin.Kotlin;
import com.android.tools.r8.kotlin.KotlinInfo;
@@ -321,6 +322,8 @@
appView.rootSet().checkAllRulesAreUsed(options);
+ new LibraryOptimizationInfoInitializer(appView).run();
+
if (options.proguardSeedsConsumer != null) {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
PrintStream out = new PrintStream(bytes);
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 8f3f853..7d568f6 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -34,6 +34,7 @@
import com.google.common.base.Strings;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Streams;
@@ -527,6 +528,9 @@
classMethods.getName,
classMethods.getSimpleName,
classMethods.forName,
+ objectsMethods.requireNonNull,
+ objectsMethods.requireNonNullWithMessage,
+ objectsMethods.requireNonNullWithMessageSupplier,
stringMethods.valueOf);
// We assume library methods listed here are `public`, i.e., free from visibility side effects.
@@ -681,11 +685,29 @@
public class ObjectsMethods {
- public DexMethod requireNonNull;
+ public final DexMethod requireNonNull;
+ public final DexMethod requireNonNullWithMessage;
+ public final DexMethod requireNonNullWithMessageSupplier;
private ObjectsMethods() {
- requireNonNull = createMethod(objectsDescriptor,
- createString("requireNonNull"), objectDescriptor, new DexString[]{objectDescriptor});
+ DexString requireNonNullMethodName = createString("requireNonNull");
+ requireNonNull =
+ createMethod(objectsType, createProto(objectType, objectType), requireNonNullMethodName);
+ requireNonNullWithMessage =
+ createMethod(
+ objectsType,
+ createProto(objectType, objectType, stringType),
+ requireNonNullMethodName);
+ requireNonNullWithMessageSupplier =
+ createMethod(
+ objectsType,
+ createProto(objectType, objectType, supplierType),
+ requireNonNullMethodName);
+ }
+
+ public Iterable<DexMethod> requireNonNullMethods() {
+ return ImmutableList.of(
+ requireNonNull, requireNonNullWithMessage, requireNonNullWithMessageSupplier);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
index 4b4099b..1b6054b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -87,7 +87,7 @@
@Override
public void methodReturnsArgument(DexEncodedMethod method, int argument) {
- // Ignored.
+ method.getMutableOptimizationInfo().markReturnsArgument(argument);
}
@Override
@@ -173,12 +173,12 @@
@Override
public void setNonNullParamOrThrow(DexEncodedMethod method, BitSet facts) {
- // Ignored.
+ method.getMutableOptimizationInfo().setNonNullParamOrThrow(facts);
}
@Override
public void setNonNullParamOnNormalExits(DexEncodedMethod method, BitSet facts) {
- // Ignored.
+ method.getMutableOptimizationInfo().setNonNullParamOnNormalExits(facts);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
new file mode 100644
index 0000000..2eb0450
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
@@ -0,0 +1,46 @@
+// Copyright (c) 2020, 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.library;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
+import java.util.BitSet;
+
+public class LibraryOptimizationInfoInitializer {
+
+ private final AppView<?> appView;
+ private final DexItemFactory dexItemFactory;
+
+ private final OptimizationFeedbackSimple feedback = OptimizationFeedbackSimple.getInstance();
+
+ public LibraryOptimizationInfoInitializer(AppView<?> appView) {
+ this.appView = appView;
+ this.dexItemFactory = appView.dexItemFactory();
+ }
+
+ public void run() {
+ modelRequireNonNullMethods();
+ }
+
+ private void modelRequireNonNullMethods() {
+ for (DexMethod requireNonNullMethod : dexItemFactory.objectsMethods.requireNonNullMethods()) {
+ DexEncodedMethod definition = appView.definitionFor(requireNonNullMethod);
+ if (definition != null) {
+ feedback.methodReturnsArgument(definition, 0);
+
+ BitSet nonNullParamOrThrow = new BitSet();
+ nonNullParamOrThrow.set(0);
+ feedback.setNonNullParamOrThrow(definition, nonNullParamOrThrow);
+
+ BitSet nonNullParamOnNormalExits = new BitSet();
+ nonNullParamOnNormalExits.set(0);
+ feedback.setNonNullParamOnNormalExits(definition, nonNullParamOnNormalExits);
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/nonnull/RequireNonNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/nonnull/RequireNonNullTest.java
new file mode 100644
index 0000000..a6f8740
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/nonnull/RequireNonNullTest.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2020, 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.nonnull;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.util.Objects;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RequireNonNullTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public RequireNonNullTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(RequireNonNullTest.class)
+ .addKeepMainRule(TestClass.class)
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Live!", "Live!");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject testClassSubject = inspector.clazz(TestClass.class);
+ assertThat(testClassSubject.uniqueMethodWithName("live"), isPresent());
+ assertThat(testClassSubject.uniqueMethodWithName("dead"), not(isPresent()));
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ Object a = System.currentTimeMillis() >= 0 ? new Object() : null;
+ Objects.requireNonNull(a);
+ if (a != null) {
+ live();
+ } else {
+ dead();
+ }
+
+ Object b = System.currentTimeMillis() >= 0 ? new Object() : null;
+ Object c = Objects.requireNonNull(b);
+ if (c != null) {
+ live();
+ } else {
+ dead();
+ }
+ }
+
+ @NeverInline
+ static void live() {
+ System.out.println("Live!");
+ }
+
+ @NeverInline
+ static void dead() {
+ System.out.println("Dead!");
+ }
+ }
+}