Add backporting more Java 9 methods
Long.parseUnsignedLong(CharSequence s, int beginIndex, int endIndex, int radix)
Long.parseLong(CharSequence s, int beginIndex, int endIndex, int radix)
Integer.parseInt(CharSequence s, int beginIndex, int endIndex, int radix)
Bug: 181533278
Change-Id: Id4f6a55fabef9385eed220a21788e0d96790679c
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 bd5c0b3..7d07a7d 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
@@ -194,6 +194,7 @@
}
// These are currently not implemented at any API level in Android.
+ initializeJava9MethodProviders(factory);
initializeJava10MethodProviders(factory);
initializeJava11MethodProviders(factory);
}
@@ -1043,6 +1044,60 @@
}
}
+ private void initializeJava9MethodProviders(DexItemFactory factory) {
+ // Integer
+ DexType type = factory.boxedIntType;
+ // long Long.parseLong(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(
+ new MethodGenerator(
+ method,
+ BackportedMethods::IntegerMethods_parseIntSubsequenceWithRadix,
+ "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(
+ new MethodGenerator(
+ method,
+ BackportedMethods::LongMethods_parseLongSubsequenceWithRadix,
+ "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"));
+ }
+
private void initializeJava10MethodProviders(DexItemFactory factory) {
// List
DexType type = factory.listType;
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 da56b69..b7b0db0 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
@@ -2115,6 +2115,53 @@
ImmutableList.of());
}
+ public static CfCode IntegerMethods_parseIntSubsequenceWithRadix(
+ 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("parseInt")),
+ false),
+ new CfReturn(ValueType.INT),
+ label1),
+ ImmutableList.of(),
+ ImmutableList.of());
+ }
+
public static CfCode IntegerMethods_parseUnsignedInt(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
@@ -2643,6 +2690,53 @@
ImmutableList.of());
}
+ public static CfCode LongMethods_parseLongSubsequenceWithRadix(
+ 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/Long;"),
+ options.itemFactory.createProto(
+ options.itemFactory.longType,
+ options.itemFactory.stringType,
+ options.itemFactory.intType),
+ options.itemFactory.createString("parseLong")),
+ false),
+ new CfReturn(ValueType.LONG),
+ label1),
+ ImmutableList.of(),
+ ImmutableList.of());
+ }
+
public static CfCode LongMethods_parseUnsignedLong(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
@@ -2670,7 +2764,7 @@
ImmutableList.of());
}
- public static CfCode LongMethods_parseUnsignedLongWithRadix(
+ public static CfCode LongMethods_parseUnsignedLongSubsequenceWithRadix(
InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
@@ -2697,20 +2791,15 @@
return new CfCode(
method.holder,
5,
- 10,
+ 12,
ImmutableList.of(
label0,
- new CfLoad(ValueType.OBJECT, 0),
- new CfInvoke(
- 182,
- options.itemFactory.createMethod(
- options.itemFactory.stringType,
- options.itemFactory.createProto(options.itemFactory.intType),
- options.itemFactory.createString("length")),
- false),
- new CfStore(ValueType.INT, 2),
- label1,
new CfLoad(ValueType.INT, 2),
+ new CfLoad(ValueType.INT, 1),
+ new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+ new CfStore(ValueType.INT, 4),
+ label1,
+ new CfLoad(ValueType.INT, 4),
new CfIf(If.Type.NE, ValueType.INT, label3),
label2,
new CfNew(options.itemFactory.createType("Ljava/lang/NumberFormatException;")),
@@ -2728,25 +2817,29 @@
label3,
new CfFrame(
new Int2ReferenceAVLTreeMap<>(
- new int[] {0, 1, 2},
+ new int[] {0, 1, 2, 3, 4},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initialized(options.itemFactory.charSequenceType),
+ FrameType.initialized(options.itemFactory.intType),
+ FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
new ArrayDeque<>(Arrays.asList())),
- new CfLoad(ValueType.INT, 1),
+ new CfLoad(ValueType.INT, 3),
new CfConstNumber(2, ValueType.INT),
new CfIfCmp(If.Type.LT, ValueType.INT, label4),
- new CfLoad(ValueType.INT, 1),
+ new CfLoad(ValueType.INT, 3),
new CfConstNumber(36, ValueType.INT),
new CfIfCmp(If.Type.LE, ValueType.INT, label5),
label4,
new CfFrame(
new Int2ReferenceAVLTreeMap<>(
- new int[] {0, 1, 2},
+ new int[] {0, 1, 2, 3, 4},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initialized(options.itemFactory.charSequenceType),
+ FrameType.initialized(options.itemFactory.intType),
+ FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
@@ -2754,7 +2847,7 @@
new CfNew(options.itemFactory.createType("Ljava/lang/NumberFormatException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfConstString(options.itemFactory.createString("illegal radix: ")),
- new CfLoad(ValueType.INT, 1),
+ new CfLoad(ValueType.INT, 3),
new CfInvoke(
184,
options.itemFactory.createMethod(
@@ -2783,15 +2876,17 @@
label5,
new CfFrame(
new Int2ReferenceAVLTreeMap<>(
- new int[] {0, 1, 2},
+ new int[] {0, 1, 2, 3, 4},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initialized(options.itemFactory.charSequenceType),
+ FrameType.initialized(options.itemFactory.intType),
+ FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(-1, ValueType.LONG),
- new CfLoad(ValueType.INT, 1),
+ new CfLoad(ValueType.INT, 3),
new CfNumberConversion(NumericType.INT, NumericType.LONG),
new CfInvoke(
184,
@@ -2803,62 +2898,70 @@
options.itemFactory.longType),
options.itemFactory.createString("divideUnsigned")),
false),
- new CfStore(ValueType.LONG, 3),
+ new CfStore(ValueType.LONG, 5),
label6,
new CfLoad(ValueType.OBJECT, 0),
- new CfConstNumber(0, ValueType.INT),
+ new CfLoad(ValueType.INT, 1),
new CfInvoke(
- 182,
+ 185,
options.itemFactory.createMethod(
- options.itemFactory.stringType,
+ options.itemFactory.charSequenceType,
options.itemFactory.createProto(
options.itemFactory.charType, options.itemFactory.intType),
options.itemFactory.createString("charAt")),
- false),
+ true),
new CfConstNumber(43, ValueType.INT),
new CfIfCmp(If.Type.NE, ValueType.INT, label7),
- new CfLoad(ValueType.INT, 2),
+ new CfLoad(ValueType.INT, 4),
new CfConstNumber(1, ValueType.INT),
new CfIfCmp(If.Type.LE, ValueType.INT, label7),
+ new CfLoad(ValueType.INT, 1),
new CfConstNumber(1, ValueType.INT),
+ new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
new CfGoto(label8),
label7,
new CfFrame(
new Int2ReferenceAVLTreeMap<>(
- new int[] {0, 1, 2, 3},
+ new int[] {0, 1, 2, 3, 4, 5},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initialized(options.itemFactory.charSequenceType),
+ FrameType.initialized(options.itemFactory.intType),
+ FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.longType)
}),
new ArrayDeque<>(Arrays.asList())),
- new CfConstNumber(0, ValueType.INT),
+ new CfLoad(ValueType.INT, 1),
label8,
new CfFrame(
new Int2ReferenceAVLTreeMap<>(
- new int[] {0, 1, 2, 3},
+ new int[] {0, 1, 2, 3, 4, 5},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initialized(options.itemFactory.charSequenceType),
+ FrameType.initialized(options.itemFactory.intType),
+ FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.longType)
}),
new ArrayDeque<>(
Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
- new CfStore(ValueType.INT, 5),
+ new CfStore(ValueType.INT, 7),
label9,
new CfConstNumber(0, ValueType.LONG),
- new CfStore(ValueType.LONG, 6),
+ new CfStore(ValueType.LONG, 8),
label10,
- new CfLoad(ValueType.INT, 5),
- new CfStore(ValueType.INT, 8),
+ new CfLoad(ValueType.INT, 7),
+ new CfStore(ValueType.INT, 10),
label11,
new CfFrame(
new Int2ReferenceAVLTreeMap<>(
- new int[] {0, 1, 2, 3, 5, 6, 8},
+ new int[] {0, 1, 2, 3, 4, 5, 7, 8, 10},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initialized(options.itemFactory.charSequenceType),
+ FrameType.initialized(options.itemFactory.intType),
+ FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.longType),
@@ -2867,21 +2970,21 @@
FrameType.initialized(options.itemFactory.intType)
}),
new ArrayDeque<>(Arrays.asList())),
- new CfLoad(ValueType.INT, 8),
+ new CfLoad(ValueType.INT, 10),
new CfLoad(ValueType.INT, 2),
new CfIfCmp(If.Type.GE, ValueType.INT, label20),
label12,
new CfLoad(ValueType.OBJECT, 0),
- new CfLoad(ValueType.INT, 8),
+ new CfLoad(ValueType.INT, 10),
new CfInvoke(
- 182,
+ 185,
options.itemFactory.createMethod(
- options.itemFactory.stringType,
+ options.itemFactory.charSequenceType,
options.itemFactory.createProto(
options.itemFactory.charType, options.itemFactory.intType),
options.itemFactory.createString("charAt")),
- false),
- new CfLoad(ValueType.INT, 1),
+ true),
+ new CfLoad(ValueType.INT, 3),
new CfInvoke(
184,
options.itemFactory.createMethod(
@@ -2892,9 +2995,9 @@
options.itemFactory.intType),
options.itemFactory.createString("digit")),
false),
- new CfStore(ValueType.INT, 9),
+ new CfStore(ValueType.INT, 11),
label13,
- new CfLoad(ValueType.INT, 9),
+ new CfLoad(ValueType.INT, 11),
new CfConstNumber(-1, ValueType.INT),
new CfIfCmp(If.Type.NE, ValueType.INT, label15),
label14,
@@ -2902,6 +3005,13 @@
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfLoad(ValueType.OBJECT, 0),
new CfInvoke(
+ 185,
+ options.itemFactory.createMethod(
+ options.itemFactory.charSequenceType,
+ options.itemFactory.createProto(options.itemFactory.stringType),
+ options.itemFactory.createString("toString")),
+ true),
+ new CfInvoke(
183,
options.itemFactory.createMethod(
options.itemFactory.createType("Ljava/lang/NumberFormatException;"),
@@ -2913,9 +3023,11 @@
label15,
new CfFrame(
new Int2ReferenceAVLTreeMap<>(
- new int[] {0, 1, 2, 3, 5, 6, 8, 9},
+ new int[] {0, 1, 2, 3, 4, 5, 7, 8, 10, 11},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initialized(options.itemFactory.charSequenceType),
+ FrameType.initialized(options.itemFactory.intType),
+ FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.longType),
@@ -2925,21 +3037,21 @@
FrameType.initialized(options.itemFactory.intType)
}),
new ArrayDeque<>(Arrays.asList())),
- new CfLoad(ValueType.LONG, 6),
+ new CfLoad(ValueType.LONG, 8),
new CfConstNumber(0, ValueType.LONG),
new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
new CfIf(If.Type.LT, ValueType.INT, label17),
- new CfLoad(ValueType.LONG, 6),
- new CfLoad(ValueType.LONG, 3),
+ new CfLoad(ValueType.LONG, 8),
+ new CfLoad(ValueType.LONG, 5),
new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
new CfIf(If.Type.GT, ValueType.INT, label17),
- new CfLoad(ValueType.LONG, 6),
- new CfLoad(ValueType.LONG, 3),
+ new CfLoad(ValueType.LONG, 8),
+ new CfLoad(ValueType.LONG, 5),
new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
new CfIf(If.Type.NE, ValueType.INT, label18),
- new CfLoad(ValueType.INT, 9),
+ new CfLoad(ValueType.INT, 11),
new CfConstNumber(-1, ValueType.LONG),
- new CfLoad(ValueType.INT, 1),
+ new CfLoad(ValueType.INT, 3),
new CfNumberConversion(NumericType.INT, NumericType.LONG),
label16,
new CfInvoke(
@@ -2957,9 +3069,11 @@
label17,
new CfFrame(
new Int2ReferenceAVLTreeMap<>(
- new int[] {0, 1, 2, 3, 5, 6, 8, 9},
+ new int[] {0, 1, 2, 3, 4, 5, 7, 8, 10, 11},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initialized(options.itemFactory.charSequenceType),
+ FrameType.initialized(options.itemFactory.intType),
+ FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.longType),
@@ -2974,6 +3088,13 @@
new CfConstString(options.itemFactory.createString("Too large for unsigned long: ")),
new CfLoad(ValueType.OBJECT, 0),
new CfInvoke(
+ 185,
+ options.itemFactory.createMethod(
+ options.itemFactory.charSequenceType,
+ options.itemFactory.createProto(options.itemFactory.stringType),
+ options.itemFactory.createString("toString")),
+ true),
+ new CfInvoke(
182,
options.itemFactory.createMethod(
options.itemFactory.stringType,
@@ -2993,9 +3114,11 @@
label18,
new CfFrame(
new Int2ReferenceAVLTreeMap<>(
- new int[] {0, 1, 2, 3, 5, 6, 8, 9},
+ new int[] {0, 1, 2, 3, 4, 5, 7, 8, 10, 11},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initialized(options.itemFactory.charSequenceType),
+ FrameType.initialized(options.itemFactory.intType),
+ FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.longType),
@@ -3005,23 +3128,25 @@
FrameType.initialized(options.itemFactory.intType)
}),
new ArrayDeque<>(Arrays.asList())),
- new CfLoad(ValueType.LONG, 6),
- new CfLoad(ValueType.INT, 1),
+ new CfLoad(ValueType.LONG, 8),
+ new CfLoad(ValueType.INT, 3),
new CfNumberConversion(NumericType.INT, NumericType.LONG),
new CfArithmeticBinop(CfArithmeticBinop.Opcode.Mul, NumericType.LONG),
- new CfLoad(ValueType.INT, 9),
+ new CfLoad(ValueType.INT, 11),
new CfNumberConversion(NumericType.INT, NumericType.LONG),
new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.LONG),
- new CfStore(ValueType.LONG, 6),
+ new CfStore(ValueType.LONG, 8),
label19,
- new CfIinc(8, 1),
+ new CfIinc(10, 1),
new CfGoto(label11),
label20,
new CfFrame(
new Int2ReferenceAVLTreeMap<>(
- new int[] {0, 1, 2, 3, 5, 6},
+ new int[] {0, 1, 2, 3, 4, 5, 7, 8},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initialized(options.itemFactory.charSequenceType),
+ FrameType.initialized(options.itemFactory.intType),
+ FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.longType),
@@ -3029,13 +3154,52 @@
FrameType.initialized(options.itemFactory.longType)
}),
new ArrayDeque<>(Arrays.asList())),
- new CfLoad(ValueType.LONG, 6),
+ new CfLoad(ValueType.LONG, 8),
new CfReturn(ValueType.LONG),
label21),
ImmutableList.of(),
ImmutableList.of());
}
+ public static CfCode LongMethods_parseUnsignedLongWithRadix(
+ InternalOptions options, DexMethod method) {
+ CfLabel label0 = new CfLabel();
+ CfLabel label1 = new CfLabel();
+ return new CfCode(
+ method.holder,
+ 4,
+ 2,
+ ImmutableList.of(
+ label0,
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfConstNumber(0, ValueType.INT),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.stringType,
+ options.itemFactory.createProto(options.itemFactory.intType),
+ options.itemFactory.createString("length")),
+ false),
+ new CfLoad(ValueType.INT, 1),
+ new CfInvoke(
+ 184,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/lang/Long;"),
+ options.itemFactory.createProto(
+ options.itemFactory.longType,
+ options.itemFactory.charSequenceType,
+ options.itemFactory.intType,
+ options.itemFactory.intType,
+ options.itemFactory.intType),
+ options.itemFactory.createString("parseUnsignedLong")),
+ false),
+ new CfReturn(ValueType.LONG),
+ label1),
+ ImmutableList.of(),
+ ImmutableList.of());
+ }
+
public static CfCode LongMethods_remainderUnsigned(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
diff --git a/src/test/examplesJava9/backport/IntegerBackportJava9Main.java b/src/test/examplesJava9/backport/IntegerBackportJava9Main.java
new file mode 100644
index 0000000..10c86f2
--- /dev/null
+++ b/src/test/examplesJava9/backport/IntegerBackportJava9Main.java
@@ -0,0 +1,97 @@
+// Copyright (c) 2021, 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.math.BigInteger;
+
+public final class IntegerBackportJava9Main {
+ private static final int[] interestingValues = {
+ Integer.MIN_VALUE,
+ Integer.MAX_VALUE,
+ Short.MIN_VALUE,
+ Short.MAX_VALUE,
+ Byte.MIN_VALUE,
+ Byte.MAX_VALUE,
+ 0,
+ -1,
+ 1,
+ -42,
+ 42
+ };
+
+ public static void main(String[] args) {
+ testParseIntegerSubsequenceWithRadix();
+ }
+
+ private static void testParseIntegerSubsequenceWithRadix() {
+ 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 valueString = prefix + Long.toString(value, radix) + postfix;
+ int start = prefix.length();
+ int end = valueString.length() - postfix.length();
+ assertEquals(valueString, value, Integer.parseInt(valueString, start, end, radix));
+ if (value > 0) {
+ valueString = prefix + '+' + Long.toString(value, radix) + postfix;
+ end++;
+ assertEquals(valueString, value, Integer.parseInt(valueString, start, end, radix));
+ }
+ }
+ }
+ }
+ }
+
+ try {
+ throw new AssertionError(Long.parseUnsignedLong("0", 0, 1, Character.MIN_RADIX - 1));
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ throw new AssertionError(Long.parseUnsignedLong("0", 0, 1, Character.MAX_RADIX + 1));
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ throw new AssertionError(Long.parseUnsignedLong("", 0, 0, 16));
+ } catch (NumberFormatException expected) {
+ }
+ try {
+ throw new AssertionError(Long.parseUnsignedLong("-", 0, 1, 16));
+ } catch (NumberFormatException expected) {
+ }
+ try {
+ throw new AssertionError(Long.parseUnsignedLong("+", 0, 1, 16));
+ } catch (NumberFormatException expected) {
+ }
+
+ BigInteger overflow = new BigInteger("18446744073709551616");
+ 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;
+ int start = prefix.length();
+ int end = overflowString.length() - postfix.length();
+ try {
+ throw new AssertionError(Long.parseLong(overflowString, start, end, radix));
+ } catch (NumberFormatException expected) {
+ }
+ String underflowString = prefix + '-' + overflow.toString(radix) + postfix;
+ start = prefix.length();
+ end = underflowString.length() - postfix.length();
+ try {
+ throw new AssertionError(Long.parseLong(underflowString, start, end, radix));
+ } catch (NumberFormatException expected) {
+ }
+ }
+ }
+ }
+ }
+
+ private static void assertEquals(String m, int expected, int actual) {
+ if (expected != actual) {
+ throw new AssertionError(m + " Expected <" + expected + "> but was <" + actual + '>');
+ }
+ }
+}
diff --git a/src/test/examplesJava9/backport/LongBackportJava9Main.java b/src/test/examplesJava9/backport/LongBackportJava9Main.java
new file mode 100644
index 0000000..1634a43
--- /dev/null
+++ b/src/test/examplesJava9/backport/LongBackportJava9Main.java
@@ -0,0 +1,154 @@
+// Copyright (c) 2021, 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.math.BigInteger;
+
+public final class LongBackportJava9Main {
+ private static final long[] interestingValues = {
+ Long.MIN_VALUE,
+ Long.MAX_VALUE,
+ Integer.MIN_VALUE,
+ Integer.MAX_VALUE,
+ Short.MIN_VALUE,
+ Short.MAX_VALUE,
+ Byte.MIN_VALUE,
+ Byte.MAX_VALUE,
+ 0L,
+ -1L,
+ 1L,
+ -42L,
+ 42L
+ };
+
+ public static void main(String[] args) {
+ testParseLongSubsequenceWithRadix(args.length == 0 || !args[0].startsWith("4."));
+ testParseUnsignedLongSubsequenceWithRadix();
+ }
+
+ private static void testParseLongSubsequenceWithRadix(boolean supportsPlusPrefix) {
+ for (long 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 valueString = prefix + Long.toString(value, radix) + postfix;
+ int start = prefix.length();
+ int end = valueString.length() - postfix.length();
+ assertEquals(valueString, value, Long.parseLong(valueString, start, end, radix));
+ if (value > 0 && supportsPlusPrefix) {
+ valueString = prefix + "+" + Long.toString(value, radix) + postfix;
+ end++;
+ assertEquals(valueString, value, Long.parseLong(valueString, start, end, radix));
+ }
+ }
+ }
+ }
+ }
+
+ try {
+ throw new AssertionError(Long.parseUnsignedLong("0", 0, 1, Character.MIN_RADIX - 1));
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ throw new AssertionError(Long.parseUnsignedLong("0", 0, 1, Character.MAX_RADIX + 1));
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ throw new AssertionError(Long.parseUnsignedLong("", 0, 0, 16));
+ } catch (NumberFormatException expected) {
+ }
+ try {
+ throw new AssertionError(Long.parseUnsignedLong("-", 0, 1, 16));
+ } catch (NumberFormatException expected) {
+ }
+ try {
+ throw new AssertionError(Long.parseUnsignedLong("+", 0, 1, 16));
+ } catch (NumberFormatException expected) {
+ }
+
+ BigInteger overflow = new BigInteger("18446744073709551616");
+ 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;
+ int start = prefix.length();
+ int end = overflowString.length() - postfix.length();
+ try {
+ throw new AssertionError(Long.parseLong(overflowString, start, end, radix));
+ } catch (NumberFormatException expected) {
+ }
+ }
+ }
+ }
+ }
+
+ private static void testParseUnsignedLongSubsequenceWithRadix() {
+ for (long 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 valueString = prefix + unsignedLongToBigInteger(value).toString(radix) + postfix;
+ int start = prefix.length();
+ int end = valueString.length() - postfix.length();
+ assertEquals(
+ valueString, value, Long.parseUnsignedLong(valueString, start, end, radix));
+ valueString = prefix + "+" + unsignedLongToBigInteger(value).toString(radix) + postfix;
+ end++;
+ assertEquals(
+ valueString, value, Long.parseUnsignedLong(valueString, start, end, radix));
+ }
+ }
+ }
+ }
+
+ try {
+ throw new AssertionError(Long.parseUnsignedLong("0", 0, 1, Character.MIN_RADIX - 1));
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ throw new AssertionError(Long.parseUnsignedLong("0", 0, 1, Character.MAX_RADIX + 1));
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ throw new AssertionError(Long.parseUnsignedLong("", 0, 0, 16));
+ } catch (NumberFormatException expected) {
+ }
+ try {
+ throw new AssertionError(Long.parseUnsignedLong("+", 0, 1, 16));
+ } catch (NumberFormatException expected) {
+ }
+
+ BigInteger overflow = new BigInteger("18446744073709551616");
+ for (int radix = Character.MIN_RADIX; radix <= Character.MAX_RADIX; radix++) {
+ for (String prefix : new String[] {"", "x", "xxx", "+", "x+", "xxx+"}) {
+ for (String postfix : new String[] {"", "x", "xxx"}) {
+ String overflowString = prefix + overflow.toString(radix) + postfix;
+ int start = prefix.length();
+ int end = overflowString.length() - postfix.length();
+ try {
+ throw new AssertionError(Long.parseUnsignedLong(overflowString, start, end, radix));
+ } catch (NumberFormatException expected) {
+ }
+ }
+ }
+ }
+ }
+
+ private static BigInteger unsignedLongToBigInteger(long value) {
+ BigInteger bigInt = BigInteger.valueOf(value & 0x7fffffffffffffffL);
+ if (value < 0) {
+ bigInt = bigInt.setBit(Long.SIZE - 1);
+ }
+ return bigInt;
+ }
+
+ private static void assertEquals(String m, long expected, long actual) {
+ if (expected != actual) {
+ throw new AssertionError(m + " Expected <" + expected + "> but was <" + actual + '>');
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java b/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
index 4161d87..fcc79dc 100644
--- a/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
+++ b/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.cf.CfCodePrinter;
+import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.ClassKind;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
@@ -18,6 +19,7 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.io.File;
@@ -66,6 +68,10 @@
return Paths.get(ToolHelper.SOURCE_DIR, getGeneratedType().getInternalName() + ".java");
}
+ protected CfCode getCode(String holderName, String methodName, CfCode code) {
+ return code;
+ }
+
private String getGeneratedClassName() {
return getGeneratedType().getName();
}
@@ -94,7 +100,7 @@
}
private void readMethodTemplatesInto(CfCodePrinter codePrinter) throws IOException {
- InternalOptions options = new InternalOptions();
+ InternalOptions options = new InternalOptions(factory, new Reporter());
options.testing.readInputStackMaps = true;
JarClassFileReader<DexProgramClass> reader =
new JarClassFileReader<>(
@@ -104,9 +110,13 @@
if (method.isInitializer()) {
continue;
}
- String methodName =
- method.getHolderType().getName() + "_" + method.method.name.toString();
- codePrinter.visitMethod(methodName, method.getCode().asCfCode());
+ String holderName = method.getHolderType().getName();
+ String methodName = method.method.name.toString();
+ String generatedMethodName = holderName + "_" + methodName;
+ CfCode code = getCode(holderName, methodName, method.getCode().asCfCode());
+ if (code != null) {
+ codePrinter.visitMethod(generatedMethodName, code);
+ }
}
},
ClassKind.PROGRAM);
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
index 646dbf9..784e875 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
@@ -102,7 +102,10 @@
.apply(this::configureProgram)
.setIncludeClassesChecksum(true)
.compile()
- .run(parameters.getRuntime(), testClassName)
+ .run(
+ parameters.getRuntime(),
+ testClassName,
+ parameters.getRuntime().asDex().getVm().getVersion().toString())
.assertSuccess()
.inspect(this::assertDesugaring);
}
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
new file mode 100644
index 0000000..a2e1120
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportJava9Test.java
@@ -0,0 +1,37 @@
+// Copyright (c) 2021, 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 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 IntegerBackportJava9Test extends AbstractBackportTest {
+ @Parameters(name = "{0}")
+ public static Iterable<?> data() {
+ return getTestParameters()
+ .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+ .withDexRuntimes()
+ .withAllApiLevels()
+ .build();
+ }
+
+ private static final Path TEST_JAR =
+ 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.
+ }
+}
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
new file mode 100644
index 0000000..4bd90c8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/LongBackportJava9Test.java
@@ -0,0 +1,37 @@
+// Copyright (c) 2021, 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 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 LongBackportJava9Test extends AbstractBackportTest {
+ @Parameters(name = "{0}")
+ public static Iterable<?> data() {
+ return getTestParameters()
+ .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+ .withDexRuntimes()
+ .withAllApiLevels()
+ .build();
+ }
+
+ private static final Path TEST_JAR =
+ 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.
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
index d78a1c7..455c5c2 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
@@ -9,7 +9,11 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.cfmethodgeneration.MethodGenerationBase;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.FileUtils;
import com.google.common.collect.ImmutableList;
@@ -17,6 +21,7 @@
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
+import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -73,6 +78,44 @@
return 2020;
}
+ private static CfInstruction rewriteToJava9API(
+ DexItemFactory itemFactory, CfInstruction instruction) {
+ // Rewrite static invoke of javaUtilLongParseUnsignedLongStub to j.l.Long.parseUnsignedLong.
+ if (instruction.isInvoke()
+ && instruction
+ .asInvoke()
+ .getMethod()
+ .getName()
+ .toString()
+ .equals("javaLangLongParseUnsignedLongStub")) {
+ CfInvoke invoke = instruction.asInvoke();
+ return new CfInvoke(
+ invoke.getOpcode(),
+ itemFactory.createMethod(
+ itemFactory.createType("Ljava/lang/Long;"),
+ invoke.getMethod().getProto(),
+ itemFactory.createString("parseUnsignedLong")),
+ invoke.isInterface());
+ } else {
+ return instruction;
+ }
+ }
+
+ @Override
+ protected CfCode getCode(String holderName, String methodName, CfCode code) {
+ if (methodName.endsWith("Stub")) {
+ // Don't include stubs targeted only for rewriting in the generated code.
+ return null;
+ }
+ if (holderName.equals("LongMethods") && methodName.equals("parseUnsignedLongWithRadix")) {
+ code.setInstructions(
+ code.getInstructions().stream()
+ .map(instruction -> rewriteToJava9API(factory, instruction))
+ .collect(Collectors.toList()));
+ }
+ return code;
+ }
+
@Test
public void testBackportsGenerated() throws Exception {
ArrayList<Class<?>> sorted = new ArrayList<>(getMethodTemplateClasses());
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 0560b97..a3bd157 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
@@ -57,4 +57,9 @@
long asLong = i & 0xffffffffL;
return Long.toString(asLong, radix);
}
+
+ public static int parseIntSubsequenceWithRadix(
+ CharSequence s, int beginIndex, int endIndex, int radix) throws NumberFormatException {
+ return Integer.parseInt(s.subSequence(beginIndex, endIndex).toString(), radix);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/LongMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/LongMethods.java
index 643d295..65a60ef 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/LongMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/LongMethods.java
@@ -10,6 +10,11 @@
return (int) (l ^ (l >>> 32));
}
+ public static long parseLongSubsequenceWithRadix(
+ CharSequence s, int beginIndex, int endIndex, int radix) {
+ return Long.parseLong(s.subSequence(beginIndex, endIndex).toString(), radix);
+ }
+
public static long divideUnsigned(long dividend, long divisor) {
// This implementation is adapted from Guava's UnsignedLongs.java and Longs.java.
@@ -92,10 +97,20 @@
return Long.parseUnsignedLong(s, 10);
}
+ private static long javaLangLongParseUnsignedLongStub(
+ CharSequence s, int beginIndex, int endIndex, int radix) {
+ throw new RuntimeException("Stub invoked");
+ }
+
public static long parseUnsignedLongWithRadix(String s, int radix) {
+ return javaLangLongParseUnsignedLongStub(s, 0, s.length(), radix);
+ }
+
+ public static long parseUnsignedLongSubsequenceWithRadix(
+ CharSequence s, int beginIndex, int endIndex, int radix) {
// This implementation is adapted from Guava's UnsignedLongs.java
- int length = s.length();
+ int length = endIndex - beginIndex;
if (length == 0) {
throw new NumberFormatException("empty string");
}
@@ -106,23 +121,23 @@
long maxValueBeforeRadixMultiply = Long.divideUnsigned(-1L, radix);
// If the string starts with '+' and contains at least two characters, skip the plus.
- int start = s.charAt(0) == '+' && length > 1 ? 1 : 0;
+ int start = s.charAt(beginIndex) == '+' && length > 1 ? beginIndex + 1 : beginIndex;
long value = 0;
- for (int pos = start; pos < length; pos++) {
+ for (int pos = start; pos < endIndex; pos++) {
int digit = Character.digit(s.charAt(pos), radix);
if (digit == -1) {
- throw new NumberFormatException(s);
+ throw new NumberFormatException(s.toString());
}
- if (// high bit is already set
- value < 0
+ if ( // high bit is already set
+ value < 0
// or radix multiply will overflow
|| value > maxValueBeforeRadixMultiply
// or digit add will overflow after radix multiply
|| (value == maxValueBeforeRadixMultiply
&& digit > (int) Long.remainderUnsigned(-1L, radix))) {
// Explicit String.concat to work around https://issuetracker.google.com/issues/136596951.
- throw new NumberFormatException("Too large for unsigned long: ".concat(s));
+ throw new NumberFormatException("Too large for unsigned long: ".concat(s.toString()));
}
value = (value * radix) + digit;
}