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.CLinker;
 30 import jdk.incubator.foreign.MemoryAddress;
 31 import jdk.incubator.foreign.MemoryHandles;
 32 import jdk.incubator.foreign.MemorySegment;
 33 import jdk.incubator.foreign.SegmentAllocator;
 34 import jdk.incubator.foreign.ResourceScope;
 35 import jdk.internal.clang.libclang.Index_h;
 36 
 37 import java.lang.invoke.VarHandle;
 38 import java.nio.file.Path;
 39 import java.util.ArrayList;
 40 import java.util.List;
 41 import java.util.function.Consumer;
 42 import java.util.stream.Stream;
 43 
 44 import static jdk.internal.clang.libclang.Index_h.C_POINTER;
 45 
 46 public class Index implements AutoCloseable {
 47     // Pointer to CXIndex
 48     private MemoryAddress ptr;
 49     // Set of TranslationUnit
 50     public final List<TranslationUnit> translationUnits;
 51 
 52     Index(MemoryAddress ptr) {
 53         this.ptr = ptr;
 54         translationUnits = new ArrayList<>();
 55     }
 56 
 57     public static class UnsavedFile {
 58         final String file;
 59         final String contents;
 60 
 61         private UnsavedFile(Path path, String contents) {
 62             this.file = path.toAbsolutePath().toString();
 63             this.contents = contents;
 64         }
 65 
 66         public static UnsavedFile of(Path path, String contents) {
 67             return new UnsavedFile(path, contents);
 68         }
 69     }
 70 
 71     public static class ParsingFailedException extends RuntimeException {
 72         private static final long serialVersionUID = -1L;
 73         private final Path srcFile;
 74         private final ErrorCode code;
 75 
 76         public ParsingFailedException(Path srcFile, ErrorCode code) {
 77             super("Failed to parse " + srcFile.toAbsolutePath().toString() + ": " + code);
 78             this.srcFile = srcFile;
 79             this.code = code;
 80         }
 81     }
 82 
 83     public TranslationUnit parseTU(String file, Consumer<Diagnostic> dh, int options, String... args)
 84             throws ParsingFailedException {
 85         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
 86             SegmentAllocator allocator = SegmentAllocator.newNativeArena(scope);
 87             MemorySegment src = allocator.allocateUtf8String(file);
 88             MemorySegment cargs = args.length == 0 ? null : allocator.allocateArray(C_POINTER, args.length);
 89             for (int i = 0 ; i < args.length ; i++) {
 90                 cargs.set(C_POINTER, i * C_POINTER.byteSize(), allocator.allocateUtf8String(args[i]));
 91             }
 92             MemorySegment outAddress = allocator.allocate(C_POINTER);
 93             ErrorCode code = ErrorCode.valueOf(Index_h.clang_parseTranslationUnit2(
 94                     ptr,
 95                     src,
 96                     cargs == null ? MemoryAddress.NULL : cargs,
 97                     args.length, MemoryAddress.NULL,
 98                     0,
 99                     options,
100                     outAddress));
101 
102             MemoryAddress tu = outAddress.get(C_POINTER, 0);
103             TranslationUnit rv = new TranslationUnit(tu);
104             // even if we failed to parse, we might still have diagnostics
105             rv.processDiagnostics(dh);
106 
107             if (code != ErrorCode.Success) {
108                 throw new ParsingFailedException(Path.of(file).toAbsolutePath(), code);
109             }
110 
111             translationUnits.add(rv);
112             return rv;
113         }
114     }
115 
116     private int defaultOptions(boolean detailedPreprocessorRecord) {
117         int rv = Index_h.CXTranslationUnit_ForSerialization();
118         rv |= Index_h.CXTranslationUnit_SkipFunctionBodies();
119         if (detailedPreprocessorRecord) {
120             rv |= Index_h.CXTranslationUnit_DetailedPreprocessingRecord();
121         }
122         return rv;
123     }
124 
125     public TranslationUnit parse(String file, Consumer<Diagnostic> dh, boolean detailedPreprocessorRecord, String... args)
126     throws ParsingFailedException {
127         return parseTU(file, dh, defaultOptions(detailedPreprocessorRecord), args);
128     }
129 
130     public TranslationUnit parse(String file, boolean detailedPreprocessorRecord, String... args)
131     throws ParsingFailedException {
132         return parse(file, dh -> {}, detailedPreprocessorRecord, args);
133     }
134 
135     @Override
136     public void close() {
137         dispose();
138     }
139 
140     public void dispose() {
141         for (TranslationUnit tu: translationUnits) {
142             tu.dispose();
143         }
144         if (ptr != MemoryAddress.NULL) {
145             Index_h.clang_disposeIndex(ptr);
146         }
147         ptr = MemoryAddress.NULL;
148     }
149 
150 }