Mercurial > hg > graal-compiler
comparison graal/Compiler/src/com/sun/c1x/value/FrameState.java @ 2507:9ec15d6914ca
Pull over of compiler from maxine repository.
author | Thomas Wuerthinger <thomas@wuerthinger.net> |
---|---|
date | Wed, 27 Apr 2011 11:43:22 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
2506:4a3bf8a5bf41 | 2507:9ec15d6914ca |
---|---|
1 /* | |
2 * Copyright (c) 2009, 2011, 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 */ | |
23 package com.sun.c1x.value; | |
24 | |
25 import java.util.*; | |
26 | |
27 import com.sun.c1x.*; | |
28 import com.sun.c1x.graph.*; | |
29 import com.sun.c1x.ir.*; | |
30 import com.sun.cri.ci.*; | |
31 import com.sun.cri.ri.*; | |
32 | |
33 /** | |
34 * The {@code FrameState} class encapsulates the frame state (i.e. local variables and | |
35 * operand stack) at a particular point in the abstract interpretation. | |
36 * | |
37 * @author Ben L. Titzer | |
38 */ | |
39 public abstract class FrameState { | |
40 | |
41 /** | |
42 * The operand stack and local variables. | |
43 * The local variables occupy the index range {@code [0 .. maxLocals)}. | |
44 * The operand stack occupies the index range {@code [maxLocals .. values.length)}. | |
45 * The top of the operand stack is at index {@code maxLocals + stackIndex}. | |
46 * This does not include the operand stack or local variables of parent frames. | |
47 * | |
48 * {@linkplain CiKind#isDoubleWord() Double-word} local variables and | |
49 * operand stack values occupy 2 slots in this array with the second slot | |
50 * being {@code null}. | |
51 */ | |
52 protected final Value[] values; | |
53 | |
54 /** | |
55 * The depth of the operand stack. | |
56 * The top of stack value is in {@code values[maxLocals + stackIndex]}. | |
57 */ | |
58 protected int stackIndex; | |
59 | |
60 /** | |
61 * The number of local variables. | |
62 */ | |
63 protected final int maxLocals; | |
64 | |
65 protected final IRScope scope; | |
66 | |
67 /** | |
68 * The bytecode index to which this frame state applies. This will be {@code -1} | |
69 * iff this state is mutable. | |
70 */ | |
71 public final int bci; | |
72 | |
73 /** | |
74 * The list of locks held by this frame state. | |
75 * This does not include locks held by parent frames. | |
76 */ | |
77 protected ArrayList<Value> locks; | |
78 | |
79 /** | |
80 * The number of minimum stack slots required for doing IR wrangling during | |
81 * {@linkplain GraphBuilder bytecode parsing}. While this may hide stack | |
82 * overflow issues in the original bytecode, the assumption is that such | |
83 * issues must be caught by the verifier. | |
84 */ | |
85 private static final int MINIMUM_STACK_SLOTS = 1; | |
86 | |
87 /** | |
88 * Creates a {@code FrameState} for the given scope and maximum number of stack and local variables. | |
89 * | |
90 * @param irScope the inlining context of the method | |
91 * @param bci the bytecode index of the frame state | |
92 * @param maxLocals maximum number of locals | |
93 * @param maxStack maximum size of the stack | |
94 */ | |
95 public FrameState(IRScope irScope, int bci, int maxLocals, int maxStack) { | |
96 this.scope = irScope; | |
97 this.bci = bci; | |
98 this.values = new Value[maxLocals + Math.max(maxStack, MINIMUM_STACK_SLOTS)]; | |
99 this.maxLocals = maxLocals; | |
100 C1XMetrics.FrameStatesCreated++; | |
101 C1XMetrics.FrameStateValuesCreated += this.values.length; | |
102 } | |
103 | |
104 /** | |
105 * Copies the contents of this frame state so that further updates to either stack aren't reflected in the other. | |
106 * @param bci the bytecode index of the copy | |
107 * @param withLocals indicates whether to copy the local state | |
108 * @param withStack indicates whether to copy the stack state | |
109 * @param withLocks indicates whether to copy the lock state | |
110 * | |
111 * @return a new frame state with the specified components | |
112 */ | |
113 public MutableFrameState copy(int bci, boolean withLocals, boolean withStack, boolean withLocks) { | |
114 final MutableFrameState other = new MutableFrameState(scope, bci, localsSize(), maxStackSize()); | |
115 if (withLocals && withStack) { | |
116 // fast path: use array copy | |
117 int valuesSize = valuesSize(); | |
118 assert other.values.length >= valuesSize : "array size: " + other.values.length + ", valuesSize: " + valuesSize + ", maxStackSize: " + maxStackSize() + ", localsSize: " + localsSize(); | |
119 assert values.length >= valuesSize : "array size: " + values.length + ", valuesSize: " + valuesSize + ", maxStackSize: " + maxStackSize() + ", localsSize: " + localsSize(); | |
120 System.arraycopy(values, 0, other.values, 0, valuesSize); | |
121 other.stackIndex = stackIndex; | |
122 } else { | |
123 if (withLocals) { | |
124 other.replaceLocals(this); | |
125 } | |
126 if (withStack) { | |
127 other.replaceStack(this); | |
128 } | |
129 } | |
130 if (withLocks) { | |
131 other.replaceLocks(this); | |
132 } | |
133 return other; | |
134 } | |
135 | |
136 /** | |
137 * Gets a mutable copy ({@link MutableFrameState}) of this frame state. | |
138 */ | |
139 public MutableFrameState copy() { | |
140 return copy(bci, true, true, true); | |
141 } | |
142 | |
143 /** | |
144 * Gets an immutable copy of this frame state but without the stack. | |
145 */ | |
146 public FrameState immutableCopyWithEmptyStack() { | |
147 return copy(bci, true, false, true); | |
148 } | |
149 | |
150 /** | |
151 * Gets an immutable copy of this frame state but without the frame info. | |
152 */ | |
153 public FrameState immutableCopyCodePosOnly() { | |
154 return copy(bci, false, false, false); | |
155 } | |
156 | |
157 public boolean isSameAcrossScopes(FrameState other) { | |
158 assert stackSize() == other.stackSize(); | |
159 assert localsSize() == other.localsSize(); | |
160 assert locksSize() == other.locksSize(); | |
161 for (int i = 0; i < stackIndex; i++) { | |
162 Value x = stackAt(i); | |
163 Value y = other.stackAt(i); | |
164 if (x != y && typeMismatch(x, y)) { | |
165 return false; | |
166 } | |
167 } | |
168 if (locks != null) { | |
169 for (int i = 0; i < locks.size(); i++) { | |
170 if (lockAt(i) != other.lockAt(i)) { | |
171 return false; | |
172 } | |
173 } | |
174 } | |
175 return true; | |
176 } | |
177 | |
178 /** | |
179 * Returns the inlining context associated with this frame state. | |
180 * | |
181 * @return the inlining context | |
182 */ | |
183 public IRScope scope() { | |
184 return scope; | |
185 } | |
186 | |
187 /** | |
188 * Returns the size of the local variables. | |
189 * | |
190 * @return the size of the local variables | |
191 */ | |
192 public int localsSize() { | |
193 return maxLocals; | |
194 } | |
195 | |
196 /** | |
197 * Gets number of locks held by this frame state. | |
198 */ | |
199 public int locksSize() { | |
200 return locks == null ? 0 : locks.size(); | |
201 } | |
202 | |
203 public int totalLocksSize() { | |
204 return locksSize() + ((callerState() == null) ? 0 : callerState().totalLocksSize()); | |
205 } | |
206 | |
207 /** | |
208 * Gets the current size (height) of the stack. | |
209 */ | |
210 public int stackSize() { | |
211 return stackIndex; | |
212 } | |
213 | |
214 /** | |
215 * Gets the maximum size (height) of the stack. | |
216 ] */ | |
217 public int maxStackSize() { | |
218 return values.length - maxLocals; | |
219 } | |
220 | |
221 /** | |
222 * Checks whether the stack is empty. | |
223 * | |
224 * @return {@code true} the stack is currently empty | |
225 */ | |
226 public boolean stackEmpty() { | |
227 return stackIndex == 0; | |
228 } | |
229 | |
230 /** | |
231 * Invalidates the local variable at the specified index. If the specified index refers to a doubleword local, then | |
232 * invalidates the high word as well. | |
233 * | |
234 * @param i the index of the local to invalidate | |
235 */ | |
236 public void invalidateLocal(int i) { | |
237 // note that for double word locals, the high slot should already be null | |
238 // unless the local is actually dead and the high slot is being reused; | |
239 // in either case, it is not necessary to null the high slot | |
240 values[i] = null; | |
241 } | |
242 | |
243 /** | |
244 * Loads the local variable at the specified index. | |
245 * | |
246 * @param i the index of the local variable to load | |
247 * @return the instruction that produced the specified local | |
248 */ | |
249 public Value loadLocal(int i) { | |
250 assert i < maxLocals : "local variable index out of range: " + i; | |
251 Value x = values[i]; | |
252 if (x != null) { | |
253 if (x.isIllegal()) { | |
254 return null; | |
255 } | |
256 assert x.kind.isSingleWord() || values[i + 1] == null || values[i + 1] instanceof Phi; | |
257 } | |
258 return x; | |
259 } | |
260 | |
261 /** | |
262 * Stores a given local variable at the specified index. If the value is a {@linkplain CiKind#isDoubleWord() double word}, | |
263 * then the next local variable index is also overwritten. | |
264 * | |
265 * @param i the index at which to store | |
266 * @param x the instruction which produces the value for the local | |
267 */ | |
268 public void storeLocal(int i, Value x) { | |
269 assert i < maxLocals : "local variable index out of range: " + i; | |
270 invalidateLocal(i); | |
271 values[i] = x; | |
272 if (isDoubleWord(x)) { | |
273 // (tw) if this was a double word then kill i+1 | |
274 values[i + 1] = null; | |
275 } | |
276 if (i > 0) { | |
277 // if there was a double word at i - 1, then kill it | |
278 Value p = values[i - 1]; | |
279 if (isDoubleWord(p)) { | |
280 values[i - 1] = null; | |
281 } | |
282 } | |
283 } | |
284 | |
285 /** | |
286 * Get the value on the stack at the specified stack index. | |
287 * | |
288 * @param i the index into the stack, with {@code 0} being the bottom of the stack | |
289 * @return the instruction at the specified position in the stack | |
290 */ | |
291 public final Value stackAt(int i) { | |
292 assert i < stackIndex; | |
293 return values[i + maxLocals]; | |
294 } | |
295 | |
296 /** | |
297 * Gets the value in the local variables at the specified index. | |
298 * | |
299 * @param i the index into the locals | |
300 * @return the instruction that produced the value for the specified local | |
301 */ | |
302 public final Value localAt(int i) { | |
303 assert i < maxLocals : "local variable index out of range: " + i; | |
304 return values[i]; | |
305 } | |
306 | |
307 /** | |
308 * Retrieves the lock at the specified index in the lock stack. | |
309 * @param i the index into the lock stack | |
310 * @return the instruction which produced the object at the specified location in the lock stack | |
311 */ | |
312 public final Value lockAt(int i) { | |
313 return locks.get(i); | |
314 } | |
315 | |
316 /** | |
317 * Inserts a phi statement into the stack at the specified stack index. | |
318 * @param block the block begin for which we are creating the phi | |
319 * @param i the index into the stack for which to create a phi | |
320 */ | |
321 public void setupPhiForStack(BlockBegin block, int i) { | |
322 Value p = stackAt(i); | |
323 if (p != null) { | |
324 if (p instanceof Phi) { | |
325 Phi phi = (Phi) p; | |
326 if (phi.block() == block && phi.isOnStack() && phi.stackIndex() == i) { | |
327 return; | |
328 } | |
329 } | |
330 values[maxLocals + i] = new Phi(p.kind, block, -i - 1); | |
331 } | |
332 } | |
333 | |
334 /** | |
335 * Inserts a phi statement for the local at the specified index. | |
336 * @param block the block begin for which we are creating the phi | |
337 * @param i the index of the local variable for which to create the phi | |
338 */ | |
339 public void setupPhiForLocal(BlockBegin block, int i) { | |
340 Value p = values[i]; | |
341 if (p instanceof Phi) { | |
342 Phi phi = (Phi) p; | |
343 if (phi.block() == block && phi.isLocal() && phi.localIndex() == i) { | |
344 return; | |
345 } | |
346 } | |
347 storeLocal(i, new Phi(p.kind, block, i)); | |
348 } | |
349 | |
350 /** | |
351 * Gets the value at a specified index in the set of operand stack and local values represented by this frame. | |
352 * This method should only be used to iterate over all the values in this frame, irrespective of whether | |
353 * they are on the stack or in local variables. | |
354 * To iterate the stack slots, the {@link #stackAt(int)} and {@link #stackSize()} methods should be used. | |
355 * To iterate the local variables, the {@link #localAt(int)} and {@link #localsSize()} methods should be used. | |
356 * | |
357 * @param i a value in the range {@code [0 .. valuesSize()]} | |
358 * @return the value at index {@code i} which may be {@code null} | |
359 */ | |
360 public final Value valueAt(int i) { | |
361 return values[i]; | |
362 } | |
363 | |
364 /** | |
365 * The number of operand stack slots and local variables in this frame. | |
366 * This method should typically only be used in conjunction with {@link #valueAt(int)}. | |
367 * To iterate the stack slots, the {@link #stackAt(int)} and {@link #stackSize()} methods should be used. | |
368 * To iterate the local variables, the {@link #localAt(int)} and {@link #localsSize()} methods should be used. | |
369 * | |
370 * @return the number of local variables in this frame | |
371 */ | |
372 public final int valuesSize() { | |
373 return maxLocals + stackIndex; | |
374 } | |
375 | |
376 public final int callerStackSize() { | |
377 FrameState callerState = scope().callerState; | |
378 return callerState == null ? 0 : callerState.stackSize(); | |
379 } | |
380 | |
381 public void checkPhis(BlockBegin block, FrameState other) { | |
382 checkSize(other); | |
383 final int max = valuesSize(); | |
384 for (int i = 0; i < max; i++) { | |
385 Value x = values[i]; | |
386 Value y = other.values[i]; | |
387 if (x != null && x != y) { | |
388 if (x instanceof Phi) { | |
389 Phi phi = (Phi) x; | |
390 if (phi.block() == block) { | |
391 for (int j = 0; j < phi.inputCount(); j++) { | |
392 if (phi.inputIn(other) == null) { | |
393 throw new CiBailout("phi " + phi + " has null operand at new predecessor"); | |
394 } | |
395 } | |
396 continue; | |
397 } | |
398 } | |
399 throw new CiBailout("instruction is not a phi or null at " + i); | |
400 } | |
401 } | |
402 } | |
403 | |
404 private void checkSize(FrameState other) { | |
405 if (other.stackIndex != stackIndex) { | |
406 throw new CiBailout("stack sizes do not match"); | |
407 } else if (other.maxLocals != maxLocals) { | |
408 throw new CiBailout("local sizes do not match"); | |
409 } | |
410 } | |
411 | |
412 public void merge(BlockBegin block, FrameState other) { | |
413 checkSize(other); | |
414 for (int i = 0; i < valuesSize(); i++) { | |
415 Value x = values[i]; | |
416 if (x != null) { | |
417 Value y = other.values[i]; | |
418 if (x != y) { | |
419 if (typeMismatch(x, y)) { | |
420 if (x instanceof Phi) { | |
421 Phi phi = (Phi) x; | |
422 if (phi.block() == block) { | |
423 phi.makeDead(); | |
424 } | |
425 } | |
426 values[i] = null; | |
427 continue; | |
428 } | |
429 if (i < maxLocals) { | |
430 // this a local | |
431 setupPhiForLocal(block, i); | |
432 } else { | |
433 // this is a stack slot | |
434 setupPhiForStack(block, i - maxLocals); | |
435 } | |
436 } | |
437 } | |
438 } | |
439 } | |
440 | |
441 private static boolean typeMismatch(Value x, Value y) { | |
442 return y == null || x.kind != y.kind; | |
443 } | |
444 | |
445 private static boolean isDoubleWord(Value x) { | |
446 return x != null && x.kind.isDoubleWord(); | |
447 } | |
448 | |
449 /** | |
450 * The interface implemented by a client of {@link FrameState#forEachPhi(BlockBegin, PhiProcedure)} and | |
451 * {@link FrameState#forEachLivePhi(BlockBegin, PhiProcedure)}. | |
452 */ | |
453 public static interface PhiProcedure { | |
454 boolean doPhi(Phi phi); | |
455 } | |
456 | |
457 /** | |
458 * Traverses all {@linkplain Phi phis} of a given block in this frame state. | |
459 * | |
460 * @param block only phis {@linkplain Phi#block() associated} with this block are traversed | |
461 * @param proc the call back invoked for each live phi traversed | |
462 */ | |
463 public boolean forEachPhi(BlockBegin block, PhiProcedure proc) { | |
464 int max = this.valuesSize(); | |
465 for (int i = 0; i < max; i++) { | |
466 Value instr = values[i]; | |
467 if (instr instanceof Phi && !instr.isDeadPhi()) { | |
468 Phi phi = (Phi) instr; | |
469 if (block == null || phi.block() == block) { | |
470 if (!proc.doPhi(phi)) { | |
471 return false; | |
472 } | |
473 } | |
474 } | |
475 } | |
476 return true; | |
477 } | |
478 | |
479 /** | |
480 * Traverses all live {@linkplain Phi phis} of a given block in this frame state. | |
481 * | |
482 * @param block only phis {@linkplain Phi#block() associated} with this block are traversed | |
483 * @param proc the call back invoked for each live phi traversed | |
484 */ | |
485 public final boolean forEachLivePhi(BlockBegin block, PhiProcedure proc) { | |
486 int max = this.valuesSize(); | |
487 for (int i = 0; i < max; i++) { | |
488 Value instr = values[i]; | |
489 if (instr instanceof Phi && instr.isLive() && !instr.isDeadPhi()) { | |
490 Phi phi = (Phi) instr; | |
491 if (block == null || phi.block() == block) { | |
492 if (!proc.doPhi(phi)) { | |
493 return false; | |
494 } | |
495 } | |
496 } | |
497 } | |
498 return true; | |
499 } | |
500 | |
501 /** | |
502 * Checks whether this frame state has any {@linkplain Phi phi} statements. | |
503 */ | |
504 public boolean hasPhis() { | |
505 int max = valuesSize(); | |
506 for (int i = 0; i < max; i++) { | |
507 Value value = values[i]; | |
508 if (value instanceof Phi) { | |
509 return true; | |
510 } | |
511 } | |
512 return false; | |
513 } | |
514 | |
515 /** | |
516 * Iterates over all the values in this frame state and its callers, including the stack, locals, and locks. | |
517 * @param closure the closure to apply to each value | |
518 */ | |
519 public void valuesDo(ValueClosure closure) { | |
520 valuesDo(this, closure); | |
521 } | |
522 | |
523 /** | |
524 * Iterates over all the values of a given frame state and its callers, including the stack, locals, and locks. | |
525 * @param closure the closure to apply to each value | |
526 */ | |
527 public static void valuesDo(FrameState state, ValueClosure closure) { | |
528 do { | |
529 final int max = state.valuesSize(); | |
530 for (int i = 0; i < max; i++) { | |
531 if (state.values[i] != null) { | |
532 Value newValue = closure.apply(state.values[i]); | |
533 state.values[i] = newValue; | |
534 } | |
535 } | |
536 if (state.locks != null) { | |
537 for (int i = 0; i < state.locks.size(); i++) { | |
538 Value instr = state.locks.get(i); | |
539 if (instr != null) { | |
540 state.locks.set(i, closure.apply(instr)); | |
541 } | |
542 } | |
543 } | |
544 state = state.callerState(); | |
545 } while (state != null); | |
546 } | |
547 | |
548 /** | |
549 * The interface implemented by a client of {@link FrameState#forEachLiveStateValue(ValueProcedure)}. | |
550 */ | |
551 public static interface ValueProcedure { | |
552 void doValue(Value value); | |
553 } | |
554 | |
555 /** | |
556 * Traverses all {@linkplain Value#isLive() live values} of this frame state and it's callers. | |
557 * | |
558 * @param proc the call back called to process each live value traversed | |
559 */ | |
560 public final void forEachLiveStateValue(ValueProcedure proc) { | |
561 FrameState state = this; | |
562 while (state != null) { | |
563 final int max = state.valuesSize(); | |
564 for (int i = 0; i < max; i++) { | |
565 Value value = state.values[i]; | |
566 if (value != null && value.isLive()) { | |
567 proc.doValue(value); | |
568 } | |
569 } | |
570 state = state.callerState(); | |
571 } | |
572 } | |
573 | |
574 public static String toString(FrameState fs) { | |
575 StringBuilder sb = new StringBuilder(); | |
576 String nl = String.format("%n"); | |
577 while (fs != null) { | |
578 if (fs.scope == null) { | |
579 sb.append("<no method>").append(" [bci: ").append(fs.bci).append(']'); | |
580 break; | |
581 } else { | |
582 CiUtil.appendLocation(sb, fs.scope.method, fs.bci).append(nl); | |
583 for (int i = 0; i < fs.localsSize(); ++i) { | |
584 Value value = fs.localAt(i); | |
585 sb.append(String.format(" local[%d] = %-8s : %s%n", i, value == null ? "bogus" : value.kind.javaName, value)); | |
586 } | |
587 for (int i = 0; i < fs.stackSize(); ++i) { | |
588 Value value = fs.stackAt(i); | |
589 sb.append(String.format(" stack[%d] = %-8s : %s%n", i, value == null ? "bogus" : value.kind.javaName, value)); | |
590 } | |
591 for (int i = 0; i < fs.locksSize(); ++i) { | |
592 Value value = fs.lockAt(i); | |
593 sb.append(String.format(" lock[%d] = %-8s : %s%n", i, value == null ? "bogus" : value.kind.javaName, value)); | |
594 } | |
595 fs = fs.callerState(); | |
596 } | |
597 } | |
598 return sb.toString(); | |
599 } | |
600 | |
601 @Override | |
602 public String toString() { | |
603 return toString(this); | |
604 } | |
605 | |
606 public CiCodePos toCodePos() { | |
607 FrameState caller = callerState(); | |
608 CiCodePos callerCodePos = null; | |
609 if (caller != null) { | |
610 callerCodePos = caller.toCodePos(); | |
611 } | |
612 return new CiCodePos(callerCodePos, scope.method, bci); | |
613 } | |
614 | |
615 /** | |
616 * Creates a new {@code MutableFrameState} corresponding to inlining the specified method into this point in this frame state. | |
617 * @param scope the IRScope representing the inlined method | |
618 * @return a new frame state representing the state at the beginning of inlining the specified method into this one | |
619 */ | |
620 public MutableFrameState pushScope(IRScope scope) { | |
621 assert scope.caller == this.scope; | |
622 RiMethod method = scope.method; | |
623 return new MutableFrameState(scope, -1, method.maxLocals(), method.maxStackSize()); | |
624 } | |
625 | |
626 /** | |
627 * Creates a new {@code MutableFrameState} corresponding to the state upon returning from this inlined method into the outer | |
628 * IRScope. | |
629 * @return a new frame state representing the state at exit from this frame state | |
630 */ | |
631 public MutableFrameState popScope() { | |
632 IRScope callingScope = scope.caller; | |
633 assert callingScope != null; | |
634 FrameState callerState = scope.callerState; | |
635 MutableFrameState res = new MutableFrameState(callingScope, bci, callerState.maxLocals, callerState.maxStackSize() + stackIndex); | |
636 System.arraycopy(callerState.values, 0, res.values, 0, callerState.values.length); | |
637 System.arraycopy(values, maxLocals, res.values, res.maxLocals + callerState.stackIndex, stackIndex); | |
638 res.stackIndex = callerState.stackIndex + stackIndex; | |
639 assert res.stackIndex >= 0; | |
640 res.replaceLocks(callerState); | |
641 return res; | |
642 } | |
643 | |
644 /** | |
645 * Gets the caller frame state. | |
646 * | |
647 * @return the caller frame state or {@code null} if this is a top-level state | |
648 */ | |
649 public FrameState callerState() { | |
650 return scope.callerState; | |
651 } | |
652 } |