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 27 package jdk.internal.clang; 28 29 import jdk.incubator.foreign.MemoryAddress; 30 import jdk.incubator.foreign.MemoryLayout; 31 import jdk.incubator.foreign.MemorySegment; 32 import jdk.incubator.foreign.ResourceScope; 33 import jdk.incubator.foreign.SegmentAllocator; 34 import jdk.internal.clang.libclang.CXToken; 35 import jdk.internal.clang.libclang.Index_h; 36 import jdk.internal.clang.libclang.CXUnsavedFile; 37 38 import java.io.IOException; 39 import java.nio.file.Path; 40 import java.util.Objects; 41 import java.util.function.Consumer; 42 43 import static jdk.internal.clang.libclang.Index_h.C_INT; 44 import static jdk.internal.clang.libclang.Index_h.C_POINTER; 45 46 import static jdk.internal.clang.LibClang.IMPLICIT_ALLOCATOR; 47 48 public class TranslationUnit implements AutoCloseable { 49 50 private MemoryAddress tu; 51 52 TranslationUnit(MemoryAddress tu) { 53 this.tu = tu; 54 } 55 56 public Cursor getCursor() { 57 return new Cursor(Index_h.clang_getTranslationUnitCursor(IMPLICIT_ALLOCATOR, tu)); 58 } 59 60 public Diagnostic[] getDiagnostics() { 61 int cntDiags = Index_h.clang_getNumDiagnostics(tu); 62 Diagnostic[] rv = new Diagnostic[cntDiags]; 63 for (int i = 0; i < cntDiags; i++) { 64 MemoryAddress diag = Index_h.clang_getDiagnostic(tu, i); 65 rv[i] = new Diagnostic(diag); 66 } 67 68 return rv; 69 } 70 71 public final void save(Path path) throws TranslationUnitSaveException { 72 try (ResourceScope scope = ResourceScope.newConfinedScope()) { 73 var allocator = SegmentAllocator.nativeAllocator(scope); 74 MemorySegment pathStr = allocator.allocateUtf8String(path.toAbsolutePath().toString()); 75 SaveError res = SaveError.valueOf(Index_h.clang_saveTranslationUnit(tu, pathStr, 0)); 76 if (res != SaveError.None) { 77 throw new TranslationUnitSaveException(path, res); 78 } 79 } 80 } 81 82 void processDiagnostics(Consumer<Diagnostic> dh) { 83 Objects.requireNonNull(dh); 84 for (Diagnostic diag : getDiagnostics()) { 85 dh.accept(diag); 86 } 87 } 88 89 static long FILENAME_OFFSET = CXUnsavedFile.$LAYOUT().byteOffset(MemoryLayout.PathElement.groupElement("Filename")); 90 static long CONTENTS_OFFSET = CXUnsavedFile.$LAYOUT().byteOffset(MemoryLayout.PathElement.groupElement("Contents")); 91 static long LENGTH_OFFSET = CXUnsavedFile.$LAYOUT().byteOffset(MemoryLayout.PathElement.groupElement("Length")); 92 93 public void reparse(Index.UnsavedFile... inMemoryFiles) { 94 try (ResourceScope scope = ResourceScope.newConfinedScope()) { 95 var allocator = SegmentAllocator.newNativeArena(scope); 96 MemorySegment files = inMemoryFiles.length == 0 ? 97 null : 98 allocator.allocateArray(CXUnsavedFile.$LAYOUT(), inMemoryFiles.length); 99 for (int i = 0; i < inMemoryFiles.length; i++) { 100 MemorySegment start = files.asSlice(i * CXUnsavedFile.$LAYOUT().byteSize()); 101 start.set(C_POINTER, FILENAME_OFFSET, allocator.allocateUtf8String(inMemoryFiles[i].file)); 102 start.set(C_POINTER, CONTENTS_OFFSET, allocator.allocateUtf8String(inMemoryFiles[i].contents)); 103 start.set(C_INT, LENGTH_OFFSET, inMemoryFiles[i].contents.length()); 104 } 105 ErrorCode code = ErrorCode.valueOf(Index_h.clang_reparseTranslationUnit( 106 tu, 107 inMemoryFiles.length, 108 files == null ? MemoryAddress.NULL : files, 109 Index_h.clang_defaultReparseOptions(tu))); 110 111 if (code != ErrorCode.Success) { 112 throw new IllegalStateException("Re-parsing failed: " + code); 113 } 114 } 115 } 116 117 public void reparse(Consumer<Diagnostic> dh, Index.UnsavedFile... inMemoryFiles) { 118 reparse(inMemoryFiles); 119 processDiagnostics(dh); 120 } 121 122 public String[] tokens(SourceRange range) { 123 Tokens tokens = tokenize(range); 124 String rv[] = new String[tokens.size()]; 125 for (int i = 0; i < rv.length; i++) { 126 rv[i] = tokens.getToken(i).spelling(); 127 } 128 return rv; 129 } 130 131 public Tokens tokenize(SourceRange range) { 132 try (ResourceScope scope = ResourceScope.newConfinedScope()) { 133 MemorySegment p = MemorySegment.allocateNative(C_POINTER, scope); 134 MemorySegment pCnt = MemorySegment.allocateNative(C_INT, scope); 135 Index_h.clang_tokenize(tu, range.range, p, pCnt); 136 Tokens rv = new Tokens(p.get(C_POINTER, 0), pCnt.get(C_INT, 0)); 137 return rv; 138 } 139 } 140 141 @Override 142 public void close() { 143 dispose(); 144 } 145 146 public void dispose() { 147 if (tu != MemoryAddress.NULL) { 148 Index_h.clang_disposeTranslationUnit(tu); 149 tu = MemoryAddress.NULL; 150 } 151 } 152 153 public class Tokens { 154 private final MemoryAddress ar; 155 private final int size; 156 157 Tokens(MemoryAddress ar, int size) { 158 this.ar = ar; 159 this.size = size; 160 } 161 162 public void dispose() { 163 Index_h.clang_disposeTokens(tu, ar, size); 164 } 165 166 public int size() { 167 return size; 168 } 169 170 public MemorySegment getTokenSegment(int idx) { 171 MemoryAddress p = ar.addOffset(idx * CXToken.$LAYOUT().byteSize()); 172 return MemorySegment.ofAddress(p, CXToken.$LAYOUT().byteSize(), ResourceScope.newConfinedScope()); 173 } 174 175 public Token getToken(int index) { 176 return new Token(getTokenSegment(index)); 177 } 178 179 @Override 180 public String toString() { 181 StringBuilder sb = new StringBuilder(); 182 for (int i = 0; i < size; i++) { 183 sb.append("Token["); 184 sb.append(i); 185 sb.append("]="); 186 int pos = i; 187 sb.append(LibClang.CXStrToString(allocator -> 188 Index_h.clang_getTokenSpelling(allocator, tu, getTokenSegment(pos)))); 189 sb.append("\n"); 190 } 191 return sb.toString(); 192 } 193 } 194 195 public class Token { 196 final MemorySegment token; 197 198 Token(MemorySegment token) { 199 this.token = token; 200 } 201 202 public int kind() { 203 return Index_h.clang_getTokenKind(token); 204 } 205 206 public String spelling() { 207 return LibClang.CXStrToString(allocator -> 208 Index_h.clang_getTokenSpelling(allocator, tu, token)); 209 } 210 211 public SourceLocation getLocation() { 212 return new SourceLocation(Index_h.clang_getTokenLocation( 213 IMPLICIT_ALLOCATOR, tu, token)); 214 } 215 216 public SourceRange getExtent() { 217 return new SourceRange(Index_h.clang_getTokenExtent(IMPLICIT_ALLOCATOR, 218 tu, token)); 219 } 220 } 221 222 public static class TranslationUnitSaveException extends IOException { 223 224 static final long serialVersionUID = 1L; 225 226 private final SaveError error; 227 228 TranslationUnitSaveException(Path path, SaveError error) { 229 super("Cannot save translation unit to: " + path.toAbsolutePath() + ". Error: " + error); 230 this.error = error; 231 } 232 } 233 }