| /* |
| * 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 {@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: |
| * <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 {@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. |
| */ |
| public final int numLocals; |
| |
| /** |
| * The number of stack slots in the values array. |
| */ |
| public final int numStack; |
| |
| /** |
| * 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, {@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. |
| */ |
| public final boolean duringCall; |
| |
| /** |
| * 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 {@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. |
| */ |
| 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. |
| */ |
| 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 {@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. |
| */ |
| public static final int INVALID_FRAMESTATE_BCI = -6; |
| |
| /** |
| * 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. |
| */ |
| 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. |
| * |
| * @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. |
| */ |
| 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. |
| * |
| * @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. |
| * |
| * @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. |
| * |
| * @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. |
| * |
| * @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. |
| * |
| * @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. |
| * |
| * @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(); |
| } |
| } |