/*
 * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
 * 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.
 *
 * 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.
 */
package jdk.vm.ci.code;

import java.util.Arrays;

import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.JavaValue;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.Value;

Represents the Java bytecode frame state(s) at a given position including locations where to find the local variables, operand stack values and locked objects of the bytecode frame(s).
/** * Represents the Java bytecode frame state(s) at a given position including {@link Value locations} * where to find the local variables, operand stack values and locked objects of the bytecode * frame(s). */
public final class BytecodeFrame extends BytecodePosition {
An array of values representing how to reconstruct the state of the Java frame. This is array is partitioned as follows:

Start index (inclusive) End index (exclusive) Description
0 numLocals Local variables
numLocals numLocals + numStack Operand stack
numLocals + numStack values.length Locked objects

Note that the number of locals and the number of stack slots may be smaller than the maximum number of locals and stack slots as specified in the compiled method. This field is intentionally exposed as a mutable array that a compiler may modify (e.g. during register allocation).

/** * An array of values representing how to reconstruct the state of the Java frame. This is array * is partitioned as follows: * <p> * <table summary="" border="1" cellpadding="5" frame="void" rules="all"> * <tr> * <th>Start index (inclusive)</th> * <th>End index (exclusive)</th> * <th>Description</th> * </tr> * <tr> * <td>0</td> * <td>numLocals</td> * <td>Local variables</td> * </tr> * <tr> * <td>numLocals</td> * <td>numLocals + numStack</td> * <td>Operand stack</td> * </tr> * <tr> * <td>numLocals + numStack</td> * <td>values.length</td> * <td>Locked objects</td> * </tr> * </table> * <p> * Note that the number of locals and the number of stack slots may be smaller than the maximum * number of locals and stack slots as specified in the compiled method. * * This field is intentionally exposed as a mutable array that a compiler may modify (e.g. * during register allocation). */
@SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "field is intentionally mutable")// public final JavaValue[] values;
An array describing the Java kinds in values. It records a kind for the locals and the operand stack.
/** * An array describing the Java kinds in {@link #values}. It records a kind for the locals and * the operand stack. */
private final JavaKind[] slotKinds;
The number of locals in the values array.
/** * The number of locals in the values array. */
public final int numLocals;
The number of stack slots in the values array.
/** * The number of stack slots in the values array. */
public final int numStack;
The number of locks in the values array.
/** * The number of locks in the values array. */
public final int numLocks;
True if this is a position inside an exception handler before the exception object has been consumed. In this case, numStack == 1 and getStackValue(0) is the location of the exception object. If deoptimization happens at this position, the interpreter will rethrow the exception instead of executing the bytecode instruction at this position.
/** * True if this is a position inside an exception handler before the exception object has been * consumed. In this case, {@link #numStack} {@code == 1} and {@link #getStackValue(int) * getStackValue(0)} is the location of the exception object. If deoptimization happens at this * position, the interpreter will rethrow the exception instead of executing the bytecode * instruction at this position. */
public final boolean rethrowException;
Specifies if this object represents a frame state in the middle of executing a call. If true, the arguments to the call have been popped from the stack and the return value (for a non-void call) has not yet been pushed.
/** * Specifies if this object represents a frame state in the middle of executing a call. If true, * the arguments to the call have been popped from the stack and the return value (for a * non-void call) has not yet been pushed. */
public final boolean duringCall;
This BCI should be used for frame states that are built for code with no meaningful BCI.
/** * This BCI should be used for frame states that are built for code with no meaningful BCI. */
public static final int UNKNOWN_BCI = -5;
The BCI for exception unwind. This is synthetic code and has no representation in bytecode. In contrast with AFTER_EXCEPTION_BCI, at this point, if the method is synchronized, the monitor is still held.
/** * The BCI for exception unwind. This is synthetic code and has no representation in bytecode. * In contrast with {@link #AFTER_EXCEPTION_BCI}, at this point, if the method is synchronized, * the monitor is still held. */
public static final int UNWIND_BCI = -1;
The BCI for the state before starting to execute a method. Note that if the method is synchronized, the monitor is not yet held.
/** * The BCI for the state before starting to execute a method. Note that if the method is * synchronized, the monitor is not yet held. */
public static final int BEFORE_BCI = -2;
The BCI for the state after finishing the execution of a method and returning normally. Note that if the method was synchronized the monitor is already released.
/** * The BCI for the state after finishing the execution of a method and returning normally. Note * that if the method was synchronized the monitor is already released. */
public static final int AFTER_BCI = -3;
The BCI for exception unwind. This is synthetic code and has no representation in bytecode. In contrast with UNWIND_BCI, at this point, if the method is synchronized, the monitor is already released.
/** * The BCI for exception unwind. This is synthetic code and has no representation in bytecode. * In contrast with {@link #UNWIND_BCI}, at this point, if the method is synchronized, the * monitor is already released. */
public static final int AFTER_EXCEPTION_BCI = -4;
This BCI should be used for states that cannot be the target of a deoptimization, like snippet frame states.
/** * This BCI should be used for states that cannot be the target of a deoptimization, like * snippet frame states. */
public static final int INVALID_FRAMESTATE_BCI = -6;
Determines if a given BCI matches one of the placeholder BCI constants defined in this class.
/** * Determines if a given BCI matches one of the placeholder BCI constants defined in this class. */
public static boolean isPlaceholderBci(int bci) { return bci < 0; }
Gets the name of a given placeholder BCI.
/** * Gets the name of a given placeholder BCI. */
public static String getPlaceholderBciName(int bci) { assert isPlaceholderBci(bci); if (bci == BytecodeFrame.AFTER_BCI) { return "AFTER_BCI"; } else if (bci == BytecodeFrame.AFTER_EXCEPTION_BCI) { return "AFTER_EXCEPTION_BCI"; } else if (bci == BytecodeFrame.INVALID_FRAMESTATE_BCI) { return "INVALID_FRAMESTATE_BCI"; } else if (bci == BytecodeFrame.BEFORE_BCI) { return "BEFORE_BCI"; } else if (bci == BytecodeFrame.UNKNOWN_BCI) { return "UNKNOWN_BCI"; } else { assert bci == BytecodeFrame.UNWIND_BCI; return "UNWIND_BCI"; } }
Creates a new frame object.
Params:
  • caller – the caller frame (which may be null)
  • method – the method
  • bci – a BCI within the method
  • rethrowException – specifies if the VM should re-throw the pending exception when deopt'ing using this frame
  • values – the frame state values.
  • slotKinds – the kinds in values. This array is now owned by this object and must not be mutated by the caller.
  • numLocals – the number of local variables
  • numStack – the depth of the stack
  • numLocks – the number of locked objects
/** * Creates a new frame object. * * @param caller the caller frame (which may be {@code null}) * @param method the method * @param bci a BCI within the method * @param rethrowException specifies if the VM should re-throw the pending exception when * deopt'ing using this frame * @param values the frame state {@link #values}. * @param slotKinds the kinds in {@code values}. This array is now owned by this object and must * not be mutated by the caller. * @param numLocals the number of local variables * @param numStack the depth of the stack * @param numLocks the number of locked objects */
@SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "caller transfers ownership of `slotKinds`") public BytecodeFrame(BytecodeFrame caller, ResolvedJavaMethod method, int bci, boolean rethrowException, boolean duringCall, JavaValue[] values, JavaKind[] slotKinds, int numLocals, int numStack, int numLocks) { super(caller, method, bci); assert values != null; this.rethrowException = rethrowException; this.duringCall = duringCall; this.values = values; this.slotKinds = slotKinds; this.numLocals = numLocals; this.numStack = numStack; this.numLocks = numLocks; assert !rethrowException || numStack == 1 : "must have exception on top of the stack"; }
Ensure that the frame state is formatted as expected by the JVM, with null or Illegal in the slot following a double word item. This should really be checked in FrameState itself but because of Word type rewriting and alternative backends that can't be done.
/** * Ensure that the frame state is formatted as expected by the JVM, with null or Illegal in the * slot following a double word item. This should really be checked in FrameState itself but * because of Word type rewriting and alternative backends that can't be done. */
public boolean validateFormat() { if (caller() != null) { caller().validateFormat(); } for (int i = 0; i < numLocals + numStack; i++) { if (values[i] != null) { JavaKind kind = slotKinds[i]; if (kind.needsTwoSlots()) { assert slotKinds.length > i + 1 : String.format("missing second word %s", this); assert slotKinds[i + 1] == JavaKind.Illegal : this; } } } return true; }
Gets the kind of a local variable.
Params:
  • i – the local variable to query
Returns:the kind of local variable i
@throwIndexOutOfBoundsException if i < 0 || i >= this.numLocals
/** * Gets the kind of a local variable. * * @param i the local variable to query * @return the kind of local variable {@code i} * @throw {@link IndexOutOfBoundsException} if {@code i < 0 || i >= this.numLocals} */
public JavaKind getLocalValueKind(int i) { if (i < 0 || i >= numLocals) { throw new IndexOutOfBoundsException(); } return slotKinds[i]; }
Gets the kind of a stack slot.
Params:
  • i – the local variable to query
Returns:the kind of stack slot i
@throwIndexOutOfBoundsException if i < 0 || i >= this.numStack
/** * Gets the kind of a stack slot. * * @param i the local variable to query * @return the kind of stack slot {@code i} * @throw {@link IndexOutOfBoundsException} if {@code i < 0 || i >= this.numStack} */
public JavaKind getStackValueKind(int i) { if (i < 0 || i >= numStack) { throw new IndexOutOfBoundsException(); } return slotKinds[i + numLocals]; }
Gets the value representing the specified local variable.
Params:
  • i – the local variable index
Returns:the value that can be used to reconstruct the local's current value
@throwIndexOutOfBoundsException if i < 0 || i >= this.numLocals
/** * Gets the value representing the specified local variable. * * @param i the local variable index * @return the value that can be used to reconstruct the local's current value * @throw {@link IndexOutOfBoundsException} if {@code i < 0 || i >= this.numLocals} */
public JavaValue getLocalValue(int i) { if (i < 0 || i >= numLocals) { throw new IndexOutOfBoundsException(); } return values[i]; }
Gets the value representing the specified stack slot.
Params:
  • i – the stack index
Returns:the value that can be used to reconstruct the stack slot's current value
@throwIndexOutOfBoundsException if i < 0 || i >= this.numStack
/** * Gets the value representing the specified stack slot. * * @param i the stack index * @return the value that can be used to reconstruct the stack slot's current value * @throw {@link IndexOutOfBoundsException} if {@code i < 0 || i >= this.numStack} */
public JavaValue getStackValue(int i) { if (i < 0 || i >= numStack) { throw new IndexOutOfBoundsException(); } return values[i + numLocals]; }
Gets the value representing the specified lock.
Params:
  • i – the lock index
Returns:the value that can be used to reconstruct the lock's current value
@throwIndexOutOfBoundsException if i < 0 || i >= this.numLocks
/** * Gets the value representing the specified lock. * * @param i the lock index * @return the value that can be used to reconstruct the lock's current value * @throw {@link IndexOutOfBoundsException} if {@code i < 0 || i >= this.numLocks} */
public JavaValue getLockValue(int i) { if (i < 0 || i >= numLocks) { throw new IndexOutOfBoundsException(); } return values[i + numLocals + numStack]; }
Gets the caller of this frame.
Returns:null if this frame has no caller
/** * Gets the caller of this frame. * * @return {@code null} if this frame has no caller */
public BytecodeFrame caller() { return (BytecodeFrame) getCaller(); } @Override public int hashCode() { return (numLocals + 1) ^ (numStack + 11) ^ (numLocks + 7); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof BytecodeFrame && super.equals(obj)) { BytecodeFrame that = (BytecodeFrame) obj; // @formatter:off if (this.duringCall == that.duringCall && this.rethrowException == that.rethrowException && this.numLocals == that.numLocals && this.numLocks == that.numLocks && this.numStack == that.numStack && Arrays.equals(this.values, that.values)) { return true; } // @formatter:off return true; } return false; } @Override public String toString() { return CodeUtil.append(new StringBuilder(100), this).toString(); } }