/* * Copyright (C) 2008 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.dx.rop; /** *
* * The Rop form is intended to represent the instructions and the control-flow * graph in a reasonably programmatically useful form while closely mirroring * the dex instruction set.
* *
* * Blocks are referred to by their label, for the most part, because * {@code BasicBlock} instances are immutable and thus any modification to * the control flow graph or the instruction list results in replacement * instances (with identical labels) being created.
* * A method has a single {@link RopMethod#getFirstLabel entry block} and 0 * to N {@link RopMethod#getExitPredecessors exit predecessor blocks} which * will return. All blocks that are not the entry block will have at least * one predecessor (or are unreachable and should be removed from the block * list). All blocks that are not exit predecessors must have at least one * successor.
* * Since all blocks must branch, all blocks must have, as their final * instruction, an instruction whose opcode has a {@link Rop#getBranchingness * branchingness} other than {@link Rop.BRANCH_NONE}. Furthermore, branching * instructions may only be the final instruction in any basic block. If * no other terminating opcode is appropriate, use a {@link Rops#GOTO GOTO}.
* * Typically a block will have a {@link BasicBlock#getPrimarySuccessor * primary successor} which distinguishes a particular control flow path. * For {Rops#isCallLike}call or call-like} opcodes, this is the path taken * in the non-exceptional case, where all other successors represent * various exception paths. For comparison operators such as * {@link Rops#IF_EQZ_INT}, the primary successor represents the path taken * if the condition evaluates to false. For {@link SwitchInsn switch * instructions}, the primary successor is the default case.
* * A basic block's successor list is ordered and may refer to unique labels * multiple times. For example, if a switch statement contains multiple case * statements for the same code path, a single basic block will likely * appear in the successor list multiple times. In general, the * significance of the successor list's order (like the significance of * the primary successor) is a property of the final instruction of the basic * block. A basic block containing a {@link ThrowingInsn}, for example, has * its successor list in an order identical to the * {@link ThrowingInsn#getCatches} instruction's catches list, with the * primary successor (the no-exception case) listed at the end. * * It is legal for a basic block to have no primary successor. An obvious * example of this is a block that terminates in a {@link Rops#THROW throw} * instruction where a catch block exists inside the current method for that * exception class. Since the only possible path is the exception path, only * the exception path (which cannot be a primary successor) is a successor. * An example of this is shown in {@code dx/tests/092-ssa-cfg-edge-cases}. * *
* * In a typical case, a local variable's lifetime begins with an * assignment. The local variable whose information is present in a result's * {@link RegisterSpec#getLocalItem LocalItem} is considered to begin (or move * between registers) when the instruction is executed.
* * However, sometimes a local variable can begin its life or move without * an assignment occurring. A common example of this is occurs in the Rop * representation of the following code:
* *
* try { * Object foo = null; * foo = new Object(); * } catch (Throwable ex) { } ** * An object's initialization occurs in two steps. First, a * {@code new-instance} instruction is executed, whose result is stored in a * register. However, that register can not yet be considered to contain * "foo". That's because the instance's constructor method must be called * via an {@code invoke} instruction. The constructor method, however, may * throw an exception. And if an exception occurs, then "foo" should remain * null. So "foo" becomes the value of the result of the {@code new-instance} * instruction after the (void) constructor method is invoked and * returns successfully. In such a case, a {@code mark-local} will * typically occur at the beginning of the primary successor block following * the invocation to the constructor. */