1 /* 2 * Copyright (c) 2009, 2019, 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 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 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 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 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 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 */ 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 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 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 227 public NodeInputList<ValueNode> values() { 228 return values; 229 } 230 231 public NodeInputList<MonitorIdNode> monitorIds() { 232 return monitorIds; 233 } 234 235 public FrameState outerFrameState() { 236 return outerFrameState; 237 } 238 239 public void setOuterFrameState(FrameState x) { 240 assert x == null || (!x.isDeleted() && x.bci >= 0) : "cannot set outer frame state of:\n" + toString(this) + 241 "\nto:\n" + toString(x) + "\nisDeleted=" + x.isDeleted(); 242 updateUsages(this.outerFrameState, x); 243 this.outerFrameState = x; 244 } 245 246 public static NodeSourcePosition toSourcePosition(FrameState fs) { 247 if (fs == null) { 248 return null; 249 } 250 return new NodeSourcePosition(toSourcePosition(fs.outerFrameState()), fs.code.getMethod(), fs.bci); 251 } 252 253 /** 254 * @see BytecodeFrame#rethrowException 255 */ 256 public boolean rethrowException() { 257 return rethrowException; 258 } 259 260 public boolean duringCall() { 261 return duringCall; 262 } 263 264 public Bytecode getCode() { 265 return code; 266 } 267 268 public ResolvedJavaMethod getMethod() { 269 return code == null ? null : code.getMethod(); 270 } 271 272 /** 273 * Determines if this frame state can be converted to a {@link BytecodeFrame}. 274 * 275 * Since a {@link BytecodeFrame} encodes {@link #getMethod()} and {@link #bci}, it does not 276 * preserve {@link #getCode()}. {@link #bci} is only guaranteed to be valid in terms of 277 * {@code getCode().getCode()} which may be different from {@code getMethod().getCode()} if the 278 * latter has been subject to instrumentation. 279 */ 280 public boolean canProduceBytecodeFrame() { 281 return code != null && code.getCode() == code.getMethod().getCode(); 282 } 283 284 public void addVirtualObjectMapping(EscapeObjectState virtualObject) { 285 if (virtualObjectMappings == null) { 286 virtualObjectMappings = new NodeInputList<>(this); 287 } 288 virtualObjectMappings.add(virtualObject); 289 } 290 291 public int virtualObjectMappingCount() { 292 if (virtualObjectMappings == null) { 293 return 0; 294 } 295 return virtualObjectMappings.size(); 296 } 297 298 public EscapeObjectState virtualObjectMappingAt(int i) { 299 return virtualObjectMappings.get(i); 300 } 301 302 public NodeInputList<EscapeObjectState> virtualObjectMappings() { 303 return virtualObjectMappings; 304 } 305 306 /** 307 * Gets a copy of this frame state. 308 */ 309 public FrameState duplicate(int newBci) { 310 return graph().add(new FrameState(outerFrameState(), code, newBci, values, localsSize, stackSize, rethrowException, duringCall, monitorIds, virtualObjectMappings)); 311 } 312 313 /** 314 * Gets a copy of this frame state. 315 */ 316 public FrameState duplicate() { 317 return duplicate(bci); 318 } 319 320 /** 321 * Duplicates a FrameState, along with a deep copy of all connected VirtualState (outer 322 * FrameStates, VirtualObjectStates, ...). 323 */ 324 @Override 325 public FrameState duplicateWithVirtualState() { 326 FrameState newOuterFrameState = outerFrameState(); 327 if (newOuterFrameState != null) { 328 newOuterFrameState = newOuterFrameState.duplicateWithVirtualState(); 329 } 330 ArrayList<EscapeObjectState> newVirtualMappings = null; 331 if (virtualObjectMappings != null) { 332 newVirtualMappings = new ArrayList<>(virtualObjectMappings.size()); 333 for (EscapeObjectState state : virtualObjectMappings) { 334 newVirtualMappings.add(state.duplicateWithVirtualState()); 335 } 336 } 337 return graph().add(new FrameState(newOuterFrameState, code, bci, values, localsSize, stackSize, rethrowException, duringCall, monitorIds, newVirtualMappings)); 338 } 339 340 /** 341 * Creates a copy of this frame state with one stack element of type {@code popKind} popped from 342 * the stack. 343 */ 344 public FrameState duplicateModifiedDuringCall(int newBci, JavaKind popKind) { 345 return duplicateModified(graph(), newBci, rethrowException, true, popKind, null, null); 346 } 347 348 public FrameState duplicateModifiedBeforeCall(int newBci, JavaKind popKind, JavaKind[] pushedSlotKinds, ValueNode[] pushedValues) { 349 return duplicateModified(graph(), newBci, rethrowException, false, popKind, pushedSlotKinds, pushedValues); 350 } 351 352 /** 353 * Creates a copy of this frame state with one stack element of type {@code popKind} popped from 354 * the stack and the values in {@code pushedValues} pushed on the stack. The 355 * {@code pushedValues} will be formatted correctly in slot encoding: a long or double will be 356 * followed by a null slot. 357 */ 358 public FrameState duplicateModified(int newBci, boolean newRethrowException, JavaKind popKind, JavaKind[] pushedSlotKinds, ValueNode[] pushedValues) { 359 return duplicateModified(graph(), newBci, newRethrowException, duringCall, popKind, pushedSlotKinds, pushedValues); 360 } 361 362 public FrameState duplicateModified(int newBci, boolean newRethrowException, boolean newDuringCall, JavaKind popKind, JavaKind[] pushedSlotKinds, ValueNode[] pushedValues) { 363 return duplicateModified(graph(), newBci, newRethrowException, newDuringCall, popKind, pushedSlotKinds, pushedValues); 364 } 365 366 /** 367 * Creates a copy of this frame state with the top of stack replaced with with 368 * {@code pushedValue} which must be of type {@code popKind}. 369 */ 370 public FrameState duplicateModified(JavaKind popKind, JavaKind pushedSlotKind, ValueNode pushedValue) { 371 assert pushedValue != null && pushedValue.getStackKind() == popKind; 372 return duplicateModified(graph(), bci, rethrowException, duringCall, popKind, new JavaKind[]{pushedSlotKind}, new ValueNode[]{pushedValue}); 373 } 374 375 /** 376 * Creates a copy of this frame state with one stack element of type popKind popped from the 377 * stack and the values in pushedValues pushed on the stack. The pushedValues will be formatted 378 * correctly in slot encoding: a long or double will be followed by a null slot. The bci will be 379 * changed to newBci. 380 */ 381 public FrameState duplicateModified(StructuredGraph graph, int newBci, boolean newRethrowException, boolean newDuringCall, JavaKind popKind, JavaKind[] pushedSlotKinds, ValueNode[] pushedValues) { 382 ArrayList<ValueNode> copy; 383 if (newRethrowException && !rethrowException && popKind == JavaKind.Void) { 384 assert popKind == JavaKind.Void; 385 copy = new ArrayList<>(values.subList(0, localsSize)); 386 } else { 387 copy = new ArrayList<>(values.subList(0, localsSize + stackSize)); 388 if (popKind != JavaKind.Void) { 389 if (stackAt(stackSize() - 1) == null) { 390 copy.remove(copy.size() - 1); 391 } 392 ValueNode lastSlot = copy.get(copy.size() - 1); 393 assert lastSlot.getStackKind() == popKind.getStackKind(); 394 copy.remove(copy.size() - 1); 395 } 396 } 397 if (pushedValues != null) { 398 assert pushedSlotKinds.length == pushedValues.length; 399 for (int i = 0; i < pushedValues.length; i++) { 400 copy.add(pushedValues[i]); 401 if (pushedSlotKinds[i].needsTwoSlots()) { 402 copy.add(null); 403 } 404 } 405 } 406 int newStackSize = copy.size() - localsSize; 407 copy.addAll(values.subList(localsSize + stackSize, values.size())); 408 409 assert checkStackDepth(bci, stackSize, duringCall, rethrowException, newBci, newStackSize, newDuringCall, newRethrowException); 410 return graph.add(new FrameState(outerFrameState(), code, newBci, copy, localsSize, newStackSize, newRethrowException, newDuringCall, monitorIds, virtualObjectMappings)); 411 } 412 413 /** 414 * Perform a few sanity checks on the transformation of the stack state. The current expectation 415 * is that a stateAfter is being transformed into a stateDuring, so the stack depth may change. 416 */ 417 private boolean checkStackDepth(int oldBci, int oldStackSize, boolean oldDuringCall, boolean oldRethrowException, int newBci, int newStackSize, boolean newDuringCall, 418 boolean newRethrowException) { 419 if (BytecodeFrame.isPlaceholderBci(oldBci)) { 420 return true; 421 } 422 /* 423 * It would be nice to have a complete check of the shape of the FrameState based on a 424 * dataflow of the bytecodes but for now just check for obvious expression stack depth 425 * mistakes. 426 */ 427 byte[] codes = code.getCode(); 428 if (codes == null) { 429 /* Graph was constructed manually. */ 430 return true; 431 } 432 byte newCode = codes[newBci]; 433 if (oldBci == newBci) { 434 assert oldStackSize == newStackSize || oldDuringCall != newDuringCall || oldRethrowException != newRethrowException : "bci is unchanged, stack depth shouldn't change"; 435 } else { 436 byte oldCode = codes[oldBci]; 437 assert Bytecodes.lengthOf(newCode) + newBci == oldBci || Bytecodes.lengthOf(oldCode) + oldBci == newBci : "expecting roll back or forward"; 438 } 439 return true; 440 } 441 442 /** 443 * Gets the size of the local variables. 444 */ 445 public int localsSize() { 446 return localsSize; 447 } 448 449 /** 450 * Gets the current size (height) of the stack. 451 */ 452 public int stackSize() { 453 return stackSize; 454 } 455 456 /** 457 * Gets the number of locked monitors in this frame state. 458 */ 459 public int locksSize() { 460 return values.size() - localsSize - stackSize; 461 } 462 463 /** 464 * Gets the number of locked monitors in this frame state and all {@linkplain #outerFrameState() 465 * outer} frame states. 466 */ 467 public int nestedLockDepth() { 468 int depth = locksSize(); 469 for (FrameState outer = outerFrameState(); outer != null; outer = outer.outerFrameState()) { 470 depth += outer.locksSize(); 471 } 472 return depth; 473 } 474 475 /** 476 * Gets the value in the local variables at the specified index. 477 * 478 * @param i the index into the locals 479 * @return the instruction that produced the value for the specified local 480 */ 481 public ValueNode localAt(int i) { 482 assert i >= 0 && i < localsSize : "local variable index out of range: " + i; 483 return values.get(i); 484 } 485 486 /** 487 * Get the value on the stack at the specified stack index. 488 * 489 * @param i the index into the stack, with {@code 0} being the bottom of the stack 490 * @return the instruction at the specified position in the stack 491 */ 492 public ValueNode stackAt(int i) { 493 assert i >= 0 && i < stackSize; 494 return values.get(localsSize + i); 495 } 496 497 /** 498 * Get the monitor owner at the specified index. 499 * 500 * @param i the index into the list of locked monitors. 501 * @return the lock owner at the given index. 502 */ 503 public ValueNode lockAt(int i) { 504 assert i >= 0 && i < locksSize(); 505 return values.get(localsSize + stackSize + i); 506 } 507 508 /** 509 * Get the MonitorIdNode that corresponds to the locked object at the specified index. 510 */ 511 public MonitorIdNode monitorIdAt(int i) { 512 assert monitorIds != null && i >= 0 && i < locksSize(); 513 return monitorIds.get(i); 514 } 515 516 public int monitorIdCount() { 517 if (monitorIds == null) { 518 return 0; 519 } else { 520 return monitorIds.size(); 521 } 522 } 523 524 public NodeIterable<FrameState> innerFrameStates() { 525 return usages().filter(FrameState.class); 526 } 527 528 private static String toString(FrameState frameState) { 529 StringBuilder sb = new StringBuilder(); 530 String nl = CodeUtil.NEW_LINE; 531 FrameState fs = frameState; 532 while (fs != null) { 533 Bytecode.appendLocation(sb, fs.getCode(), fs.bci); 534 if (BytecodeFrame.isPlaceholderBci(fs.bci)) { 535 sb.append("//").append(getPlaceholderBciName(fs.bci)); 536 } 537 sb.append(nl); 538 sb.append("locals: ["); 539 for (int i = 0; i < fs.localsSize(); i++) { 540 sb.append(i == 0 ? "" : ", ").append(fs.localAt(i) == null ? "_" : fs.localAt(i).toString(Verbosity.Id)); 541 } 542 sb.append("]").append(nl).append("stack: ["); 543 for (int i = 0; i < fs.stackSize(); i++) { 544 sb.append(i == 0 ? "" : ", ").append(fs.stackAt(i) == null ? "_" : fs.stackAt(i).toString(Verbosity.Id)); 545 } 546 sb.append("]").append(nl).append("locks: ["); 547 for (int i = 0; i < fs.locksSize(); i++) { 548 sb.append(i == 0 ? "" : ", ").append(fs.lockAt(i) == null ? "_" : fs.lockAt(i).toString(Verbosity.Id)); 549 } 550 sb.append(']').append(nl); 551 fs = fs.outerFrameState(); 552 } 553 return sb.toString(); 554 } 555 556 @Override 557 public String toString(Verbosity verbosity) { 558 if (verbosity == Verbosity.Debugger) { 559 return toString(this); 560 } else if (verbosity == Verbosity.Name) { 561 String res = super.toString(Verbosity.Name) + "@" + bci; 562 if (BytecodeFrame.isPlaceholderBci(bci)) { 563 res += "[" + getPlaceholderBciName(bci) + "]"; 564 } 565 return res; 566 } else { 567 return super.toString(verbosity); 568 } 569 } 570 571 @Override 572 public Map<Object, Object> getDebugProperties(Map<Object, Object> map) { 573 Map<Object, Object> properties = super.getDebugProperties(map); 574 if (code != null) { 575 // properties.put("method", MetaUtil.format("%H.%n(%p):%r", method)); 576 StackTraceElement ste = code.asStackTraceElement(bci); 577 if (ste.getFileName() != null && ste.getLineNumber() >= 0) { 578 properties.put("sourceFile", ste.getFileName()); 579 properties.put("sourceLine", ste.getLineNumber()); 580 } 581 } 582 if (isPlaceholderBci(bci)) { 583 properties.put("bci", getPlaceholderBciName(bci)); 584 } 585 properties.put("locksSize", values.size() - stackSize - localsSize); 586 return properties; 587 } 588 589 @Override 590 public boolean verify() { 591 if (virtualObjectMappingCount() > 0) { 592 for (EscapeObjectState state : virtualObjectMappings()) { 593 assertTrue(state != null, "must be non-null"); 594 } 595 } 596 /* 597 * The outermost FrameState should have a method that matches StructuredGraph.method except 598 * when it's a substitution or it's null. 599 */ 600 assertTrue(outerFrameState != null || graph() == null || graph().method() == null || code == null || Objects.equals(graph().method(), code.getMethod()) || 601 graph().method().getAnnotation(MethodSubstitution.class) != null, "wrong outerFrameState %s != %s", code == null ? "null" : code.getMethod(), graph().method()); 602 if (monitorIds() != null && monitorIds().size() > 0) { 603 int depth = outerLockDepth(); 604 for (MonitorIdNode monitor : monitorIds()) { 605 assertTrue(monitor.getLockDepth() == depth++, "wrong depth"); 606 } 607 } 608 assertTrue(locksSize() == monitorIdCount(), "mismatch in number of locks"); 609 for (ValueNode value : values) { 610 assertTrue(value == null || !value.isDeleted(), "frame state must not contain deleted nodes: %s", value); 611 assertTrue(value == null || value instanceof VirtualObjectNode || (value.getStackKind() != JavaKind.Void), "unexpected value: %s", value); 612 } 613 verifyAfterExceptionState(); 614 return super.verify(); 615 } 616 617 private int outerLockDepth() { 618 int depth = 0; 619 FrameState outer = outerFrameState; 620 while (outer != null) { 621 depth += outer.monitorIdCount(); 622 outer = outer.outerFrameState; 623 } 624 return depth; 625 } 626 627 @Override 628 public void applyToNonVirtual(NodeClosure<? super ValueNode> closure) { 629 for (ValueNode value : values) { 630 if (value != null) { 631 closure.apply(this, value); 632 } 633 } 634 635 if (monitorIds != null) { 636 for (MonitorIdNode monitorId : monitorIds) { 637 if (monitorId != null) { 638 closure.apply(this, monitorId); 639 } 640 } 641 } 642 643 if (virtualObjectMappings != null) { 644 for (EscapeObjectState state : virtualObjectMappings) { 645 state.applyToNonVirtual(closure); 646 } 647 } 648 649 if (outerFrameState() != null) { 650 outerFrameState().applyToNonVirtual(closure); 651 } 652 } 653 654 @Override 655 public void applyToVirtual(VirtualClosure closure) { 656 closure.apply(this); 657 if (virtualObjectMappings != null) { 658 for (EscapeObjectState state : virtualObjectMappings) { 659 state.applyToVirtual(closure); 660 } 661 } 662 if (outerFrameState() != null) { 663 outerFrameState().applyToVirtual(closure); 664 } 665 } 666 667 @Override 668 public boolean isPartOfThisState(VirtualState state) { 669 if (state == this) { 670 return true; 671 } 672 if (outerFrameState() != null && outerFrameState().isPartOfThisState(state)) { 673 return true; 674 } 675 if (virtualObjectMappings != null) { 676 for (EscapeObjectState objectState : virtualObjectMappings) { 677 if (objectState.isPartOfThisState(state)) { 678 return true; 679 } 680 } 681 } 682 return false; 683 } 684 685 public boolean isExceptionHandlingBCI() { 686 return bci == BytecodeFrame.AFTER_EXCEPTION_BCI || bci == BytecodeFrame.UNWIND_BCI; 687 } 688 }