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