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 }