| /* |
| * 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())); |
| } |
| } |
| } |
| } |