1 /* 2 * Copyright (c) 2016, 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.graph; 26 27 import static org.graalvm.compiler.graph.NodeSourcePosition.Marker.None; 28 import static org.graalvm.compiler.graph.NodeSourcePosition.Marker.Placeholder; 29 import static org.graalvm.compiler.graph.NodeSourcePosition.Marker.Substitution; 30 31 import java.util.Iterator; 32 import java.util.Objects; 33 34 import org.graalvm.compiler.bytecode.BytecodeDisassembler; 35 import org.graalvm.compiler.bytecode.Bytecodes; 36 37 import jdk.vm.ci.code.BytecodeFrame; 38 import jdk.vm.ci.code.BytecodePosition; 39 import jdk.vm.ci.code.CodeUtil; 40 import jdk.vm.ci.meta.JavaMethod; 41 import jdk.vm.ci.meta.MetaUtil; 42 import jdk.vm.ci.meta.ResolvedJavaMethod; 43 import jdk.vm.ci.services.Services; 44 45 public class NodeSourcePosition extends BytecodePosition implements Iterable<NodeSourcePosition> { 46 47 private static final boolean STRICT_SOURCE_POSITION = Boolean.parseBoolean(Services.getSavedProperties().get("debug.graal.SourcePositionStrictChecks")); 48 private static final boolean SOURCE_POSITION_BYTECODES = Boolean.parseBoolean(Services.getSavedProperties().get("debug.graal.SourcePositionDisassemble")); 49 50 private final int hashCode; 51 private final Marker marker; 52 private final SourceLanguagePosition sourceLanguagePosition; 53 54 /** 55 * Remove marker frames. 56 */ 57 public NodeSourcePosition trim() { 58 NodeSourcePosition lastMarker = null; 59 for (NodeSourcePosition current = this; current != null; current = current.getCaller()) { 60 if (current.marker != None) { 61 lastMarker = current; 62 } 63 } 64 if (lastMarker == null) { 65 return this; 66 } 67 return lastMarker.getCaller(); 68 } 69 70 public ResolvedJavaMethod getRootMethod() { 71 NodeSourcePosition cur = this; 72 while (cur.getCaller() != null) { 73 cur = cur.getCaller(); 74 } 75 return cur.getMethod(); 76 } 77 78 public boolean verifyRootMethod(ResolvedJavaMethod root) { 79 JavaMethod currentRoot = getRootMethod(); 80 assert root.equals(currentRoot) || root.getName().equals(currentRoot.getName()) && root.getSignature().toMethodDescriptor().equals(currentRoot.getSignature().toMethodDescriptor()) && 81 root.getDeclaringClass().getName().equals(currentRoot.getDeclaringClass().getName()) : root + " " + currentRoot; 82 return true; 83 } 84 85 @Override 86 public Iterator<NodeSourcePosition> iterator() { 87 return new Iterator<NodeSourcePosition>() { 88 private NodeSourcePosition currentPosition = NodeSourcePosition.this; 89 90 @Override 91 public boolean hasNext() { 92 return currentPosition != null; 93 } 94 95 @Override 96 public NodeSourcePosition next() { 97 NodeSourcePosition current = currentPosition; 98 currentPosition = currentPosition.getCaller(); 99 return current; 100 } 101 }; 102 } 103 104 enum Marker { 105 None, 106 Placeholder, 107 Substitution 108 } 109 110 public NodeSourcePosition(NodeSourcePosition caller, ResolvedJavaMethod method, int bci) { 111 this(caller, method, bci, None); 112 } 113 114 public NodeSourcePosition(NodeSourcePosition caller, ResolvedJavaMethod method, int bci, Marker marker) { 115 this(null, caller, method, bci, marker); 116 117 } 118 119 public NodeSourcePosition(SourceLanguagePosition sourceLanguagePosition, NodeSourcePosition caller, ResolvedJavaMethod method, int bci) { 120 this(sourceLanguagePosition, caller, method, bci, None); 121 } 122 123 public NodeSourcePosition(SourceLanguagePosition sourceLanguagePosition, NodeSourcePosition caller, ResolvedJavaMethod method, int bci, Marker marker) { 124 super(caller, method, bci); 125 if (caller == null) { 126 this.hashCode = 31 * bci + method.hashCode(); 127 } else { 128 this.hashCode = caller.hashCode * 7 + 31 * bci + method.hashCode(); 129 } 130 this.marker = marker; 131 this.sourceLanguagePosition = sourceLanguagePosition; 132 } 133 134 public static NodeSourcePosition placeholder(ResolvedJavaMethod method) { 135 return new NodeSourcePosition(null, method, BytecodeFrame.INVALID_FRAMESTATE_BCI, Placeholder); 136 } 137 138 public static NodeSourcePosition placeholder(ResolvedJavaMethod method, int bci) { 139 return new NodeSourcePosition(null, method, bci, Placeholder); 140 } 141 142 public boolean isPlaceholder() { 143 return marker == Placeholder; 144 } 145 146 public static NodeSourcePosition substitution(ResolvedJavaMethod method) { 147 return substitution(null, method, BytecodeFrame.INVALID_FRAMESTATE_BCI); 148 } 149 150 public static NodeSourcePosition substitution(ResolvedJavaMethod method, int bci) { 151 return substitution(null, method, bci); 152 } 153 154 public static NodeSourcePosition substitution(NodeSourcePosition caller, ResolvedJavaMethod method) { 155 return substitution(caller, method, BytecodeFrame.INVALID_FRAMESTATE_BCI); 156 } 157 158 public static NodeSourcePosition substitution(NodeSourcePosition caller, ResolvedJavaMethod method, int bci) { 159 return new NodeSourcePosition(caller, method, bci, Substitution); 160 } 161 162 public boolean isSubstitution() { 163 return marker == Substitution; 164 } 165 166 @Override 167 public boolean equals(Object obj) { 168 if (obj == this) { 169 return true; 170 } 171 if (obj != null && getClass() == obj.getClass()) { 172 NodeSourcePosition that = (NodeSourcePosition) obj; 173 if (hashCode != that.hashCode) { 174 return false; 175 } 176 if (this.getBCI() == that.getBCI() && Objects.equals(this.getMethod(), that.getMethod()) && Objects.equals(this.getCaller(), that.getCaller()) && 177 Objects.equals(this.sourceLanguagePosition, that.sourceLanguagePosition)) { 178 return true; 179 } 180 } 181 return false; 182 } 183 184 @Override 185 public int hashCode() { 186 return hashCode; 187 } 188 189 public int depth() { 190 int d = 0; 191 NodeSourcePosition pos = this; 192 while (pos != null) { 193 d++; 194 pos = pos.getCaller(); 195 } 196 return d; 197 } 198 199 public SourceLanguagePosition getSourceLanguage() { 200 return sourceLanguagePosition; 201 } 202 203 @Override 204 public NodeSourcePosition getCaller() { 205 return (NodeSourcePosition) super.getCaller(); 206 } 207 208 public NodeSourcePosition addCaller(SourceLanguagePosition newSourceLanguagePosition, NodeSourcePosition link) { 209 return addCaller(newSourceLanguagePosition, link, false); 210 } 211 212 public NodeSourcePosition addCaller(NodeSourcePosition link) { 213 return addCaller(null, link, false); 214 } 215 216 public NodeSourcePosition addCaller(NodeSourcePosition link, boolean isSubstitution) { 217 return addCaller(null, link, isSubstitution); 218 } 219 220 public NodeSourcePosition addCaller(SourceLanguagePosition newSourceLanguagePosition, NodeSourcePosition link, boolean isSubstitution) { 221 if (getCaller() == null) { 222 if (isPlaceholder()) { 223 return new NodeSourcePosition(newSourceLanguagePosition, link, getMethod(), 0); 224 } 225 assert link == null || isSubstitution || verifyCaller(this, link) : link; 226 assert !isSubstitution || marker == None; 227 return new NodeSourcePosition(newSourceLanguagePosition, link, getMethod(), getBCI(), isSubstitution ? Substitution : None); 228 } else { 229 return new NodeSourcePosition(getCaller().addCaller(newSourceLanguagePosition, link, isSubstitution), getMethod(), getBCI(), marker); 230 } 231 } 232 233 @Override 234 public String toString() { 235 StringBuilder sb = new StringBuilder(100); 236 NodeSourcePosition pos = this; 237 while (pos != null) { 238 format(sb, pos); 239 if (pos.sourceLanguagePosition != null) { 240 sb.append(" source=" + pos.sourceLanguagePosition.toShortString()); 241 } 242 pos = pos.getCaller(); 243 if (pos != null) { 244 sb.append(CodeUtil.NEW_LINE); 245 } 246 } 247 return sb.toString(); 248 } 249 250 private static void format(StringBuilder sb, NodeSourcePosition pos) { 251 MetaUtil.appendLocation(sb.append("at "), pos.getMethod(), pos.getBCI()); 252 if (pos.marker != None) { 253 sb.append(" " + pos.marker); 254 } 255 if (SOURCE_POSITION_BYTECODES) { 256 String disassembly = BytecodeDisassembler.disassembleOne(pos.getMethod(), pos.getBCI()); 257 if (disassembly != null && disassembly.length() > 0) { 258 sb.append(" // "); 259 sb.append(disassembly); 260 } 261 } 262 } 263 264 String shallowToString() { 265 StringBuilder sb = new StringBuilder(100); 266 format(sb, this); 267 return sb.toString(); 268 } 269 270 public boolean verify() { 271 NodeSourcePosition current = this; 272 NodeSourcePosition caller = getCaller(); 273 while (caller != null) { 274 assert verifyCaller(current, caller) : current; 275 current = caller; 276 caller = caller.getCaller(); 277 } 278 return true; 279 } 280 281 private static boolean verifyCaller(NodeSourcePosition current, NodeSourcePosition caller) { 282 if (!STRICT_SOURCE_POSITION) { 283 return true; 284 } 285 if (BytecodeFrame.isPlaceholderBci(caller.getBCI())) { 286 return true; 287 } 288 int opcode = BytecodeDisassembler.getBytecodeAt(caller.getMethod(), caller.getBCI()); 289 JavaMethod method = BytecodeDisassembler.getInvokedMethodAt(caller.getMethod(), caller.getBCI()); 290 /* 291 * It's not really possible to match the declaring classes since this might be an interface 292 * invoke. Matching name and signature probably provides enough accuracy. 293 */ 294 assert method == null || (method.getName().equals(current.getMethod().getName()) && 295 method.getSignature().equals(current.getMethod().getSignature())) || 296 caller.getMethod().getName().equals("linkToTargetMethod") || 297 opcode == Bytecodes.INVOKEDYNAMIC || 298 caller.getMethod().getDeclaringClass().getName().startsWith("Ljava/lang/invoke/LambdaForm$") || 299 current.getMethod().getName().equals("callInlined") : "expected " + method + " but found " + 300 current.getMethod(); 301 return true; 302 } 303 }