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