1 /* 2 * Copyright (c) 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 package com.sun.tools.jextract.parser; 24 25 import java.nio.file.Path; 26 import java.nio.file.Paths; 27 import java.io.PrintWriter; 28 import java.util.Arrays; 29 import java.util.ArrayList; 30 import java.util.Collection; 31 import java.util.Collections; 32 import java.util.LinkedHashMap; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.Optional; 36 import java.util.logging.Logger; 37 import java.util.stream.Collectors; 38 39 import com.sun.tools.jextract.Context; 40 import jdk.internal.clang.Cursor; 41 import jdk.internal.clang.CursorKind; 42 import jdk.internal.clang.Diagnostic; 43 import jdk.internal.clang.Index; 44 import jdk.internal.clang.LibClang; 45 import jdk.internal.clang.SourceLocation; 46 import jdk.internal.clang.SourceRange; 47 import jdk.internal.clang.TranslationUnit; 48 import com.sun.tools.jextract.tree.HeaderTree; 49 import com.sun.tools.jextract.tree.MacroTree; 50 import com.sun.tools.jextract.tree.SimpleTreeVisitor; 51 import com.sun.tools.jextract.tree.Tree; 52 import com.sun.tools.jextract.tree.TreeMaker; 53 import com.sun.tools.jextract.tree.TreePrinter; 54 55 public class Parser { 56 private final PrintWriter out; 57 private final PrintWriter err; 58 private final TreeMaker treeMaker; 59 private final boolean supportMacros; 60 private final Logger logger = Logger.getLogger(getClass().getPackage().getName()); 61 62 public Parser(Context context, boolean supportMacros) { 63 this.out = context.out; 64 this.err = context.err; 65 this.treeMaker = new TreeMaker(); 66 this.supportMacros = supportMacros; 67 } 68 69 public List<HeaderTree> parse(Collection<Path> paths, Collection<String> args) { 70 final List<HeaderTree> headers = new ArrayList<>(); 71 final Index index = LibClang.createIndex(); 72 for (Path path : paths) { 73 logger.info(() -> { 74 StringBuilder sb = new StringBuilder( 75 "Parsing header file " + path + " with following args:\n"); 76 int i = 0; 77 for (String arg : args) { 78 sb.append("arg["); 79 sb.append(i++); 80 sb.append("] = "); 81 sb.append(arg); 82 sb.append("\n"); 83 } 84 return sb.toString(); 85 }); 86 87 Cursor tuCursor = index.parse(path.toString(), 88 d -> { 89 err.println(d); 90 if (d.severity() > Diagnostic.CXDiagnostic_Warning) { 91 throw new RuntimeException(d.toString()); 92 } 93 }, 94 supportMacros, args.toArray(new String[0])); 95 96 MacroParser macroParser = new MacroParser(); 97 List<Tree> decls = new ArrayList<>(); 98 tuCursor.children(). 99 peek(c -> logger.finest( 100 () -> "Cursor: " + c.spelling() + "@" + c.USR() + "?" + c.isDeclaration())). 101 forEach(c -> { 102 SourceLocation loc = c.getSourceLocation(); 103 if (loc == null) { 104 logger.info(() -> "Ignore Cursor " + c.spelling() + "@" + c.USR() + " has no SourceLocation"); 105 return; 106 } 107 108 SourceLocation.Location src = loc.getFileLocation(); 109 if (src == null) { 110 logger.info(() -> "Cursor " + c.spelling() + "@" + c.USR() + " has no FileLocation"); 111 return; 112 } 113 114 logger.fine(() -> "Do cursor: " + c.spelling() + "@" + c.USR()); 115 116 if (c.isDeclaration()) { 117 if (c.kind() == CursorKind.UnexposedDecl || 118 c.kind() == CursorKind.Namespace) { 119 c.children().forEach(cu -> decls.add(treeMaker.createTree(cu))); 120 } else { 121 decls.add(treeMaker.createTree(c)); 122 } 123 } else if (supportMacros && isMacro(c) && src.path() != null) { 124 handleMacro(macroParser, c); 125 } 126 }); 127 128 decls.addAll(macros(macroParser)); 129 headers.add(treeMaker.createHeader(tuCursor, path, decls)); 130 } 131 132 return Collections.unmodifiableList(headers); 133 } 134 135 private List<MacroTree> macros(MacroParser macroParser) { 136 return macroParser.macros().stream() 137 .map(m -> treeMaker.createMacro(m.cursor(), macroValue(m))) 138 .collect(Collectors.toList()); 139 } 140 141 private Optional<Object> macroValue(MacroParser.Macro m) { 142 try { 143 return Optional.ofNullable(m.value()); 144 } catch (Exception e) { 145 return Optional.empty(); 146 } 147 } 148 149 private void handleMacro(MacroParser macroParser, Cursor cursor) { 150 String macroName = cursor.spelling(); 151 if (cursor.isMacroFunctionLike()) { 152 logger.fine(() -> "Skipping function-like macro " + macroName); 153 return; 154 } 155 156 if (macroParser.isDefined(macroName)) { 157 logger.fine(() -> "Macro " + macroName + " already handled"); 158 return; 159 } 160 161 logger.fine(() -> "Defining macro " + macroName); 162 163 TranslationUnit tu = cursor.getTranslationUnit(); 164 SourceRange range = cursor.getExtent(); 165 String[] tokens = tu.tokens(range); 166 167 macroParser.parse(cursor, tokens); 168 } 169 170 private boolean isMacro(Cursor c) { 171 return c.isPreprocessing() && c.kind() == CursorKind.MacroDefinition; 172 } 173 174 public static void main(String[] args) { 175 if (args.length == 0) { 176 System.err.println("Expected a header file"); 177 return; 178 } 179 180 Context context = new Context(); 181 Parser p = new Parser(context,true); 182 List<Path> paths = Arrays.stream(args).map(Paths::get).collect(Collectors.toList()); 183 Path builtinInc = Paths.get(System.getProperty("java.home"), "conf", "jextract"); 184 List<String> clangArgs = List.of("-I" + builtinInc); 185 List<HeaderTree> headers = p.parse(paths, clangArgs); 186 TreePrinter printer = new TreePrinter(); 187 for (HeaderTree ht : headers) { 188 ht.accept(printer, null); 189 } 190 } 191 } --- EOF ---