Check for out-value in string builder optimizer
Change-Id: I1a79c066a10aa1c43aefeb3de693abc353ce8dd6
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 58b768d..fe33ab9 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -572,7 +572,7 @@
createString("makeConcat")
);
- public final Set<DexMethod> libraryMethodsReturningReceiver =
+ public Set<DexMethod> libraryMethodsReturningReceiver =
ImmutableSet.<DexMethod>builder()
.addAll(stringBufferMethods.appendMethods)
.addAll(stringBuilderMethods.appendMethods)
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
index 8e8d2da..6c714c9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
@@ -828,6 +828,9 @@
public boolean isSupportedAppendMethod(InvokeMethod invoke) {
DexMethod invokedMethod = invoke.getInvokedMethod();
assert isAppendMethod(invokedMethod);
+ if (invoke.hasOutValue()) {
+ return false;
+ }
// Any methods other than append(arg) are not trivial since they may change the builder
// state not monotonically.
if (invoke.inValues().size() > 2) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithEscapingAliasTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithEscapingAliasTest.java
new file mode 100644
index 0000000..2c61465
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithEscapingAliasTest.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2020, 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 com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.Sets;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class StringBuilderWithEscapingAliasTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public StringBuilderWithEscapingAliasTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class)
+ .addKeepMainRule(TestClass.class)
+ .addOptionsModification(
+ options ->
+ options.itemFactory.libraryMethodsReturningReceiver = Sets.newIdentityHashSet())
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(StringUtils.times(StringUtils.lines("Hello world!"), 2));
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ StringBuilder builder = new StringBuilder();
+ StringBuilder alias = builder.append("Hello");
+ builder.append(" world!");
+ System.out.println(builder.toString());
+ System.out.println(alias.toString());
+ }
+ }
+}