blob: 08eb01119d03f4d0b5cad72e3b062b3bca90f1c3 [file] [log] [blame]
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.jack.cfg;
import com.android.jack.ir.ast.JCaseStatement;
import com.android.jack.ir.ast.JCatchBlock;
import com.android.jack.ir.ast.JStatement;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
/**
* Resolve forward branch that appears during control flow graph building.
*/
class ForwardBranchResolver {
@Nonnull
private final ExitBlock exitBlock;
public ForwardBranchResolver(@Nonnull ExitBlock exitBlock) {
this.exitBlock = exitBlock;
}
private static interface BlockToResolve {
void resolve();
}
@Nonnull
private final ArrayList<BlockToResolve> blocksToResolve = new ArrayList<BlockToResolve>();
void addNormalBasicBlock(@Nonnull NormalBasicBlock block,
@CheckForNull JStatement targetStatement) {
assert block != null;
blocksToResolve.add(new NormalBasicBlockToResolve(block, targetStatement));
}
void addConditionalBasicBlock(@Nonnull ConditionalBasicBlock block,
@Nonnull JStatement thenStatement, @CheckForNull JStatement elseStatement) {
assert block != null;
assert thenStatement != null;
blocksToResolve.add(new ConditionalBasicBlockToResolve(block, thenStatement, elseStatement));
}
void addSwitchBasicBlock(@Nonnull SwitchBasicBlock block, @Nonnull List<JCaseStatement> cases,
@CheckForNull JStatement defaultCase) {
assert block != null;
assert cases != null;
blocksToResolve.add(new SwitchBasicBlockToResolve(block, cases, defaultCase));
}
void addPeiBasicBlock(@Nonnull PeiBasicBlock block, @CheckForNull JStatement targetStatement,
@Nonnull List<JCatchBlock> catchBlocks) {
assert block != null;
assert catchBlocks != null;
blocksToResolve.add(new PeiBasicBlockToResolve(block, targetStatement, catchBlocks));
}
/**
* Resolve forward branches by updating basic bloc successors.
*/
void resolve() {
for (int i = 0, len = blocksToResolve.size(); i < len; ++i) {
blocksToResolve.get(i).resolve();
}
}
@Nonnull
private static BasicBlock getTargetBlock(@Nonnull JStatement statement) {
assert statement != null;
BasicBlockMarker bbm = statement.getMarker(BasicBlockMarker.class);
assert bbm != null;
BasicBlock targetBb = bbm.getBasicBlock();
assert targetBb != null;
return targetBb;
}
private class NormalBasicBlockToResolve implements BlockToResolve {
@Nonnull
private final NormalBasicBlock block;
@CheckForNull
private final JStatement statement;
public NormalBasicBlockToResolve(@Nonnull NormalBasicBlock block,
@CheckForNull JStatement statement) {
assert block != null;
this.block = block;
this.statement = statement;
}
@Override
public void resolve() {
if (statement == null) {
block.setTarget(exitBlock);
} else {
block.setTarget(getTargetBlock(statement));
}
}
}
private class ConditionalBasicBlockToResolve implements BlockToResolve {
@Nonnull
private final ConditionalBasicBlock block;
@Nonnull
private final JStatement ifStatement;
@CheckForNull
private final JStatement elseStatement;
public ConditionalBasicBlockToResolve(@Nonnull ConditionalBasicBlock block,
@Nonnull JStatement ifStatement, @CheckForNull JStatement elseStatement) {
assert block != null;
assert ifStatement != null;
this.block = block;
this.ifStatement = ifStatement;
this.elseStatement = elseStatement;
}
@Override
public void resolve() {
block.setThenBlock(getTargetBlock(ifStatement));
if (elseStatement == null) {
// elseStatement means statement contained by the else block of the JIfStatement or the
// statement following the JIfStatement.
// A conditional block without an else statement will target the exit block. Indeed, by
// building a conditional block must have two targets (it is required by the backend).
// A conditional without else statement can happen after FinallyRemover where a finally
// block containing a JIfStatement is inlined at the end of the try block that is composed
// by an infinite loop for instance. In this case, JIfStatement will be dead code and
// elseStatement will be null.
block.setElseBlock(exitBlock);
} else {
block.setElseBlock(getTargetBlock(elseStatement));
}
}
}
private class SwitchBasicBlockToResolve implements BlockToResolve {
@Nonnull
private final SwitchBasicBlock block;
@Nonnull
private final List<JCaseStatement> cases;
@CheckForNull
private final JStatement defaultCase;
public SwitchBasicBlockToResolve(@Nonnull SwitchBasicBlock block,
@Nonnull List<JCaseStatement> cases, @CheckForNull JStatement defaultCase) {
assert block != null;
assert cases != null;
this.block = block;
this.cases = cases;
this.defaultCase = defaultCase;
}
@Override
public void resolve() {
for (JCaseStatement caseStatement : cases) {
block.addCaseBlock(getTargetBlock(caseStatement));
}
if (defaultCase == null) {
// defaultCase means statement representing the default case of the JSwitchstatement or the
// statement following the JSwitchStatement.
// A Switch block without a default statement will target the exit block. Indeed, by
// building a switch block must have at least a default target (it is required by the
// backend).
// A switch without a default case can happen after FinallyRemover where a finally
// block containing a JSwitchStatement is inlined at the end of the try block that is
// composed by an infinite loop for instance. In this case, JSwitchtatement will be dead
// code and default case will be null.
block.setDefault(exitBlock);
} else {
block.setDefault(getTargetBlock(defaultCase));
}
}
}
private static class PeiBasicBlockToResolve implements BlockToResolve {
@Nonnull
private final PeiBasicBlock block;
@CheckForNull
private final JStatement statement;
@Nonnull
private final List<JCatchBlock> catchBlocks;
public PeiBasicBlockToResolve(@Nonnull PeiBasicBlock block, @CheckForNull JStatement statement,
@Nonnull List<JCatchBlock> catchBlocks) {
assert block != null;
assert catchBlocks != null;
this.block = block;
this.statement = statement;
this.catchBlocks = catchBlocks;
}
@Override
public void resolve() {
if (statement != null) {
block.setTarget(getTargetBlock(statement));
}
// addExceptionBlock has to be called in reverse order because it adds an exception block
// before all exception blocks which have been already added.
ListIterator<JCatchBlock> catchBlocksIter = catchBlocks.listIterator(catchBlocks.size());
while (catchBlocksIter.hasPrevious()) {
block.addExceptionBlock((CatchBasicBlock) getTargetBlock(catchBlocksIter.previous()));
}
}
}
}