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!");
+    }
+  }
+}