Desguar unsigned operations on <26

This is different from the rest of the Java 8 method desugaring which is only <24.

Bug: 130408977
Test: Java8MethodsTest
Change-Id: I6cd05e862faa187eb34be64c189d8a08ed537137
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/Java8MethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/Java8MethodRewriter.java
index 36a08cc..36eabbe 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/Java8MethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/Java8MethodRewriter.java
@@ -30,6 +30,7 @@
 import com.android.tools.r8.ir.desugar.Java8MethodRewriter.RewritableMethods.MethodGenerator;
 import com.android.tools.r8.ir.synthetic.TemplateMethodCode;
 import com.android.tools.r8.origin.SynthesizedOrigin;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.Sets;
@@ -59,7 +60,7 @@
     this.appView = appView;
     this.converter = converter;
     this.factory = appView.dexItemFactory();
-    this.rewritableMethods = new RewritableMethods(factory);
+    this.rewritableMethods = new RewritableMethods(factory, appView.options().minApiLevel);
   }
 
   public void desugar(IRCode code) {
@@ -493,11 +494,19 @@
 
   public static final class RewritableMethods {
     // Map class, method, proto to a generator for creating the code and method.
-    private final Map<DexString, Map<DexString, Map<DexProto, MethodGenerator>>> rewritable;
+    private final Map<DexString, Map<DexString, Map<DexProto, MethodGenerator>>> rewritable =
+        new HashMap<>();
 
+    public RewritableMethods(DexItemFactory factory, int minApiLevel) {
+      if (minApiLevel < AndroidApiLevel.N.getLevel()) {
+        initializeAndroidNMethods(factory);
+      }
+      if (minApiLevel < AndroidApiLevel.O.getLevel()) {
+        initializeAndroidOMethods(factory);
+      }
+    }
 
-    public RewritableMethods(DexItemFactory factory) {
-      rewritable = new HashMap<>();
+    private void initializeAndroidNMethods(DexItemFactory factory) {
       // Byte
       DexString clazz = factory.boxedByteDescriptor;
       // int Byte.hashCode(byte i)
@@ -661,19 +670,6 @@
       addOrGetMethod(clazz, method)
           .put(proto, new MethodGenerator(LongMethods::sumCode, clazz, method, proto));
 
-      // long Long.divideUnsigned(long a, long b)
-      method = factory.createString("divideUnsigned");
-      proto = factory.createProto(factory.longType, factory.longType, factory.longType);
-      addOrGetMethod(clazz, method)
-          .put(proto, new MethodGenerator(LongMethods::divideUnsignedCode, clazz, method, proto));
-
-      // long Long.remainderUnsigned(long a, long b)
-      method = factory.createString("remainderUnsigned");
-      proto = factory.createProto(factory.longType, factory.longType, factory.longType);
-      addOrGetMethod(clazz, method)
-          .put(proto,
-              new MethodGenerator(LongMethods::remainderUnsignedCode, clazz, method, proto));
-
       // Character
       clazz = factory.boxedCharDescriptor;
 
@@ -684,6 +680,22 @@
           .put(proto, new MethodGenerator(CharacterMethods::hashCodeCode, clazz, method, proto));
     }
 
+    private void initializeAndroidOMethods(DexItemFactory factory) {
+      DexString clazz = factory.boxedLongDescriptor;
+
+      // long Long.divideUnsigned(long a, long b)
+      DexString method = factory.createString("divideUnsigned");
+      DexProto proto = factory.createProto(factory.longType, factory.longType, factory.longType);
+      addOrGetMethod(clazz, method).put(proto,
+          new MethodGenerator(LongMethods::divideUnsignedCode, clazz, method, proto));
+
+      // long Long.remainderUnsigned(long a, long b)
+      method = factory.createString("remainderUnsigned");
+      proto = factory.createProto(factory.longType, factory.longType, factory.longType);
+      addOrGetMethod(clazz, method).put(proto,
+          new MethodGenerator(LongMethods::remainderUnsignedCode, clazz, method, proto));
+    }
+
     private Map<DexString, Map<DexProto, MethodGenerator>> addOrGetClass(DexString clazz) {
       return rewritable.computeIfAbsent(clazz, k -> new HashMap<>());
     }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 174753e..e13b5c4 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -667,7 +667,9 @@
   }
 
   public boolean canUseJava8Methods() {
-    return hasMinApi(AndroidApiLevel.N);
+    // This value must be set to the highest API level of all methods we desugar. The implementation
+    // will conditionally desugar based on the specified minimum API level.
+    return hasMinApi(AndroidApiLevel.O);
   }
 
   public boolean canUsePrivateInterfaceMethods() {
diff --git a/src/test/java/com/android/tools/r8/desugar/Java8MethodsTest.java b/src/test/java/com/android/tools/r8/desugar/Java8MethodsTest.java
index 29a8cc9..ebc9b29 100644
--- a/src/test/java/com/android/tools/r8/desugar/Java8MethodsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/Java8MethodsTest.java
@@ -4,9 +4,18 @@
 
 package com.android.tools.r8.desugar;
 
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static java.util.stream.Collectors.toList;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.D8TestCompileResult;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import java.nio.file.Path;
+import java.util.List;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -26,6 +35,37 @@
         .addProgramClasses(Java8Methods.class)
         .run(Java8Methods.class)
         .assertSuccessWithOutput(expectedOutput);
+
+    assertDesugaring(AndroidApiLevel.O, 27);
+    assertDesugaring(AndroidApiLevel.N, 25);
+    assertDesugaring(AndroidApiLevel.M, 0);
+  }
+
+  private void assertDesugaring(AndroidApiLevel apilevel, int expectedJavaLangInvokeStatics)
+      throws Exception {
+    D8TestCompileResult runResult = testForD8()
+        .addProgramClasses(Java8Methods.class)
+        .setMinApi(apilevel)
+        .compile();
+
+    MethodSubject mainMethod = runResult.inspector()
+        .clazz(Java8Methods.class)
+        .mainMethod();
+    assertThat(mainMethod, isPresent());
+
+    List<InstructionSubject> javaLangInvokeStatics = mainMethod
+        .streamInstructions()
+        .filter(InstructionSubject::isInvokeStatic)
+        .filter(is -> is.getMethod().holder.toDescriptorString().startsWith("Ljava/lang/"))
+        .collect(toList());
+
+    int actualJavaLangInvokeStatics = javaLangInvokeStatics.size();
+    assertEquals("Expected "
+        + expectedJavaLangInvokeStatics
+        + " invoke-static on java/lang/<Type> but found "
+        + actualJavaLangInvokeStatics
+        + ": "
+        + javaLangInvokeStatics, expectedJavaLangInvokeStatics, actualJavaLangInvokeStatics);
   }
 
   @Test
@@ -138,7 +178,7 @@
       }
 
       for (boolean aBoolean : new boolean[]{true, false}) {
-        System.out.println(aBoolean);
+        System.out.println(Boolean.hashCode(aBoolean));
         for (boolean bBoolean : new boolean[]{true, false}) {
           System.out.println(Boolean.logicalAnd(aBoolean, bBoolean));
           System.out.println(Boolean.logicalOr(aBoolean, bBoolean));