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