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