FrameState.java revision 12651:6ef01bd40ce2
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 org.graalvm.compiler.nodes;
24
25import static org.graalvm.compiler.nodeinfo.InputType.Association;
26import static org.graalvm.compiler.nodeinfo.InputType.State;
27import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_0;
28import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_1;
29import static jdk.vm.ci.code.BytecodeFrame.getPlaceholderBciName;
30import static jdk.vm.ci.code.BytecodeFrame.isPlaceholderBci;
31
32import java.util.ArrayList;
33import java.util.Collections;
34import java.util.List;
35import java.util.Map;
36import java.util.Objects;
37
38import org.graalvm.compiler.api.replacements.MethodSubstitution;
39import org.graalvm.compiler.bytecode.Bytecode;
40import org.graalvm.compiler.bytecode.Bytecodes;
41import org.graalvm.compiler.core.common.type.StampFactory;
42import org.graalvm.compiler.debug.Debug;
43import org.graalvm.compiler.debug.DebugCounter;
44import org.graalvm.compiler.debug.GraalError;
45import org.graalvm.compiler.graph.IterableNodeType;
46import org.graalvm.compiler.graph.NodeClass;
47import org.graalvm.compiler.graph.NodeInputList;
48import org.graalvm.compiler.graph.NodeSourcePosition;
49import org.graalvm.compiler.graph.iterators.NodeIterable;
50import org.graalvm.compiler.nodeinfo.InputType;
51import org.graalvm.compiler.nodeinfo.NodeInfo;
52import org.graalvm.compiler.nodeinfo.Verbosity;
53import org.graalvm.compiler.nodes.java.MonitorIdNode;
54import org.graalvm.compiler.nodes.virtual.EscapeObjectState;
55import org.graalvm.compiler.nodes.virtual.VirtualObjectNode;
56
57import jdk.vm.ci.code.BytecodeFrame;
58import jdk.vm.ci.code.CodeUtil;
59import jdk.vm.ci.meta.JavaKind;
60import jdk.vm.ci.meta.ResolvedJavaMethod;
61
62/**
63 * The {@code FrameState} class encapsulates the frame state (i.e. local variables and operand
64 * stack) at a particular point in the abstract interpretation.
65 *
66 * This can be used as debug or deoptimization information.
67 */
68@NodeInfo(nameTemplate = "@{p#code/s}:{p#bci}", cycles = CYCLES_0, size = SIZE_1)
69public final class FrameState extends VirtualState implements IterableNodeType {
70    public static final NodeClass<FrameState> TYPE = NodeClass.create(FrameState.class);
71
72    private static final DebugCounter FRAMESTATES_COUNTER = Debug.counter("FrameStateCount");
73
74    /**
75     * Marker value for the second slot of values that occupy two local variable or expression stack
76     * slots. The marker value is used by the bytecode parser, but replaced with {@code null} in the
77     * {@link #values} of the {@link FrameState}.
78     */
79    public static final ValueNode TWO_SLOT_MARKER = new TwoSlotMarker();
80
81    @NodeInfo
82    private static final class TwoSlotMarker extends ValueNode {
83        public static final NodeClass<TwoSlotMarker> TYPE = NodeClass.create(TwoSlotMarker.class);
84
85        protected TwoSlotMarker() {
86            super(TYPE, StampFactory.forKind(JavaKind.Illegal));
87        }
88    }
89
90    protected final int localsSize;
91
92    protected final int stackSize;
93
94    /**
95     * @see BytecodeFrame#rethrowException
96     */
97    protected boolean rethrowException;
98
99    protected final boolean duringCall;
100
101    @OptionalInput(value = InputType.State) FrameState outerFrameState;
102
103    /**
104     * Contains the locals, the expressions and the locked objects, in this order.
105     */
106    @OptionalInput NodeInputList<ValueNode> values;
107
108    @OptionalInput(Association) NodeInputList<MonitorIdNode> monitorIds;
109
110    @OptionalInput(State) NodeInputList<EscapeObjectState> virtualObjectMappings;
111
112    /**
113     * The bytecode index to which this frame state applies.
114     */
115    public final int bci;
116
117    /**
118     * The bytecode to which this frame state applies.
119     */
120    protected final Bytecode code;
121
122    public FrameState(FrameState outerFrameState, Bytecode code, int bci, int localsSize, int stackSize, int lockSize, boolean rethrowException, boolean duringCall,
123                    List<MonitorIdNode> monitorIds, List<EscapeObjectState> virtualObjectMappings) {
124        super(TYPE);
125        if (code != null) {
126            /*
127             * Make sure the bci is within range of the bytecodes. If the code size is 0 then allow
128             * any value, otherwise the bci must be less than the code size. Any negative value is
129             * also allowed to represent special bytecode states.
130             */
131            int codeSize = code.getCodeSize();
132            if (codeSize != 0 && bci >= codeSize) {
133                throw new GraalError("bci %d is out of range for %s %d bytes", bci, code.getMethod().format("%H.%n(%p)"), codeSize);
134            }
135        }
136        assert stackSize >= 0;
137        this.outerFrameState = outerFrameState;
138        this.code = code;
139        this.bci = bci;
140        this.localsSize = localsSize;
141        this.stackSize = stackSize;
142        this.values = new NodeInputList<>(this, localsSize + stackSize + lockSize);
143
144        if (monitorIds != null && monitorIds.size() > 0) {
145            this.monitorIds = new NodeInputList<>(this, monitorIds);
146        }
147
148        if (virtualObjectMappings != null && virtualObjectMappings.size() > 0) {
149            this.virtualObjectMappings = new NodeInputList<>(this, virtualObjectMappings);
150        }
151
152        this.rethrowException = rethrowException;
153        this.duringCall = duringCall;
154        assert !this.rethrowException || this.stackSize == 1 : "must have exception on top of the stack";
155        assert this.locksSize() == this.monitorIdCount();
156        FRAMESTATES_COUNTER.increment();
157    }
158
159    public FrameState(FrameState outerFrameState, Bytecode code, int bci, List<ValueNode> values, int localsSize, int stackSize, boolean rethrowException, boolean duringCall,
160                    List<MonitorIdNode> monitorIds, List<EscapeObjectState> virtualObjectMappings) {
161        this(outerFrameState, code, bci, localsSize, stackSize, values.size() - localsSize - stackSize, rethrowException, duringCall, monitorIds, virtualObjectMappings);
162        for (int i = 0; i < values.size(); ++i) {
163            this.values.initialize(i, values.get(i));
164        }
165    }
166
167    private void verifyAfterExceptionState() {
168        if (this.bci == BytecodeFrame.AFTER_EXCEPTION_BCI) {
169            assert this.outerFrameState == null;
170            for (int i = 0; i < this.localsSize; i++) {
171                assertTrue(this.values.get(i) == null, "locals should be null in AFTER_EXCEPTION_BCI state");
172            }
173        }
174    }
175
176    public FrameState(int bci) {
177        this(null, null, bci, 0, 0, 0, false, false, null, Collections.<EscapeObjectState> emptyList());
178        assert bci == BytecodeFrame.BEFORE_BCI || bci == BytecodeFrame.AFTER_BCI || bci == BytecodeFrame.AFTER_EXCEPTION_BCI || bci == BytecodeFrame.UNKNOWN_BCI ||
179                        bci == BytecodeFrame.INVALID_FRAMESTATE_BCI;
180    }
181
182    /**
183     * Creates a placeholder frame state with a single element on the stack representing a return
184     * value. This allows the parsing of an intrinsic to communicate the returned value in a
185     * {@link StateSplit#stateAfter() stateAfter} to the inlining call site.
186     *
187     * @param bci this must be {@link BytecodeFrame#AFTER_BCI}
188     */
189    public FrameState(int bci, ValueNode returnValue) {
190        this(null, null, bci, 0, returnValue.getStackKind().getSlotCount(), 0, false, false, null, Collections.<EscapeObjectState> emptyList());
191        assert bci == BytecodeFrame.AFTER_BCI;
192        this.values.initialize(0, returnValue);
193    }
194
195    public FrameState(FrameState outerFrameState, Bytecode code, int bci, ValueNode[] locals, ValueNode[] stack, int stackSize, ValueNode[] locks, List<MonitorIdNode> monitorIds,
196                    boolean rethrowException, boolean duringCall) {
197        this(outerFrameState, code, bci, locals.length, stackSize, locks.length, rethrowException, duringCall, monitorIds, Collections.<EscapeObjectState> emptyList());
198        createValues(locals, stack, locks);
199    }
200
201    private void createValues(ValueNode[] locals, ValueNode[] stack, ValueNode[] locks) {
202        int index = 0;
203        for (int i = 0; i < locals.length; ++i) {
204            ValueNode value = locals[i];
205            if (value == TWO_SLOT_MARKER) {
206                value = null;
207            }
208            this.values.initialize(index++, value);
209        }
210        for (int i = 0; i < stackSize; ++i) {
211            ValueNode value = stack[i];
212            if (value == TWO_SLOT_MARKER) {
213                value = null;
214            }
215            this.values.initialize(index++, value);
216        }
217        for (int i = 0; i < locks.length; ++i) {
218            ValueNode value = locks[i];
219            assert value != TWO_SLOT_MARKER;
220            this.values.initialize(index++, value);
221        }
222    }
223
224    public NodeInputList<ValueNode> values() {
225        return values;
226    }
227
228    public NodeInputList<MonitorIdNode> monitorIds() {
229        return monitorIds;
230    }
231
232    public FrameState outerFrameState() {
233        return outerFrameState;
234    }
235
236    public void setOuterFrameState(FrameState x) {
237        assert x == null || !x.isDeleted();
238        updateUsages(this.outerFrameState, x);
239        this.outerFrameState = x;
240    }
241
242    public static NodeSourcePosition toSourcePosition(FrameState fs) {
243        if (fs == null) {
244            return null;
245        }
246        return new NodeSourcePosition(null, toSourcePosition(fs.outerFrameState()), fs.code.getMethod(), fs.bci);
247    }
248
249    /**
250     * @see BytecodeFrame#rethrowException
251     */
252    public boolean rethrowException() {
253        return rethrowException;
254    }
255
256    public boolean duringCall() {
257        return duringCall;
258    }
259
260    public Bytecode getCode() {
261        return code;
262    }
263
264    public ResolvedJavaMethod getMethod() {
265        return code == null ? null : code.getMethod();
266    }
267
268    /**
269     * Determines if this frame state can be converted to a {@link BytecodeFrame}.
270     *
271     * Since a {@link BytecodeFrame} encodes {@link #getMethod()} and {@link #bci}, it does not
272     * preserve {@link #getCode()}. {@link #bci} is only guaranteed to be valid in terms of
273     * {@code getCode().getCode()} which may be different from {@code getMethod().getCode()} if the
274     * latter has been subject to instrumentation.
275     */
276    public boolean canProduceBytecodeFrame() {
277        return code != null && code.getCode() == code.getMethod().getCode();
278    }
279
280    public void addVirtualObjectMapping(EscapeObjectState virtualObject) {
281        if (virtualObjectMappings == null) {
282            virtualObjectMappings = new NodeInputList<>(this);
283        }
284        virtualObjectMappings.add(virtualObject);
285    }
286
287    public int virtualObjectMappingCount() {
288        if (virtualObjectMappings == null) {
289            return 0;
290        }
291        return virtualObjectMappings.size();
292    }
293
294    public EscapeObjectState virtualObjectMappingAt(int i) {
295        return virtualObjectMappings.get(i);
296    }
297
298    public NodeInputList<EscapeObjectState> virtualObjectMappings() {
299        return virtualObjectMappings;
300    }
301
302    /**
303     * Gets a copy of this frame state.
304     */
305    public FrameState duplicate(int newBci) {
306        return graph().add(new FrameState(outerFrameState(), code, newBci, values, localsSize, stackSize, rethrowException, duringCall, monitorIds, virtualObjectMappings));
307    }
308
309    /**
310     * Gets a copy of this frame state.
311     */
312    public FrameState duplicate() {
313        return duplicate(bci);
314    }
315
316    /**
317     * Duplicates a FrameState, along with a deep copy of all connected VirtualState (outer
318     * FrameStates, VirtualObjectStates, ...).
319     */
320    @Override
321    public FrameState duplicateWithVirtualState() {
322        FrameState newOuterFrameState = outerFrameState();
323        if (newOuterFrameState != null) {
324            newOuterFrameState = newOuterFrameState.duplicateWithVirtualState();
325        }
326        ArrayList<EscapeObjectState> newVirtualMappings = null;
327        if (virtualObjectMappings != null) {
328            newVirtualMappings = new ArrayList<>(virtualObjectMappings.size());
329            for (EscapeObjectState state : virtualObjectMappings) {
330                newVirtualMappings.add(state.duplicateWithVirtualState());
331            }
332        }
333        return graph().add(new FrameState(newOuterFrameState, code, bci, values, localsSize, stackSize, rethrowException, duringCall, monitorIds, newVirtualMappings));
334    }
335
336    /**
337     * Creates a copy of this frame state with one stack element of type {@code popKind} popped from
338     * the stack.
339     */
340    public FrameState duplicateModifiedDuringCall(int newBci, JavaKind popKind) {
341        return duplicateModified(graph(), newBci, rethrowException, true, popKind, null, null);
342    }
343
344    public FrameState duplicateModifiedBeforeCall(int newBci, JavaKind popKind, JavaKind[] pushedSlotKinds, ValueNode[] pushedValues) {
345        return duplicateModified(graph(), newBci, rethrowException, false, popKind, pushedSlotKinds, pushedValues);
346    }
347
348    /**
349     * Creates a copy of this frame state with one stack element of type {@code popKind} popped from
350     * the stack and the values in {@code pushedValues} pushed on the stack. The
351     * {@code pushedValues} will be formatted correctly in slot encoding: a long or double will be
352     * followed by a null slot.
353     */
354    public FrameState duplicateModified(int newBci, boolean newRethrowException, JavaKind popKind, JavaKind[] pushedSlotKinds, ValueNode[] pushedValues) {
355        return duplicateModified(graph(), newBci, newRethrowException, duringCall, popKind, pushedSlotKinds, pushedValues);
356    }
357
358    public FrameState duplicateModified(int newBci, boolean newRethrowException, boolean newDuringCall, JavaKind popKind, JavaKind[] pushedSlotKinds, ValueNode[] pushedValues) {
359        return duplicateModified(graph(), newBci, newRethrowException, newDuringCall, popKind, pushedSlotKinds, pushedValues);
360    }
361
362    /**
363     * Creates a copy of this frame state with the top of stack replaced with with
364     * {@code pushedValue} which must be of type {@code popKind}.
365     */
366    public FrameState duplicateModified(JavaKind popKind, JavaKind pushedSlotKind, ValueNode pushedValue) {
367        assert pushedValue != null && pushedValue.getStackKind() == popKind;
368        return duplicateModified(graph(), bci, rethrowException, duringCall, popKind, new JavaKind[]{pushedSlotKind}, new ValueNode[]{pushedValue});
369    }
370
371    /**
372     * Creates a copy of this frame state with one stack element of type popKind popped from the
373     * stack and the values in pushedValues pushed on the stack. The pushedValues will be formatted
374     * correctly in slot encoding: a long or double will be followed by a null slot. The bci will be
375     * changed to newBci.
376     */
377    public FrameState duplicateModified(StructuredGraph graph, int newBci, boolean newRethrowException, boolean newDuringCall, JavaKind popKind, JavaKind[] pushedSlotKinds, ValueNode[] pushedValues) {
378        ArrayList<ValueNode> copy;
379        if (newRethrowException && !rethrowException && popKind == JavaKind.Void) {
380            assert popKind == JavaKind.Void;
381            copy = new ArrayList<>(values.subList(0, localsSize));
382        } else {
383            copy = new ArrayList<>(values.subList(0, localsSize + stackSize));
384            if (popKind != JavaKind.Void) {
385                if (stackAt(stackSize() - 1) == null) {
386                    copy.remove(copy.size() - 1);
387                }
388                ValueNode lastSlot = copy.get(copy.size() - 1);
389                assert lastSlot.getStackKind() == popKind.getStackKind();
390                copy.remove(copy.size() - 1);
391            }
392        }
393        if (pushedValues != null) {
394            assert pushedSlotKinds.length == pushedValues.length;
395            for (int i = 0; i < pushedValues.length; i++) {
396                copy.add(pushedValues[i]);
397                if (pushedSlotKinds[i].needsTwoSlots()) {
398                    copy.add(null);
399                }
400            }
401        }
402        int newStackSize = copy.size() - localsSize;
403        copy.addAll(values.subList(localsSize + stackSize, values.size()));
404
405        assert checkStackDepth(bci, stackSize, duringCall, rethrowException, newBci, newStackSize, newDuringCall, newRethrowException);
406        return graph.add(new FrameState(outerFrameState(), code, newBci, copy, localsSize, newStackSize, newRethrowException, newDuringCall, monitorIds, virtualObjectMappings));
407    }
408
409    /**
410     * Perform a few sanity checks on the transformation of the stack state. The current expectation
411     * is that a stateAfter is being transformed into a stateDuring, so the stack depth may change.
412     */
413    private boolean checkStackDepth(int oldBci, int oldStackSize, boolean oldDuringCall, boolean oldRethrowException, int newBci, int newStackSize, boolean newDuringCall,
414                    boolean newRethrowException) {
415        if (BytecodeFrame.isPlaceholderBci(oldBci)) {
416            return true;
417        }
418        /*
419         * It would be nice to have a complete check of the shape of the FrameState based on a
420         * dataflow of the bytecodes but for now just check for obvious expression stack depth
421         * mistakes.
422         */
423        byte[] codes = code.getCode();
424        if (codes == null) {
425            /* Graph was constructed manually. */
426            return true;
427        }
428        byte newCode = codes[newBci];
429        if (oldBci == newBci) {
430            assert oldStackSize == newStackSize || oldDuringCall != newDuringCall || oldRethrowException != newRethrowException : "bci is unchanged, stack depth shouldn't change";
431        } else {
432            byte oldCode = codes[oldBci];
433            assert Bytecodes.lengthOf(newCode) + newBci == oldBci || Bytecodes.lengthOf(oldCode) + oldBci == newBci : "expecting roll back or forward";
434        }
435        return true;
436    }
437
438    /**
439     * Gets the size of the local variables.
440     */
441    public int localsSize() {
442        return localsSize;
443    }
444
445    /**
446     * Gets the current size (height) of the stack.
447     */
448    public int stackSize() {
449        return stackSize;
450    }
451
452    /**
453     * Gets the number of locked monitors in this frame state.
454     */
455    public int locksSize() {
456        return values.size() - localsSize - stackSize;
457    }
458
459    /**
460     * Gets the number of locked monitors in this frame state and all {@linkplain #outerFrameState()
461     * outer} frame states.
462     */
463    public int nestedLockDepth() {
464        int depth = locksSize();
465        for (FrameState outer = outerFrameState(); outer != null; outer = outer.outerFrameState()) {
466            depth += outer.locksSize();
467        }
468        return depth;
469    }
470
471    /**
472     * Gets the value in the local variables at the specified index.
473     *
474     * @param i the index into the locals
475     * @return the instruction that produced the value for the specified local
476     */
477    public ValueNode localAt(int i) {
478        assert i >= 0 && i < localsSize : "local variable index out of range: " + i;
479        return values.get(i);
480    }
481
482    /**
483     * Get the value on the stack at the specified stack index.
484     *
485     * @param i the index into the stack, with {@code 0} being the bottom of the stack
486     * @return the instruction at the specified position in the stack
487     */
488    public ValueNode stackAt(int i) {
489        assert i >= 0 && i < stackSize;
490        return values.get(localsSize + i);
491    }
492
493    /**
494     * Get the monitor owner at the specified index.
495     *
496     * @param i the index into the list of locked monitors.
497     * @return the lock owner at the given index.
498     */
499    public ValueNode lockAt(int i) {
500        assert i >= 0 && i < locksSize();
501        return values.get(localsSize + stackSize + i);
502    }
503
504    /**
505     * Get the MonitorIdNode that corresponds to the locked object at the specified index.
506     */
507    public MonitorIdNode monitorIdAt(int i) {
508        assert monitorIds != null && i >= 0 && i < locksSize();
509        return monitorIds.get(i);
510    }
511
512    public int monitorIdCount() {
513        if (monitorIds == null) {
514            return 0;
515        } else {
516            return monitorIds.size();
517        }
518    }
519
520    public NodeIterable<FrameState> innerFrameStates() {
521        return usages().filter(FrameState.class);
522    }
523
524    private static String toString(FrameState frameState) {
525        StringBuilder sb = new StringBuilder();
526        String nl = CodeUtil.NEW_LINE;
527        FrameState fs = frameState;
528        while (fs != null) {
529            Bytecode.appendLocation(sb, fs.getCode(), fs.bci);
530            if (BytecodeFrame.isPlaceholderBci(fs.bci)) {
531                sb.append("//").append(getPlaceholderBciName(fs.bci));
532            }
533            sb.append(nl);
534            sb.append("locals: [");
535            for (int i = 0; i < fs.localsSize(); i++) {
536                sb.append(i == 0 ? "" : ", ").append(fs.localAt(i) == null ? "_" : fs.localAt(i).toString(Verbosity.Id));
537            }
538            sb.append("]").append(nl).append("stack: [");
539            for (int i = 0; i < fs.stackSize(); i++) {
540                sb.append(i == 0 ? "" : ", ").append(fs.stackAt(i) == null ? "_" : fs.stackAt(i).toString(Verbosity.Id));
541            }
542            sb.append("]").append(nl).append("locks: [");
543            for (int i = 0; i < fs.locksSize(); i++) {
544                sb.append(i == 0 ? "" : ", ").append(fs.lockAt(i) == null ? "_" : fs.lockAt(i).toString(Verbosity.Id));
545            }
546            sb.append(']').append(nl);
547            fs = fs.outerFrameState();
548        }
549        return sb.toString();
550    }
551
552    @Override
553    public String toString(Verbosity verbosity) {
554        if (verbosity == Verbosity.Debugger) {
555            return toString(this);
556        } else if (verbosity == Verbosity.Name) {
557            String res = super.toString(Verbosity.Name) + "@" + bci;
558            if (BytecodeFrame.isPlaceholderBci(bci)) {
559                res += "[" + getPlaceholderBciName(bci) + "]";
560            }
561            return res;
562        } else {
563            return super.toString(verbosity);
564        }
565    }
566
567    @Override
568    public Map<Object, Object> getDebugProperties(Map<Object, Object> map) {
569        Map<Object, Object> properties = super.getDebugProperties(map);
570        if (code != null) {
571            // properties.put("method", MetaUtil.format("%H.%n(%p):%r", method));
572            StackTraceElement ste = code.asStackTraceElement(bci);
573            if (ste.getFileName() != null && ste.getLineNumber() >= 0) {
574                properties.put("sourceFile", ste.getFileName());
575                properties.put("sourceLine", ste.getLineNumber());
576            }
577        }
578        if (isPlaceholderBci(bci)) {
579            properties.put("bci", getPlaceholderBciName(bci));
580        }
581        properties.put("locksSize", values.size() - stackSize - localsSize);
582        return properties;
583    }
584
585    @Override
586    public boolean verify() {
587        if (virtualObjectMappingCount() > 0) {
588            for (EscapeObjectState state : virtualObjectMappings()) {
589                assertTrue(state != null, "must be non-null");
590            }
591        }
592        /*
593         * The outermost FrameState should have a method that matches StructuredGraph.method except
594         * when it's a substitution or it's null.
595         */
596        assertTrue(outerFrameState != null || graph() == null || graph().method() == null || code == null || Objects.equals(code.getMethod(), graph().method()) ||
597                        graph().method().getAnnotation(MethodSubstitution.class) != null, "wrong outerFrameState %s != %s", code == null ? "null" : code.getMethod(), graph().method());
598        if (monitorIds() != null && monitorIds().size() > 0) {
599            int depth = outerLockDepth();
600            for (MonitorIdNode monitor : monitorIds()) {
601                assertTrue(monitor.getLockDepth() == depth++, "wrong depth");
602            }
603        }
604        assertTrue(locksSize() == monitorIdCount(), "mismatch in number of locks");
605        for (ValueNode value : values) {
606            assertTrue(value == null || !value.isDeleted(), "frame state must not contain deleted nodes: %s", value);
607            assertTrue(value == null || value instanceof VirtualObjectNode || (value.getStackKind() != JavaKind.Void), "unexpected value: %s", value);
608        }
609        verifyAfterExceptionState();
610        return super.verify();
611    }
612
613    private int outerLockDepth() {
614        int depth = 0;
615        FrameState outer = outerFrameState;
616        while (outer != null) {
617            depth += outer.monitorIdCount();
618            outer = outer.outerFrameState;
619        }
620        return depth;
621    }
622
623    @Override
624    public void applyToNonVirtual(NodeClosure<? super ValueNode> closure) {
625        for (ValueNode value : values) {
626            if (value != null) {
627                closure.apply(this, value);
628            }
629        }
630
631        if (monitorIds != null) {
632            for (MonitorIdNode monitorId : monitorIds) {
633                if (monitorId != null) {
634                    closure.apply(this, monitorId);
635                }
636            }
637        }
638
639        if (virtualObjectMappings != null) {
640            for (EscapeObjectState state : virtualObjectMappings) {
641                state.applyToNonVirtual(closure);
642            }
643        }
644
645        if (outerFrameState() != null) {
646            outerFrameState().applyToNonVirtual(closure);
647        }
648    }
649
650    @Override
651    public void applyToVirtual(VirtualClosure closure) {
652        closure.apply(this);
653        if (virtualObjectMappings != null) {
654            for (EscapeObjectState state : virtualObjectMappings) {
655                state.applyToVirtual(closure);
656            }
657        }
658        if (outerFrameState() != null) {
659            outerFrameState().applyToVirtual(closure);
660        }
661    }
662
663    @Override
664    public boolean isPartOfThisState(VirtualState state) {
665        if (state == this) {
666            return true;
667        }
668        if (outerFrameState() != null && outerFrameState().isPartOfThisState(state)) {
669            return true;
670        }
671        if (virtualObjectMappings != null) {
672            for (EscapeObjectState objectState : virtualObjectMappings) {
673                if (objectState.isPartOfThisState(state)) {
674                    return true;
675                }
676            }
677        }
678        return false;
679    }
680}
681