BackportedMethods for Android U

- Fix Math.StrictMath missing methods
- Fix Objects methods
- Fix levels at which some desugaring are disabled

RELNOTES: R8 now backports methods of Objects and Math/StrictMath up to Android U.

Change-Id: I289a676af68e74ba34dad1c0a8f08a98451d0cf1
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 3826986..a7771eb 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
@@ -253,18 +253,18 @@
         if (typeIsPresentWithoutBackportsFrom(factory.optionalType, AndroidApiLevel.T)) {
           initializeAndroidOptionalTMethodProviders(factory);
         }
+        if (typeIsPresentWithoutNeverIntroducedBackports(factory.predicateType)) {
+          initializeAndroidTPredicateMethodProviders(factory);
+        }
+      }
+      if (options.getMinApiLevel().isLessThan(AndroidApiLevel.U)) {
+        if (typeIsPresentWithoutNeverIntroducedBackports(factory.streamType)) {
+          initializeAndroidUStreamMethodProviders(factory);
+        }
+        initializeAndroidUMethodProviders(factory);
       }
 
-      // These are currently not implemented at any API level in Android.
-      if (typeIsPresentWithoutNeverIntroducedBackports(factory.streamType)) {
-        initializeStreamMethodProviders(factory);
-      }
-      if (typeIsPresentWithoutNeverIntroducedBackports(factory.predicateType)) {
-        initializePredicateMethodProviders(factory);
-      }
-      initializeJava9MethodProviders(factory);
-      initializeJava10MethodProviders(factory);
-      initializeJava11MethodProviders(factory);
+      initializeMethodProvidersUnimplementedOnAndroid(factory);
     }
 
     private Map<DexType, AndroidApiLevel> initializeTypeMinApi(DexItemFactory factory) {
@@ -803,9 +803,17 @@
         addProvider(new MethodGenerator(method, BackportedMethods::MathMethods_toIntExact));
       }
 
-      // Math (APIs which are not mirrored by StrictMath)
-      type = factory.mathType;
+      initializeMathExactApis(factory, factory.mathType);
+    }
 
+    /**
+     * Math APIs which are mirrored by StrictMath in a later Android api level than the one they are
+     * introduced for Math.
+     */
+    private void initializeMathExactApis(DexItemFactory factory, DexType type) {
+      DexMethod method;
+      DexProto proto;
+      DexString name;
       // int Math.decrementExact(int)
       name = factory.createString("decrementExact");
       proto = factory.createProto(factory.intType, factory.intType);
@@ -1590,15 +1598,57 @@
       }
     }
 
-    private void initializeJava9MethodProviders(DexItemFactory factory) {
-      // Nothing right now.
+    private void initializeAndroidUMethodProviders(DexItemFactory factory) {
+      DexType type;
+      DexString name;
+      DexProto proto;
+      DexMethod method;
+
+      // Objects
+      type = factory.objectsType;
+
+      // long Objects.checkIndex(long, long)
+      name = factory.createString("checkIndex");
+      proto = factory.createProto(factory.longType, factory.longType, factory.longType);
+      method = factory.createMethod(type, proto, name);
+      addProvider(new MethodGenerator(method, BackportedMethods::ObjectsMethods_checkIndexLong));
+
+      // long Objects.checkFromToIndex(long, long, long)
+      name = factory.createString("checkFromToIndex");
+      proto =
+          factory.createProto(
+              factory.longType, factory.longType, factory.longType, factory.longType);
+      method = factory.createMethod(type, proto, name);
+      addProvider(
+          new MethodGenerator(method, BackportedMethods::ObjectsMethods_checkFromToIndexLong));
+
+      // long Objects.checkFromIndexSize(long, long, long)
+      name = factory.createString("checkFromIndexSize");
+      proto =
+          factory.createProto(
+              factory.longType, factory.longType, factory.longType, factory.longType);
+      method = factory.createMethod(type, proto, name);
+      addProvider(
+          new MethodGenerator(method, BackportedMethods::ObjectsMethods_checkFromIndexSizeLong));
+
+      DexType[] mathTypes = {factory.mathType, factory.strictMathType};
+      for (DexType mathType : mathTypes) {
+        // int {StrictMath.Math}.absExact(int)
+        // long {StrictMath.Math}.absExact(long)
+        name = factory.createString("absExact");
+        proto = factory.createProto(factory.intType, factory.intType);
+        method = factory.createMethod(mathType, proto, name);
+        addProvider(new MethodGenerator(method, BackportedMethods::MathMethods_absExact));
+
+        proto = factory.createProto(factory.longType, factory.longType);
+        method = factory.createMethod(mathType, proto, name);
+        addProvider(new MethodGenerator(method, BackportedMethods::MathMethods_absExactLong));
+      }
+
+      initializeMathExactApis(factory, factory.strictMathType);
     }
 
-    private void initializeJava10MethodProviders(DexItemFactory factory) {
-      // Nothing right now.
-    }
-
-    private void initializeJava11MethodProviders(DexItemFactory factory) {
+    private void initializeMethodProvidersUnimplementedOnAndroid(DexItemFactory factory) {
       // Character
       DexType type = factory.boxedCharType;
 
@@ -1622,7 +1672,7 @@
           new MethodGenerator(method, BackportedMethods::CharSequenceMethods_compare, "compare"));
     }
 
-    private void initializeStreamMethodProviders(DexItemFactory factory) {
+    private void initializeAndroidUStreamMethodProviders(DexItemFactory factory) {
       // Stream
       DexType streamType = factory.streamType;
 
@@ -1634,7 +1684,7 @@
           new MethodGenerator(method, BackportedMethods::StreamMethods_ofNullable, "ofNullable"));
     }
 
-    private void initializePredicateMethodProviders(DexItemFactory factory) {
+    private void initializeAndroidTPredicateMethodProviders(DexItemFactory factory) {
       // Predicate
       DexType predicateType = factory.predicateType;
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
index d2efd78..2fdebe0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
@@ -4171,6 +4171,97 @@
         ImmutableList.of());
   }
 
+  public static CfCode MathMethods_absExact(DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        3,
+        1,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.INT, 0),
+            new CfConstNumber(-2147483648, ValueType.INT),
+            new CfIfCmp(If.Type.NE, ValueType.INT, label2),
+            label1,
+            new CfNew(factory.createType("Ljava/lang/ArithmeticException;")),
+            new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+            new CfConstString(
+                factory.createString("Overflow to represent absolute value of Integer.MIN_VALUE")),
+            new CfInvoke(
+                183,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/ArithmeticException;"),
+                    factory.createProto(factory.voidType, factory.stringType),
+                    factory.createString("<init>")),
+                false),
+            new CfThrow(),
+            label2,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(new int[] {0}, new FrameType[] {FrameType.intType()})),
+            new CfLoad(ValueType.INT, 0),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType),
+                    factory.createString("abs")),
+                false),
+            new CfReturn(ValueType.INT),
+            label3),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode MathMethods_absExactLong(DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        4,
+        2,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.LONG, 0),
+            new CfConstNumber(-9223372036854775808L, ValueType.LONG),
+            new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
+            new CfIf(If.Type.NE, ValueType.INT, label2),
+            label1,
+            new CfNew(factory.createType("Ljava/lang/ArithmeticException;")),
+            new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+            new CfConstString(
+                factory.createString("Overflow to represent absolute value of Long.MIN_VALUE")),
+            new CfInvoke(
+                183,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/ArithmeticException;"),
+                    factory.createProto(factory.voidType, factory.stringType),
+                    factory.createString("<init>")),
+                false),
+            new CfThrow(),
+            label2,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {FrameType.longType(), FrameType.longHighType()})),
+            new CfLoad(ValueType.LONG, 0),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.longType, factory.longType),
+                    factory.createString("abs")),
+                false),
+            new CfReturn(ValueType.LONG),
+            label3),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
   public static CfCode MathMethods_addExactInt(DexItemFactory factory, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
@@ -5819,6 +5910,157 @@
         ImmutableList.of());
   }
 
+  public static CfCode ObjectsMethods_checkFromIndexSizeLong(
+      DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        6,
+        6,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.LONG, 0),
+            new CfConstNumber(0, ValueType.LONG),
+            new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
+            new CfIf(If.Type.LT, ValueType.INT, label1),
+            new CfLoad(ValueType.LONG, 2),
+            new CfConstNumber(0, ValueType.LONG),
+            new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
+            new CfIf(If.Type.LT, ValueType.INT, label1),
+            new CfLoad(ValueType.LONG, 4),
+            new CfConstNumber(0, ValueType.LONG),
+            new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
+            new CfIf(If.Type.LT, ValueType.INT, label1),
+            new CfLoad(ValueType.LONG, 0),
+            new CfLoad(ValueType.LONG, 4),
+            new CfLoad(ValueType.LONG, 2),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.LONG),
+            new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
+            new CfIf(If.Type.LE, ValueType.INT, label2),
+            label1,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5},
+                    new FrameType[] {
+                      FrameType.longType(),
+                      FrameType.longHighType(),
+                      FrameType.longType(),
+                      FrameType.longHighType(),
+                      FrameType.longType(),
+                      FrameType.longHighType()
+                    })),
+            new CfNew(factory.createType("Ljava/lang/IndexOutOfBoundsException;")),
+            new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+            new CfNew(factory.stringBuilderType),
+            new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+            new CfInvoke(
+                183,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.voidType),
+                    factory.createString("<init>")),
+                false),
+            new CfConstString(factory.createString("Range [")),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.stringBuilderType, factory.stringType),
+                    factory.createString("append")),
+                false),
+            new CfLoad(ValueType.LONG, 0),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.stringBuilderType, factory.longType),
+                    factory.createString("append")),
+                false),
+            new CfConstString(factory.createString(", ")),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.stringBuilderType, factory.stringType),
+                    factory.createString("append")),
+                false),
+            new CfLoad(ValueType.LONG, 0),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.stringBuilderType, factory.longType),
+                    factory.createString("append")),
+                false),
+            new CfConstString(factory.createString(" + ")),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.stringBuilderType, factory.stringType),
+                    factory.createString("append")),
+                false),
+            new CfLoad(ValueType.LONG, 2),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.stringBuilderType, factory.longType),
+                    factory.createString("append")),
+                false),
+            new CfConstString(factory.createString(") out of bounds for length ")),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.stringBuilderType, factory.stringType),
+                    factory.createString("append")),
+                false),
+            new CfLoad(ValueType.LONG, 4),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.stringBuilderType, factory.longType),
+                    factory.createString("append")),
+                false),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.stringType),
+                    factory.createString("toString")),
+                false),
+            new CfInvoke(
+                183,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/IndexOutOfBoundsException;"),
+                    factory.createProto(factory.voidType, factory.stringType),
+                    factory.createString("<init>")),
+                false),
+            new CfThrow(),
+            label2,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5},
+                    new FrameType[] {
+                      FrameType.longType(),
+                      FrameType.longHighType(),
+                      FrameType.longType(),
+                      FrameType.longHighType(),
+                      FrameType.longType(),
+                      FrameType.longHighType()
+                    })),
+            new CfLoad(ValueType.LONG, 0),
+            new CfReturn(ValueType.LONG),
+            label3),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
   public static CfCode ObjectsMethods_checkFromToIndex(DexItemFactory factory, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
@@ -5933,6 +6175,135 @@
         ImmutableList.of());
   }
 
+  public static CfCode ObjectsMethods_checkFromToIndexLong(
+      DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        5,
+        6,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.LONG, 0),
+            new CfConstNumber(0, ValueType.LONG),
+            new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
+            new CfIf(If.Type.LT, ValueType.INT, label1),
+            new CfLoad(ValueType.LONG, 0),
+            new CfLoad(ValueType.LONG, 2),
+            new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
+            new CfIf(If.Type.GT, ValueType.INT, label1),
+            new CfLoad(ValueType.LONG, 2),
+            new CfLoad(ValueType.LONG, 4),
+            new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
+            new CfIf(If.Type.LE, ValueType.INT, label2),
+            label1,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5},
+                    new FrameType[] {
+                      FrameType.longType(),
+                      FrameType.longHighType(),
+                      FrameType.longType(),
+                      FrameType.longHighType(),
+                      FrameType.longType(),
+                      FrameType.longHighType()
+                    })),
+            new CfNew(factory.createType("Ljava/lang/IndexOutOfBoundsException;")),
+            new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+            new CfNew(factory.stringBuilderType),
+            new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+            new CfInvoke(
+                183,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.voidType),
+                    factory.createString("<init>")),
+                false),
+            new CfConstString(factory.createString("Range [")),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.stringBuilderType, factory.stringType),
+                    factory.createString("append")),
+                false),
+            new CfLoad(ValueType.LONG, 0),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.stringBuilderType, factory.longType),
+                    factory.createString("append")),
+                false),
+            new CfConstString(factory.createString(", ")),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.stringBuilderType, factory.stringType),
+                    factory.createString("append")),
+                false),
+            new CfLoad(ValueType.LONG, 2),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.stringBuilderType, factory.longType),
+                    factory.createString("append")),
+                false),
+            new CfConstString(factory.createString(") out of bounds for length ")),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.stringBuilderType, factory.stringType),
+                    factory.createString("append")),
+                false),
+            new CfLoad(ValueType.LONG, 4),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.stringBuilderType, factory.longType),
+                    factory.createString("append")),
+                false),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.stringType),
+                    factory.createString("toString")),
+                false),
+            new CfInvoke(
+                183,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/IndexOutOfBoundsException;"),
+                    factory.createProto(factory.voidType, factory.stringType),
+                    factory.createString("<init>")),
+                false),
+            new CfThrow(),
+            label2,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5},
+                    new FrameType[] {
+                      FrameType.longType(),
+                      FrameType.longHighType(),
+                      FrameType.longType(),
+                      FrameType.longHighType(),
+                      FrameType.longType(),
+                      FrameType.longHighType()
+                    })),
+            new CfLoad(ValueType.LONG, 0),
+            new CfReturn(ValueType.LONG),
+            label3),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
   public static CfCode ObjectsMethods_checkIndex(DexItemFactory factory, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
@@ -6022,6 +6393,110 @@
         ImmutableList.of());
   }
 
+  public static CfCode ObjectsMethods_checkIndexLong(DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        5,
+        4,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.LONG, 0),
+            new CfConstNumber(0, ValueType.LONG),
+            new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
+            new CfIf(If.Type.LT, ValueType.INT, label1),
+            new CfLoad(ValueType.LONG, 0),
+            new CfLoad(ValueType.LONG, 2),
+            new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
+            new CfIf(If.Type.LT, ValueType.INT, label2),
+            label1,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.longType(),
+                      FrameType.longHighType(),
+                      FrameType.longType(),
+                      FrameType.longHighType()
+                    })),
+            new CfNew(factory.createType("Ljava/lang/IndexOutOfBoundsException;")),
+            new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+            new CfNew(factory.stringBuilderType),
+            new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+            new CfInvoke(
+                183,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.voidType),
+                    factory.createString("<init>")),
+                false),
+            new CfConstString(factory.createString("Index ")),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.stringBuilderType, factory.stringType),
+                    factory.createString("append")),
+                false),
+            new CfLoad(ValueType.LONG, 0),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.stringBuilderType, factory.longType),
+                    factory.createString("append")),
+                false),
+            new CfConstString(factory.createString(" out of bounds for length ")),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.stringBuilderType, factory.stringType),
+                    factory.createString("append")),
+                false),
+            new CfLoad(ValueType.LONG, 2),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.stringBuilderType, factory.longType),
+                    factory.createString("append")),
+                false),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.stringType),
+                    factory.createString("toString")),
+                false),
+            new CfInvoke(
+                183,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/IndexOutOfBoundsException;"),
+                    factory.createProto(factory.voidType, factory.stringType),
+                    factory.createString("<init>")),
+                false),
+            new CfThrow(),
+            label2,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.longType(),
+                      FrameType.longHighType(),
+                      FrameType.longType(),
+                      FrameType.longHighType()
+                    })),
+            new CfLoad(ValueType.LONG, 0),
+            new CfReturn(ValueType.LONG),
+            label3),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
   public static CfCode ObjectsMethods_compare(DexItemFactory factory, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
diff --git a/src/test/examplesJava17/backport/MathBackportJava17Main.java b/src/test/examplesJava17/backport/MathBackportJava17Main.java
new file mode 100644
index 0000000..7eeed95
--- /dev/null
+++ b/src/test/examplesJava17/backport/MathBackportJava17Main.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2023, 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;
+
+public class MathBackportJava17Main {
+
+  public static void main(String[] args) {
+    // The methods are actually from Java 15, but we can test them from Java 17.
+    testAbsExactInteger();
+    testAbsExactLong();
+  }
+
+  private static void testAbsExactInteger() {
+    assertEquals(42, Math.absExact(42));
+    assertEquals(42, Math.absExact(-42));
+    assertEquals(Integer.MAX_VALUE, Math.absExact(Integer.MAX_VALUE));
+    try {
+      throw new AssertionError(Math.absExact(Integer.MIN_VALUE));
+    } catch (ArithmeticException expected) {
+
+    }
+  }
+
+  private static void testAbsExactLong() {
+    assertEquals(42L, Math.absExact(42L));
+    assertEquals(42L, Math.absExact(-42L));
+    assertEquals(Long.MAX_VALUE, Math.absExact(Long.MAX_VALUE));
+    try {
+      throw new AssertionError(Math.absExact(Long.MIN_VALUE));
+    } catch (ArithmeticException expected) {
+
+    }
+  }
+
+  private static void assertEquals(int expected, int actual) {
+    if (expected != actual) {
+      throw new AssertionError("Expected <" + expected + "> but was <" + actual + '>');
+    }
+  }
+
+  private static void assertEquals(long expected, long actual) {
+    if (expected != actual) {
+      throw new AssertionError("Expected <" + expected + "> but was <" + actual + '>');
+    }
+  }
+}
diff --git a/src/test/examplesJava17/backport/ObjectsBackportJava17Main.java b/src/test/examplesJava17/backport/ObjectsBackportJava17Main.java
new file mode 100644
index 0000000..b950c17
--- /dev/null
+++ b/src/test/examplesJava17/backport/ObjectsBackportJava17Main.java
@@ -0,0 +1,116 @@
+// Copyright (c) 2023, 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.Objects;
+
+public class ObjectsBackportJava17Main {
+
+  public static void main(String[] args) {
+    // The methods are actually from Java 16, but we can test them from Java 17.
+    testCheckIndex();
+    testCheckFromToIndex();
+    testCheckFromIndexSize();
+  }
+
+  private static void testCheckIndex() {
+    for (long i = 0L; i < 10L; i++) {
+      assertEquals(i, Objects.checkIndex(i, 10L));
+    }
+
+    try {
+      throw new AssertionError(Objects.checkIndex(-1L, 10L));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      throw new AssertionError(Objects.checkIndex(10L, 0L));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      throw new AssertionError(Objects.checkIndex(0L, 0L));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+  }
+
+  private static void testCheckFromToIndex() {
+    for (long i = 0L; i <= 10L; i++) {
+      for (long j = i; j <= 10L; j++) {
+        assertEquals(i, Objects.checkFromToIndex(i, j, 10L));
+      }
+    }
+    assertEquals(0L, Objects.checkFromToIndex(0L, 0L, 0L));
+
+    try {
+      throw new AssertionError(Objects.checkFromToIndex(4L, 2L, 10L));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      throw new AssertionError(Objects.checkFromToIndex(-1L, 5L, 10L));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      throw new AssertionError(Objects.checkFromToIndex(0L, -1L, 10L));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      throw new AssertionError(Objects.checkFromToIndex(11L, 11L, 10L));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      throw new AssertionError(Objects.checkFromToIndex(0L, 1L, 0L));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      throw new AssertionError(Objects.checkFromToIndex(1L, 1L, 0L));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+  }
+
+  private static void testCheckFromIndexSize() {
+    for (long i = 0L; i <= 10L; i++) {
+      for (long j = 10L - i; j >= 0L; j--) {
+        assertEquals(i, Objects.checkFromIndexSize(i, j, 10L));
+      }
+    }
+    assertEquals(0, Objects.checkFromIndexSize(0L, 0L, 0L));
+
+    try {
+      throw new AssertionError(Objects.checkFromIndexSize(8L, 4L, 10L));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      throw new AssertionError(Objects.checkFromIndexSize(-1L, 5L, 10L));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      throw new AssertionError(Objects.checkFromIndexSize(11L, 0L, 10L));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      throw new AssertionError(Objects.checkFromIndexSize(0L, 1L, 0L));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      throw new AssertionError(Objects.checkFromIndexSize(1L, 1L, 0L));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+
+    // Check for cases where overflow might occur producing incorrect results.
+    try {
+      throw new AssertionError(Objects.checkFromIndexSize(Long.MAX_VALUE, 1L, Long.MAX_VALUE));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      throw new AssertionError(Objects.checkFromIndexSize(0L, 1L, Long.MIN_VALUE));
+    } catch (IndexOutOfBoundsException expected) {
+    }
+  }
+
+  private static void assertEquals(long expected, long actual) {
+    if (expected != actual) {
+      throw new AssertionError("Expected <" + expected + "> but was <" + actual + '>');
+    }
+  }
+}
diff --git a/src/test/examplesJava17/backport/StrictMathBackportJava17Main.java b/src/test/examplesJava17/backport/StrictMathBackportJava17Main.java
new file mode 100644
index 0000000..028e24f
--- /dev/null
+++ b/src/test/examplesJava17/backport/StrictMathBackportJava17Main.java
@@ -0,0 +1,119 @@
+// Copyright (c) 2023, 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;
+
+public class StrictMathBackportJava17Main {
+
+  public static void main(String[] args) {
+    // The methods are actually from Java 15, but we can test them from Java 17.
+    testAbsExactInteger();
+    testAbsExactLong();
+    // The methods are actually from Java 14, but we can test them from Java 17.
+    testDecrementExactInteger();
+    testDecrementExactLong();
+    testIncrementExactInteger();
+    testIncrementExactLong();
+    testNegateExactInteger();
+    testNegateExactLong();
+  }
+
+  private static void testAbsExactInteger() {
+    assertEquals(42, StrictMath.absExact(42));
+    assertEquals(42, StrictMath.absExact(-42));
+    assertEquals(Integer.MAX_VALUE, StrictMath.absExact(Integer.MAX_VALUE));
+    try {
+      throw new AssertionError(StrictMath.absExact(Integer.MIN_VALUE));
+    } catch (ArithmeticException expected) {
+
+    }
+  }
+
+  private static void testAbsExactLong() {
+    assertEquals(42L, StrictMath.absExact(42L));
+    assertEquals(42L, StrictMath.absExact(-42L));
+    assertEquals(Long.MAX_VALUE, StrictMath.absExact(Long.MAX_VALUE));
+    try {
+      throw new AssertionError(StrictMath.absExact(Long.MIN_VALUE));
+    } catch (ArithmeticException expected) {
+
+    }
+  }
+
+  private static void testDecrementExactInteger() {
+    assertEquals(-1, StrictMath.decrementExact(0));
+    assertEquals(Integer.MIN_VALUE, StrictMath.decrementExact(Integer.MIN_VALUE + 1));
+
+    try {
+      throw new AssertionError(StrictMath.decrementExact(Integer.MIN_VALUE));
+    } catch (ArithmeticException expected) {
+    }
+  }
+
+  private static void testDecrementExactLong() {
+    assertEquals(-1L, StrictMath.decrementExact(0L));
+    assertEquals(Long.MIN_VALUE, StrictMath.decrementExact(Long.MIN_VALUE + 1L));
+
+    try {
+      throw new AssertionError(StrictMath.decrementExact(Long.MIN_VALUE));
+    } catch (ArithmeticException expected) {
+    }
+  }
+
+  private static void testIncrementExactInteger() {
+    assertEquals(1, StrictMath.incrementExact(0));
+    assertEquals(Integer.MAX_VALUE, StrictMath.incrementExact(Integer.MAX_VALUE - 1));
+
+    try {
+      throw new AssertionError(StrictMath.incrementExact(Integer.MAX_VALUE));
+    } catch (ArithmeticException expected) {
+    }
+  }
+
+  private static void testIncrementExactLong() {
+    assertEquals(1L, StrictMath.incrementExact(0L));
+    assertEquals(Long.MAX_VALUE, StrictMath.incrementExact(Long.MAX_VALUE - 1L));
+
+    try {
+      throw new AssertionError(StrictMath.incrementExact(Long.MAX_VALUE));
+    } catch (ArithmeticException expected) {
+    }
+  }
+
+  private static void testNegateExactInteger() {
+    assertEquals(0, StrictMath.negateExact(0));
+    assertEquals(-1, StrictMath.negateExact(1));
+    assertEquals(1, StrictMath.negateExact(-1));
+    assertEquals(-2_147_483_647, StrictMath.negateExact(Integer.MAX_VALUE));
+
+    try {
+      throw new AssertionError(StrictMath.negateExact(Integer.MIN_VALUE));
+    } catch (ArithmeticException expected) {
+    }
+  }
+
+  private static void testNegateExactLong() {
+    assertEquals(0L, StrictMath.negateExact(0L));
+    assertEquals(-1L, StrictMath.negateExact(1L));
+    assertEquals(1L, StrictMath.negateExact(-1L));
+    assertEquals(-9_223_372_036_854_775_807L, StrictMath.negateExact(Long.MAX_VALUE));
+
+    try {
+      throw new AssertionError(StrictMath.negateExact(Long.MIN_VALUE));
+    } catch (ArithmeticException expected) {
+    }
+  }
+
+  private static void assertEquals(int expected, int actual) {
+    if (expected != actual) {
+      throw new AssertionError("Expected <" + expected + "> but was <" + actual + '>');
+    }
+  }
+
+  private static void assertEquals(long expected, long actual) {
+    if (expected != actual) {
+      throw new AssertionError("Expected <" + expected + "> but was <" + actual + '>');
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava17Test.java b/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava17Test.java
new file mode 100644
index 0000000..fde89f0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava17Test.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2023, 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 static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+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;
+
+@RunWith(Parameterized.class)
+public final class MathBackportJava17Test extends AbstractBackportTest {
+  @Parameters(name = "{0}")
+  public static Iterable<?> data() {
+    return getTestParameters()
+        .withDexRuntimes()
+        .withAllApiLevels()
+        .withCfRuntimesStartingFromIncluding(CfVm.JDK17)
+        .build();
+  }
+
+  private static final Path TEST_JAR =
+      Paths.get(ToolHelper.TESTS_BUILD_DIR).resolve("examplesJava17/backport" + JAR_EXTENSION);
+
+  public MathBackportJava17Test(TestParameters parameters) {
+    super(parameters, Math.class, TEST_JAR, "backport.MathBackportJava17Main");
+
+    // Math.absExact.
+    // The exact number needs to be updated once we test Android U.
+    registerTarget(AndroidApiLevel.U, 27);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava17Test.java b/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava17Test.java
new file mode 100644
index 0000000..d54a044
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava17Test.java
@@ -0,0 +1,41 @@
+// Copyright (c) 2023, 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 static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Objects;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public final class ObjectsBackportJava17Test extends AbstractBackportTest {
+  @Parameters(name = "{0}")
+  public static Iterable<?> data() {
+    return getTestParameters()
+        .withDexRuntimes()
+        .withAllApiLevels()
+        .withCfRuntimesStartingFromIncluding(CfVm.JDK17)
+        .build();
+  }
+
+  private static final Path TEST_JAR =
+      Paths.get(ToolHelper.TESTS_BUILD_DIR).resolve("examplesJava17/backport" + JAR_EXTENSION);
+  private static final String TEST_CLASS = "backport.ObjectsBackportJava17Main";
+
+  public ObjectsBackportJava17Test(TestParameters parameters) {
+    super(parameters, Objects.class, TEST_JAR, TEST_CLASS);
+    // Objects.checkFromIndexSize, Objects.checkFromToIndex, Objects.checkIndex.
+    // The exact number needs to be updated once we test Android U.
+    registerTarget(AndroidApiLevel.U, 28);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava17Test.java b/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava17Test.java
new file mode 100644
index 0000000..3089d40
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava17Test.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2023, 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 static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+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;
+
+@RunWith(Parameterized.class)
+public final class StrictMathBackportJava17Test extends AbstractBackportTest {
+
+  @Parameters(name = "{0}")
+  public static Iterable<?> data() {
+    return getTestParameters()
+        .withDexRuntimes()
+        .withAllApiLevels()
+        .withCfRuntimesStartingFromIncluding(CfVm.JDK17)
+        .build();
+  }
+
+  private static final Path TEST_JAR =
+      Paths.get(ToolHelper.TESTS_BUILD_DIR).resolve("examplesJava17/backport" + JAR_EXTENSION);
+
+  public StrictMathBackportJava17Test(TestParameters parameters) {
+    super(parameters, StrictMath.class, TEST_JAR, "backport.StrictMathBackportJava17Main");
+    // The exact number needs to be updated once we test Android U.
+    registerTarget(AndroidApiLevel.U, 75);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/MathMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/MathMethods.java
index a41259d..84b218b 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/MathMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/MathMethods.java
@@ -227,4 +227,18 @@
     }
     return result;
   }
+
+  public static int absExact(int a) {
+    if (a == Integer.MIN_VALUE) {
+      throw new ArithmeticException();
+    }
+    return Math.abs(a);
+  }
+
+  public static long absExactLong(long a) {
+    if (a == Long.MIN_VALUE) {
+      throw new ArithmeticException();
+    }
+    return Math.abs(a);
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/ObjectsMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/ObjectsMethods.java
index 566d8f2..f8e31b0 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/ObjectsMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/ObjectsMethods.java
@@ -131,4 +131,34 @@
     }
     return fromIndex;
   }
+
+  public static long checkIndexLong(long index, long length) {
+    if (index < 0 || index >= length) {
+      throw new IndexOutOfBoundsException("Index " + index + " out of bounds for length " + length);
+    }
+    return index;
+  }
+
+  public static long checkFromToIndexLong(long fromIndex, long toIndex, long length) {
+    if (fromIndex < 0 || fromIndex > toIndex || toIndex > length) {
+      throw new IndexOutOfBoundsException(
+          "Range [" + fromIndex + ", " + toIndex + ") out of bounds for length " + length);
+    }
+    return fromIndex;
+  }
+
+  public static long checkFromIndexSizeLong(long fromIndex, long size, long 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;
+  }
 }