1 /*
  2  *  Copyright (c) 2020, 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.  Oracle designates this
  8  *  particular file as subject to the "Classpath" exception as provided
  9  *  by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  *  This code is distributed in the hope that it will be useful, but WITHOUT
 12  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  *  version 2 for more details (a copy is included in the LICENSE file that
 15  *  accompanied this code).
 16  *
 17  *  You should have received a copy of the GNU General Public License version
 18  *  2 along with this work; if not, write to the Free Software Foundation,
 19  *  Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  *   Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  *  or visit www.oracle.com if you need additional information or have any
 23  *  questions.
 24  *
 25  */
 26 package jdk.internal.clang;
 27 
 28 import jdk.incubator.foreign.Addressable;
 29 import jdk.incubator.foreign.MemoryAddress;
 30 import jdk.incubator.foreign.MemorySegment;
 31 import jdk.incubator.foreign.ResourceScope;
 32 import jdk.incubator.foreign.SegmentAllocator;
 33 import jdk.internal.clang.libclang.Index_h;
 34 
 35 import java.nio.file.Path;
 36 import java.nio.file.Paths;
 37 import java.util.Objects;
 38 
 39 import static jdk.internal.clang.libclang.Index_h.C_INT;
 40 import static jdk.internal.clang.libclang.Index_h.C_POINTER;
 41 
 42 public class SourceLocation {
 43 
 44     private final MemorySegment loc;
 45 
 46     SourceLocation(MemorySegment loc) {
 47         this.loc = loc;
 48     }
 49 
 50     @FunctionalInterface
 51     private interface LocationFactory {
 52         void get(MemorySegment loc, Addressable file,
 53                  Addressable line, Addressable column, Addressable offset);
 54     }
 55 
 56     @SuppressWarnings("unchecked")
 57     private Location getLocation(LocationFactory fn) {
 58         try (var scope = ResourceScope.newConfinedScope()) {
 59              MemorySegment file = MemorySegment.allocateNative(C_POINTER, scope);
 60              MemorySegment line = MemorySegment.allocateNative(C_INT, scope);
 61              MemorySegment col = MemorySegment.allocateNative(C_INT, scope);
 62              MemorySegment offset = MemorySegment.allocateNative(C_INT, scope);
 63 
 64             fn.get(loc, file, line, col, offset);
 65             MemoryAddress fname = file.get(C_POINTER, 0);
 66             String str = fname == MemoryAddress.NULL ?  null : getFileName(fname);
 67 
 68             return new Location(str, line.get(C_INT, 0),
 69                 col.get(C_INT, 0), offset.get(C_INT, 0));
 70         }
 71     }
 72 
 73     private static String getFileName(MemoryAddress fname) {
 74         return LibClang.CXStrToString(allocator ->
 75                 Index_h.clang_getFileName(allocator, fname));
 76     }
 77 
 78     public Location getFileLocation() { return getLocation(Index_h::clang_getFileLocation); }
 79     public Location getExpansionLocation() { return getLocation(Index_h::clang_getExpansionLocation); }
 80     public Location getSpellingLocation() { return getLocation(Index_h::clang_getSpellingLocation); }
 81     public boolean isInSystemHeader() {
 82         return Index_h.clang_Location_isInSystemHeader(loc) != 0;
 83     }
 84 
 85     public boolean isFromMainFile() {
 86         return Index_h.clang_Location_isFromMainFile(loc) != 0;
 87     }
 88 
 89     @Override
 90     public boolean equals(Object other) {
 91         if (this == other) {
 92             return true;
 93         }
 94         if (!(other instanceof SourceLocation)) {
 95             return false;
 96         }
 97         SourceLocation sloc = (SourceLocation)other;
 98         return Objects.equals(getFileLocation(), sloc.getFileLocation());
 99     }
100 
101     @Override
102     public int hashCode() {
103         return getFileLocation().hashCode();
104     }
105 
106     public final static class Location {
107         private final Path path;
108         private final int line;
109         private final int column;
110         private final int offset;
111 
112         private Location(String filename, int line, int column, int offset) {
113             if (filename == null || filename.isEmpty()) {
114                 this.path = null;
115             } else {
116                 this.path = Paths.get(filename);
117             }
118 
119             this.line = line;
120             this.column = column;
121             this.offset = offset;
122         }
123 
124         public Path path() {
125             return path;
126         }
127 
128         public int line() {
129             return line;
130         }
131 
132         public int column() {
133             return column;
134         }
135 
136         public int offset() {
137             return offset;
138         }
139 
140         @Override
141         public boolean equals(Object other) {
142             if (this == other) {
143                 return true;
144             }
145             if (!(other instanceof Location)) {
146                 return false;
147             }
148             Location loc = (Location)other;
149             return Objects.equals(path, loc.path) &&
150                 line == loc.line && column == loc.column &&
151                 offset == loc.offset;
152         }
153 
154         @Override
155         public int hashCode() {
156             return Objects.hashCode(path) ^ line ^ column ^ offset;
157         }
158 
159         @Override
160         public String toString() {
161             return Objects.toString(path) + ":" + line + ":" + column + ":" + offset;
162         }
163     }
164 }