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 ---