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

 184         List<Path> paths = Arrays.stream(args).map(Paths::get).collect(Collectors.toList());
 185         Path builtinInc = Paths.get(System.getProperty("java.home"), "conf", "jextract");
 186         List<String> clangArgs = List.of("-I" + builtinInc);
 187         List<HeaderTree> headers = p.parse(paths, clangArgs);
 188         TreePrinter printer = new TreePrinter();
 189         for (HeaderTree ht : headers) {
 190             ht.accept(printer, null);
 191         }
 192     }
 193 }
--- EOF ---