Add modeling of String.trim()
Bug: 142354450
Change-Id: Iaacadf6f21fd4d56eb1237936d4706cde064b4cf
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 3bdaf06..0cc264a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -156,6 +156,7 @@
public final DexString compareToIgnoreCaseMethodName = createString("compareToIgnoreCase");
public final DexString cloneMethodName = createString("clone");
public final DexString substringName = createString("substring");
+ public final DexString trimName = createString("trim");
public final DexString valueOfMethodName = createString("valueOf");
public final DexString toStringMethodName = createString("toString");
@@ -882,6 +883,8 @@
public final DexMethod toString;
public final DexMethod intern;
+ public final DexMethod trim = createMethod(stringType, createProto(stringType), trimName);
+
private StringMethods() {
isEmpty = createMethod(
stringDescriptor, isEmptyMethodName, booleanDescriptor, DexString.EMPTY_ARRAY);
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 15eda31..7728c1b 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
@@ -111,8 +111,6 @@
}
}
- // int String#hashCode()
- // int String#length()
// boolean String#isEmpty()
// boolean String#startsWith(String)
// boolean String#endsWith(String)
@@ -120,6 +118,8 @@
// boolean String#equals(String)
// boolean String#equalsIgnoreCase(String)
// boolean String#contentEquals(String)
+ // int String#hashCode()
+ // int String#length()
// int String#indexOf(String)
// int String#indexOf(int)
// int String#lastIndexOf(String)
@@ -128,6 +128,7 @@
// int String#compareToIgnoreCase(String)
// String String#substring(int)
// String String#substring(int, int)
+ // String String#trim()
public void computeTrivialOperationsOnConstString(IRCode code) {
if (!code.metadata().mayHaveConstString()) {
return;
@@ -191,6 +192,23 @@
continue;
}
+ if (invokedMethod == factory.stringMethods.trim) {
+ Value receiver = invoke.getReceiver().getAliasedValue();
+ if (receiver.hasLocalInfo() || receiver.isPhi() || !receiver.definition.isConstString()) {
+ continue;
+ }
+ DexString resultString =
+ factory.createString(receiver.definition.asConstString().getValue().toString().trim());
+ Value newOutValue =
+ code.createValue(
+ TypeLatticeElement.stringClassType(appView, definitelyNotNull()),
+ invoke.getLocalInfo());
+ affectedValues.addAll(invoke.outValue().affectedValues());
+ it.replaceCurrentInstruction(new ConstString(newOutValue, resultString, throwingInfo));
+ numberOfSimplifiedConversions++;
+ continue;
+ }
+
Function<DexString, Integer> operatorWithNoArg = null;
BiFunction<DexString, DexString, Integer> operatorWithString = null;
BiFunction<DexString, Integer, Integer> operatorWithInt = null;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringTrimTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringTrimTest.java
new file mode 100644
index 0000000..cd7618d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringTrimTest.java
@@ -0,0 +1,71 @@
+// Copyright (c) 2019, 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.ir.optimize.string;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject.JumboStringMode;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class StringTrimTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public StringTrimTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject classSubject = inspector.clazz(TestClass.class);
+ assertThat(classSubject, isPresent());
+
+ MethodSubject mainMethodSubject = classSubject.mainMethod();
+ assertThat(mainMethodSubject, isPresent());
+ assertTrue(
+ mainMethodSubject
+ .streamInstructions()
+ .anyMatch(x -> x.isConstString("Hello world!", JumboStringMode.ALLOW)));
+ assertTrue(
+ mainMethodSubject
+ .streamInstructions()
+ .noneMatch(x -> x.isConstString(" Hello world! ", JumboStringMode.ALLOW)));
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ System.out.println(" Hello world! ".trim());
+ }
+ }
+}