Don't backport java.lang.Integer and java.lang.Long methods on Android T
Android T has all java.lang.Integer and java.lang.Long methods
present in Java 11.
Add backporting of
int Integer.parseUnsignedInt(
CharSequence s, int beginIndex, int endIndex, int radix)
to have support for all java.lang.Integer and java.lang.Long methods.
Bug: 217158412
Change-Id: I5157a1f3c20b5c3031290520083fd297c5e64bad
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 dd2bb76..c622cfa 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
@@ -179,6 +179,9 @@
if (options.getMinApiLevel().isLessThan(AndroidApiLevel.Sv2)) {
initializeAndroidSv2MethodProviders(factory);
}
+ if (options.getMinApiLevel().isLessThan(AndroidApiLevel.T)) {
+ initializeAndroidTMethodProviders(factory);
+ }
// The following providers are implemented at API level T. For backporting they require
// the java.util.Optional class to be present, either through library desugaring or natively.
@@ -1158,6 +1161,94 @@
}
}
+ private void initializeAndroidTMethodProviders(DexItemFactory factory) {
+ // java.lang.Integer.
+ {
+ // int Integer.parseInt(CharSequence s, int beginIndex, int endIndex, int radix)
+ DexType type = factory.boxedIntType;
+ DexString name = factory.createString("parseInt");
+ DexProto proto =
+ factory.createProto(
+ factory.intType,
+ factory.charSequenceType,
+ factory.intType,
+ factory.intType,
+ factory.intType);
+ DexMethod method = factory.createMethod(type, proto, name);
+ addProvider(
+ appView.options().canParseNumbersWithPlusPrefix()
+ ? new MethodGenerator(
+ method,
+ BackportedMethods::IntegerMethods_parseIntSubsequenceWithRadix,
+ "parseIntSubsequenceWithRadix")
+ : new MethodGenerator(
+ method,
+ BackportedMethods::IntegerMethods_parseIntSubsequenceWithRadixDalvik,
+ "parseIntSubsequenceWithRadix"));
+ }
+ {
+ // int Integer.parseUnsignedInt(CharSequence s, int beginIndex, int endIndex, int radix)
+ DexType type = factory.boxedIntType;
+ DexString name = factory.createString("parseUnsignedInt");
+ DexProto proto =
+ factory.createProto(
+ factory.intType,
+ factory.charSequenceType,
+ factory.intType,
+ factory.intType,
+ factory.intType);
+ DexMethod method = factory.createMethod(type, proto, name);
+ addProvider(
+ new MethodGenerator(
+ method,
+ BackportedMethods::IntegerMethods_parseUnsignedIntSubsequenceWithRadix,
+ "parseIntSubsequenceWithRadix"));
+ }
+
+ // java.lang.Long.
+ {
+ // long Long.parseLong(CharSequence s, int beginIndex, int endIndex, int radix)
+ DexType type = factory.boxedLongType;
+ DexString name = factory.createString("parseLong");
+ DexProto proto =
+ factory.createProto(
+ factory.longType,
+ factory.charSequenceType,
+ factory.intType,
+ factory.intType,
+ factory.intType);
+ DexMethod method = factory.createMethod(type, proto, name);
+ addProvider(
+ appView.options().canParseNumbersWithPlusPrefix()
+ ? new MethodGenerator(
+ method,
+ BackportedMethods::LongMethods_parseLongSubsequenceWithRadix,
+ "parseLongSubsequenceWithRadix")
+ : new MethodGenerator(
+ method,
+ BackportedMethods::LongMethods_parseLongSubsequenceWithRadixDalvik,
+ "parseLongSubsequenceWithRadix"));
+ }
+ {
+ // long Long.parseUnsignedLong(CharSequence s, int beginIndex, int endIndex, int radix)
+ DexType type = factory.boxedLongType;
+ DexString name = factory.createString("parseUnsignedLong");
+ DexProto proto =
+ factory.createProto(
+ factory.longType,
+ factory.charSequenceType,
+ factory.intType,
+ factory.intType,
+ factory.intType);
+ DexMethod method = factory.createMethod(type, proto, name);
+ addProvider(
+ new MethodGenerator(
+ method,
+ BackportedMethods::LongMethods_parseUnsignedLongSubsequenceWithRadix,
+ "parseUnsignedLongSubsequenceWithRadix"));
+ }
+ }
+
private void initializeAndroidOptionalTMethodProviders(DexItemFactory factory) {
DexType optionalType = factory.optionalType;
DexType[] optionalTypes =
@@ -1275,67 +1366,7 @@
}
private void initializeJava9MethodProviders(DexItemFactory factory) {
- // Integer
- DexType type = factory.boxedIntType;
- // int Integer.parseInt(CharSequence s, int beginIndex, int endIndex, int radix)
- DexString name = factory.createString("parseInt");
- DexProto proto =
- factory.createProto(
- factory.intType,
- factory.charSequenceType,
- factory.intType,
- factory.intType,
- factory.intType);
- DexMethod method = factory.createMethod(type, proto, name);
- addProvider(
- appView.options().canParseNumbersWithPlusPrefix()
- ? new MethodGenerator(
- method,
- BackportedMethods::IntegerMethods_parseIntSubsequenceWithRadix,
- "parseIntSubsequenceWithRadix")
- : new MethodGenerator(
- method,
- BackportedMethods::IntegerMethods_parseIntSubsequenceWithRadixDalvik,
- "parseIntSubsequenceWithRadix"));
-
- // Long
- type = factory.boxedLongType;
- // long Long.parseLong(CharSequence s, int beginIndex, int endIndex, int radix)
- name = factory.createString("parseLong");
- proto =
- factory.createProto(
- factory.longType,
- factory.charSequenceType,
- factory.intType,
- factory.intType,
- factory.intType);
- method = factory.createMethod(type, proto, name);
- addProvider(
- appView.options().canParseNumbersWithPlusPrefix()
- ? new MethodGenerator(
- method,
- BackportedMethods::LongMethods_parseLongSubsequenceWithRadix,
- "parseLongSubsequenceWithRadix")
- : new MethodGenerator(
- method,
- BackportedMethods::LongMethods_parseLongSubsequenceWithRadixDalvik,
- "parseLongSubsequenceWithRadix"));
-
- // long Long.parseUnsignedLong(CharSequence s, int beginIndex, int endIndex, int radix)
- name = factory.createString("parseUnsignedLong");
- proto =
- factory.createProto(
- factory.longType,
- factory.charSequenceType,
- factory.intType,
- factory.intType,
- factory.intType);
- method = factory.createMethod(type, proto, name);
- addProvider(
- new MethodGenerator(
- method,
- BackportedMethods::LongMethods_parseUnsignedLongSubsequenceWithRadix,
- "parseUnsignedLongSubsequenceWithRadix"));
+ // Nothing right now.
}
private void initializeJava10MethodProviders(DexItemFactory factory) {
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 ccd00a9..1b2332b 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
@@ -2597,6 +2597,53 @@
ImmutableList.of());
}
+ public static CfCode IntegerMethods_parseUnsignedIntSubsequenceWithRadix(
+ InternalOptions options, DexMethod method) {
+ CfLabel label0 = new CfLabel();
+ CfLabel label1 = new CfLabel();
+ return new CfCode(
+ method.holder,
+ 3,
+ 4,
+ ImmutableList.of(
+ label0,
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfLoad(ValueType.INT, 1),
+ new CfLoad(ValueType.INT, 2),
+ new CfInvoke(
+ 185,
+ options.itemFactory.createMethod(
+ options.itemFactory.charSequenceType,
+ options.itemFactory.createProto(
+ options.itemFactory.charSequenceType,
+ options.itemFactory.intType,
+ options.itemFactory.intType),
+ options.itemFactory.createString("subSequence")),
+ true),
+ new CfInvoke(
+ 185,
+ options.itemFactory.createMethod(
+ options.itemFactory.charSequenceType,
+ options.itemFactory.createProto(options.itemFactory.stringType),
+ options.itemFactory.createString("toString")),
+ true),
+ new CfLoad(ValueType.INT, 3),
+ new CfInvoke(
+ 184,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/lang/Integer;"),
+ options.itemFactory.createProto(
+ options.itemFactory.intType,
+ options.itemFactory.stringType,
+ options.itemFactory.intType),
+ options.itemFactory.createString("parseUnsignedInt")),
+ false),
+ new CfReturn(ValueType.INT),
+ label1),
+ ImmutableList.of(),
+ ImmutableList.of());
+ }
+
public static CfCode IntegerMethods_parseUnsignedIntWithRadix(
InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
diff --git a/src/test/examplesJava9/backport/IntegerBackportJava9Main.java b/src/test/examplesJava9/backport/IntegerBackportJava9Main.java
index 8188fe1..acf18d2 100644
--- a/src/test/examplesJava9/backport/IntegerBackportJava9Main.java
+++ b/src/test/examplesJava9/backport/IntegerBackportJava9Main.java
@@ -4,8 +4,6 @@
package backport;
-import java.math.BigInteger;
-
public final class IntegerBackportJava9Main {
private static final int[] interestingValues = {
Integer.MIN_VALUE,
@@ -23,6 +21,7 @@
public static void main(String[] args) {
testParseIntegerSubsequenceWithRadix();
+ testParseUnsignedIntegerSubsequenceWithRadix();
}
private static void testParseIntegerSubsequenceWithRadix() {
@@ -71,22 +70,87 @@
} catch (NumberFormatException expected) {
}
- BigInteger overflow = new BigInteger("18446744073709551616");
+ long overflow = 73709551616L;
for (int radix = Character.MIN_RADIX; radix <= Character.MAX_RADIX; radix++) {
for (String prefix : new String[] {"", "x", "xxx"}) {
for (String postfix : new String[] {"", "x", "xxx"}) {
- String overflowString = prefix + overflow.toString(radix) + postfix;
+ String overflowString = prefix + Long.toString(overflow, radix) + postfix;
int start = prefix.length();
int end = overflowString.length() - postfix.length();
try {
- throw new AssertionError(Long.parseLong(overflowString, start, end, radix));
+ throw new AssertionError(Integer.parseInt(overflowString, start, end, radix));
} catch (NumberFormatException expected) {
}
- String underflowString = prefix + '-' + overflow.toString(radix) + postfix;
+ String underflowString = prefix + '-' + Long.toString(overflow, radix) + postfix;
start = prefix.length();
end = underflowString.length() - postfix.length();
try {
- throw new AssertionError(Long.parseLong(underflowString, start, end, radix));
+ throw new AssertionError(Integer.parseInt(underflowString, start, end, radix));
+ } catch (NumberFormatException expected) {
+ }
+ }
+ }
+ }
+ }
+
+ private static void testParseUnsignedIntegerSubsequenceWithRadix() {
+ for (int value : interestingValues) {
+ for (int radix = Character.MIN_RADIX; radix <= Character.MAX_RADIX; radix++) {
+ for (String prefix : new String[] {"", "x", "xxx"}) {
+ for (String postfix : new String[] {"", "x", "xxx"}) {
+ String unsignedIntergerString = Long.toString(Integer.toUnsignedLong(value), radix);
+ String valueString = prefix + unsignedIntergerString + postfix;
+ int start = prefix.length();
+ int end = valueString.length() - postfix.length();
+ assertEquals(
+ valueString, value, Integer.parseUnsignedInt(valueString, start, end, radix));
+ if (value > 0) {
+ valueString = prefix + '+' + unsignedIntergerString + postfix;
+ end++;
+ assertEquals(
+ valueString, value, Integer.parseUnsignedInt(valueString, start, end, radix));
+ }
+ }
+ }
+ }
+ }
+
+ try {
+ throw new AssertionError(Integer.parseUnsignedInt("0", 0, 1, Character.MIN_RADIX - 1));
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ throw new AssertionError(Integer.parseUnsignedInt("0", 0, 1, Character.MAX_RADIX + 1));
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ throw new AssertionError(Integer.parseUnsignedInt("", 0, 0, 16));
+ } catch (NumberFormatException expected) {
+ }
+ try {
+ throw new AssertionError(Integer.parseUnsignedInt("-", 0, 1, 16));
+ } catch (NumberFormatException expected) {
+ }
+ try {
+ throw new AssertionError(Integer.parseUnsignedInt("+", 0, 1, 16));
+ } catch (NumberFormatException expected) {
+ }
+
+ try {
+ throw new AssertionError(Integer.parseUnsignedInt("+a", 0, 2, 10));
+ } catch (NumberFormatException expected) {
+ }
+
+ long overflow = 73709551616L;
+ for (int radix = Character.MIN_RADIX; radix <= Character.MAX_RADIX; radix++) {
+ for (String prefix : new String[] {"", "x", "xxx"}) {
+ for (String postfix : new String[] {"", "x", "xxx"}) {
+ String overflowString = prefix + Long.toString(overflow, radix) + postfix;
+ int start = prefix.length();
+ int end = overflowString.length() - postfix.length();
+ try {
+ throw new AssertionError(Integer.parseUnsignedInt(overflowString, start, end, radix));
} catch (NumberFormatException expected) {
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportJava9Test.java
index a2e1120..f8c5a53 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportJava9Test.java
@@ -9,6 +9,7 @@
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;
@@ -30,8 +31,11 @@
Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR).resolve("backport" + JAR_EXTENSION);
public IntegerBackportJava9Test(TestParameters parameters) {
- super(parameters, Short.class, TEST_JAR, "backport.IntegerBackportJava9Main");
- // 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 IntegerBackportTest.
+ super(parameters, Integer.class, TEST_JAR, "backport.IntegerBackportJava9Main");
+ // Note: The methods in this test exist in android.jar from Android T. When R8 builds targeting
+ // Java 11 move these tests to IntegerBackportTest (out of examplesJava9).
+
+ registerTarget(AndroidApiLevel.O, 1);
+ registerTarget(AndroidApiLevel.T, 20);
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/LongBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/LongBackportJava9Test.java
index 4bd90c8..487f80f 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/LongBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/LongBackportJava9Test.java
@@ -9,6 +9,7 @@
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;
@@ -30,8 +31,12 @@
Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR).resolve("backport" + JAR_EXTENSION);
public LongBackportJava9Test(TestParameters parameters) {
- super(parameters, Short.class, TEST_JAR, "backport.LongBackportJava9Main");
- // 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 LongBackportTest.
+ super(parameters, Long.class, TEST_JAR, "backport.LongBackportJava9Main");
+ // Note: The methods in this test exist in android.jar from Android T. When R8 builds targeting
+ // Java 11 move these tests to LongBackportTest (out of examplesJava9).
+
+ ignoreInvokes("toString");
+
+ registerTarget(AndroidApiLevel.T, 17);
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/IntegerMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/IntegerMethods.java
index bc84108..b7ddc31 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/IntegerMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/IntegerMethods.java
@@ -73,4 +73,9 @@
}
return Integer.parseInt(s.subSequence(beginIndex, endIndex).toString(), radix);
}
+
+ public static int parseUnsignedIntSubsequenceWithRadix(
+ CharSequence s, int beginIndex, int endIndex, int radix) throws NumberFormatException {
+ return Integer.parseUnsignedInt(s.subSequence(beginIndex, endIndex).toString(), radix);
+ }
}