Add compareTo and compareToIgnoreCase to compile-time string computation
Bug: 119364907
Change-Id: I90817bbd254ff58dcff9c0ab3a1cf07839534ed7
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 1757459..e7b3967 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -140,6 +140,8 @@
public final DexString contentEqualsMethodName = createString("contentEquals");
public final DexString indexOfMethodName = createString("indexOf");
public final DexString lastIndexOfMethodName = createString("lastIndexOf");
+ public final DexString compareToMethodName = createString("compareTo");
+ public final DexString compareToIgnoreCaseMethodName = createString("compareToIgnoreCase");
public final DexString cloneMethodName = createString("clone");
public final DexString valueOfMethodName = createString("valueOf");
@@ -613,6 +615,8 @@
public final DexMethod indexOfString;
public final DexMethod lastIndexOfInt;
public final DexMethod lastIndexOfString;
+ public final DexMethod compareTo;
+ public final DexMethod compareToIgnoreCase;
public final DexMethod valueOf;
public final DexMethod toString;
@@ -649,6 +653,11 @@
createMethod(stringDescriptor, lastIndexOfMethodName, intDescriptor, needsOneString);
lastIndexOfInt =
createMethod(stringDescriptor, lastIndexOfMethodName, intDescriptor, needsOneInt);
+ compareTo =
+ createMethod(stringDescriptor, compareToMethodName, intDescriptor, needsOneString);
+ compareToIgnoreCase =
+ createMethod(stringDescriptor, compareToIgnoreCaseMethodName, intDescriptor,
+ needsOneString);
valueOf = createMethod(
stringDescriptor, valueOfMethodName, stringDescriptor, needsOneObject);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
index 3fc442a..5dfbb78 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.EscapeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
@@ -53,16 +54,18 @@
// int String#length()
// boolean String#isEmpty()
- // boolean String#startsWith(str)
- // boolean String#endsWith(str)
- // boolean String#contains(str)
- // boolean String#equals(str)
- // boolean String#equalsIgnoreCase(str)
- // boolean String#contentEquals(str)
- // int String#indexOf(str)
+ // boolean String#startsWith(String)
+ // boolean String#endsWith(String)
+ // boolean String#contains(String)
+ // boolean String#equals(String)
+ // boolean String#equalsIgnoreCase(String)
+ // boolean String#contentEquals(String)
+ // int String#indexOf(String)
// int String#indexOf(int)
- // int String#lastIndexOf(str)
+ // int String#lastIndexOf(String)
// int String#lastIndexOf(int)
+ // int String#compareTo(String)
+ // int String#compareToIgnoreCase(String)
public void computeTrivialOperationsOnConstString(IRCode code) {
if (!code.hasConstString) {
return;
@@ -102,6 +105,10 @@
operatorWithInt = String::lastIndexOf;
} else if (invokedMethod == factory.stringMethods.lastIndexOfString) {
operatorWithString = String::lastIndexOf;
+ } else if (invokedMethod == factory.stringMethods.compareTo) {
+ operatorWithString = String::compareTo;
+ } else if (invokedMethod == factory.stringMethods.compareToIgnoreCase) {
+ operatorWithString = String::compareToIgnoreCase;
} else {
continue;
}
@@ -111,12 +118,12 @@
|| !rcv.isConstant()) {
continue;
}
+ DexString rcvString = rcv.definition.asConstString().getValue();
ConstNumber constNumber;
if (operatorWithNoArg != null) {
assert invoke.inValues().size() == 1;
- ConstString rcvString = rcv.definition.asConstString();
- int v = operatorWithNoArg.apply(rcvString.getValue().toString());
+ int v = operatorWithNoArg.apply(rcvString.toString());
constNumber = code.createIntConstant(v);
} else if (operatorWithString != null) {
assert invoke.inValues().size() == 2;
@@ -126,10 +133,9 @@
|| !arg.isConstant()) {
continue;
}
- ConstString rcvString = rcv.definition.asConstString();
- ConstString argString = arg.definition.asConstString();
int v = operatorWithString.apply(
- rcvString.getValue().toString(), argString.getValue().toString());
+ rcvString.toString(),
+ arg.definition.asConstString().getValue().toString());
constNumber = code.createIntConstant(v);
} else {
assert operatorWithInt != null;
@@ -140,10 +146,9 @@
|| !arg.isConstant()) {
continue;
}
- ConstString rcvString = rcv.definition.asConstString();
- ConstNumber argInt = arg.definition.asConstNumber();
int v = operatorWithInt.apply(
- rcvString.getValue().toString(), argInt.getIntValue());
+ rcvString.toString(),
+ arg.definition.asConstNumber().getIntValue());
constNumber = code.createIntConstant(v);
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringContentCheckTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringContentCheckTest.java
index cd3a131..7044366 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringContentCheckTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringContentCheckTest.java
@@ -35,7 +35,9 @@
&& "CONST".equalsIgnoreCase(arg)
&& "CONST".contentEquals(arg)
&& "CONST".indexOf(arg) > 0
- && "CONST".lastIndexOf(arg) > 0;
+ && "CONST".lastIndexOf(arg) > 0
+ && "CONST".compareTo(arg) > 0
+ && "CONST".compareToIgnoreCase(arg) > 0;
}
public static void main(String[] args) {
@@ -52,6 +54,8 @@
System.out.println(s1.indexOf("ix"));
System.out.println(s1.lastIndexOf('f'));
System.out.println(s1.lastIndexOf("ix"));
+ System.out.println(s1.compareTo("prefix-CONST-suffix") == 0);
+ System.out.println(s1.compareToIgnoreCase("PREFIX-const-SUFFIX") == 0);
}
{
@@ -68,6 +72,8 @@
System.out.println(s2.indexOf("ix"));
System.out.println(s2.lastIndexOf('f'));
System.out.println(s2.lastIndexOf("ix"));
+ System.out.println(s2.compareTo("prefix-CONST-suffix") == 0);
+ System.out.println(s2.compareToIgnoreCase("pre-con-suf") == 0);
}
{
@@ -90,15 +96,15 @@
StringContentCheckTestMain.class
);
private static final String JAVA_OUTPUT = StringUtils.lines(
- // s1, contains
+ // s1, contains(String)
"true",
- // s1, startsWith
+ // s1, startsWith(String)
"true",
- // s1, endsWith
+ // s1, endsWith(String)
"true",
- // s1, equals
+ // s1, equals(String)
"true",
- // s1, equalsIgnoreCase
+ // s1, equalsIgnoreCase(String)
"true",
// s1, contentEquals(CharSequence)
"true",
@@ -112,15 +118,19 @@
"16",
// s1, lastIndexOf(String)
"17",
- // s2, contains
+ // s1, compareTo(String)
+ "true",
+ // s1, compareToIgnoreCase(String)
+ "true",
+ // s2, contains(String)
"false",
- // s2, startsWith
+ // s2, startsWith(String)
"false",
- // s2, endsWith
+ // s2, endsWith(String)
"false",
- // s2, equals
+ // s2, equals(String)
"false",
- // s2, equalsIgnoreCase
+ // s2, equalsIgnoreCase(String)
"false",
// s2, contentEquals(CharSequence)
"false",
@@ -134,6 +144,10 @@
"-1",
// s2, lastIndexOf(String)
"-1",
+ // s2, compareTo(String)
+ "false",
+ // s2, compareToIgnoreCase(String)
+ "false",
// argCouldBeNull
"false"
);
@@ -166,7 +180,9 @@
|| method.name.toString().equals("equalsIgnoreCase")
|| method.name.toString().equals("contentEquals")
|| method.name.toString().equals("indexOf")
- || method.name.toString().equals("lastIndexOf"));
+ || method.name.toString().equals("lastIndexOf")
+ || method.name.toString().equals("compareTo")
+ || method.name.toString().equals("compareToIgnoreCase"));
}
private long countStringContentChecker(MethodSubject method) {
@@ -190,7 +206,7 @@
"boolean", "argCouldBeNull", ImmutableList.of("java.lang.String"));
assertThat(argCouldBeNull, isPresent());
// Because of nullable argument, all checkers should remain.
- assertEquals(8, countStringContentChecker(argCouldBeNull));
+ assertEquals(10, countStringContentChecker(argCouldBeNull));
}
@Test
@@ -202,14 +218,14 @@
.addProgramClasses(CLASSES)
.run(MAIN)
.assertSuccessWithOutput(JAVA_OUTPUT);
- test(result, 22);
+ test(result, 26);
result = testForD8()
.release()
.addProgramClasses(CLASSES)
.run(MAIN)
.assertSuccessWithOutput(JAVA_OUTPUT);
- test(result, 12);
+ test(result, 14);
}
@Test
@@ -221,6 +237,6 @@
.addKeepMainRule(MAIN)
.run(MAIN)
.assertSuccessWithOutput(JAVA_OUTPUT);
- test(result, 12);
+ test(result, 14);
}
}