1/*
2 * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23package jdk.vm.ci.code;
24
25import java.util.Arrays;
26
27import jdk.vm.ci.meta.JavaKind;
28import jdk.vm.ci.meta.JavaValue;
29import jdk.vm.ci.meta.ResolvedJavaMethod;
30import jdk.vm.ci.meta.Value;
31
32/**
33 * Represents the Java bytecode frame state(s) at a given position including {@link Value locations}
34 * where to find the local variables, operand stack values and locked objects of the bytecode
35 * frame(s).
36 */
37public final class BytecodeFrame extends BytecodePosition {
38
39    /**
40     * An array of values representing how to reconstruct the state of the Java frame. This is array
41     * is partitioned as follows:
42     * <p>
43     * <table summary="" border="1" cellpadding="5" frame="void" rules="all">
44     * <tr>
45     * <th>Start index (inclusive)</th>
46     * <th>End index (exclusive)</th>
47     * <th>Description</th>
48     * </tr>
49     * <tr>
50     * <td>0</td>
51     * <td>numLocals</td>
52     * <td>Local variables</td>
53     * </tr>
54     * <tr>
55     * <td>numLocals</td>
56     * <td>numLocals + numStack</td>
57     * <td>Operand stack</td>
58     * </tr>
59     * <tr>
60     * <td>numLocals + numStack</td>
61     * <td>values.length</td>
62     * <td>Locked objects</td>
63     * </tr>
64     * </table>
65     * <p>
66     * Note that the number of locals and the number of stack slots may be smaller than the maximum
67     * number of locals and stack slots as specified in the compiled method.
68     *
69     * This field is intentionally exposed as a mutable array that a compiler may modify (e.g.
70     * during register allocation).
71     */
72    @SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "field is intentionally mutable")//
73    public final JavaValue[] values;
74
75    /**
76     * An array describing the Java kinds in {@link #values}. It records a kind for the locals and
77     * the operand stack.
78     */
79    private final JavaKind[] slotKinds;
80
81    /**
82     * The number of locals in the values array.
83     */
84    public final int numLocals;
85
86    /**
87     * The number of stack slots in the values array.
88     */
89    public final int numStack;
90
91    /**
92     * The number of locks in the values array.
93     */
94    public final int numLocks;
95
96    /**
97     * True if this is a position inside an exception handler before the exception object has been
98     * consumed. In this case, {@link #numStack} {@code == 1} and {@link #getStackValue(int)
99     * getStackValue(0)} is the location of the exception object. If deoptimization happens at this
100     * position, the interpreter will rethrow the exception instead of executing the bytecode
101     * instruction at this position.
102     */
103    public final boolean rethrowException;
104
105    /**
106     * Specifies if this object represents a frame state in the middle of executing a call. If true,
107     * the arguments to the call have been popped from the stack and the return value (for a
108     * non-void call) has not yet been pushed.
109     */
110    public final boolean duringCall;
111
112    /**
113     * This BCI should be used for frame states that are built for code with no meaningful BCI.
114     */
115    public static final int UNKNOWN_BCI = -5;
116
117    /**
118     * The BCI for exception unwind. This is synthetic code and has no representation in bytecode.
119     * In contrast with {@link #AFTER_EXCEPTION_BCI}, at this point, if the method is synchronized,
120     * the monitor is still held.
121     */
122    public static final int UNWIND_BCI = -1;
123
124    /**
125     * The BCI for the state before starting to execute a method. Note that if the method is
126     * synchronized, the monitor is not yet held.
127     */
128    public static final int BEFORE_BCI = -2;
129
130    /**
131     * The BCI for the state after finishing the execution of a method and returning normally. Note
132     * that if the method was synchronized the monitor is already released.
133     */
134    public static final int AFTER_BCI = -3;
135
136    /**
137     * The BCI for exception unwind. This is synthetic code and has no representation in bytecode.
138     * In contrast with {@link #UNWIND_BCI}, at this point, if the method is synchronized, the
139     * monitor is already released.
140     */
141    public static final int AFTER_EXCEPTION_BCI = -4;
142
143    /**
144     * This BCI should be used for states that cannot be the target of a deoptimization, like
145     * snippet frame states.
146     */
147    public static final int INVALID_FRAMESTATE_BCI = -6;
148
149    /**
150     * Determines if a given BCI matches one of the placeholder BCI constants defined in this class.
151     */
152    public static boolean isPlaceholderBci(int bci) {
153        return bci < 0;
154    }
155
156    /**
157     * Gets the name of a given placeholder BCI.
158     */
159    public static String getPlaceholderBciName(int bci) {
160        assert isPlaceholderBci(bci);
161        if (bci == BytecodeFrame.AFTER_BCI) {
162            return "AFTER_BCI";
163        } else if (bci == BytecodeFrame.AFTER_EXCEPTION_BCI) {
164            return "AFTER_EXCEPTION_BCI";
165        } else if (bci == BytecodeFrame.INVALID_FRAMESTATE_BCI) {
166            return "INVALID_FRAMESTATE_BCI";
167        } else if (bci == BytecodeFrame.BEFORE_BCI) {
168            return "BEFORE_BCI";
169        } else if (bci == BytecodeFrame.UNKNOWN_BCI) {
170            return "UNKNOWN_BCI";
171        } else {
172            assert bci == BytecodeFrame.UNWIND_BCI;
173            return "UNWIND_BCI";
174        }
175    }
176
177    /**
178     * Creates a new frame object.
179     *
180     * @param caller the caller frame (which may be {@code null})
181     * @param method the method
182     * @param bci a BCI within the method
183     * @param rethrowException specifies if the VM should re-throw the pending exception when
184     *            deopt'ing using this frame
185     * @param values the frame state {@link #values}.
186     * @param slotKinds the kinds in {@code values}. This array is now owned by this object and must
187     *            not be mutated by the caller.
188     * @param numLocals the number of local variables
189     * @param numStack the depth of the stack
190     * @param numLocks the number of locked objects
191     */
192    @SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "caller transfers ownership of `slotKinds`")
193    public BytecodeFrame(BytecodeFrame caller, ResolvedJavaMethod method, int bci, boolean rethrowException, boolean duringCall, JavaValue[] values, JavaKind[] slotKinds, int numLocals, int numStack,
194                    int numLocks) {
195        super(caller, method, bci);
196        assert values != null;
197        this.rethrowException = rethrowException;
198        this.duringCall = duringCall;
199        this.values = values;
200        this.slotKinds = slotKinds;
201        this.numLocals = numLocals;
202        this.numStack = numStack;
203        this.numLocks = numLocks;
204        assert !rethrowException || numStack == 1 : "must have exception on top of the stack";
205    }
206
207    /**
208     * Ensure that the frame state is formatted as expected by the JVM, with null or Illegal in the
209     * slot following a double word item. This should really be checked in FrameState itself but
210     * because of Word type rewriting and alternative backends that can't be done.
211     */
212    public boolean validateFormat() {
213        if (caller() != null) {
214            caller().validateFormat();
215        }
216        for (int i = 0; i < numLocals + numStack; i++) {
217            if (values[i] != null) {
218                JavaKind kind = slotKinds[i];
219                if (kind.needsTwoSlots()) {
220                    assert slotKinds.length > i + 1 : String.format("missing second word %s", this);
221                    assert slotKinds[i + 1] == JavaKind.Illegal : this;
222                }
223            }
224        }
225        return true;
226    }
227
228    /**
229     * Gets the kind of a local variable.
230     *
231     * @param i the local variable to query
232     * @return the kind of local variable {@code i}
233     * @throw {@link IndexOutOfBoundsException} if {@code i < 0 || i >= this.numLocals}
234     */
235    public JavaKind getLocalValueKind(int i) {
236        if (i < 0 || i >= numLocals) {
237            throw new IndexOutOfBoundsException();
238        }
239        return slotKinds[i];
240    }
241
242    /**
243     * Gets the kind of a stack slot.
244     *
245     * @param i the local variable to query
246     * @return the kind of stack slot {@code i}
247     * @throw {@link IndexOutOfBoundsException} if {@code i < 0 || i >= this.numStack}
248     */
249    public JavaKind getStackValueKind(int i) {
250        if (i < 0 || i >= numStack) {
251            throw new IndexOutOfBoundsException();
252        }
253        return slotKinds[i + numLocals];
254    }
255
256    /**
257     * Gets the value representing the specified local variable.
258     *
259     * @param i the local variable index
260     * @return the value that can be used to reconstruct the local's current value
261     * @throw {@link IndexOutOfBoundsException} if {@code i < 0 || i >= this.numLocals}
262     */
263    public JavaValue getLocalValue(int i) {
264        if (i < 0 || i >= numLocals) {
265            throw new IndexOutOfBoundsException();
266        }
267        return values[i];
268    }
269
270    /**
271     * Gets the value representing the specified stack slot.
272     *
273     * @param i the stack index
274     * @return the value that can be used to reconstruct the stack slot's current value
275     * @throw {@link IndexOutOfBoundsException} if {@code i < 0 || i >= this.numStack}
276     */
277    public JavaValue getStackValue(int i) {
278        if (i < 0 || i >= numStack) {
279            throw new IndexOutOfBoundsException();
280        }
281        return values[i + numLocals];
282    }
283
284    /**
285     * Gets the value representing the specified lock.
286     *
287     * @param i the lock index
288     * @return the value that can be used to reconstruct the lock's current value
289     * @throw {@link IndexOutOfBoundsException} if {@code i < 0 || i >= this.numLocks}
290     */
291    public JavaValue getLockValue(int i) {
292        if (i < 0 || i >= numLocks) {
293            throw new IndexOutOfBoundsException();
294        }
295        return values[i + numLocals + numStack];
296    }
297
298    /**
299     * Gets the caller of this frame.
300     *
301     * @return {@code null} if this frame has no caller
302     */
303    public BytecodeFrame caller() {
304        return (BytecodeFrame) getCaller();
305    }
306
307    @Override
308    public int hashCode() {
309        return (numLocals + 1) ^ (numStack + 11) ^ (numLocks + 7);
310    }
311
312    @Override
313    public boolean equals(Object obj) {
314        if (this == obj) {
315            return true;
316        }
317        if (obj instanceof BytecodeFrame && super.equals(obj)) {
318            BytecodeFrame that = (BytecodeFrame) obj;
319            // @formatter:off
320            if (this.duringCall == that.duringCall &&
321                this.rethrowException == that.rethrowException &&
322                this.numLocals == that.numLocals &&
323                this.numLocks == that.numLocks &&
324                this.numStack == that.numStack &&
325                Arrays.equals(this.values, that.values)) {
326                return true;
327            }
328            // @formatter:off
329            return true;
330        }
331        return false;
332    }
333
334    @Override
335    public String toString() {
336        return CodeUtil.append(new StringBuilder(100), this).toString();
337    }
338}
339