Extend catch-all range for monitors to include exceptional monitor-exit
Fixes: b/348785664
Change-Id: I39cc17d08b97274fcd0324511b2780154eacad9d
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
index 48241f6..982ef6a 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.conversion;
+import static com.android.tools.r8.ir.conversion.IRBuilder.EXCEPTIONAL_SYNC_EXIT_MONITOR_EXIT_OFFSET;
+import static com.android.tools.r8.ir.conversion.IRBuilder.EXCEPTIONAL_SYNC_EXIT_THROW_OFFSET;
import static it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMaps.emptyMap;
import com.android.tools.r8.cf.code.CfFrame;
@@ -59,12 +61,26 @@
public class CfSourceCode implements SourceCode {
+ private enum GeneratedMethodSynchronizationBlock {
+ METHOD_ENTER,
+ METHOD_EXIT,
+ EXCEPTIONAL_MONITOR_EXIT,
+ EXCEPTIONAL_THROW,
+ NONE;
+
+ static GeneratedMethodSynchronizationBlock fromOffset(int offset) {
+ assert isExceptionalExitForMethodSynchronization(offset);
+ return isExceptionalMonitorExitForMethodSynchronization(offset)
+ ? EXCEPTIONAL_MONITOR_EXIT
+ : EXCEPTIONAL_THROW;
+ }
+ }
+
private BlockInfo currentBlockInfo;
private boolean hasExitingInstruction = false;
- private static final int EXCEPTIONAL_SYNC_EXIT_OFFSET = -2;
private final boolean needsGeneratedMethodSynchronization;
- private boolean currentlyGeneratingMethodSynchronization = false;
+ private GeneratedMethodSynchronizationBlock currentlyGeneratingMethodSynchronization;
private Monitor monitorEnter = null;
private static class TryHandlerList {
@@ -129,7 +145,7 @@
}
if (needsGeneratedMethodSynchronization && !seenCatchAll) {
guards.add(factory.throwableType);
- offsets.add(EXCEPTIONAL_SYNC_EXIT_OFFSET);
+ offsets.add(EXCEPTIONAL_SYNC_EXIT_MONITOR_EXIT_OFFSET);
}
return new TryHandlerList(startOffset, endOffset, guards, offsets);
}
@@ -239,6 +255,18 @@
&& getMethod().isSynchronized();
}
+ void setCurrentlyGeneratingMethodSynchronization(
+ GeneratedMethodSynchronizationBlock currentlyGeneratingMethodSynchronization) {
+ assert this.currentlyGeneratingMethodSynchronization
+ == GeneratedMethodSynchronizationBlock.NONE;
+ this.currentlyGeneratingMethodSynchronization = currentlyGeneratingMethodSynchronization;
+ }
+
+ void unsetCurrentlyGeneratingMethodSynchronization() {
+ assert currentlyGeneratingMethodSynchronization != GeneratedMethodSynchronizationBlock.NONE;
+ currentlyGeneratingMethodSynchronization = GeneratedMethodSynchronizationBlock.NONE;
+ }
+
private DexEncodedMethod getMethod() {
return method.getDefinition();
}
@@ -272,7 +300,6 @@
@Override
public int traceInstruction(int instructionIndex, IRBuilder builder) {
CfInstruction instruction = code.getInstructions().get(instructionIndex);
- AppView<?> appView = builder.appView;
assert appView.options().isGeneratingClassFiles()
== internalOutputMode.isGeneratingClassFiles();
if (instruction.canThrow()) {
@@ -393,16 +420,30 @@
}
private boolean isCurrentlyGeneratingMethodSynchronization() {
- return currentlyGeneratingMethodSynchronization;
+ return currentlyGeneratingMethodSynchronization != GeneratedMethodSynchronizationBlock.NONE;
}
- private boolean isExceptionalExitForMethodSynchronization(int instructionIndex) {
- return instructionIndex == EXCEPTIONAL_SYNC_EXIT_OFFSET;
+ private boolean isCurrentlyGeneratingExceptionalMonitorExitForMethodSynchronization() {
+ return currentlyGeneratingMethodSynchronization
+ == GeneratedMethodSynchronizationBlock.EXCEPTIONAL_MONITOR_EXIT;
+ }
+
+ public static boolean isExceptionalMonitorExitForMethodSynchronization(int instructionIndex) {
+ return instructionIndex == EXCEPTIONAL_SYNC_EXIT_MONITOR_EXIT_OFFSET;
+ }
+
+ public static boolean isExceptionalThrowForMethodSynchronization(int instructionIndex) {
+ return instructionIndex == EXCEPTIONAL_SYNC_EXIT_THROW_OFFSET;
+ }
+
+ public static boolean isExceptionalExitForMethodSynchronization(int instructionIndex) {
+ return isExceptionalMonitorExitForMethodSynchronization(instructionIndex)
+ || isExceptionalThrowForMethodSynchronization(instructionIndex);
}
private void buildMethodEnterSynchronization(IRBuilder builder) {
assert needsGeneratedMethodSynchronization;
- currentlyGeneratingMethodSynchronization = true;
+ setCurrentlyGeneratingMethodSynchronization(GeneratedMethodSynchronizationBlock.METHOD_ENTER);
DexType type = method.getHolderType();
int monitorRegister;
if (getMethod().isStatic()) {
@@ -414,24 +455,30 @@
}
// Build the monitor enter and save it for when generating exits later.
monitorEnter = builder.addMonitor(MonitorType.ENTER, monitorRegister);
- currentlyGeneratingMethodSynchronization = false;
+ unsetCurrentlyGeneratingMethodSynchronization();
}
- private void buildExceptionalExitMethodSynchronization(IRBuilder builder) {
+ private void buildExceptionalExitForMethodSynchronization(
+ IRBuilder builder, int instructionIndex) {
assert needsGeneratedMethodSynchronization;
- currentlyGeneratingMethodSynchronization = true;
- state.setPosition(getCanonicalDebugPositionAtOffset(EXCEPTIONAL_SYNC_EXIT_OFFSET));
- builder.add(new Monitor(MonitorType.EXIT, monitorEnter.inValues().get(0)));
- builder.addThrow(getMoveExceptionRegister(0));
- currentlyGeneratingMethodSynchronization = false;
+ setCurrentlyGeneratingMethodSynchronization(
+ GeneratedMethodSynchronizationBlock.fromOffset(instructionIndex));
+ state.setPosition(getCanonicalDebugPositionAtOffset(instructionIndex));
+ if (isExceptionalMonitorExitForMethodSynchronization(instructionIndex)) {
+ builder.add(new Monitor(MonitorType.EXIT, monitorEnter.object()));
+ builder.addGoto(EXCEPTIONAL_SYNC_EXIT_THROW_OFFSET);
+ } else {
+ builder.addThrow(getMoveExceptionRegister(0));
+ }
+ unsetCurrentlyGeneratingMethodSynchronization();
}
@Override
public void buildPostlude(IRBuilder builder) {
if (needsGeneratedMethodSynchronization) {
- currentlyGeneratingMethodSynchronization = true;
- builder.add(new Monitor(MonitorType.EXIT, monitorEnter.inValues().get(0)));
- currentlyGeneratingMethodSynchronization = false;
+ setCurrentlyGeneratingMethodSynchronization(GeneratedMethodSynchronizationBlock.METHOD_EXIT);
+ builder.add(new Monitor(MonitorType.EXIT, monitorEnter.object()));
+ unsetCurrentlyGeneratingMethodSynchronization();
}
}
@@ -484,7 +531,7 @@
public void buildInstruction(
IRBuilder builder, int instructionIndex, boolean firstBlockInstruction) {
if (isExceptionalExitForMethodSynchronization(instructionIndex)) {
- buildExceptionalExitMethodSynchronization(builder);
+ buildExceptionalExitForMethodSynchronization(builder, instructionIndex);
return;
}
CfInstruction instruction = code.getInstructions().get(instructionIndex);
@@ -507,7 +554,7 @@
if (instruction.canThrow()) {
Snapshot exceptionTransfer =
- state.getSnapshot().exceptionTransfer(builder.appView.dexItemFactory().throwableType);
+ state.getSnapshot().exceptionTransfer(appView.dexItemFactory().throwableType);
for (int target : currentBlockInfo.exceptionalSuccessors) {
recordStateForTarget(target, exceptionTransfer);
}
@@ -820,12 +867,19 @@
if (inPrelude) {
return null;
}
+ TryHandlerList tryHandlers;
if (isCurrentlyGeneratingMethodSynchronization()) {
- return null;
+ if (!isCurrentlyGeneratingExceptionalMonitorExitForMethodSynchronization()) {
+ return null;
+ }
+ // Ensure that the exceptional monitor-exit instruction is guarded by a catch-all handler that
+ // repeats the monitor-exit.
+ tryHandlers =
+ getTryHandlers(EXCEPTIONAL_SYNC_EXIT_MONITOR_EXIT_OFFSET, appView.dexItemFactory());
+ } else {
+ tryHandlers =
+ getTryHandlers(instructionOffset(currentInstructionIndex), appView.dexItemFactory());
}
- TryHandlerList tryHandlers =
- getTryHandlers(
- instructionOffset(currentInstructionIndex), builder.appView.dexItemFactory());
if (tryHandlers.isEmpty()) {
return null;
}
@@ -857,7 +911,7 @@
@Override
public Position getCanonicalDebugPositionAtOffset(int offset) {
- if (offset == EXCEPTIONAL_SYNC_EXIT_OFFSET) {
+ if (isExceptionalExitForMethodSynchronization(offset)) {
return canonicalPositions.getExceptionalExitPosition(
appView.options().debug,
() ->
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 8d03811..2c84747 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -14,6 +14,9 @@
import static com.android.tools.r8.ir.analysis.type.TypeElement.getNull;
import static com.android.tools.r8.ir.analysis.type.TypeElement.getSingle;
import static com.android.tools.r8.ir.analysis.type.TypeElement.getWide;
+import static com.android.tools.r8.ir.conversion.CfSourceCode.isExceptionalExitForMethodSynchronization;
+import static com.android.tools.r8.ir.conversion.CfSourceCode.isExceptionalMonitorExitForMethodSynchronization;
+import static com.android.tools.r8.ir.conversion.CfSourceCode.isExceptionalThrowForMethodSynchronization;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.errors.CompilationError;
@@ -156,6 +159,14 @@
public static final int INITIAL_BLOCK_OFFSET = -1;
+ // The (synthetic) offset of the monitor-exit instruction on the exceptional exit, when desugaring
+ // declared synchronized methods.
+ public static final int EXCEPTIONAL_SYNC_EXIT_MONITOR_EXIT_OFFSET = -2;
+
+ // The (synthetic) offset of the throw instruction that follows the monitor-exit instruction on
+ // the exceptional exit, when desugaring declared synchronized methods.
+ public static final int EXCEPTIONAL_SYNC_EXIT_THROW_OFFSET = -3;
+
private static TypeElement fromMemberType(MemberType type) {
switch (type) {
case BOOLEAN_OR_BYTE:
@@ -378,6 +389,8 @@
@SuppressWarnings("JdkObsolete")
private final Queue<Integer> traceBlocksWorklist = new LinkedList<>();
+ private boolean processedExceptionalMonitorExitForMethodSynchronization;
+
// Bitmap to ensure we don't process an instruction more than once.
private boolean[] processedInstructions = null;
@@ -631,6 +644,9 @@
traceBlocksWorklist.add(0);
while (!traceBlocksWorklist.isEmpty()) {
int startOfBlockOffset = traceBlocksWorklist.remove();
+ if (handleExceptionalExitForMethodSynchronization(startOfBlockOffset)) {
+ continue;
+ }
int startOfBlockIndex = source.instructionIndex(startOfBlockOffset);
// Check that the block has not been processed after being added.
if (isIndexProcessed(startOfBlockIndex)) {
@@ -2470,14 +2486,24 @@
// Ensure there is a block starting at offset and add it to the work-list if it needs processing.
private BlockInfo ensureBlock(int offset) {
// We don't enqueue negative targets (these are special blocks, eg, an argument prelude).
- if (offset >= 0 && !isOffsetProcessed(offset)) {
+ if (!isOffsetProcessed(offset)) {
traceBlocksWorklist.add(offset);
}
return ensureBlockWithoutEnqueuing(offset);
}
private boolean isOffsetProcessed(int offset) {
- return isIndexProcessed(source.instructionIndex(offset));
+ if (offset >= 0) {
+ return isIndexProcessed(source.instructionIndex(offset));
+ }
+ if (isExceptionalMonitorExitForMethodSynchronization(offset)) {
+ return isExceptionalMonitorExitForMethodSynchronizationProcessed();
+ } else {
+ // We never need to process this block since it has no successors and its predecessor is set
+ // up when processing the monitor-exit block.
+ assert isExceptionalThrowForMethodSynchronization(offset);
+ return true;
+ }
}
private boolean isIndexProcessed(int index) {
@@ -2498,6 +2524,34 @@
processedSubroutineInstructions.add(index);
}
+ private boolean handleExceptionalExitForMethodSynchronization(int startOfBlockOffset) {
+ if (!isExceptionalExitForMethodSynchronization(startOfBlockOffset)) {
+ return false;
+ }
+ // We never need to process the throw block. We therefore treat it as always processed, meaning
+ // it should never be traced here.
+ assert isExceptionalMonitorExitForMethodSynchronization(startOfBlockOffset);
+ if (markExceptionalMonitorExitForMethodSynchronizationProcessed()) {
+ ensureNormalSuccessorBlock(
+ EXCEPTIONAL_SYNC_EXIT_MONITOR_EXIT_OFFSET, EXCEPTIONAL_SYNC_EXIT_THROW_OFFSET);
+ ensureExceptionalSuccessorBlock(
+ EXCEPTIONAL_SYNC_EXIT_MONITOR_EXIT_OFFSET, EXCEPTIONAL_SYNC_EXIT_MONITOR_EXIT_OFFSET);
+ }
+ return true;
+ }
+
+ private boolean isExceptionalMonitorExitForMethodSynchronizationProcessed() {
+ return processedExceptionalMonitorExitForMethodSynchronization;
+ }
+
+ private boolean markExceptionalMonitorExitForMethodSynchronizationProcessed() {
+ if (isExceptionalMonitorExitForMethodSynchronizationProcessed()) {
+ return false;
+ }
+ processedExceptionalMonitorExitForMethodSynchronization = true;
+ return true;
+ }
+
private void ensureSubroutineProcessedInstructions() {
if (processedSubroutineInstructions == null) {
processedSubroutineInstructions = new HashSet<>();
@@ -2732,4 +2786,3 @@
return builder.toString();
}
}
-
diff --git a/src/test/java/com/android/tools/r8/workaround/ExpectedToBeWithinCatchAllAfterInliningWithSoftVerificationErrorTest.java b/src/test/java/com/android/tools/r8/workaround/ExpectedToBeWithinCatchAllAfterInliningWithSoftVerificationErrorTest.java
index 90de297..316af73 100644
--- a/src/test/java/com/android/tools/r8/workaround/ExpectedToBeWithinCatchAllAfterInliningWithSoftVerificationErrorTest.java
+++ b/src/test/java/com/android/tools/r8/workaround/ExpectedToBeWithinCatchAllAfterInliningWithSoftVerificationErrorTest.java
@@ -6,7 +6,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
import java.util.Objects;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -37,11 +36,7 @@
.setMinApi(parameters)
.compile()
.run(parameters.getRuntime(), Main.class)
- .applyIf(
- parameters.isCfRuntime()
- || !parameters.getDexRuntimeVersion().isEqualTo(Version.V5_1_1),
- runResult -> runResult.assertSuccessWithOutputLines("Hello, world!"),
- runResult -> runResult.assertFailureWithErrorThatThrows(VerifyError.class));
+ .assertSuccessWithOutputLines("Hello, world!");
}
static class Main {