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