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