/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2011 INRIA, France Telecom
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.springframework.asm;
/**
* A label represents a position in the bytecode of a method. Labels are used
* for jump, goto, and switch instructions, and for try catch blocks. A label
* designates the <i>instruction</i> that is just after. Note however that there
* can be other elements between a label and the instruction it designates (such
* as other labels, stack map frames, line numbers, etc.).
*
* @author Eric Bruneton
*/
public class
Label {
/**
* Indicates if this label is only used for debug attributes. Such a label
* is not the start of a basic block, the target of a jump instruction, or
* an exception handler. It can be safely ignored in control flow graph
* analysis algorithms (for optimization purposes).
*/
static final int
DEBUG = 1;
/**
* Indicates if the position of this label is known.
*/
static final int
RESOLVED = 2;
/**
* Indicates if this label has been updated, after instruction resizing.
*/
static final int
RESIZED = 4;
/**
* Indicates if this basic block has been pushed in the basic block stack.
* See {@link MethodWriter#visitMaxs visitMaxs}.
*/
static final int
PUSHED = 8;
/**
* Indicates if this label is the target of a jump instruction, or the start
* of an exception handler.
*/
static final int
TARGET = 16;
/**
* Indicates if a stack map frame must be stored for this label.
*/
static final int
STORE = 32;
/**
* Indicates if this label corresponds to a reachable basic block.
*/
static final int
REACHABLE = 64;
/**
* Indicates if this basic block ends with a JSR instruction.
*/
static final int
JSR = 128;
/**
* Indicates if this basic block ends with a RET instruction.
*/
static final int
RET = 256;
/**
* Indicates if this basic block is the start of a subroutine.
*/
static final int
SUBROUTINE = 512;
/**
* Indicates if this subroutine basic block has been visited by a
* visitSubroutine(null, ...) call.
*/
static final int
VISITED = 1024;
/**
* Indicates if this subroutine basic block has been visited by a
* visitSubroutine(!null, ...) call.
*/
static final int
VISITED2 = 2048;
/**
* Field used to associate user information to a label. Warning: this field
* is used by the ASM tree package. In order to use it with the ASM tree
* package you must override the
* {@code org.objectweb.asm.tree.MethodNode#getLabelNode} method.
*/
public
Object info;
/**
* Flags that indicate the status of this label.
*
* @see #DEBUG
* @see #RESOLVED
* @see #RESIZED
* @see #PUSHED
* @see #TARGET
* @see #STORE
* @see #REACHABLE
* @see #JSR
* @see #RET
*/
int
status;
/**
* The line number corresponding to this label, if known. If there are
* several lines, each line is stored in a separate label, all linked via
* their next field (these links are created in ClassReader and removed just
* before visitLabel is called, so that this does not impact the rest of the
* code).
*/
int
line;
/**
* The position of this label in the code, if known.
*/
int
position;
/**
* Number of forward references to this label, times two.
*/
private int
referenceCount;
/**
* Informations about forward references. Each forward reference is
* described by two consecutive integers in this array: the first one is the
* position of the first byte of the bytecode instruction that contains the
* forward reference, while the second is the position of the first byte of
* the forward reference itself. In fact the sign of the first integer
* indicates if this reference uses 2 or 4 bytes, and its absolute value
* gives the position of the bytecode instruction. This array is also used
* as a bitset to store the subroutines to which a basic block belongs. This
* information is needed in {@link MethodWriter#visitMaxs}, after all
* forward references have been resolved. Hence the same array can be used
* for both purposes without problems.
*/
private int[]
srcAndRefPositions;
// ------------------------------------------------------------------------
/*
* Fields for the control flow and data flow graph analysis algorithms (used
* to compute the maximum stack size or the stack map frames). A control
* flow graph contains one node per "basic block", and one edge per "jump"
* from one basic block to another. Each node (i.e., each basic block) is
* represented by the Label object that corresponds to the first instruction
* of this basic block. Each node also stores the list of its successors in
* the graph, as a linked list of Edge objects.
*
* The control flow analysis algorithms used to compute the maximum stack
* size or the stack map frames are similar and use two steps. The first
* step, during the visit of each instruction, builds information about the
* state of the local variables and the operand stack at the end of each
* basic block, called the "output frame", <i>relatively</i> to the frame
* state at the beginning of the basic block, which is called the "input
* frame", and which is <i>unknown</i> during this step. The second step, in
* {@link MethodWriter#visitMaxs}, is a fix point algorithm that computes
* information about the input frame of each basic block, from the input
* state of the first basic block (known from the method signature), and by
* the using the previously computed relative output frames.
*
* The algorithm used to compute the maximum stack size only computes the
* relative output and absolute input stack heights, while the algorithm
* used to compute stack map frames computes relative output frames and
* absolute input frames.
*/
/**
* Start of the output stack relatively to the input stack. The exact
* semantics of this field depends on the algorithm that is used.
*
* When only the maximum stack size is computed, this field is the number of
* elements in the input stack.
*
* When the stack map frames are completely computed, this field is the
* offset of the first output stack element relatively to the top of the
* input stack. This offset is always negative or null. A null offset means
* that the output stack must be appended to the input stack. A -n offset
* means that the first n output stack elements must replace the top n input
* stack elements, and that the other elements must be appended to the input
* stack.
*/
int
inputStackTop;
/**
* Maximum height reached by the output stack, relatively to the top of the
* input stack. This maximum is always positive or null.
*/
int
outputStackMax;
/**
* Information about the input and output stack map frames of this basic
* block. This field is only used when {@link ClassWriter#COMPUTE_FRAMES}
* option is used.
*/
Frame frame;
/**
* The successor of this label, in the order they are visited. This linked
* list does not include labels used for debug info only. If
* {@link ClassWriter#COMPUTE_FRAMES} option is used then, in addition, it
* does not contain successive labels that denote the same bytecode position
* (in this case only the first label appears in this list).
*/
Label successor;
/**
* The successors of this node in the control flow graph. These successors
* are stored in a linked list of {@link Edge Edge} objects, linked to each
* other by their {@link Edge#next} field.
*/
Edge successors;
/**
* The next basic block in the basic block stack. This stack is used in the
* main loop of the fix point algorithm used in the second step of the
* control flow analysis algorithms. It is also used in
* {@link #visitSubroutine} to avoid using a recursive method, and in
* ClassReader to temporarily store multiple source lines for a label.
*
* @see MethodWriter#visitMaxs
*/
Label next;
// ------------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------------
/**
* Constructs a new label.
*/
public
Label() {
}
// ------------------------------------------------------------------------
// Methods to compute offsets and to manage forward references
// ------------------------------------------------------------------------
/**
* Returns the offset corresponding to this label. This offset is computed
* from the start of the method's bytecode. <i>This method is intended for
* {@link Attribute} sub classes, and is normally not needed by class
* generators or adapters.</i>
*
* @return the offset corresponding to this label.
* @throws IllegalStateException
* if this label is not resolved yet.
*/
public int
getOffset() {
if ((
status &
RESOLVED) == 0) {
throw new
IllegalStateException(
"Label offset position has not been resolved yet");
}
return
position;
}
/**
* Puts a reference to this label in the bytecode of a method. If the
* position of the label is known, the offset is computed and written
* directly. Otherwise, a null offset is written and a new forward reference
* is declared for this label.
*
* @param owner
* the code writer that calls this method.
* @param out
* the bytecode of the method.
* @param source
* the position of first byte of the bytecode instruction that
* contains this label.
* @param wideOffset
* <tt>true</tt> if the reference must be stored in 4 bytes, or
* <tt>false</tt> if it must be stored with 2 bytes.
* @throws IllegalArgumentException
* if this label has not been created by the given code writer.
*/
void
put(final
MethodWriter owner, final
ByteVector out, final int
source,
final boolean
wideOffset) {
if ((
status &
RESOLVED) == 0) {
if (
wideOffset) {
addReference(-1 -
source,
out.
length);
out.
putInt(-1);
} else {
addReference(
source,
out.
length);
out.
putShort(-1);
}
} else {
if (
wideOffset) {
out.
putInt(
position -
source);
} else {
out.
putShort(
position -
source);
}
}
}
/**
* Adds a forward reference to this label. This method must be called only
* for a true forward reference, i.e. only if this label is not resolved
* yet. For backward references, the offset of the reference can be, and
* must be, computed and stored directly.
*
* @param sourcePosition
* the position of the referencing instruction. This position
* will be used to compute the offset of this forward reference.
* @param referencePosition
* the position where the offset for this forward reference must
* be stored.
*/
private void
addReference(final int
sourcePosition,
final int
referencePosition) {
if (
srcAndRefPositions == null) {
srcAndRefPositions = new int[6];
}
if (
referenceCount >=
srcAndRefPositions.length) {
int[]
a = new int[
srcAndRefPositions.length + 6];
System.
arraycopy(
srcAndRefPositions, 0,
a, 0,
srcAndRefPositions.length);
srcAndRefPositions =
a;
}
srcAndRefPositions[
referenceCount++] =
sourcePosition;
srcAndRefPositions[
referenceCount++] =
referencePosition;
}
/**
* Resolves all forward references to this label. This method must be called
* when this label is added to the bytecode of the method, i.e. when its
* position becomes known. This method fills in the blanks that where left
* in the bytecode by each forward reference previously added to this label.
*
* @param owner
* the code writer that calls this method.
* @param position
* the position of this label in the bytecode.
* @param data
* the bytecode of the method.
* @return <tt>true</tt> if a blank that was left for this label was to
* small to store the offset. In such a case the corresponding jump
* instruction is replaced with a pseudo instruction (using unused
* opcodes) using an unsigned two bytes offset. These pseudo
* instructions will be replaced with standard bytecode instructions
* with wider offsets (4 bytes instead of 2), in ClassReader.
* @throws IllegalArgumentException
* if this label has already been resolved, or if it has not
* been created by the given code writer.
*/
boolean
resolve(final
MethodWriter owner, final int
position,
final byte[]
data) {
boolean
needUpdate = false;
this.
status |=
RESOLVED;
this.
position =
position;
int
i = 0;
while (
i <
referenceCount) {
int
source =
srcAndRefPositions[
i++];
int
reference =
srcAndRefPositions[
i++];
int
offset;
if (
source >= 0) {
offset =
position -
source;
if (
offset <
Short.
MIN_VALUE ||
offset >
Short.
MAX_VALUE) {
/*
* changes the opcode of the jump instruction, in order to
* be able to find it later (see resizeInstructions in
* MethodWriter). These temporary opcodes are similar to
* jump instruction opcodes, except that the 2 bytes offset
* is unsigned (and can therefore represent values from 0 to
* 65535, which is sufficient since the size of a method is
* limited to 65535 bytes).
*/
int
opcode =
data[
reference - 1] & 0xFF;
if (
opcode <=
Opcodes.
JSR) {
// changes IFEQ ... JSR to opcodes 202 to 217
data[
reference - 1] = (byte) (
opcode + 49);
} else {
// changes IFNULL and IFNONNULL to opcodes 218 and 219
data[
reference - 1] = (byte) (
opcode + 20);
}
needUpdate = true;
}
data[
reference++] = (byte) (
offset >>> 8);
data[
reference] = (byte)
offset;
} else {
offset =
position +
source + 1;
data[
reference++] = (byte) (
offset >>> 24);
data[
reference++] = (byte) (
offset >>> 16);
data[
reference++] = (byte) (
offset >>> 8);
data[
reference] = (byte)
offset;
}
}
return
needUpdate;
}
/**
* Returns the first label of the series to which this label belongs. For an
* isolated label or for the first label in a series of successive labels,
* this method returns the label itself. For other labels it returns the
* first label of the series.
*
* @return the first label of the series to which this label belongs.
*/
Label getFirst() {
return !
ClassReader.
FRAMES ||
frame == null ? this :
frame.
owner;
}
// ------------------------------------------------------------------------
// Methods related to subroutines
// ------------------------------------------------------------------------
/**
* Returns true is this basic block belongs to the given subroutine.
*
* @param id
* a subroutine id.
* @return true is this basic block belongs to the given subroutine.
*/
boolean
inSubroutine(final long
id) {
if ((
status &
Label.
VISITED) != 0) {
return (
srcAndRefPositions[(int) (
id >>> 32)] & (int)
id) != 0;
}
return false;
}
/**
* Returns true if this basic block and the given one belong to a common
* subroutine.
*
* @param block
* another basic block.
* @return true if this basic block and the given one belong to a common
* subroutine.
*/
boolean
inSameSubroutine(final
Label block) {
if ((
status &
VISITED) == 0 || (
block.
status &
VISITED) == 0) {
return false;
}
for (int
i = 0;
i <
srcAndRefPositions.length; ++
i) {
if ((
srcAndRefPositions[
i] &
block.
srcAndRefPositions[
i]) != 0) {
return true;
}
}
return false;
}
/**
* Marks this basic block as belonging to the given subroutine.
*
* @param id
* a subroutine id.
* @param nbSubroutines
* the total number of subroutines in the method.
*/
void
addToSubroutine(final long
id, final int
nbSubroutines) {
if ((
status &
VISITED) == 0) {
status |=
VISITED;
srcAndRefPositions = new int[
nbSubroutines / 32 + 1];
}
srcAndRefPositions[(int) (
id >>> 32)] |= (int)
id;
}
/**
* Finds the basic blocks that belong to a given subroutine, and marks these
* blocks as belonging to this subroutine. This method follows the control
* flow graph to find all the blocks that are reachable from the current
* block WITHOUT following any JSR target.
*
* @param JSR
* a JSR block that jumps to this subroutine. If this JSR is not
* null it is added to the successor of the RET blocks found in
* the subroutine.
* @param id
* the id of this subroutine.
* @param nbSubroutines
* the total number of subroutines in the method.
*/
void
visitSubroutine(final
Label JSR, final long
id, final int
nbSubroutines) {
// user managed stack of labels, to avoid using a recursive method
// (recursivity can lead to stack overflow with very large methods)
Label stack = this;
while (
stack != null) {
// removes a label l from the stack
Label l =
stack;
stack =
l.
next;
l.
next = null;
if (
JSR != null) {
if ((
l.
status &
VISITED2) != 0) {
continue;
}
l.
status |=
VISITED2;
// adds JSR to the successors of l, if it is a RET block
if ((
l.
status &
RET) != 0) {
if (!
l.
inSameSubroutine(
JSR)) {
Edge e = new
Edge();
e.
info =
l.
inputStackTop;
e.
successor =
JSR.
successors.
successor;
e.
next =
l.
successors;
l.
successors =
e;
}
}
} else {
// if the l block already belongs to subroutine 'id', continue
if (
l.
inSubroutine(
id)) {
continue;
}
// marks the l block as belonging to subroutine 'id'
l.
addToSubroutine(
id,
nbSubroutines);
}
// pushes each successor of l on the stack, except JSR targets
Edge e =
l.
successors;
while (
e != null) {
// if the l block is a JSR block, then 'l.successors.next' leads
// to the JSR target (see {@link #visitJumpInsn}) and must
// therefore not be followed
if ((
l.
status &
Label.
JSR) == 0 ||
e !=
l.
successors.
next) {
// pushes e.successor on the stack if it not already added
if (
e.
successor.
next == null) {
e.
successor.
next =
stack;
stack =
e.
successor;
}
}
e =
e.
next;
}
}
}
// ------------------------------------------------------------------------
// Overriden Object methods
// ------------------------------------------------------------------------
/**
* Returns a string representation of this label.
*
* @return a string representation of this label.
*/
@
Override
public
String toString() {
return "L" +
System.
identityHashCode(this);
}
}