Move the rest of Twr desugaring to cf to cf

Bug: 193002915
Change-Id: I8a6161cb19318c46675fe6c7a788cce9501c160a
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 0b3c524..b01f268 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -105,7 +105,6 @@
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.InternalOptions.DesugarState;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.ThreadUtils;
@@ -1445,12 +1444,6 @@
     deadCodeRemover.run(code, timing);
     assert code.isConsistentSSA();
 
-    if (options.desugarState == DesugarState.ON && options.enableTryWithResourcesDesugaring()) {
-      timing.begin("Rewrite Throwable suppresed methods");
-      codeRewriter.rewriteThrowableAddAndGetSuppressed(code);
-      timing.end();
-    }
-
     previous = printMethod(code, "IR after lambda desugaring (SSA)", previous);
 
     assert code.verifyTypes(appView);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index 8ddd45c..150fc0a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -23,7 +23,7 @@
 import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring;
 import com.android.tools.r8.ir.desugar.records.RecordRewriter;
 import com.android.tools.r8.ir.desugar.stringconcat.StringConcatInstructionDesugaring;
-import com.android.tools.r8.ir.desugar.twr.TwrCloseResourceInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.twr.TwrInstructionDesugaring;
 import com.android.tools.r8.utils.IntBox;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.StringDiagnostic;
@@ -65,7 +65,7 @@
     }
     // Place TWR before Interface desugaring to eliminate potential $closeResource interface calls.
     if (appView.options().enableTryWithResourcesDesugaring()) {
-      desugarings.add(new TwrCloseResourceInstructionDesugaring(appView));
+      desugarings.add(new TwrInstructionDesugaring(appView));
     }
     // TODO(b/183998768): Enable interface method rewriter cf to cf also in R8.
     if (appView.options().isInterfaceMethodDesugaringEnabled()
@@ -288,7 +288,7 @@
                   || (appliedDesugaring instanceof InterfaceMethodRewriter
                       && (desugaring instanceof InvokeToPrivateRewriter
                           || desugaring instanceof D8NestBasedAccessDesugaring))
-                  || (appliedDesugaring instanceof TwrCloseResourceInstructionDesugaring
+                  || (appliedDesugaring instanceof TwrInstructionDesugaring
                       && desugaring instanceof InterfaceMethodRewriter)
               : "Desugaring of "
                   + instruction
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrCloseResourceInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrCloseResourceInstructionDesugaring.java
deleted file mode 100644
index 212259a..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrCloseResourceInstructionDesugaring.java
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright (c) 2021, 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.desugar.twr;
-
-import com.android.tools.r8.cf.code.CfInstruction;
-import com.android.tools.r8.cf.code.CfInvoke;
-import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
-import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
-import com.android.tools.r8.ir.desugar.FreshLocalProvider;
-import com.android.tools.r8.ir.desugar.LocalStackAllocator;
-import com.android.tools.r8.ir.desugar.backports.BackportedMethods;
-import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
-import com.google.common.collect.ImmutableList;
-import java.util.Collection;
-import org.objectweb.asm.Opcodes;
-
-public class TwrCloseResourceInstructionDesugaring implements CfInstructionDesugaring {
-
-  private final AppView<?> appView;
-  private final DexItemFactory dexItemFactory;
-  private final DexProto twrCloseResourceProto;
-
-  public TwrCloseResourceInstructionDesugaring(AppView<?> appView) {
-    this.appView = appView;
-    this.dexItemFactory = appView.dexItemFactory();
-    this.twrCloseResourceProto =
-        dexItemFactory.createProto(
-            dexItemFactory.voidType, dexItemFactory.throwableType, dexItemFactory.objectType);
-  }
-
-  @Override
-  public Collection<CfInstruction> desugarInstruction(
-      CfInstruction instruction,
-      FreshLocalProvider freshLocalProvider,
-      LocalStackAllocator localStackAllocator,
-      CfInstructionDesugaringEventConsumer eventConsumer,
-      ProgramMethod context,
-      MethodProcessingContext methodProcessingContext,
-      DexItemFactory dexItemFactory) {
-    if (!instruction.isInvokeStatic()) {
-      return null;
-    }
-
-    CfInvoke invoke = instruction.asInvoke();
-    DexMethod invokedMethod = invoke.getMethod();
-    if (!isTwrCloseResourceMethod(invokedMethod)) {
-      return null;
-    }
-
-    // Synthesize a new method.
-    ProgramMethod closeMethod = createSyntheticCloseResourceMethod(methodProcessingContext);
-    eventConsumer.acceptTwrCloseResourceMethod(closeMethod, context);
-
-    // Rewrite the invoke to the new synthetic.
-    return ImmutableList.of(new CfInvoke(Opcodes.INVOKESTATIC, closeMethod.getReference(), false));
-  }
-
-  private ProgramMethod createSyntheticCloseResourceMethod(
-      MethodProcessingContext methodProcessingContext) {
-    return appView
-        .getSyntheticItems()
-        .createMethod(
-            SyntheticKind.TWR_CLOSE_RESOURCE,
-            methodProcessingContext.createUniqueContext(),
-            appView,
-            methodBuilder ->
-                methodBuilder
-                    .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
-                    .setProto(twrCloseResourceProto)
-                    .setCode(
-                        m ->
-                            BackportedMethods.CloseResourceMethod_closeResourceImpl(
-                                appView.options(), m)));
-  }
-
-  @Override
-  public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
-    return instruction.isInvokeStatic()
-        && isTwrCloseResourceMethod(instruction.asInvoke().getMethod());
-  }
-
-  private boolean isTwrCloseResourceMethod(DexMethod method) {
-    return method.name == dexItemFactory.twrCloseResourceMethodName
-        && method.proto == dexItemFactory.twrCloseResourceMethodProto;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrInstructionDesugaring.java
new file mode 100644
index 0000000..b63846d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrInstructionDesugaring.java
@@ -0,0 +1,166 @@
+// Copyright (c) 2021, 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.desugar.twr;
+
+import com.android.tools.r8.cf.code.CfConstNumber;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfNewArray;
+import com.android.tools.r8.cf.code.CfStackInstruction;
+import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.FreshLocalProvider;
+import com.android.tools.r8.ir.desugar.LocalStackAllocator;
+import com.android.tools.r8.ir.desugar.backports.BackportedMethods;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import org.jetbrains.annotations.NotNull;
+import org.objectweb.asm.Opcodes;
+
+public class TwrInstructionDesugaring implements CfInstructionDesugaring {
+
+  private final AppView<?> appView;
+  private final DexItemFactory dexItemFactory;
+  private final DexProto twrCloseResourceProto;
+  private final DexMethod addSuppressed;
+  private final DexMethod getSuppressed;
+
+  public TwrInstructionDesugaring(AppView<?> appView) {
+    this.appView = appView;
+    this.dexItemFactory = appView.dexItemFactory();
+    this.twrCloseResourceProto =
+        dexItemFactory.createProto(
+            dexItemFactory.voidType, dexItemFactory.throwableType, dexItemFactory.objectType);
+    this.addSuppressed = dexItemFactory.throwableMethods.addSuppressed;
+    this.getSuppressed = dexItemFactory.throwableMethods.getSuppressed;
+  }
+
+  @Override
+  public Collection<CfInstruction> desugarInstruction(
+      CfInstruction instruction,
+      FreshLocalProvider freshLocalProvider,
+      LocalStackAllocator localStackAllocator,
+      CfInstructionDesugaringEventConsumer eventConsumer,
+      ProgramMethod context,
+      MethodProcessingContext methodProcessingContext,
+      DexItemFactory dexItemFactory) {
+    if (!instruction.isInvoke()) {
+      return null;
+    }
+    if (isTwrCloseResourceInvoke(instruction)) {
+      return rewriteTwrCloseResourceInvoke(eventConsumer, context, methodProcessingContext);
+    }
+    if (isTwrSuppressedInvoke(instruction, addSuppressed)) {
+      return rewriteTwrAddSuppressedInvoke();
+    }
+    if (isTwrSuppressedInvoke(instruction, getSuppressed)) {
+      return rewriteTwrGetSuppressedInvoke();
+    }
+    return null;
+  }
+
+  private Collection<CfInstruction> rewriteTwrAddSuppressedInvoke() {
+    // Remove Throwable::addSuppressed(Throwable) call.
+    return ImmutableList.of(new CfStackInstruction(Opcode.Pop), new CfStackInstruction(Opcode.Pop));
+  }
+
+  private Collection<CfInstruction> rewriteTwrGetSuppressedInvoke() {
+    // Replace call to Throwable::getSuppressed() with new Throwable[0].
+    return ImmutableList.of(
+        new CfStackInstruction(Opcode.Pop),
+        new CfConstNumber(0, ValueType.INT),
+        new CfNewArray(dexItemFactory.createArrayType(1, dexItemFactory.throwableType)));
+  }
+
+  @NotNull
+  private ImmutableList<CfInstruction> rewriteTwrCloseResourceInvoke(
+      CfInstructionDesugaringEventConsumer eventConsumer,
+      ProgramMethod context,
+      MethodProcessingContext methodProcessingContext) {
+    // Synthesize a new method.
+    ProgramMethod closeMethod = createSyntheticCloseResourceMethod(methodProcessingContext);
+    eventConsumer.acceptTwrCloseResourceMethod(closeMethod, context);
+    // Rewrite the invoke to the new synthetic.
+    return ImmutableList.of(new CfInvoke(Opcodes.INVOKESTATIC, closeMethod.getReference(), false));
+  }
+
+  private ProgramMethod createSyntheticCloseResourceMethod(
+      MethodProcessingContext methodProcessingContext) {
+    return appView
+        .getSyntheticItems()
+        .createMethod(
+            SyntheticKind.TWR_CLOSE_RESOURCE,
+            methodProcessingContext.createUniqueContext(),
+            appView,
+            methodBuilder ->
+                methodBuilder
+                    .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+                    .setProto(twrCloseResourceProto)
+                    .setCode(
+                        m ->
+                            BackportedMethods.CloseResourceMethod_closeResourceImpl(
+                                appView.options(), m)));
+  }
+
+  @Override
+  public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
+    if (!instruction.isInvoke()) {
+      return false;
+    }
+    return isTwrCloseResourceInvoke(instruction)
+        || isTwrSuppressedInvoke(instruction, addSuppressed)
+        || isTwrSuppressedInvoke(instruction, getSuppressed);
+  }
+
+  private boolean isTwrSuppressedInvoke(CfInstruction instruction, DexMethod suppressed) {
+    return instruction.isInvoke()
+        && matchesMethodOfThrowable(instruction.asInvoke().getMethod(), suppressed);
+  }
+
+  private boolean matchesMethodOfThrowable(DexMethod invoked, DexMethod expected) {
+    return invoked.name == expected.name
+        && invoked.proto == expected.proto
+        && isSubtypeOfThrowable(invoked.holder);
+  }
+
+  private boolean isSubtypeOfThrowable(DexType type) {
+    while (type != null && type != dexItemFactory.objectType) {
+      if (type == dexItemFactory.throwableType) {
+        return true;
+      }
+      DexClass dexClass = appView.definitionFor(type);
+      if (dexClass == null) {
+        throw new CompilationError(
+            "Class or interface "
+                + type.toSourceString()
+                + " required for desugaring of try-with-resources is not found.");
+      }
+      type = dexClass.superType;
+    }
+    return false;
+  }
+
+  private boolean isTwrCloseResourceInvoke(CfInstruction instruction) {
+    return instruction.isInvokeStatic()
+        && isTwrCloseResourceMethod(instruction.asInvoke().getMethod());
+  }
+
+  private boolean isTwrCloseResourceMethod(DexMethod method) {
+    return method.name == dexItemFactory.twrCloseResourceMethodName
+        && method.proto == dexItemFactory.twrCloseResourceMethodProto;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 2a1ec57..86fb8e9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -21,7 +21,6 @@
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexItemFactory.ThrowableMethods;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexProto;
@@ -3651,75 +3650,6 @@
     }
   }
 
-  // Removes calls to Throwable.addSuppressed(Throwable) and rewrites
-  // Throwable.getSuppressed() into new Throwable[0].
-  //
-  // Note that addSuppressed() and getSuppressed() methods are final in
-  // Throwable, so these changes don't have to worry about overrides.
-  public void rewriteThrowableAddAndGetSuppressed(IRCode code) {
-    ThrowableMethods throwableMethods = dexItemFactory.throwableMethods;
-
-    for (BasicBlock block : code.blocks) {
-      InstructionListIterator iterator = block.listIterator(code);
-      while (iterator.hasNext()) {
-        Instruction current = iterator.next();
-        if (current.isInvokeMethod()) {
-          DexMethod invokedMethod = current.asInvokeMethod().getInvokedMethod();
-          if (matchesMethodOfThrowable(invokedMethod, throwableMethods.addSuppressed)) {
-            // Remove Throwable::addSuppressed(Throwable) call.
-            iterator.removeOrReplaceByDebugLocalRead();
-          } else if (matchesMethodOfThrowable(invokedMethod, throwableMethods.getSuppressed)) {
-            Value destValue = current.outValue();
-            if (destValue == null) {
-              // If the result of the call was not used we don't create
-              // an empty array and just remove the call.
-              iterator.removeOrReplaceByDebugLocalRead();
-              continue;
-            }
-
-            // Replace call to Throwable::getSuppressed() with new Throwable[0].
-
-            // First insert the constant value *before* the current instruction.
-            ConstNumber zero = code.createIntConstant(0);
-            zero.setPosition(current.getPosition());
-            assert iterator.hasPrevious();
-            iterator.previous();
-            iterator.add(zero);
-
-            // Then replace the invoke instruction with new-array instruction.
-            Instruction next = iterator.next();
-            assert current == next;
-            NewArrayEmpty newArray = new NewArrayEmpty(destValue, zero.outValue(),
-                dexItemFactory.createType(dexItemFactory.throwableArrayDescriptor));
-            iterator.replaceCurrentInstruction(newArray);
-          }
-        }
-      }
-    }
-    assert code.isConsistentSSA();
-  }
-
-  private boolean matchesMethodOfThrowable(DexMethod invoked, DexMethod expected) {
-    return invoked.name == expected.name
-        && invoked.proto == expected.proto
-        && isSubtypeOfThrowable(invoked.holder);
-  }
-
-  private boolean isSubtypeOfThrowable(DexType type) {
-    while (type != null && type != dexItemFactory.objectType) {
-      if (type == dexItemFactory.throwableType) {
-        return true;
-      }
-      DexClass dexClass = appView.definitionFor(type);
-      if (dexClass == null) {
-        throw new CompilationError("Class or interface " + type.toSourceString() +
-            " required for desugaring of try-with-resources is not found.");
-      }
-      type = dexClass.superType;
-    }
-    return false;
-  }
-
   private Value addConstString(IRCode code, InstructionListIterator iterator, String s) {
     TypeElement typeLattice = TypeElement.stringClassType(appView, definitelyNotNull());
     Value value = code.createValue(typeLattice);
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
index c7544d5..f0f4e90 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
@@ -9,6 +9,7 @@
 import static org.hamcrest.CoreMatchers.startsWith;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 
 import com.android.tools.r8.L8Command;
 import com.android.tools.r8.OutputMode;
@@ -17,8 +18,11 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import org.junit.Assume;
@@ -104,6 +108,21 @@
     }
     assertThat(inspector.clazz("j$.util.Optional"), isPresent());
     assertThat(inspector.clazz("j$.util.function.Function"), isPresent());
+    if (parameters.getApiLevel().isLessThan(AndroidApiLevel.K)) {
+      inspector.forAllClasses(clazz -> clazz.forAllMethods(this::assertNoSupressedInvocations));
+    }
+  }
+
+  private void assertNoSupressedInvocations(FoundMethodSubject method) {
+    if (method.isAbstract()) {
+      return;
+    }
+    for (InstructionSubject instruction : method.instructions()) {
+      if (instruction.isInvoke() && instruction.getMethod() != null) {
+        assertNotEquals(
+            instruction.getMethod(), new DexItemFactory().throwableMethods.addSuppressed);
+      }
+    }
   }
 
 }