blob: 17cce8127937518830b63fcc6cbf2b77c341595d [file] [log] [blame]
// Copyright (c) 2016, 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.smali;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.Return;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
import java.util.Arrays;
import java.util.Collections;
import org.junit.Test;
/**
* Regression test to ensure that we do not ignore the exceptional / on-throw value of a
* throwing instruction in the special case where the exceptional edge and the normal edge target
* the same block.
*/
public class CatchSuccessorFallthroughTest extends SmaliTestBase {
@Test
public void catchSuccessorFallthroughTest() throws Exception {
SmaliBuilder builder = new SmaliBuilder("Test");
builder.addStaticMethod("int", "maybeThrow", Arrays.asList("int"), 0,
" if-eqz v0, :throw",
" const v0, 42",
" return v0",
":throw",
" div-int/2addr v0, v0",
" return v0");
MethodSignature methodSig = builder.addStaticMethod(
"int", "method", Collections.singletonList("int"), 0,
":try_start",
" invoke-static {v0}, LTest;->maybeThrow(I)I",
" move-result v0",
":try_end",
" .catch Ljava/lang/Throwable; {:try_start .. :try_end} :return",
":return",
" return v0"
);
builder.addStaticMethod(
"void", "main", Arrays.asList("java.lang.String[]"), 2,
" sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;",
" const v0, 1",
" invoke-static {v0}, LTest;->method(I)I",
" move-result v0",
" invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(I)V",
" const v0, 0",
" invoke-static {v0}, LTest;->method(I)I",
" move-result v0",
" invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(I)V",
" return-void");
AndroidApp originalApplication = buildApplication(builder);
InternalOptions options = new InternalOptions();
options.programConsumer = DexIndexedConsumer.emptyConsumer();
DexApplication application =
new ApplicationReader(originalApplication, options, Timing.empty()).read();
ProgramMethod method = getProgramMethod(originalApplication, methodSig);
// Get the IR pre-optimization.
IRCode code = method.buildIR(AppView.createForD8(new AppInfo(application), options));
// Find the exit block and assert that the value is a phi merging the exceptional edge
// with the normal edge.
boolean hasReturn = false;
for (BasicBlock block : code.blocks) {
if (block.exit() instanceof Return) {
// Find the return block.
// Check it has one phi with two operands / two predecessors.
Return ret = block.exit().asReturn();
assertEquals(2, block.getPredecessors().size());
assertEquals(1, block.getPhis().size());
assertEquals(ret.returnValue(), block.getPhis().get(0));
// Next we find and check that the phi values come from the expected predecessor.
boolean hasNormalPredecessor = false;
boolean hasExceptionalPredecessor = false;
Phi phi = block.getPhis().get(0);
for (Value operand : phi.getOperands()) {
BasicBlock defBlock = operand.definition.getBlock();
if (defBlock.canThrow()) {
// Found the invoke instruction / block.
assertEquals(2, defBlock.getSuccessors().size());
assertTrue(
defBlock.getInstructions().get(defBlock.getInstructions().size() - 2).isInvoke());
for (BasicBlock returnPredecessor : block.getPredecessors()) {
if (defBlock.hasCatchSuccessor(returnPredecessor)) {
hasExceptionalPredecessor = true;
} else if (defBlock == returnPredecessor) {
// Normal flow goes to return.
hasNormalPredecessor = true;
} else if (defBlock.getSuccessors().contains(returnPredecessor)) {
// Normal flow goes to return after an edge split.
assertTrue(returnPredecessor.isTrivialGoto());
hasNormalPredecessor = true;
}
}
}
}
assertTrue(hasNormalPredecessor);
assertTrue(hasExceptionalPredecessor);
hasReturn = true;
}
}
assertTrue(hasReturn);
}
}