Reland "Re-enable StringBuilder#append in Enum unboxer"
Bug: 166397278
Change-Id: I99b523740a5328ec89df2a35c0ae4015cac5de14
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index 9b1e05e..e9c4127 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -1256,6 +1256,11 @@
addRequiredNameData(enumClass);
return Reason.ELIGIBLE;
}
+ if (singleTargetReference == factory.stringBuilderMethods.appendObject
+ || singleTargetReference == factory.stringBufferMethods.appendObject) {
+ addRequiredNameData(enumClass);
+ return Reason.ELIGIBLE;
+ }
if (singleTargetReference == factory.objectMembers.getClass
&& (!invoke.hasOutValue() || !invoke.outValue().hasAnyUsers())) {
// This is a hidden null check.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index 7ddfbaa..bbe0796 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -38,6 +38,7 @@
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.StaticGet;
@@ -159,8 +160,8 @@
if (instruction.isInvokeMethodWithReceiver()) {
InvokeMethodWithReceiver invokeMethod = instruction.asInvokeMethodWithReceiver();
DexType enumType = getEnumTypeOrNull(invokeMethod.getReceiver(), convertedEnums);
+ DexMethod invokedMethod = invokeMethod.getInvokedMethod();
if (enumType != null) {
- DexMethod invokedMethod = invokeMethod.getInvokedMethod();
if (invokedMethod == factory.enumMembers.ordinalMethod
|| invokedMethod.match(factory.enumMembers.hashCode)) {
replaceEnumInvoke(
@@ -190,6 +191,42 @@
assert !invokeMethod.hasOutValue() || !invokeMethod.outValue().hasAnyUsers();
replaceEnumInvoke(
iterator, invokeMethod, zeroCheckMethod, m -> synthesizeZeroCheckMethod());
+ continue;
+ }
+ } else if (invokedMethod == factory.stringBuilderMethods.appendObject
+ || invokedMethod == factory.stringBufferMethods.appendObject) {
+ // Rewrites stringBuilder.append(enumInstance) as if it was
+ // stringBuilder.append(String.valueOf(unboxedEnumInstance));
+ Value enumArg = invokeMethod.getArgument(1);
+ DexType enumArgType = getEnumTypeOrNull(enumArg, convertedEnums);
+ if (enumArgType != null) {
+ DexMethod stringValueOfMethod = computeStringValueOfUtilityMethod(enumArgType);
+ InvokeStatic toStringInvoke =
+ InvokeStatic.builder()
+ .setMethod(stringValueOfMethod)
+ .setSingleArgument(enumArg)
+ .setFreshOutValue(appView, code)
+ .setPosition(invokeMethod)
+ .build();
+ DexMethod newAppendMethod =
+ invokedMethod == factory.stringBuilderMethods.appendObject
+ ? factory.stringBuilderMethods.appendString
+ : factory.stringBufferMethods.appendString;
+ List<Value> arguments =
+ ImmutableList.of(invokeMethod.getReceiver(), toStringInvoke.outValue());
+ InvokeVirtual invokeAppendString =
+ new InvokeVirtual(newAppendMethod, invokeMethod.clearOutValue(), arguments);
+ invokeAppendString.setPosition(invokeMethod.getPosition());
+ iterator.replaceCurrentInstruction(toStringInvoke);
+ if (block.hasCatchHandlers()) {
+ iterator
+ .splitCopyCatchHandlers(code, blocks, appView.options())
+ .listIterator(code)
+ .add(invokeAppendString);
+ } else {
+ iterator.add(invokeAppendString);
+ }
+ continue;
}
}
} else if (instruction.isInvokeStatic()) {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/StringValueOfEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/StringValueOfEnumUnboxingTest.java
index 904fc48f..2211d08 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/StringValueOfEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/StringValueOfEnumUnboxingTest.java
@@ -59,12 +59,76 @@
public static void main(String[] args) {
System.out.println(MyEnum.A.ordinal());
System.out.println(0);
+ stringValueOf();
+ stringBuilder();
+ }
+
+ private static void stringValueOf() {
System.out.println(getString(MyEnum.A));
System.out.println("A");
System.out.println(getString(null));
System.out.println("null");
}
+ private static void stringBuilder() {
+ StringBuilder stringBuilder = new StringBuilder();
+ append(stringBuilder, MyEnum.A);
+ append(stringBuilder, MyEnum.B);
+ append(stringBuilder, null);
+ appendTryCatch(stringBuilder, MyEnum.A);
+ appendTryCatch(stringBuilder, MyEnum.B);
+ appendTryCatch(stringBuilder, null);
+ System.out.println(stringBuilder.toString());
+ System.out.println("ABnullABnull");
+
+ StringBuffer stringBuffer = new StringBuffer();
+ append(stringBuffer, MyEnum.A);
+ append(stringBuffer, MyEnum.B);
+ append(stringBuffer, null);
+ appendTryCatch(stringBuffer, MyEnum.A);
+ appendTryCatch(stringBuffer, MyEnum.B);
+ appendTryCatch(stringBuffer, null);
+ System.out.println(stringBuffer.toString());
+ System.out.println("ABnullABnull");
+ }
+
+ @NeverInline
+ private static StringBuilder append(StringBuilder sb, MyEnum e) {
+ return sb.append(e);
+ }
+
+ @NeverInline
+ private static StringBuffer append(StringBuffer sb, MyEnum e) {
+ return sb.append(e);
+ }
+
+ @NeverInline
+ private static StringBuilder appendTryCatch(StringBuilder sb, MyEnum e) {
+ try {
+ sb.append(e);
+ throwNull();
+ } catch (NullPointerException ignored) {
+ }
+ return sb;
+ }
+
+ @NeverInline
+ private static StringBuffer appendTryCatch(StringBuffer sb, MyEnum e) {
+ try {
+ sb.append(e);
+ throwNull();
+ } catch (NullPointerException ignored) {
+ }
+ return sb;
+ }
+
+ @NeverInline
+ private static void throwNull() {
+ if (System.currentTimeMillis() > 0) {
+ throw new NullPointerException("exception");
+ }
+ }
+
@NeverInline
private static String getString(MyEnum e) {
return String.valueOf(e);