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 }