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));