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