Backport Java 9 Objects methods

Test: tools/test.py --dex_vm all --no-internal -v *Backport* *corelibjdktests*
Change-Id: Icea5518fb116dea894cae253d8e4e9b30ee78b6d
diff --git a/build.gradle b/build.gradle
index 2edc245..889620f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -130,6 +130,12 @@
             include '**ExactArithTests.java'
         }
     }
+    jdk11ObjectsTests {
+        java {
+            srcDirs = ['third_party/openjdk/jdk-11-test/java/util/Objects']
+            include '**BasicObjectsTest.java'
+        }
+    }
     jdk11StrictMathTests {
         java {
             srcDirs = ['third_party/openjdk/jdk-11-test/java/lang/StrictMath']
@@ -553,6 +559,7 @@
 
 setJava11Compilation(sourceSets.examplesJava11.compileJavaTaskName)
 setJava11Compilation(sourceSets.jdk11MathTests.compileJavaTaskName)
+setJava11Compilation(sourceSets.jdk11ObjectsTests.compileJavaTaskName)
 setJava11Compilation(sourceSets.jdk11StrictMathTests.compileJavaTaskName)
 
 task compileMainWithJava11 (type: JavaCompile) {
@@ -1861,6 +1868,7 @@
         dependsOn buildPreNJdwpTestsJar
         dependsOn buildPreNJdwpTestsDex
         dependsOn sourceSets.jdk11MathTests.output
+        dependsOn sourceSets.jdk11ObjectsTests.output
         dependsOn sourceSets.jdk11StrictMathTests.output
     } else {
         logger.lifecycle("WARNING: Testing in not supported on your platform. Testing is only fully supported on " +
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 2adbb70..ee9d9ff 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -215,6 +215,7 @@
   public final DexString listDescriptor = createString("Ljava/util/List;");
   public final DexString comparatorDescriptor = createString("Ljava/util/Comparator;");
   public final DexString callableDescriptor = createString("Ljava/util/concurrent/Callable;");
+  public final DexString supplierDescriptor = createString("Ljava/util/function/Supplier;");
 
   public final DexString throwableDescriptor = createString(throwableDescriptorString);
   public final DexString illegalAccessErrorDescriptor =
@@ -294,6 +295,7 @@
   public final DexType listType = createType(listDescriptor);
   public final DexType comparatorType = createType(comparatorDescriptor);
   public final DexType callableType = createType(callableDescriptor);
+  public final DexType supplierType = createType(supplierDescriptor);
 
   public final DexType throwableType = createType(throwableDescriptor);
   public final DexType illegalAccessErrorType = createType(illegalAccessErrorDescriptor);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 7a830e8..4b617f3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -800,6 +800,36 @@
       method = factory.createString("compareUnsigned");
       proto = factory.createProto(factory.intType, factory.shortType, factory.shortType);
       addProvider(new MethodGenerator(clazz, method, proto, ShortMethods::new));
+
+      // Objects
+      clazz = factory.objectsDescriptor;
+
+      // T Objects.requireNonNullElse(T, T)
+      method = factory.createString("requireNonNullElse");
+      proto = factory.createProto(factory.objectType, factory.objectType, factory.objectType);
+      addProvider(new MethodGenerator(clazz, method, proto, ObjectsMethods::new));
+
+      // T Objects.requireNonNullElseGet(T, Supplier<? extends T>)
+      method = factory.createString("requireNonNullElseGet");
+      proto = factory.createProto(factory.objectType, factory.objectType, factory.supplierType);
+      addProvider(new MethodGenerator(clazz, method, proto, ObjectsMethods::new));
+
+      // int Objects.checkIndex(int, int)
+      method = factory.createString("checkIndex");
+      proto = factory.createProto(factory.intType, factory.intType, factory.intType);
+      addProvider(new MethodGenerator(clazz, method, proto, ObjectsMethods::new));
+
+      // int Objects.checkFromToIndex(int, int, int)
+      method = factory.createString("checkFromToIndex");
+      proto =
+          factory.createProto(factory.intType, factory.intType, factory.intType, factory.intType);
+      addProvider(new MethodGenerator(clazz, method, proto, ObjectsMethods::new));
+
+      // int Objects.checkFromIndexSize(int, int, int)
+      method = factory.createString("checkFromIndexSize");
+      proto =
+          factory.createProto(factory.intType, factory.intType, factory.intType, factory.intType);
+      addProvider(new MethodGenerator(clazz, method, proto, ObjectsMethods::new));
     }
 
     private void warnMissingRetargetCoreLibraryMember(DexType type, AppView<?> appView) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/ObjectsMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/ObjectsMethods.java
index 44e8dca..4fc3cb1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/ObjectsMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/ObjectsMethods.java
@@ -10,6 +10,7 @@
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.Objects;
+import java.util.function.Supplier;
 
 public final class ObjectsMethods extends TemplateMethodCode {
   public ObjectsMethods(InternalOptions options, DexMethod method, String methodName) {
@@ -80,6 +81,17 @@
     return obj;
   }
 
+  public static <T> T requireNonNullElse(T obj, T defaultObj) {
+    if (obj != null) return obj;
+    return Objects.requireNonNull(defaultObj, "defaultObj");
+  }
+
+  public static <T> T requireNonNullElseGet(T obj, Supplier<? extends T> supplier) {
+    if (obj != null) return obj;
+    T defaultObj = Objects.requireNonNull(supplier, "supplier").get();
+    return Objects.requireNonNull(defaultObj, "supplier.get()");
+  }
+
   public static String toString(Object o) {
     return Objects.toString(o, "null");
   }
@@ -87,4 +99,33 @@
   public static String toStringDefault(Object o, String nullDefault) {
     return o == null ? nullDefault : o.toString();
   }
+
+  public static int checkIndex(int index, int length) {
+    if (index < 0 || index >= length) {
+      throw new IndexOutOfBoundsException("Index " + index + " out of bounds for length " + length);
+    }
+    return index;
+  }
+
+  public static int checkFromToIndex(int fromIndex, int toIndex, int length) {
+    if (fromIndex < 0 || fromIndex > toIndex || toIndex > length) {
+      throw new IndexOutOfBoundsException(
+          "Range [" + fromIndex + ", " + toIndex + ") out of bounds for length " + length);
+    }
+    return fromIndex;
+  }
+
+  public static int checkFromIndexSize(int fromIndex, int size, int length) {
+    if (fromIndex < 0 || size < 0 || length < 0 || fromIndex > length - size) {
+      throw new IndexOutOfBoundsException("Range ["
+          + fromIndex
+          + ", "
+          + fromIndex
+          + " + "
+          + size
+          + ") out of bounds for length "
+          + length);
+    }
+    return fromIndex;
+  }
 }
diff --git a/src/test/examplesJava9/backport/ObjectsBackportJava9Main.java b/src/test/examplesJava9/backport/ObjectsBackportJava9Main.java
new file mode 100644
index 0000000..483b176
--- /dev/null
+++ b/src/test/examplesJava9/backport/ObjectsBackportJava9Main.java
@@ -0,0 +1,155 @@
+package backport;
+
+import java.util.Objects;
+
+public final class ObjectsBackportJava9Main {
+  public static void main(String[] args) {
+    boolean isAndroid = "Dalvik".equals(System.getProperty("java.vm.name"));
+    String majorVersion = System.getProperty("java.vm.version").split("\\.", -1)[0];
+
+    testRequireNonNullElse();
+    if (!isAndroid || Integer.parseInt(majorVersion) >= 7) {
+      // TODO desugaring corelib is blocked by https://issuetracker.google.com/issues/114481425
+      testRequireNonNullElseGet();
+    }
+    testCheckIndex();
+    testCheckFromToIndex();
+    testCheckFromIndexSize();
+  }
+
+  private static void testRequireNonNullElse() {
+    Object one = new Object();
+    Object two = new Object();
+
+    assertSame(one, Objects.requireNonNullElse(one, two));
+    assertSame(two, Objects.requireNonNullElse(null, two));
+
+    try {
+      throw new AssertionError(Objects.requireNonNullElse(null, null));
+    } catch (NullPointerException expected) {
+    }
+  }
+
+  private static void testRequireNonNullElseGet() {
+    Object one = new Object();
+    Object two = new Object();
+
+    assertSame(one, Objects.requireNonNullElseGet(one, () -> two));
+    assertSame(two, Objects.requireNonNullElseGet(null, () -> two));
+
+    try {
+      throw new AssertionError(Objects.requireNonNullElseGet(null, null));
+    } catch (NullPointerException expected) {
+    }
+    try {
+      throw new AssertionError(Objects.requireNonNullElseGet(null, () -> null));
+    } catch (NullPointerException expected) {
+    }
+  }
+
+  private static void testCheckIndex() {
+    for (int i = 0; i < 10; i++) {
+      assertEquals(i, Objects.checkIndex(i, 10));
+    }
+
+    try {
+      throw new AssertionError(Objects.checkIndex(-1, 10));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      throw new AssertionError(Objects.checkIndex(10, 0));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      throw new AssertionError(Objects.checkIndex(0, 0));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+  }
+
+  private static void testCheckFromToIndex() {
+    for (int i = 0; i <= 10; i++) {
+      for (int j = i; j <= 10; j++) {
+        assertEquals(i, Objects.checkFromToIndex(i, j, 10));
+      }
+    }
+    assertEquals(0, Objects.checkFromToIndex(0, 0, 0));
+
+    try {
+      throw new AssertionError(Objects.checkFromToIndex(4, 2, 10));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      throw new AssertionError(Objects.checkFromToIndex(-1, 5, 10));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      throw new AssertionError(Objects.checkFromToIndex(0, -1, 10));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      throw new AssertionError(Objects.checkFromToIndex(11, 11, 10));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      throw new AssertionError(Objects.checkFromToIndex(0, 1, 0));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      throw new AssertionError(Objects.checkFromToIndex(1, 1, 0));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+  }
+
+  private static void testCheckFromIndexSize() {
+    for (int i = 0; i <= 10; i++) {
+      for (int j = 10 - i; j >= 0; j--) {
+        assertEquals(i, Objects.checkFromIndexSize(i, j, 10));
+      }
+    }
+    assertEquals(0, Objects.checkFromIndexSize(0, 0, 0));
+
+    try {
+      throw new AssertionError(Objects.checkFromIndexSize(8, 4, 10));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      throw new AssertionError(Objects.checkFromIndexSize(-1, 5, 10));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      throw new AssertionError(Objects.checkFromIndexSize(11, 0, 10));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      throw new AssertionError(Objects.checkFromIndexSize(0, 1, 0));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      throw new AssertionError(Objects.checkFromIndexSize(1, 1, 0));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+
+    // Check for cases where overflow might occur producing incorrect results.
+    try {
+      throw new AssertionError(Objects.checkFromIndexSize(Integer.MAX_VALUE, 1, Integer.MAX_VALUE));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      throw new AssertionError(Objects.checkFromIndexSize(0, 1, Integer.MIN_VALUE));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+  }
+
+  private static void assertEquals(int expected, int actual) {
+    if (expected != actual) {
+      throw new AssertionError("Expected <" + expected + "> but was <" + actual + '>');
+    }
+  }
+
+  private static void assertSame(Object expected, Object actual) {
+    if (expected != actual) {
+      throw new AssertionError(
+          "Expected <" + expected + "> to be same instance as <" + actual + '>');
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava9Test.java
new file mode 100644
index 0000000..da50daf
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava9Test.java
@@ -0,0 +1,35 @@
+// Copyright (c) 2019, 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.desugar.backports;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
+@RunWith(Parameterized.class)
+public final class ObjectsBackportJava9Test extends AbstractBackportTest {
+  @Parameters(name = "{0}")
+  public static Iterable<?> data() {
+    return getTestParameters()
+        .withDexRuntimes()
+        .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+        .build();
+  }
+
+  private static final Path TEST_JAR =
+      Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR).resolve("backport" + JAR_EXTENSION);
+
+  public ObjectsBackportJava9Test(TestParameters parameters) {
+    super(parameters, Short.class, TEST_JAR, "backport.ObjectsBackportJava9Main");
+    // TODO Once shipped in an actual API level, migrate to ObjectsBackportTest
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11ObjectsTests.java b/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11ObjectsTests.java
new file mode 100644
index 0000000..00e85da
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11ObjectsTests.java
@@ -0,0 +1,73 @@
+// Copyright (c) 2019, 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.desugar.corelib.corelibjdktests;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Jdk11ObjectsTests extends TestBase {
+
+  private static final Path JDK_11_OBJECTS_TESTS_DIR =
+      Paths.get(ToolHelper.JAVA_CLASSES_DIR + "jdk11ObjectsTests");
+  private static final String BASICOBJECTSTEST = "BasicObjectsTest";
+  private static final String CLASS_SUFFIX = ".class";
+  private static final Path[] JDK_11_OBJECTS_TEST_CLASS_FILES =
+      new Path[] {
+          JDK_11_OBJECTS_TESTS_DIR.resolve(BASICOBJECTSTEST + CLASS_SUFFIX),
+      };
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters()
+        // TODO desugaring corelib is blocked by https://issuetracker.google.com/issues/114481425
+        .withDexRuntimesStartingFromIncluding(DexVm.Version.V8_1_0)
+        .withCfRuntime(CfVm.JDK11)
+        .build();
+  }
+
+  public Jdk11ObjectsTests(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testD8Objects() throws Exception {
+    Assume.assumeTrue(parameters.isDexRuntime());
+    testForD8()
+        .addProgramFiles(JDK_11_OBJECTS_TEST_CLASS_FILES)
+        .setMinApi(parameters.getRuntime())
+        .run(parameters.getRuntime(), BASICOBJECTSTEST)
+        .assertSuccessWithOutput("");
+  }
+
+  @Test
+  public void testR8Objects() throws Exception {
+    Path libraryJar;
+    if (parameters.isDexRuntime()) {
+      libraryJar = ToolHelper.getAndroidJar(parameters.getRuntime().asDex().getMinApiLevel());
+    } else {
+      libraryJar = ToolHelper.getJava8RuntimeJar();
+    }
+    testForR8(parameters.getBackend())
+        .addLibraryFiles(libraryJar)
+        .addKeepMainRule(BASICOBJECTSTEST)
+        .addProgramFiles(JDK_11_OBJECTS_TEST_CLASS_FILES)
+        .setMinApi(parameters.getRuntime())
+        .run(parameters.getRuntime(), BASICOBJECTSTEST)
+        .assertSuccessWithOutput("");
+  }
+}