Backport Optional/OptionalInt/OptionalLong/OptionalDouble.orElseThrow()

Test: tools/test.py --dex_vm all --no-internal -v *Backport*Test*
Test: tools/test.py --no-internal -v *GenerateBackportMethods*
Change-Id: I6b1e9c09f033c4a1fe95c939fdef02095ef8d5a1
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 faad0e6..5d85611 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -230,6 +230,9 @@
   public final DexString consumerDescriptor = createString("Ljava/util/function/Consumer;");
   public final DexString runnableDescriptor = createString("Ljava/lang/Runnable;");
   public final DexString optionalDescriptor = createString("Ljava/util/Optional;");
+  public final DexString optionalDoubleDescriptor = createString("Ljava/util/OptionalDouble;");
+  public final DexString optionalIntDescriptor = createString("Ljava/util/OptionalInt;");
+  public final DexString optionalLongDescriptor = createString("Ljava/util/OptionalLong;");
   public final DexString streamDescriptor = createString("Ljava/util/stream/Stream;");
   public final DexString arraysDescriptor = createString("Ljava/util/Arrays;");
 
@@ -328,6 +331,9 @@
   public final DexType consumerType = createType(consumerDescriptor);
   public final DexType runnableType = createType(runnableDescriptor);
   public final DexType optionalType = createType(optionalDescriptor);
+  public final DexType optionalDoubleType = createType(optionalDoubleDescriptor);
+  public final DexType optionalIntType = createType(optionalIntDescriptor);
+  public final DexType optionalLongType = createType(optionalLongDescriptor);
   public final DexType streamType = createType(streamDescriptor);
 
   public final DexType runtimeExceptionType = createType(runtimeExceptionDescriptor);
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 424604f..d6650ab 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
@@ -40,6 +40,7 @@
 import com.android.tools.r8.ir.desugar.backports.LongMethodRewrites;
 import com.android.tools.r8.ir.desugar.backports.NumericMethodRewrites;
 import com.android.tools.r8.ir.desugar.backports.ObjectsMethodRewrites;
+import com.android.tools.r8.ir.desugar.backports.OptionalMethodRewrites;
 import com.android.tools.r8.origin.SynthesizedOrigin;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.InternalOptions;
@@ -504,7 +505,8 @@
       // we do not desugar to avoid confusion in error messages.
       if (appView.rewritePrefix.hasRewrittenType(factory.optionalType)
           || options.minApiLevel >= AndroidApiLevel.N.getLevel()) {
-        initializeOptionalMethodProviders(factory);
+        initializeJava9OptionalMethodProviders(factory);
+        initializeJava10OptionalMethodProviders(factory);
       }
       if (appView.rewritePrefix.hasRewrittenType(factory.streamType)
           || options.minApiLevel >= AndroidApiLevel.N.getLevel()) {
@@ -1416,7 +1418,7 @@
               method, BackportedMethods::CharacterMethods_toStringCodepoint, "toStringCodepoint"));
     }
 
-    private void initializeOptionalMethodProviders(DexItemFactory factory) {
+    private void initializeJava9OptionalMethodProviders(DexItemFactory factory) {
       // Optional
       DexType optionalType = factory.optionalType;
 
@@ -1432,9 +1434,9 @@
       DexType[] optionalTypes =
           new DexType[] {
               optionalType,
-              factory.createType(factory.createString("Ljava/util/OptionalDouble;")),
-              factory.createType(factory.createString("Ljava/util/OptionalLong;")),
-              factory.createType(factory.createString("Ljava/util/OptionalInt;")),
+              factory.optionalDoubleType,
+              factory.optionalLongType,
+              factory.optionalIntType,
           };
       DexType[] streamReturnTypes =
           new DexType[] {
@@ -1487,6 +1489,37 @@
       }
     }
 
+    private void initializeJava10OptionalMethodProviders(DexItemFactory factory) {
+      // Optional{void,Int,Long,Double}.orElseThrow()
+      DexType[] optionalTypes =
+          new DexType[] {
+              factory.optionalType,
+              factory.optionalDoubleType,
+              factory.optionalLongType,
+              factory.optionalIntType,
+          };
+      DexType[] returnTypes =
+          new DexType[] {
+              factory.objectType,
+              factory.doubleType,
+              factory.longType,
+              factory.intType,
+          };
+      MethodInvokeRewriter[] rewriters =
+          new MethodInvokeRewriter[] {
+              OptionalMethodRewrites::rewriteOrElseGet,
+              OptionalMethodRewrites::rewriteDoubleOrElseGet,
+              OptionalMethodRewrites::rewriteLongOrElseGet,
+              OptionalMethodRewrites::rewriteIntOrElseGet,
+          };
+      DexString name = factory.createString("orElseThrow");
+      for (int i = 0; i < optionalTypes.length; i++) {
+        DexProto proto = factory.createProto(returnTypes[i]);
+        DexMethod method = factory.createMethod(optionalTypes[i], proto, name);
+        addProvider(new InvokeRewriter(method, rewriters[i]));
+      }
+    }
+
     private void initializeStreamMethodProviders(DexItemFactory factory) {
       // Stream
       DexType streamType = factory.streamType;
@@ -1497,7 +1530,7 @@
       DexMethod method = factory.createMethod(streamType, proto, name);
       addProvider(
           new MethodGenerator(
-              method, BackportedMethods::StreamMethods_ofNullable, "ofNullable") {});
+              method, BackportedMethods::StreamMethods_ofNullable, "ofNullable"));
     }
 
     private void warnMissingRetargetCoreLibraryMember(DexType type, AppView<?> appView) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/OptionalMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/OptionalMethodRewrites.java
new file mode 100644
index 0000000..7965b0b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/OptionalMethodRewrites.java
@@ -0,0 +1,47 @@
+// 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.ir.desugar.backports;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeVirtual;
+
+public final class OptionalMethodRewrites {
+
+  private OptionalMethodRewrites() {}
+
+  public static void rewriteOrElseGet(
+      InvokeMethod invoke, InstructionListIterator iterator, DexItemFactory factory) {
+    InvokeVirtual getInvoke = new InvokeVirtual(
+        factory.createMethod(factory.optionalType, invoke.getInvokedMethod().proto,
+            "get"), invoke.outValue(), invoke.inValues());
+    iterator.replaceCurrentInstruction(getInvoke);
+  }
+
+  public static void rewriteDoubleOrElseGet(
+      InvokeMethod invoke, InstructionListIterator iterator, DexItemFactory factory) {
+    InvokeVirtual getInvoke = new InvokeVirtual(
+        factory.createMethod(factory.optionalDoubleType, invoke.getInvokedMethod().proto,
+            "getAsDouble"), invoke.outValue(), invoke.inValues());
+    iterator.replaceCurrentInstruction(getInvoke);
+  }
+
+  public static void rewriteIntOrElseGet(
+      InvokeMethod invoke, InstructionListIterator iterator, DexItemFactory factory) {
+    InvokeVirtual getInvoke = new InvokeVirtual(
+        factory.createMethod(factory.optionalIntType, invoke.getInvokedMethod().proto,
+            "getAsInt"), invoke.outValue(), invoke.inValues());
+    iterator.replaceCurrentInstruction(getInvoke);
+  }
+
+  public static void rewriteLongOrElseGet(
+      InvokeMethod invoke, InstructionListIterator iterator, DexItemFactory factory) {
+    InvokeVirtual getInvoke = new InvokeVirtual(
+        factory.createMethod(factory.optionalLongType, invoke.getInvokedMethod().proto,
+            "getAsLong"), invoke.outValue(), invoke.inValues());
+    iterator.replaceCurrentInstruction(getInvoke);
+  }
+}
diff --git a/src/test/examplesJava10/backport/OptionalBackportJava10Main.java b/src/test/examplesJava10/backport/OptionalBackportJava10Main.java
new file mode 100644
index 0000000..3d2c919
--- /dev/null
+++ b/src/test/examplesJava10/backport/OptionalBackportJava10Main.java
@@ -0,0 +1,32 @@
+// 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 backport;
+
+import java.util.NoSuchElementException;
+import java.util.Optional;
+
+public final class OptionalBackportJava10Main {
+
+  public static void main(String[] args) {
+    testOrElseThrow();
+  }
+
+  private static void testOrElseThrow() {
+    Optional<String> present = Optional.of("hey");
+    assertEquals("hey", present.orElseThrow());
+
+    Optional<String> absent = Optional.empty();
+    try {
+      throw new AssertionError(absent.orElseThrow());
+    } catch (NoSuchElementException expected) {
+    }
+  }
+
+  private static void assertEquals(Object expected, Object actual) {
+    if (expected != actual && !expected.equals(actual)) {
+      throw new AssertionError("Expected <" + expected + "> but was <" + actual + ">");
+    }
+  }
+}
diff --git a/src/test/examplesJava10/backport/OptionalDoubleBackportJava10Main.java b/src/test/examplesJava10/backport/OptionalDoubleBackportJava10Main.java
new file mode 100644
index 0000000..1c27538
--- /dev/null
+++ b/src/test/examplesJava10/backport/OptionalDoubleBackportJava10Main.java
@@ -0,0 +1,32 @@
+// 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 backport;
+
+import java.util.NoSuchElementException;
+import java.util.OptionalDouble;
+
+public final class OptionalDoubleBackportJava10Main {
+
+  public static void main(String[] args) {
+    testOrElseThrow();
+  }
+
+  private static void testOrElseThrow() {
+    OptionalDouble present = OptionalDouble.of(2d);
+    assertEquals(2d, present.orElseThrow());
+
+    OptionalDouble absent = OptionalDouble.empty();
+    try {
+      throw new AssertionError(absent.orElseThrow());
+    } catch (NoSuchElementException expected) {
+    }
+  }
+
+  private static void assertEquals(Object expected, Object actual) {
+    if (expected != actual && !expected.equals(actual)) {
+      throw new AssertionError("Expected <" + expected + "> but was <" + actual + ">");
+    }
+  }
+}
diff --git a/src/test/examplesJava10/backport/OptionalIntBackportJava10Main.java b/src/test/examplesJava10/backport/OptionalIntBackportJava10Main.java
new file mode 100644
index 0000000..06c85f5
--- /dev/null
+++ b/src/test/examplesJava10/backport/OptionalIntBackportJava10Main.java
@@ -0,0 +1,32 @@
+// 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 backport;
+
+import java.util.NoSuchElementException;
+import java.util.OptionalInt;
+
+public final class OptionalIntBackportJava10Main {
+
+  public static void main(String[] args) {
+    testOrElseThrow();
+  }
+
+  private static void testOrElseThrow() {
+    OptionalInt present = OptionalInt.of(2);
+    assertEquals(2, present.orElseThrow());
+
+    OptionalInt absent = OptionalInt.empty();
+    try {
+      throw new AssertionError(absent.orElseThrow());
+    } catch (NoSuchElementException expected) {
+    }
+  }
+
+  private static void assertEquals(Object expected, Object actual) {
+    if (expected != actual && !expected.equals(actual)) {
+      throw new AssertionError("Expected <" + expected + "> but was <" + actual + ">");
+    }
+  }
+}
diff --git a/src/test/examplesJava10/backport/OptionalLongBackportJava10Main.java b/src/test/examplesJava10/backport/OptionalLongBackportJava10Main.java
new file mode 100644
index 0000000..e92b9a5
--- /dev/null
+++ b/src/test/examplesJava10/backport/OptionalLongBackportJava10Main.java
@@ -0,0 +1,32 @@
+// 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 backport;
+
+import java.util.NoSuchElementException;
+import java.util.OptionalLong;
+
+public final class OptionalLongBackportJava10Main {
+
+  public static void main(String[] args) {
+    testOrElseThrow();
+  }
+
+  private static void testOrElseThrow() {
+    OptionalLong present = OptionalLong.of(2L);
+    assertEquals(2L, present.orElseThrow());
+
+    OptionalLong absent = OptionalLong.empty();
+    try {
+      throw new AssertionError(absent.orElseThrow());
+    } catch (NoSuchElementException expected) {
+    }
+  }
+
+  private static void assertEquals(Object expected, Object actual) {
+    if (expected != actual && !expected.equals(actual)) {
+      throw new AssertionError("Expected <" + expected + "> but was <" + actual + ">");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava10Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava10Test.java
new file mode 100644
index 0000000..25d4081
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava10Test.java
@@ -0,0 +1,45 @@
+// 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 com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Optional;
+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 OptionalBackportJava10Test extends AbstractBackportTest {
+  @Parameters(name = "{0}")
+  public static Iterable<?> data() {
+    return getTestParameters()
+        .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
+        .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
+        .withCfRuntimesStartingFromIncluding(CfVm.JDK10)
+        .build();
+  }
+
+  private static final Path TEST_JAR =
+      Paths.get(ToolHelper.EXAMPLES_JAVA10_BUILD_DIR).resolve("backport" + JAR_EXTENSION);
+
+  public OptionalBackportJava10Test(TestParameters parameters) {
+    super(parameters, Optional.class, TEST_JAR, "backport.OptionalBackportJava10Main");
+    // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
+    // an actual API level, migrate these tests to OptionalBackportTest.
+
+    // Available since N as part of library desugaring.
+    ignoreInvokes("empty");
+    ignoreInvokes("get");
+    ignoreInvokes("of");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava10Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava10Test.java
new file mode 100644
index 0000000..97d70df
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava10Test.java
@@ -0,0 +1,46 @@
+// 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 com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Optional;
+import java.util.OptionalDouble;
+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 OptionalDoubleBackportJava10Test extends AbstractBackportTest {
+  @Parameters(name = "{0}")
+  public static Iterable<?> data() {
+    return getTestParameters()
+        .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
+        .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
+        .withCfRuntimesStartingFromIncluding(CfVm.JDK10)
+        .build();
+  }
+
+  private static final Path TEST_JAR =
+      Paths.get(ToolHelper.EXAMPLES_JAVA10_BUILD_DIR).resolve("backport" + JAR_EXTENSION);
+
+  public OptionalDoubleBackportJava10Test(TestParameters parameters) {
+    super(parameters, OptionalDouble.class, TEST_JAR, "backport.OptionalDoubleBackportJava10Main");
+    // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
+    // an actual API level, migrate these tests to OptionalBackportTest.
+
+    // Available since N as part of library desugaring.
+    ignoreInvokes("empty");
+    ignoreInvokes("getAsDouble");
+    ignoreInvokes("of");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava10Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava10Test.java
new file mode 100644
index 0000000..b44f1cf
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava10Test.java
@@ -0,0 +1,46 @@
+// 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 com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.OptionalInt;
+import java.util.OptionalLong;
+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 OptionalIntBackportJava10Test extends AbstractBackportTest {
+  @Parameters(name = "{0}")
+  public static Iterable<?> data() {
+    return getTestParameters()
+        .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
+        .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
+        .withCfRuntimesStartingFromIncluding(CfVm.JDK10)
+        .build();
+  }
+
+  private static final Path TEST_JAR =
+      Paths.get(ToolHelper.EXAMPLES_JAVA10_BUILD_DIR).resolve("backport" + JAR_EXTENSION);
+
+  public OptionalIntBackportJava10Test(TestParameters parameters) {
+    super(parameters, OptionalInt.class, TEST_JAR, "backport.OptionalIntBackportJava10Main");
+    // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
+    // an actual API level, migrate these tests to OptionalBackportTest.
+
+    // Available since N as part of library desugaring.
+    ignoreInvokes("empty");
+    ignoreInvokes("getAsInt");
+    ignoreInvokes("of");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava10Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava10Test.java
new file mode 100644
index 0000000..a4bef8c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava10Test.java
@@ -0,0 +1,45 @@
+// 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 com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.OptionalLong;
+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 OptionalLongBackportJava10Test extends AbstractBackportTest {
+  @Parameters(name = "{0}")
+  public static Iterable<?> data() {
+    return getTestParameters()
+        .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
+        .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
+        .withCfRuntimesStartingFromIncluding(CfVm.JDK10)
+        .build();
+  }
+
+  private static final Path TEST_JAR =
+      Paths.get(ToolHelper.EXAMPLES_JAVA10_BUILD_DIR).resolve("backport" + JAR_EXTENSION);
+
+  public OptionalLongBackportJava10Test(TestParameters parameters) {
+    super(parameters, OptionalLong.class, TEST_JAR, "backport.OptionalLongBackportJava10Main");
+    // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
+    // an actual API level, migrate these tests to OptionalBackportTest.
+
+    // Available since N as part of library desugaring.
+    ignoreInvokes("empty");
+    ignoreInvokes("getAsLong");
+    ignoreInvokes("of");
+  }
+}