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