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());
+    }
+  }
+}