rlm@10: /*** rlm@10: * ASM: a very small and fast Java bytecode manipulation framework rlm@10: * Copyright (c) 2000-2005 INRIA, France Telecom rlm@10: * All rights reserved. rlm@10: * rlm@10: * Redistribution and use in source and binary forms, with or without rlm@10: * modification, are permitted provided that the following conditions rlm@10: * are met: rlm@10: * 1. Redistributions of source code must retain the above copyright rlm@10: * notice, this list of conditions and the following disclaimer. rlm@10: * 2. Redistributions in binary form must reproduce the above copyright rlm@10: * notice, this list of conditions and the following disclaimer in the rlm@10: * documentation and/or other materials provided with the distribution. rlm@10: * 3. Neither the name of the copyright holders nor the names of its rlm@10: * contributors may be used to endorse or promote products derived from rlm@10: * this software without specific prior written permission. rlm@10: * rlm@10: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" rlm@10: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE rlm@10: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE rlm@10: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE rlm@10: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR rlm@10: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF rlm@10: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS rlm@10: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN rlm@10: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) rlm@10: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF rlm@10: * THE POSSIBILITY OF SUCH DAMAGE. rlm@10: */ rlm@10: package clojure.asm; rlm@10: rlm@10: /** rlm@10: * A label represents a position in the bytecode of a method. Labels are used rlm@10: * for jump, goto, and switch instructions, and for try catch blocks. rlm@10: * rlm@10: * @author Eric Bruneton rlm@10: */ rlm@10: public class Label{ rlm@10: rlm@10: /** rlm@10: * Indicates if this label is only used for debug attributes. Such a label rlm@10: * is not the start of a basic block, the target of a jump instruction, or rlm@10: * an exception handler. It can be safely ignored in control flow graph rlm@10: * analysis algorithms (for optimization purposes). rlm@10: */ rlm@10: final static int DEBUG = 1; rlm@10: rlm@10: /** rlm@10: * Indicates if the position of this label is known. rlm@10: */ rlm@10: final static int RESOLVED = 2; rlm@10: rlm@10: /** rlm@10: * Indicates if this label has been updated, after instruction resizing. rlm@10: */ rlm@10: final static int RESIZED = 4; rlm@10: rlm@10: /** rlm@10: * Indicates if this basic block has been pushed in the basic block stack. rlm@10: * See {@link MethodWriter#visitMaxs visitMaxs}. rlm@10: */ rlm@10: final static int PUSHED = 8; rlm@10: rlm@10: /** rlm@10: * Indicates if this label is the target of a jump instruction, or the start rlm@10: * of an exception handler. rlm@10: */ rlm@10: final static int TARGET = 16; rlm@10: rlm@10: /** rlm@10: * Indicates if a stack map frame must be stored for this label. rlm@10: */ rlm@10: final static int STORE = 32; rlm@10: rlm@10: /** rlm@10: * Indicates if this label corresponds to a reachable basic block. rlm@10: */ rlm@10: final static int REACHABLE = 64; rlm@10: rlm@10: /** rlm@10: * Indicates if this basic block ends with a JSR instruction. rlm@10: */ rlm@10: final static int JSR = 128; rlm@10: rlm@10: /** rlm@10: * Indicates if this basic block ends with a RET instruction. rlm@10: */ rlm@10: final static int RET = 256; rlm@10: rlm@10: /** rlm@10: * Field used to associate user information to a label. rlm@10: */ rlm@10: public Object info; rlm@10: rlm@10: /** rlm@10: * Flags that indicate the status of this label. rlm@10: * rlm@10: * @see #DEBUG rlm@10: * @see #RESOLVED rlm@10: * @see #RESIZED rlm@10: * @see #PUSHED rlm@10: * @see #TARGET rlm@10: * @see #STORE rlm@10: * @see #REACHABLE rlm@10: * @see #JSR rlm@10: * @see #RET rlm@10: */ rlm@10: int status; rlm@10: rlm@10: /** rlm@10: * The line number corresponding to this label, if known. rlm@10: */ rlm@10: int line; rlm@10: rlm@10: /** rlm@10: * The position of this label in the code, if known. rlm@10: */ rlm@10: int position; rlm@10: rlm@10: /** rlm@10: * Number of forward references to this label, times two. rlm@10: */ rlm@10: private int referenceCount; rlm@10: rlm@10: /** rlm@10: * Informations about forward references. Each forward reference is rlm@10: * described by two consecutive integers in this array: the first one is the rlm@10: * position of the first byte of the bytecode instruction that contains the rlm@10: * forward reference, while the second is the position of the first byte of rlm@10: * the forward reference itself. In fact the sign of the first integer rlm@10: * indicates if this reference uses 2 or 4 bytes, and its absolute value rlm@10: * gives the position of the bytecode instruction. rlm@10: */ rlm@10: private int[] srcAndRefPositions; rlm@10: rlm@10: // ------------------------------------------------------------------------ rlm@10: rlm@10: /* rlm@10: * Fields for the control flow and data flow graph analysis algorithms (used rlm@10: * to compute the maximum stack size or the stack map frames). A control rlm@10: * flow graph contains one node per "basic block", and one edge per "jump" rlm@10: * from one basic block to another. Each node (i.e., each basic block) is rlm@10: * represented by the Label object that corresponds to the first instruction rlm@10: * of this basic block. Each node also stores the list of its successors in rlm@10: * the graph, as a linked list of Edge objects. rlm@10: * rlm@10: * The control flow analysis algorithms used to compute the maximum stack rlm@10: * size or the stack map frames are similar and use two steps. The first rlm@10: * step, during the visit of each instruction, builds information about the rlm@10: * state of the local variables and the operand stack at the end of each rlm@10: * basic block, called the "output frame", relatively to the frame rlm@10: * state at the beginning of the basic block, which is called the "input rlm@10: * frame", and which is unknown during this step. The second step, rlm@10: * in {@link MethodWriter#visitMaxs}, is a fix point algorithm that rlm@10: * computes information about the input frame of each basic block, from the rlm@10: * input state of the first basic block (known from the method signature), rlm@10: * and by the using the previously computed relative output frames. rlm@10: * rlm@10: * The algorithm used to compute the maximum stack size only computes the rlm@10: * relative output and absolute input stack heights, while the algorithm rlm@10: * used to compute stack map frames computes relative output frames and rlm@10: * absolute input frames. rlm@10: */ rlm@10: rlm@10: /** rlm@10: * Start of the output stack relatively to the input stack. The exact rlm@10: * semantics of this field depends on the algorithm that is used. rlm@10: *

rlm@10: * When only the maximum stack size is computed, this field is the number of rlm@10: * elements in the input stack. rlm@10: *

rlm@10: * When the stack map frames are completely computed, this field is the rlm@10: * offset of the first output stack element relatively to the top of the rlm@10: * input stack. This offset is always negative or null. A null offset means rlm@10: * that the output stack must be appended to the input stack. A -n offset rlm@10: * means that the first n output stack elements must replace the top n input rlm@10: * stack elements, and that the other elements must be appended to the input rlm@10: * stack. rlm@10: */ rlm@10: int inputStackTop; rlm@10: rlm@10: /** rlm@10: * Maximum height reached by the output stack, relatively to the top of the rlm@10: * input stack. This maximum is always positive or null. rlm@10: */ rlm@10: int outputStackMax; rlm@10: rlm@10: /** rlm@10: * Information about the input and output stack map frames of this basic rlm@10: * block. This field is only used when {@link ClassWriter#COMPUTE_FRAMES} rlm@10: * option is used. rlm@10: */ rlm@10: Frame frame; rlm@10: rlm@10: /** rlm@10: * The successor of this label, in the order they are visited. This linked rlm@10: * list does not include labels used for debug info only. If rlm@10: * {@link ClassWriter#COMPUTE_FRAMES} option is used then, in addition, it rlm@10: * does not contain successive labels that denote the same bytecode position rlm@10: * (in this case only the first label appears in this list). rlm@10: */ rlm@10: Label successor; rlm@10: rlm@10: /** rlm@10: * The successors of this node in the control flow graph. These successors rlm@10: * are stored in a linked list of {@link Edge Edge} objects, linked to each rlm@10: * other by their {@link Edge#next} field. rlm@10: */ rlm@10: Edge successors; rlm@10: rlm@10: /** rlm@10: * The next basic block in the basic block stack. This stack is used in the rlm@10: * main loop of the fix point algorithm used in the second step of the rlm@10: * control flow analysis algorithms. rlm@10: * rlm@10: * @see MethodWriter#visitMaxs rlm@10: */ rlm@10: Label next; rlm@10: rlm@10: // ------------------------------------------------------------------------ rlm@10: // Constructor rlm@10: // ------------------------------------------------------------------------ rlm@10: rlm@10: /** rlm@10: * Constructs a new label. rlm@10: */ rlm@10: public Label(){ rlm@10: } rlm@10: rlm@10: /** rlm@10: * Constructs a new label. rlm@10: * rlm@10: * @param debug if this label is only used for debug attributes. rlm@10: */ rlm@10: Label(final boolean debug){ rlm@10: this.status = debug ? DEBUG : 0; rlm@10: } rlm@10: rlm@10: // ------------------------------------------------------------------------ rlm@10: // Methods to compute offsets and to manage forward references rlm@10: // ------------------------------------------------------------------------ rlm@10: rlm@10: /** rlm@10: * Returns the offset corresponding to this label. This offset is computed rlm@10: * from the start of the method's bytecode. This method is intended for rlm@10: * {@link Attribute} sub classes, and is normally not needed by class rlm@10: * generators or adapters. rlm@10: * rlm@10: * @return the offset corresponding to this label. rlm@10: * @throws IllegalStateException if this label is not resolved yet. rlm@10: */ rlm@10: public int getOffset(){ rlm@10: if((status & RESOLVED) == 0) rlm@10: { rlm@10: throw new IllegalStateException("Label offset position has not been resolved yet"); rlm@10: } rlm@10: return position; rlm@10: } rlm@10: rlm@10: /** rlm@10: * Puts a reference to this label in the bytecode of a method. If the rlm@10: * position of the label is known, the offset is computed and written rlm@10: * directly. Otherwise, a null offset is written and a new forward reference rlm@10: * is declared for this label. rlm@10: * rlm@10: * @param owner the code writer that calls this method. rlm@10: * @param out the bytecode of the method. rlm@10: * @param source the position of first byte of the bytecode instruction that rlm@10: * contains this label. rlm@10: * @param wideOffset true if the reference must be stored in 4 rlm@10: * bytes, or false if it must be stored with 2 bytes. rlm@10: * @throws IllegalArgumentException if this label has not been created by rlm@10: * the given code writer. rlm@10: */ rlm@10: void put( rlm@10: final MethodWriter owner, rlm@10: final ByteVector out, rlm@10: final int source, rlm@10: final boolean wideOffset){ rlm@10: if((status & RESOLVED) != 0) rlm@10: { rlm@10: if(wideOffset) rlm@10: { rlm@10: out.putInt(position - source); rlm@10: } rlm@10: else rlm@10: { rlm@10: out.putShort(position - source); rlm@10: } rlm@10: } rlm@10: else rlm@10: { rlm@10: if(wideOffset) rlm@10: { rlm@10: addReference(-1 - source, out.length); rlm@10: out.putInt(-1); rlm@10: } rlm@10: else rlm@10: { rlm@10: addReference(source, out.length); rlm@10: out.putShort(-1); rlm@10: } rlm@10: } rlm@10: } rlm@10: rlm@10: /** rlm@10: * Adds a forward reference to this label. This method must be called only rlm@10: * for a true forward reference, i.e. only if this label is not resolved rlm@10: * yet. For backward references, the offset of the reference can be, and rlm@10: * must be, computed and stored directly. rlm@10: * rlm@10: * @param sourcePosition the position of the referencing instruction. This rlm@10: * position will be used to compute the offset of this forward rlm@10: * reference. rlm@10: * @param referencePosition the position where the offset for this forward rlm@10: * reference must be stored. rlm@10: */ rlm@10: private void addReference( rlm@10: final int sourcePosition, rlm@10: final int referencePosition){ rlm@10: if(srcAndRefPositions == null) rlm@10: { rlm@10: srcAndRefPositions = new int[6]; rlm@10: } rlm@10: if(referenceCount >= srcAndRefPositions.length) rlm@10: { rlm@10: int[] a = new int[srcAndRefPositions.length + 6]; rlm@10: System.arraycopy(srcAndRefPositions, rlm@10: 0, rlm@10: a, rlm@10: 0, rlm@10: srcAndRefPositions.length); rlm@10: srcAndRefPositions = a; rlm@10: } rlm@10: srcAndRefPositions[referenceCount++] = sourcePosition; rlm@10: srcAndRefPositions[referenceCount++] = referencePosition; rlm@10: } rlm@10: rlm@10: /** rlm@10: * Resolves all forward references to this label. This method must be called rlm@10: * when this label is added to the bytecode of the method, i.e. when its rlm@10: * position becomes known. This method fills in the blanks that where left rlm@10: * in the bytecode by each forward reference previously added to this label. rlm@10: * rlm@10: * @param owner the code writer that calls this method. rlm@10: * @param position the position of this label in the bytecode. rlm@10: * @param data the bytecode of the method. rlm@10: * @return true if a blank that was left for this label was to rlm@10: * small to store the offset. In such a case the corresponding jump rlm@10: * instruction is replaced with a pseudo instruction (using unused rlm@10: * opcodes) using an unsigned two bytes offset. These pseudo rlm@10: * instructions will need to be replaced with true instructions with rlm@10: * wider offsets (4 bytes instead of 2). This is done in rlm@10: * {@link MethodWriter#resizeInstructions}. rlm@10: * @throws IllegalArgumentException if this label has already been resolved, rlm@10: * or if it has not been created by the given code writer. rlm@10: */ rlm@10: boolean resolve( rlm@10: final MethodWriter owner, rlm@10: final int position, rlm@10: final byte[] data){ rlm@10: boolean needUpdate = false; rlm@10: this.status |= RESOLVED; rlm@10: this.position = position; rlm@10: int i = 0; rlm@10: while(i < referenceCount) rlm@10: { rlm@10: int source = srcAndRefPositions[i++]; rlm@10: int reference = srcAndRefPositions[i++]; rlm@10: int offset; rlm@10: if(source >= 0) rlm@10: { rlm@10: offset = position - source; rlm@10: if(offset < Short.MIN_VALUE || offset > Short.MAX_VALUE) rlm@10: { rlm@10: /* rlm@10: * changes the opcode of the jump instruction, in order to rlm@10: * be able to find it later (see resizeInstructions in rlm@10: * MethodWriter). These temporary opcodes are similar to rlm@10: * jump instruction opcodes, except that the 2 bytes offset rlm@10: * is unsigned (and can therefore represent values from 0 to rlm@10: * 65535, which is sufficient since the size of a method is rlm@10: * limited to 65535 bytes). rlm@10: */ rlm@10: int opcode = data[reference - 1] & 0xFF; rlm@10: if(opcode <= Opcodes.JSR) rlm@10: { rlm@10: // changes IFEQ ... JSR to opcodes 202 to 217 rlm@10: data[reference - 1] = (byte) (opcode + 49); rlm@10: } rlm@10: else rlm@10: { rlm@10: // changes IFNULL and IFNONNULL to opcodes 218 and 219 rlm@10: data[reference - 1] = (byte) (opcode + 20); rlm@10: } rlm@10: needUpdate = true; rlm@10: } rlm@10: data[reference++] = (byte) (offset >>> 8); rlm@10: data[reference] = (byte) offset; rlm@10: } rlm@10: else rlm@10: { rlm@10: offset = position + source + 1; rlm@10: data[reference++] = (byte) (offset >>> 24); rlm@10: data[reference++] = (byte) (offset >>> 16); rlm@10: data[reference++] = (byte) (offset >>> 8); rlm@10: data[reference] = (byte) offset; rlm@10: } rlm@10: } rlm@10: return needUpdate; rlm@10: } rlm@10: rlm@10: /** rlm@10: * Returns the first label of the series to which this label belongs. For an rlm@10: * isolated label or for the first label in a series of successive labels, rlm@10: * this method returns the label itself. For other labels it returns the rlm@10: * first label of the series. rlm@10: * rlm@10: * @return the first label of the series to which this label belongs. rlm@10: */ rlm@10: Label getFirst(){ rlm@10: return frame == null ? this : frame.owner; rlm@10: } rlm@10: rlm@10: // ------------------------------------------------------------------------ rlm@10: // Overriden Object methods rlm@10: // ------------------------------------------------------------------------ rlm@10: rlm@10: /** rlm@10: * Returns a string representation of this label. rlm@10: * rlm@10: * @return a string representation of this label. rlm@10: */ rlm@10: public String toString(){ rlm@10: return "L" + System.identityHashCode(this); rlm@10: } rlm@10: }