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 package org.graalvm.compiler.nodes;
  24 
  25 import static org.graalvm.compiler.nodeinfo.InputType.Association;
  26 import static org.graalvm.compiler.nodeinfo.InputType.State;
  27 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_0;
  28 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_1;
  29 import static jdk.vm.ci.code.BytecodeFrame.getPlaceholderBciName;
  30 import static jdk.vm.ci.code.BytecodeFrame.isPlaceholderBci;
  31 
  32 import java.util.ArrayList;
  33 import java.util.Collections;
  34 import java.util.List;
  35 import java.util.Map;
  36 import java.util.Objects;
  37 
  38 import org.graalvm.compiler.api.replacements.MethodSubstitution;
  39 import org.graalvm.compiler.bytecode.Bytecode;
  40 import org.graalvm.compiler.bytecode.Bytecodes;
  41 import org.graalvm.compiler.core.common.type.StampFactory;
  42 import org.graalvm.compiler.debug.Debug;
  43 import org.graalvm.compiler.debug.DebugCounter;
  44 import org.graalvm.compiler.debug.GraalError;
  45 import org.graalvm.compiler.graph.IterableNodeType;
  46 import org.graalvm.compiler.graph.NodeClass;
  47 import org.graalvm.compiler.graph.NodeInputList;
  48 import org.graalvm.compiler.graph.NodeSourcePosition;
  49 import org.graalvm.compiler.graph.iterators.NodeIterable;
  50 import org.graalvm.compiler.nodeinfo.InputType;
  51 import org.graalvm.compiler.nodeinfo.NodeInfo;
  52 import org.graalvm.compiler.nodeinfo.Verbosity;
  53 import org.graalvm.compiler.nodes.java.MonitorIdNode;
  54 import org.graalvm.compiler.nodes.virtual.EscapeObjectState;
  55 import org.graalvm.compiler.nodes.virtual.VirtualObjectNode;
  56 
  57 import jdk.vm.ci.code.BytecodeFrame;
  58 import jdk.vm.ci.code.CodeUtil;
  59 import jdk.vm.ci.meta.JavaKind;
  60 import 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)
  69 public 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(graph().method(), code.getMethod()) ||
 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 }