Back to index...
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
/*
 * This file is available under and governed by the GNU General Public
 * License version 2 only, as published by the Free Software Foundation.
 * However, the following notice accompanied the original version of this
 * file:
 *
 * 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 jdk.internal.org.objectweb.asm;
/**
 * 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 {
    /**
      * A flag indicating that a 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 FLAG_DEBUG_ONLY = 1;
    /**
      * A flag indicating that a label is the target of a jump instruction, or the start of an
      * exception handler.
      */
    static final int FLAG_JUMP_TARGET = 2;
    /** A flag indicating that the bytecode offset of a label is known. */
    static final int FLAG_RESOLVED = 4;
    /** A flag indicating that a label corresponds to a reachable basic block. */
    static final int FLAG_REACHABLE = 8;
    /**
      * A flag indicating that the basic block corresponding to a label ends with a subroutine call. By
      * construction in {@link MethodWriter#visitJumpInsn}, labels with this flag set have at least two
      * outgoing edges:
      *
      * <ul>
      *   <li>the first one corresponds to the instruction that follows the jsr instruction in the
      *       bytecode, i.e. where execution continues when it returns from the jsr call. This is a
      *       virtual control flow edge, since execution never goes directly from the jsr to the next
      *       instruction. Instead, it goes to the subroutine and eventually returns to the instruction
      *       following the jsr. This virtual edge is used to compute the real outgoing edges of the
      *       basic blocks ending with a ret instruction, in {@link #addSubroutineRetSuccessors}.
      *   <li>the second one corresponds to the target of the jsr instruction,
      * </ul>
      */
    static final int FLAG_SUBROUTINE_CALLER = 16;
    /**
      * A flag indicating that the basic block corresponding to a label is the start of a subroutine.
      */
    static final int FLAG_SUBROUTINE_START = 32;
    /** A flag indicating that the basic block corresponding to a label is the end of a subroutine. */
    static final int FLAG_SUBROUTINE_END = 64;
    /**
      * The number of elements to add to the {@link #otherLineNumbers} array when it needs to be
      * resized to store a new source line number.
      */
    static final int LINE_NUMBERS_CAPACITY_INCREMENT = 4;
    /**
      * The number of elements to add to the {@link #forwardReferences} array when it needs to be
      * resized to store a new forward reference.
      */
    static final int FORWARD_REFERENCES_CAPACITY_INCREMENT = 6;
    /**
      * The bit mask to extract the type of a forward reference to this label. The extracted type is
      * either {@link #FORWARD_REFERENCE_TYPE_SHORT} or {@link #FORWARD_REFERENCE_TYPE_WIDE}.
      *
      * @see #forwardReferences
      */
    static final int FORWARD_REFERENCE_TYPE_MASK = 0xF0000000;
    /**
      * The type of forward references stored with two bytes in the bytecode. This is the case, for
      * instance, of a forward reference from an ifnull instruction.
      */
    static final int FORWARD_REFERENCE_TYPE_SHORT = 0x10000000;
    /**
      * The type of forward references stored in four bytes in the bytecode. This is the case, for
      * instance, of a forward reference from a lookupswitch instruction.
      */
    static final int FORWARD_REFERENCE_TYPE_WIDE = 0x20000000;
    /**
      * The bit mask to extract the 'handle' of a forward reference to this label. The extracted handle
      * is the bytecode offset where the forward reference value is stored (using either 2 or 4 bytes,
      * as indicated by the {@link #FORWARD_REFERENCE_TYPE_MASK}).
      *
      * @see #forwardReferences
      */
    static final int FORWARD_REFERENCE_HANDLE_MASK = 0x0FFFFFFF;
    /**
      * A sentinel element used to indicate the end of a list of labels.
      *
      * @see #nextListElement
      */
    static final Label EMPTY_LIST = new Label();
    /**
      * A user managed state associated with this 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 getLabelNode method
      * in MethodNode.
      */
    public Object info;
    /**
      * The type and status of this label or its corresponding basic block. Must be zero or more of
      * {@link #FLAG_DEBUG_ONLY}, {@link #FLAG_JUMP_TARGET}, {@link #FLAG_RESOLVED}, {@link
      * #FLAG_REACHABLE}, {@link #FLAG_SUBROUTINE_CALLER}, {@link #FLAG_SUBROUTINE_START}, {@link
      * #FLAG_SUBROUTINE_END}.
      */
    short flags;
    /**
      * The source line number corresponding to this label, or 0. If there are several source line
      * numbers corresponding to this label, the first one is stored in this field, and the remaining
      * ones are stored in {@link #otherLineNumbers}.
      */
    private short lineNumber;
    /**
      * The source line numbers corresponding to this label, in addition to {@link #lineNumber}, or
      * null. The first element of this array is the number n of source line numbers it contains, which
      * are stored between indices 1 and n (inclusive).
      */
    private int[] otherLineNumbers;
    /**
      * The offset of this label in the bytecode of its method, in bytes. This value is set if and only
      * if the {@link #FLAG_RESOLVED} flag is set.
      */
    int bytecodeOffset;
    /**
      * The forward references to this label. The first element is the number of forward references,
      * times 2 (this corresponds to the index of the last element actually used in this array). Then,
      * each forward reference is described with two consecutive integers noted
      * 'sourceInsnBytecodeOffset' and 'reference':
      *
      * <ul>
      *   <li>'sourceInsnBytecodeOffset' is the bytecode offset of the instruction that contains the
      *       forward reference,
      *   <li>'reference' contains the type and the offset in the bytecode where the forward reference
      *       value must be stored, which can be extracted with {@link #FORWARD_REFERENCE_TYPE_MASK}
      *       and {@link #FORWARD_REFERENCE_HANDLE_MASK}.
      * </ul>
      *
      * <p>For instance, for an ifnull instruction at bytecode offset x, 'sourceInsnBytecodeOffset' is
      * equal to x, and 'reference' is of type {@link #FORWARD_REFERENCE_TYPE_SHORT} with value x + 1
      * (because the ifnull instruction uses a 2 bytes bytecode offset operand stored one byte after
      * the start of the instruction itself). For the default case of a lookupswitch instruction at
      * bytecode offset x, 'sourceInsnBytecodeOffset' is equal to x, and 'reference' is of type {@link
      * #FORWARD_REFERENCE_TYPE_WIDE} with value between x + 1 and x + 4 (because the lookupswitch
      * instruction uses a 4 bytes bytecode offset operand stored one to four bytes after the start of
      * the instruction itself).
      */
    private int[] forwardReferences;
    // -----------------------------------------------------------------------------------------------
    // 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 with 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#computeAllFrames} and {@link
    // MethodWriter#computeMaxStackAndLocal}, 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.
    /**
      * The number of elements in the input stack of the basic block corresponding to this label. This
      * field is computed in {@link MethodWriter#computeMaxStackAndLocal}.
      */
    short inputStackSize;
    /**
      * The number of elements in the output stack, at the end of the basic block corresponding to this
      * label. This field is only computed for basic blocks that end with a RET instruction.
      */
    short outputStackSize;
    /**
      * The maximum height reached by the output stack, relatively to the top of the input stack, in
      * the basic block corresponding to this label. This maximum is always positive or {@literal
      * null}.
      */
    short outputStackMax;
    /**
      * The id of the subroutine to which this basic block belongs, or 0. If the basic block belongs to
      * several subroutines, this is the id of the "oldest" subroutine that contains it (with the
      * convention that a subroutine calling another one is "older" than the callee). This field is
      * computed in {@link MethodWriter#computeMaxStackAndLocal}, if the method contains JSR
      * instructions.
      */
    short subroutineId;
    /**
      * The input and output stack map frames of the basic block corresponding to this label. This
      * field is only used when the {@link MethodWriter#COMPUTE_ALL_FRAMES} or {@link
      * MethodWriter#COMPUTE_INSERTED_FRAMES} option is used.
      */
    Frame frame;
    /**
      * The successor of this label, in the order they are visited in {@link MethodVisitor#visitLabel}.
      * This linked list does not include labels used for debug info only. If the {@link
      * MethodWriter#COMPUTE_ALL_FRAMES} or {@link MethodWriter#COMPUTE_INSERTED_FRAMES} option is used
      * then it does not contain either successive labels that denote the same bytecode offset (in this
      * case only the first label appears in this list).
      */
    Label nextBasicBlock;
    /**
      * The outgoing edges of the basic block corresponding to this label, in the control flow graph of
      * its method. These edges are stored in a linked list of {@link Edge} objects, linked to each
      * other by their {@link Edge#nextEdge} field.
      */
    Edge outgoingEdges;
    /**
      * The next element in the list of labels to which this label belongs, or {@literal null} if it
      * does not belong to any list. All lists of labels must end with the {@link #EMPTY_LIST}
      * sentinel, in order to ensure that this field is null if and only if this label does not belong
      * to a list of labels. Note that there can be several lists of labels at the same time, but that
      * a label can belong to at most one list at a time (unless some lists share a common tail, but
      * this is not used in practice).
      *
      * <p>List of labels are used in {@link MethodWriter#computeAllFrames} and {@link
      * MethodWriter#computeMaxStackAndLocal} to compute stack map frames and the maximum stack size,
      * respectively, as well as in {@link #markSubroutine} and {@link #addSubroutineRetSuccessors} to
      * compute the basic blocks belonging to subroutines and their outgoing edges. Outside of these
      * methods, this field should be null (this property is a precondition and a postcondition of
      * these methods).
      */
    Label nextListElement;
    // -----------------------------------------------------------------------------------------------
    // Constructor and accessors
    // -----------------------------------------------------------------------------------------------
    /** Constructs a new label. */
    public Label() {
        // Nothing to do.
    }
    /**
      * Returns the bytecode 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 bytecode offset corresponding to this label.
      * @throws IllegalStateException if this label is not resolved yet.
      */
    public int getOffset() {
        if ((flags & FLAG_RESOLVED) == 0) {
            throw new IllegalStateException("Label offset position has not been resolved yet");
        }
        return bytecodeOffset;
    }
    /**
      * Returns the "canonical" {@link Label} instance corresponding to this label's bytecode offset,
      * if known, otherwise the label itself. The canonical instance is the first label (in the order
      * of their visit by {@link MethodVisitor#visitLabel}) corresponding to this bytecode offset. It
      * cannot be known for labels which have not been visited yet.
      *
      * <p><i>This method should only be used when the {@link MethodWriter#COMPUTE_ALL_FRAMES} option
      * is used.</i>
      *
      * @return the label itself if {@link #frame} is null, otherwise the Label's frame owner. This
      *     corresponds to the "canonical" label instance described above thanks to the way the label
      *     frame is set in {@link MethodWriter#visitLabel}.
      */
    final Label getCanonicalInstance() {
        return frame == null ? this : frame.owner;
    }
    // -----------------------------------------------------------------------------------------------
    // Methods to manage line numbers
    // -----------------------------------------------------------------------------------------------
    /**
      * Adds a source line number corresponding to this label.
      *
      * @param lineNumber a source line number (which should be strictly positive).
      */
    final void addLineNumber(final int lineNumber) {
        if (this.lineNumber == 0) {
            this.lineNumber = (short) lineNumber;
        } else {
            if (otherLineNumbers == null) {
                otherLineNumbers = new int[LINE_NUMBERS_CAPACITY_INCREMENT];
            }
            int otherLineNumberIndex = ++otherLineNumbers[0];
            if (otherLineNumberIndex >= otherLineNumbers.length) {
                int[] newLineNumbers = new int[otherLineNumbers.length + LINE_NUMBERS_CAPACITY_INCREMENT];
                System.arraycopy(otherLineNumbers, 0, newLineNumbers, 0, otherLineNumbers.length);
                otherLineNumbers = newLineNumbers;
            }
            otherLineNumbers[otherLineNumberIndex] = lineNumber;
        }
    }
    /**
      * Makes the given visitor visit this label and its source line numbers, if applicable.
      *
      * @param methodVisitor a method visitor.
      * @param visitLineNumbers whether to visit of the label's source line numbers, if any.
      */
    final void accept(final MethodVisitor methodVisitor, final boolean visitLineNumbers) {
        methodVisitor.visitLabel(this);
        if (visitLineNumbers && lineNumber != 0) {
            methodVisitor.visitLineNumber(lineNumber & 0xFFFF, this);
            if (otherLineNumbers != null) {
                for (int i = 1; i <= otherLineNumbers[0]; ++i) {
                    methodVisitor.visitLineNumber(otherLineNumbers[i], this);
                }
            }
        }
    }
    // -----------------------------------------------------------------------------------------------
    // Methods to compute offsets and to manage forward references
    // -----------------------------------------------------------------------------------------------
    /**
      * Puts a reference to this label in the bytecode of a method. If the bytecode offset of the label
      * is known, the relative bytecode offset between the label and the instruction referencing it is
      * computed and written directly. Otherwise, a null relative offset is written and a new forward
      * reference is declared for this label.
      *
      * @param code the bytecode of the method. This is where the reference is appended.
      * @param sourceInsnBytecodeOffset the bytecode offset of the instruction that contains the
      *     reference to be appended.
      * @param wideReference whether the reference must be stored in 4 bytes (instead of 2 bytes).
      */
    final void put(
            final ByteVector code, final int sourceInsnBytecodeOffset, final boolean wideReference) {
        if ((flags & FLAG_RESOLVED) == 0) {
            if (wideReference) {
                addForwardReference(sourceInsnBytecodeOffset, FORWARD_REFERENCE_TYPE_WIDE, code.length);
                code.putInt(-1);
            } else {
                addForwardReference(sourceInsnBytecodeOffset, FORWARD_REFERENCE_TYPE_SHORT, code.length);
                code.putShort(-1);
            }
        } else {
            if (wideReference) {
                code.putInt(bytecodeOffset - sourceInsnBytecodeOffset);
            } else {
                code.putShort(bytecodeOffset - sourceInsnBytecodeOffset);
            }
        }
    }
    /**
      * 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 relative
      * bytecode offset of the reference can be, and must be, computed and stored directly.
      *
      * @param sourceInsnBytecodeOffset the bytecode offset of the instruction that contains the
      *     reference stored at referenceHandle.
      * @param referenceType either {@link #FORWARD_REFERENCE_TYPE_SHORT} or {@link
      *     #FORWARD_REFERENCE_TYPE_WIDE}.
      * @param referenceHandle the offset in the bytecode where the forward reference value must be
      *     stored.
      */
    private void addForwardReference(
            final int sourceInsnBytecodeOffset, final int referenceType, final int referenceHandle) {
        if (forwardReferences == null) {
            forwardReferences = new int[FORWARD_REFERENCES_CAPACITY_INCREMENT];
        }
        int lastElementIndex = forwardReferences[0];
        if (lastElementIndex + 2 >= forwardReferences.length) {
            int[] newValues = new int[forwardReferences.length + FORWARD_REFERENCES_CAPACITY_INCREMENT];
            System.arraycopy(forwardReferences, 0, newValues, 0, forwardReferences.length);
            forwardReferences = newValues;
        }
        forwardReferences[++lastElementIndex] = sourceInsnBytecodeOffset;
        forwardReferences[++lastElementIndex] = referenceType | referenceHandle;
        forwardReferences[0] = lastElementIndex;
    }
    /**
      * Sets the bytecode offset of this label to the given value and resolves the forward references
      * to this label, if any. This method must be called when this label is added to the bytecode of
      * the method, i.e. when its bytecode offset becomes known. This method fills in the blanks that
      * where left in the bytecode by each forward reference previously added to this label.
      *
      * @param code the bytecode of the method.
      * @param bytecodeOffset the bytecode offset of this label.
      * @return {@literal true} if a blank that was left for this label was too small to store the
      *     offset. In such a case the corresponding jump instruction is replaced with an equivalent
      *     ASM specific instruction using an unsigned two bytes offset. These ASM specific
      *     instructions are later replaced with standard bytecode instructions with wider offsets (4
      *     bytes instead of 2), in ClassReader.
      */
    final boolean resolve(final byte[] code, final int bytecodeOffset) {
        this.flags |= FLAG_RESOLVED;
        this.bytecodeOffset = bytecodeOffset;
        if (forwardReferences == null) {
            return false;
        }
        boolean hasAsmInstructions = false;
        for (int i = forwardReferences[0]; i > 0; i -= 2) {
            final int sourceInsnBytecodeOffset = forwardReferences[i - 1];
            final int reference = forwardReferences[i];
            final int relativeOffset = bytecodeOffset - sourceInsnBytecodeOffset;
            int handle = reference & FORWARD_REFERENCE_HANDLE_MASK;
            if ((reference & FORWARD_REFERENCE_TYPE_MASK) == FORWARD_REFERENCE_TYPE_SHORT) {
                if (relativeOffset < Short.MIN_VALUE || relativeOffset > Short.MAX_VALUE) {
                    // Change the opcode of the jump instruction, in order to be able to find it later in
                    // ClassReader. These ASM specific 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 = code[sourceInsnBytecodeOffset] & 0xFF;
                    if (opcode < Opcodes.IFNULL) {
                        // Change IFEQ ... JSR to ASM_IFEQ ... ASM_JSR.
                        code[sourceInsnBytecodeOffset] = (byte) (opcode + Constants.ASM_OPCODE_DELTA);
                    } else {
                        // Change IFNULL and IFNONNULL to ASM_IFNULL and ASM_IFNONNULL.
                        code[sourceInsnBytecodeOffset] = (byte) (opcode + Constants.ASM_IFNULL_OPCODE_DELTA);
                    }
                    hasAsmInstructions = true;
                }
                code[handle++] = (byte) (relativeOffset >>> 8);
                code[handle] = (byte) relativeOffset;
            } else {
                code[handle++] = (byte) (relativeOffset >>> 24);
                code[handle++] = (byte) (relativeOffset >>> 16);
                code[handle++] = (byte) (relativeOffset >>> 8);
                code[handle] = (byte) relativeOffset;
            }
        }
        return hasAsmInstructions;
    }
    // -----------------------------------------------------------------------------------------------
    // Methods related to subroutines
    // -----------------------------------------------------------------------------------------------
    /**
      * Finds the basic blocks that belong to the subroutine starting with the basic block
      * corresponding to this label, 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 basic block WITHOUT following any jsr target.
      *
      * <p>Note: a precondition and postcondition of this method is that all labels must have a null
      * {@link #nextListElement}.
      *
      * @param subroutineId the id of the subroutine starting with the basic block corresponding to
      *     this label.
      */
    final void markSubroutine(final short subroutineId) {
        // Data flow algorithm: put this basic block in a list of blocks to process (which are blocks
        // belonging to subroutine subroutineId) and, while there are blocks to process, remove one from
        // the list, mark it as belonging to the subroutine, and add its successor basic blocks in the
        // control flow graph to the list of blocks to process (if not already done).
        Label listOfBlocksToProcess = this;
        listOfBlocksToProcess.nextListElement = EMPTY_LIST;
        while (listOfBlocksToProcess != EMPTY_LIST) {
            // Remove a basic block from the list of blocks to process.
            Label basicBlock = listOfBlocksToProcess;
            listOfBlocksToProcess = listOfBlocksToProcess.nextListElement;
            basicBlock.nextListElement = null;
            // If it is not already marked as belonging to a subroutine, mark it as belonging to
            // subroutineId and add its successors to the list of blocks to process (unless already done).
            if (basicBlock.subroutineId == 0) {
                basicBlock.subroutineId = subroutineId;
                listOfBlocksToProcess = basicBlock.pushSuccessors(listOfBlocksToProcess);
            }
        }
    }
    /**
      * Finds the basic blocks that end a subroutine starting with the basic block corresponding to
      * this label and, for each one of them, adds an outgoing edge to the basic block following the
      * given subroutine call. In other words, completes the control flow graph by adding the edges
      * corresponding to the return from this subroutine, when called from the given caller basic
      * block.
      *
      * <p>Note: a precondition and postcondition of this method is that all labels must have a null
      * {@link #nextListElement}.
      *
      * @param subroutineCaller a basic block that ends with a jsr to the basic block corresponding to
      *     this label. This label is supposed to correspond to the start of a subroutine.
      */
    final void addSubroutineRetSuccessors(final Label subroutineCaller) {
        // Data flow algorithm: put this basic block in a list blocks to process (which are blocks
        // belonging to a subroutine starting with this label) and, while there are blocks to process,
        // remove one from the list, put it in a list of blocks that have been processed, add a return
        // edge to the successor of subroutineCaller if applicable, and add its successor basic blocks
        // in the control flow graph to the list of blocks to process (if not already done).
        Label listOfProcessedBlocks = EMPTY_LIST;
        Label listOfBlocksToProcess = this;
        listOfBlocksToProcess.nextListElement = EMPTY_LIST;
        while (listOfBlocksToProcess != EMPTY_LIST) {
            // Move a basic block from the list of blocks to process to the list of processed blocks.
            Label basicBlock = listOfBlocksToProcess;
            listOfBlocksToProcess = basicBlock.nextListElement;
            basicBlock.nextListElement = listOfProcessedBlocks;
            listOfProcessedBlocks = basicBlock;
            // Add an edge from this block to the successor of the caller basic block, if this block is
            // the end of a subroutine and if this block and subroutineCaller do not belong to the same
            // subroutine.
            if ((basicBlock.flags & FLAG_SUBROUTINE_END) != 0
                    && basicBlock.subroutineId != subroutineCaller.subroutineId) {
                basicBlock.outgoingEdges =
                        new Edge(
                                basicBlock.outputStackSize,
                                // By construction, the first outgoing edge of a basic block that ends with a jsr
                                // instruction leads to the jsr continuation block, i.e. where execution continues
                                // when ret is called (see {@link #FLAG_SUBROUTINE_CALLER}).
                                subroutineCaller.outgoingEdges.successor,
                                basicBlock.outgoingEdges);
            }
            // Add its successors to the list of blocks to process. Note that {@link #pushSuccessors} does
            // not push basic blocks which are already in a list. Here this means either in the list of
            // blocks to process, or in the list of already processed blocks. This second list is
            // important to make sure we don't reprocess an already processed block.
            listOfBlocksToProcess = basicBlock.pushSuccessors(listOfBlocksToProcess);
        }
        // Reset the {@link #nextListElement} of all the basic blocks that have been processed to null,
        // so that this method can be called again with a different subroutine or subroutine caller.
        while (listOfProcessedBlocks != EMPTY_LIST) {
            Label newListOfProcessedBlocks = listOfProcessedBlocks.nextListElement;
            listOfProcessedBlocks.nextListElement = null;
            listOfProcessedBlocks = newListOfProcessedBlocks;
        }
    }
    /**
      * Adds the successors of this label in the method's control flow graph (except those
      * corresponding to a jsr target, and those already in a list of labels) to the given list of
      * blocks to process, and returns the new list.
      *
      * @param listOfLabelsToProcess a list of basic blocks to process, linked together with their
      *     {@link #nextListElement} field.
      * @return the new list of blocks to process.
      */
    private Label pushSuccessors(final Label listOfLabelsToProcess) {
        Label newListOfLabelsToProcess = listOfLabelsToProcess;
        Edge outgoingEdge = outgoingEdges;
        while (outgoingEdge != null) {
            // By construction, the second outgoing edge of a basic block that ends with a jsr instruction
            // leads to the jsr target (see {@link #FLAG_SUBROUTINE_CALLER}).
            boolean isJsrTarget =
                    (flags & Label.FLAG_SUBROUTINE_CALLER) != 0 && outgoingEdge == outgoingEdges.nextEdge;
            if (!isJsrTarget && outgoingEdge.successor.nextListElement == null) {
                // Add this successor to the list of blocks to process, if it does not already belong to a
                // list of labels.
                outgoingEdge.successor.nextListElement = newListOfLabelsToProcess;
                newListOfLabelsToProcess = outgoingEdge.successor;
            }
            outgoingEdge = outgoingEdge.nextEdge;
        }
        return newListOfLabelsToProcess;
    }
    // -----------------------------------------------------------------------------------------------
    // Overridden 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);
    }
}
Back to index...