1 /*
   2  * Copyright (c) 2015, 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.util.Collections;
  27 import java.util.List;

  28 import java.util.concurrent.atomic.AtomicInteger;
  29 import java.util.logging.Logger;

  30 import jdk.internal.clang.Cursor;
  31 import jdk.internal.clang.CursorKind;
  32 import jdk.internal.clang.Type;
  33 import jdk.internal.clang.TypeKind;
  34 
  35 /**
  36  * This class represent a native code header file
  37  */
  38 public final class HeaderFile {
  39     private final Context ctx;
  40     final Path path;
  41     final String pkgName;
  42     final String clsName;
  43     private final TypeDictionary dict;
  44     // The top header file cause this file to be parsed
  45     private HeaderFile main;


  46     private AsmCodeFactory cf;
  47     List<String> libraries; // immutable
  48     List<String> libraryPaths; // immutable
  49 
  50     private final AtomicInteger serialNo;
  51 
  52     private final Logger logger = Logger.getLogger(getClass().getPackage().getName());
  53 
  54     HeaderFile(Context ctx, Path path, String pkgName, String clsName, HeaderFile main) {
  55         this.ctx = ctx;
  56         this.path = path;
  57         this.pkgName = pkgName;
  58         this.clsName = clsName;
  59         dict = ctx.typeDictionaryFor(pkgName);
  60         serialNo = new AtomicInteger();
  61         this.main = main == null ? this : main;





  62     }
  63 
  64     void useLibraries(List<String> libraries, List<String> libraryPaths) {
  65         this.libraries = Collections.unmodifiableList(libraries);
  66         this.libraryPaths = Collections.unmodifiableList(libraryPaths);
  67     }
  68 
  69     AsmCodeFactory getCodeFactory() {
  70         return cf;
  71     }
  72 
  73     /**
  74      * Call this function to enable code generation for this HeaderFile.
  75      * This function should only be called once to turn on code generation and before process any cursor.
  76      * @param cf The CodeFactory used to generate code
  77      */
  78     void useCodeFactory(AsmCodeFactory cf) {
  79         if (null != this.cf) {
  80             logger.config(() -> "CodeFactory had been initialized for " + path);
  81             // Diagnosis code
  82             if (Main.DEBUG) {
  83                 new Throwable().printStackTrace(ctx.err);
  84             }
  85         } else {
  86             this.cf = cf;







  87         }
  88     }
  89 
  90     @Override
  91     public String toString() {
  92         return "HeaderFile(path=" + path + ")";
  93     }
  94 
  95     private int serialNo() {
  96         return serialNo.incrementAndGet();
  97     }
  98 
  99     void processCursor(Cursor c, HeaderFile main, boolean isBuiltIn) {
 100         if (c.isDeclaration()) {
 101             logger.finest(() -> "Looking at cursor " + c.spelling() + " of kind " + c.kind());
 102             if (c.kind() == CursorKind.UnexposedDecl ||
 103                     c.kind() == CursorKind.Namespace) {
 104                 c.children()
 105                         .filter(c1 -> c1.isDeclaration())
 106                         .peek(c1 -> logger.finest(
 107                                 () -> "Cursor: " + c1.spelling() + "@" + c1.USR() + "?" + c1.isDeclaration()))
 108                         .forEach(c1 -> processCursor(c1, main, isBuiltIn));
 109                 return;
 110             }
 111             Type t = c.type();
 112             if (t.kind() == TypeKind.FunctionProto ||
 113                 t.kind() == TypeKind.FunctionNoProto) {
 114                 String name = c.spelling();
 115 
 116                 if (ctx.isSymbolExcluded(name)) {
 117                     return;
 118                 }
 119 
 120                 if (!ctx.isSymbolFound(name)) {
 121                     ctx.err.println(Main.format("warn.symbol.not.found", name));
 122                 }
 123             }
 124 
 125             // C structs and unions can have nested structs, unions and enums.
 126             // And nested types are hoisted to the containing scope - i.e., nested
 127             // structs/unions/enums are not scoped types. We recursively visit all
 128             // nested types.
 129             if (c.kind() == CursorKind.StructDecl ||
 130                 c.kind() == CursorKind.UnionDecl) {
 131                 c.children()
 132                     .filter(c1 -> c1.isDeclaration() &&
 133                          (c1.kind() == CursorKind.StructDecl ||
 134                          c1.kind() == CursorKind.UnionDecl ||
 135                          c1.kind() == CursorKind.EnumDecl))
 136                     .peek(c1 -> logger.finest(
 137                          () -> "Cursor: " + c1.spelling() + "@" + c1.USR() + "?" + c1.isDeclaration()))
 138                     .forEach(c1 -> processCursor(c1, main, isBuiltIn));
 139             }
 140 
 141             // generate nothing for anonymous structs/unions. Fields are generated in the
 142             // containing struct or union
 143             if (c.isAnonymousStruct()) {
 144                 return;
 145             }
 146 
 147             JType jt = dict.computeIfAbsent(t, type -> {
 148                 logger.fine(() -> "PH: Compute type for " + type.spelling());
 149                 return define(type);
 150             });
 151             assert (jt instanceof JType2);
 152 
 153             if (t.kind() == TypeKind.Typedef &&
 154                     t.canonicalType().kind() == TypeKind.Enum &&
 155                     t.spelling().equals(t.canonicalType().getDeclarationCursor().spelling()))
 156             {
 157                 logger.fine("Skip redundant typedef " + t.spelling());
 158                 return;
 159             }
 160 
 161             // Only main file can define interface
 162             if (cf != null && this.main == main) {
 163                 cf.addType(jt, c);
 164             }
 165         } else if (c.isPreprocessing()) {
 166             if (c.kind() == CursorKind.MacroDefinition && !isBuiltIn) {
 167                 ctx.defineMacro(c);
 168             }
 169         }
 170     }
 171 
 172     JType globalLookup(Type type) {
 173         JType jt;
 174         try {
 175             jt = dict.lookup(type);
 176             if (null == jt) {
 177                 jt = dict.computeIfAbsent(type, this::define);
 178             }
 179         } catch (TypeDictionary.NotDeclaredException ex) {
 180             // The type has no declaration, consider it local defined
 181             jt = dict.computeIfAbsent(type, this::define);
 182         }
 183         return jt;
 184     }
 185 
 186     /**
 187      * Local lookup, the type is assumed to be locally defined. Use
 188      * TypeDictionary.lookup(Type) for a global lookup or Context.getJType(Cursor)
 189      *
 190      * @param type
 191      * @return
 192      * @see TypeDictionary#lookup(Type)
 193      */
 194     JType localLookup(Type type) {
 195         return dict.computeIfAbsent(type, this::define);
 196     }
 197 
 198     private JType doRecord(Type t) {
 199         assert(t.kind() == TypeKind.Record);
 200         String name = Utils.toClassName(Utils.getIdentifier(t));
 201         Cursor dcl = t.getDeclarationCursor();
 202         // Define record locally but not declared in this file, likely a built-in type.
 203         // __builtin_va_list is such a type.
 204         boolean gen_code = (cf != null) && (dcl.getSourceLocation().getFileLocation().path() == null);
 205         JType2 jt;
 206         // case of #typedef struct Foo Bar, struct Foo is a Record type
 207         // as no definition found, we consider it an annotation
 208         Cursor defC = dcl.getDefinition();
 209         jt = JType2.bind(
 210                 new JType.InnerType(Utils.toInternalName(pkgName, clsName), name),
 211                 t, defC.isInvalid() ? dcl : defC);
 212         if (gen_code) {
 213             cf.addType(jt, defC);
 214         }
 215         return jt;
 216     }
 217 
 218     // Use of dict.lookup() and lookup() is tricky, if a type should have being
 219     // declared earlier, use dict.lookup(); otherwise use lookup() for potentially
 220     // local declaration of a type.
 221     JType define(Type t) {
 222         JType jt;
 223         JType2 jt2;
 224         logger.fine("Define " + t.kind() + ":" + t.spelling() + " for TD " + pkgName);
 225         switch (t.kind()) {
 226             case Unexposed:
 227             case Elaborated:
 228                 jt = define(t.canonicalType());
 229                 break;
 230             case ConstantArray:
 231                 jt = JType.ofArray(globalLookup(t.getElementType()));
 232                 break;
 233             case IncompleteArray:
 234                 jt = JType.ofArray(globalLookup(t.getElementType()));
 235                 break;
 236             case FunctionProto:
 237             case FunctionNoProto:
 238                 JType[] args = new JType[t.numberOfArgs()];
 239                 for (int i = 0; i < args.length; i++) {
 240                     // argument could be function pointer declared locally
 241                     args[i] = globalLookup(t.argType(i));
 242                 }
 243                 jt = new JType.Function(Utils.getFunction(t), t.isVariadic(), globalLookup(t.resultType()), args);
 244                 break;
 245             case Enum:
 246                 String name = Utils.toInternalName(pkgName, clsName,
 247                         Utils.toClassName(Utils.getIdentifier(t)));
 248                 jt = TypeAlias.of(name, JType.Int);
 249                 break;
 250             case Invalid:
 251                 throw new IllegalArgumentException("Invalid type");
 252             case Record:
 253                 jt = doRecord(t);
 254                 break;
 255             case Pointer:
 256                 Type pointee = t.getPointeeType().canonicalType();
 257                 jt2 = (JType2) globalLookup(pointee);
 258                 jt = jt2.getDelegate();
 259                 if (jt instanceof JType.Function) {
 260                     jt = new JType.FnIf(new JType.InnerType(
 261                                 Utils.toInternalName(pkgName, clsName),
 262                                 "FI" + serialNo()),
 263                             (JType.Function) jt);
 264                     if (cf != null) {
 265                         cf.addType(JType2.bind(jt, t, null), null);
 266                     }
 267                 } else {
 268                     jt = new PointerType(jt);
 269                 }
 270                 break;
 271             case Typedef:
 272                 Type truetype = t.canonicalType();
 273                 logger.fine(() -> "Typedef " + t.spelling() + " as " + truetype.spelling());
 274                 name = Utils.toInternalName(pkgName, clsName,
 275                         Utils.toClassName(t.spelling()));
 276                 jt = TypeAlias.of(name, globalLookup(truetype));
 277                 break;
 278             case BlockPointer:
 279                 // FIXME: what is BlockPointer? A FunctionalPointer as this is closure
 280                 pointee = t.getPointeeType();
 281                 jt2 = (JType2) globalLookup(pointee);
 282                 jt = jt2.getDelegate();
 283                 jt = new JType.FnIf(new JType.InnerType(
 284                             Utils.toInternalName(pkgName, clsName),
 285                             "FI" + serialNo()),
 286                         (JType.Function) jt);
 287                 if (cf != null) {
 288                     cf.addType(JType2.bind(jt, t, null), null);
 289                 }
 290                 break;
 291             default:
 292                 throw new UnsupportedOperationException("Type kind not supported: " + t.kind());
 293         }
 294 
 295         final JType finalJt = jt;
 296         logger.config(() -> "Type " + t.spelling() + " defined as " + finalJt.getSignature());
 297         return (jt instanceof JType2) ? jt : JType2.bind(jt, t, t.getDeclarationCursor());
 298     }
 299 }
--- EOF ---