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;
  24 
  25 import java.nio.file.Path;
  26 import java.nio.file.Paths;
  27 import java.util.ArrayList;
  28 import java.util.Arrays;
  29 import java.util.List;
  30 import java.util.stream.Collectors;
  31 import com.sun.tools.jextract.parser.Parser;
  32 import com.sun.tools.jextract.tree.EnumTree;
  33 import com.sun.tools.jextract.tree.FieldTree;
  34 import com.sun.tools.jextract.tree.LayoutUtils;
  35 import com.sun.tools.jextract.tree.HeaderTree;
  36 import com.sun.tools.jextract.tree.SimpleTreeVisitor;
  37 import com.sun.tools.jextract.tree.StructTree;
  38 import com.sun.tools.jextract.tree.Tree;
  39 import com.sun.tools.jextract.tree.TreeMaker;
  40 import com.sun.tools.jextract.tree.TreePhase;
  41 import com.sun.tools.jextract.tree.TreePrinter;
  42 import jdk.internal.clang.Cursor;
  43 
  44 /**
  45  * This tree visitor handles the tree empty names encountered in the tree
  46  * so that subsequent passes need not check tree.name() for empty string.
  47  *
  48  * 1. Names are generated & set for anonymous structs & unions.
  49  * 2. Anonymous (bit) FieldTree instances are removed.
  50  */
  51 final class EmptyNameHandler extends SimpleTreeVisitor<Tree, Void>
  52         implements TreePhase {
  53     private final TreeMaker treeMaker = new TreeMaker();
  54 
  55     @Override
  56     public HeaderTree transform(HeaderTree ht) {
  57         return (HeaderTree)ht.accept(this, null);
  58     }
  59 
  60     // generate unique name for an empty name
  61     private String generateName(Tree tree) {
  62         return LayoutUtils.getName(tree);
  63     }
  64 
  65     @Override
  66     public Tree defaultAction(Tree tree, Void v) {
  67         return tree;
  68     }
  69 
  70     @Override
  71     public Tree visitHeader(HeaderTree ht, Void v) {
  72         List<Tree> decls =  ht.declarations().stream().
  73             map(decl -> decl.accept(this, null)).
  74             collect(Collectors.toList());
  75         return treeMaker.createHeader(ht.cursor(), ht.path(), decls);
  76     }
  77 
  78     @Override
  79     public Tree visitStruct(StructTree s, Void v) {
  80         // Common simple case. No nested names and no anonymous field names.
  81         // We just need to check struct name itself is empty or not.
  82         if (s.nestedTypes().isEmpty() && !hasAnonymousFields(s)) {
  83             /*
  84              * Examples:
  85              *
  86              *   struct { int i } x; // global variable of anon. struct type
  87              *   void func(struct { int x; } p); // param of anon. struct type
  88              */
  89             if (s.name().isEmpty()) {
  90                 return s.withName(generateName(s));
  91             } else {
  92                 // all fine with this struct
  93                 return s;
  94             }
  95         } else {
  96             // handle all nested types
  97             return renameRecursively(s);
  98         }
  99     }
 100 
 101     // does the given struct has any anonymous (bit) field?
 102     private boolean hasAnonymousFields(StructTree s) {
 103         return s.fields().stream().map(f -> f.name().isEmpty()).findFirst().isPresent();
 104     }
 105 
 106     private StructTree renameRecursively(StructTree s) {
 107         List<Tree> newDecls = s.declarations().stream().map(decl -> {
 108             if (decl instanceof StructTree) {
 109                 return renameRecursively((StructTree)decl);
 110             } else if (decl instanceof FieldTree && decl.name().isEmpty()) {
 111                 /*
 112                  * Skip anonymous fields. This happens in the following case:
 113                  *
 114                  * struct {
 115                  *    int  :23; // anonymous bit field
 116                  *    int x:9;
 117                  * }
 118                  */
 119 
 120                 return null;
 121             } else {
 122                 return decl;
 123             }
 124         }).filter(d -> d != null).collect(Collectors.toList());
 125 
 126         return s.withNameAndDecls(generateName(s), newDecls);
 127     }
 128 
 129     // test main to manually check this visitor
 130     public static void main(String[] args) {
 131         if (args.length == 0) {
 132             System.err.println("Expected a header file");
 133             return;
 134         }
 135 
 136         Parser p = new Parser(true);
 137         List<Path> paths = Arrays.stream(args).map(Paths::get).collect(Collectors.toList());
 138         Path builtinInc = Paths.get(System.getProperty("java.home"), "conf", "jextract");
 139         List<String> clangArgs = List.of("-I" + builtinInc);
 140         List<HeaderTree> headers = p.parse(paths, clangArgs);
 141         TreePrinter printer = new TreePrinter();
 142         EmptyNameHandler handler = new EmptyNameHandler();
 143         for (HeaderTree ht : headers) {
 144             handler.transform(ht).accept(printer, null);
 145         }
 146     }
 147 }