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