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 jdk.internal.clang.Cursor;
  26 import jdk.internal.clang.CursorKind;
  27 import jdk.internal.clang.Type;
  28 import jdk.internal.clang.TypeKind;
  29 
  30 import java.nio.file.Path;
  31 import java.util.Collections;
  32 import java.util.concurrent.atomic.AtomicInteger;
  33 import java.util.logging.Logger;
  34 import java.util.List;
  35 
  36 /**
  37  * This class represent a native code header file
  38  */
  39 public final class HeaderFile {
  40     private final Context ctx;
  41     final Path path;
  42     final String pkgName;
  43     final String clsName;
  44     private final TypeDictionary dict;
  45     // The top header file cause this file to be parsed
  46     private HeaderFile main;
  47     private CodeFactory cf;
  48     List<String> libraries; // immutable
  49     List<String> libraryPaths; // immutable
  50 
  51     private final AtomicInteger serialNo;
  52     private final Context.SymbolChecker symChecker;
  53 
  54     private final Logger logger = Logger.getLogger(getClass().getPackage().getName());
  55 
  56     HeaderFile(Context ctx, Path path, String pkgName, String clsName, HeaderFile main, Context.SymbolChecker symChecker) {
  57         this.ctx = ctx;
  58         this.path = path;
  59         this.pkgName = pkgName;
  60         this.clsName = clsName;
  61         dict = ctx.typeDictionaryFor(pkgName);
  62         serialNo = new AtomicInteger();
  63         this.main = main == null ? this : main;
  64         this.symChecker = symChecker;
  65     }
  66 
  67     void useLibraries(List<String> libraries, List<String> libraryPaths) {
  68         this.libraries = Collections.unmodifiableList(libraries);
  69         this.libraryPaths = Collections.unmodifiableList(libraryPaths);
  70     }
  71 
  72     CodeFactory getCodeFactory() {
  73         return cf;
  74     }
  75 
  76     /**
  77      * Call this function to enable code generation for this HeaderFile.
  78      * This function should only be called once to turn on code generation and before process any cursor.
  79      * @param cf The CodeFactory used to generate code
  80      */
  81     void useCodeFactory(CodeFactory cf) {
  82         if (null != this.cf) {
  83             logger.config(() -> "CodeFactory had been initialized for " + path);
  84             // Diagnosis code
  85             if (Main.DEBUG) {
  86                 new Throwable().printStackTrace(ctx.err);
  87             }
  88         } else {
  89             this.cf = cf;
  90         }
  91     }
  92 
  93     @Override
  94     public String toString() {
  95         return "HeaderFile(path=" + path + ")";
  96     }
  97 
  98     private int serialNo() {
  99         return serialNo.incrementAndGet();
 100     }
 101 
 102     void processCursor(Cursor c, HeaderFile main, boolean isBuiltIn) {
 103         if (c.isDeclaration()) {
 104             Type t = c.type();
 105             if (symChecker != null) {
 106                 if (t.kind() == TypeKind.FunctionProto ||
 107                     t.kind() == TypeKind.FunctionNoProto) {
 108                     String name = c.spelling();
 109                     if (!symChecker.lookup(name)) {
 110                         ctx.err.println(Main.format("warn.symbol.not.found", name));
 111                     }
 112                 }
 113             }
 114             JType jt = dict.computeIfAbsent(t, type -> {
 115                 logger.fine(() -> "PH: Compute type for " + type.spelling());
 116                 return define(type);
 117             });
 118             assert (jt instanceof JType2);
 119             // Only main file can define interface
 120             if (cf != null && this.main == main) {
 121                 cf.addType(jt, c);
 122             }
 123         } else if (c.isPreprocessing()) {
 124             if (cf != null && c.kind() == CursorKind.MacroDefinition && !isBuiltIn && this.main == main) {
 125                 cf.addMacro(c);
 126             }
 127         }
 128     }
 129 
 130     JType globalLookup(Type type) {
 131         JType jt;
 132         try {
 133             jt = dict.lookup(type);
 134             if (null == jt) {
 135                 jt = dict.computeIfAbsent(type, this::define);
 136             }
 137         } catch (TypeDictionary.NotDeclaredException ex) {
 138             // The type has no declaration, consider it local defined
 139             jt = dict.computeIfAbsent(type, this::define);
 140         }
 141         return jt;
 142     }
 143 
 144     /**
 145      * Local lookup, the type is assumed to be locally defined. Use
 146      * TypeDictionary.lookup(Type) for a global lookup or Context.getJType(Cursor)
 147      *
 148      * @param type
 149      * @return
 150      * @see TypeDictionary#lookup(Type)
 151      */
 152     JType localLookup(Type type) {
 153         return dict.computeIfAbsent(type, this::define);
 154     }
 155 
 156     private JType doRecord(Type t) {
 157         assert(t.kind() == TypeKind.Record);
 158         String name = Utils.toClassName(Utils.getIdentifier(t));
 159         Cursor dcl = t.getDeclarationCursor();
 160         // Define record locally but not declared in this file, likely a built-in type.
 161         // __builtin_va_list is such a type.
 162         boolean gen_code = (cf != null) && (dcl.getSourceLocation().getFileLocation().path() == null);
 163         JType2 jt;
 164         // case of #typedef struct Foo Bar, struct Foo is a Record type
 165         // as no definition found, we consider it an annotation
 166         Cursor defC = dcl.getDefinition();
 167         if (defC.isInvalid()) {
 168             name = Utils.toInternalName(pkgName, clsName, name);
 169             jt = JType2.bind(new TypeAlias(name, JType.Void), t, dcl);
 170         } else {
 171             jt = JType2.bind(
 172                     new JType.InnerType(Utils.toInternalName(pkgName, clsName), name),
 173                     t, defC);
 174             if (gen_code) {
 175                 cf.addType(jt, defC);
 176             }
 177         }
 178         return jt;
 179     }
 180 
 181     // Use of dict.lookup() and lookup() is tricky, if a type should have being
 182     // declare earlier, use dict.lookup(); otherwise use lookup() for potentially
 183     // local declaration of a type.
 184     JType define(Type t) {
 185         JType jt;
 186         JType2 jt2;
 187         logger.fine("Define " + t.kind() + ":" + t.spelling() + " for TD " + pkgName);
 188         switch (t.kind()) {
 189             case Unexposed:
 190                 jt = define(t.canonicalType());
 191                 break;
 192             case ConstantArray:
 193                 jt = new JType.Array(globalLookup(t.getElementType()));
 194                 break;
 195             case IncompleteArray:
 196                 jt = new PointerType(globalLookup(t.getElementType()));
 197                 break;
 198             case FunctionProto:
 199             case FunctionNoProto:
 200                 JType[] args = new JType[t.numberOfArgs()];
 201                 for (int i = 0; i < args.length; i++) {
 202                     // argument could be function pointer declared locally
 203                     args[i] = globalLookup(t.argType(i));
 204                 }
 205                 jt = new JType.Function(t.isVariadic(), globalLookup(t.resultType()), args);
 206                 break;
 207             case Enum:
 208                 String name = Utils.toInternalName(pkgName, clsName,
 209                         Utils.toClassName(Utils.getIdentifier(t)));
 210                 jt = new TypeAlias(name, JType.Int);
 211                 break;
 212             case Invalid:
 213                 throw new IllegalArgumentException("Invalid type");
 214             case Record:
 215                 jt = doRecord(t);
 216                 break;
 217             case Pointer:
 218                 Type pointee = t.getPointeeType();
 219                 jt2 = (JType2) globalLookup(pointee);
 220                 jt = jt2.getDelegate();
 221                 if (jt instanceof JType.Function) {
 222                     jt = new JType.FnIf(new JType.InnerType(
 223                                 Utils.toInternalName(pkgName, clsName),
 224                                 "FI" + serialNo()),
 225                             (JType.Function) jt);
 226                     if (cf != null) {
 227                         cf.addType(JType2.bind(jt, t, null), null);
 228                     }
 229                 } else {
 230                     jt = new PointerType(jt);
 231                 }
 232                 break;
 233             case Typedef:
 234                 Type truetype = t.canonicalType();
 235                 logger.fine(() -> "Typedef " + t.spelling() + " as " + truetype.spelling());
 236                 name = Utils.toInternalName(pkgName, clsName,
 237                         Utils.toClassName(t.spelling()));
 238                 jt = new TypeAlias(name, globalLookup(truetype));
 239                 break;
 240             case BlockPointer:
 241                 // FIXME: what is BlockPointer? A FunctionalPointer as this is closure
 242                 pointee = t.getPointeeType();
 243                 jt2 = (JType2) globalLookup(pointee);
 244                 jt = jt2.getDelegate();
 245                 jt = new JType.FnIf(new JType.InnerType(
 246                             Utils.toInternalName(pkgName, clsName),
 247                             "FI" + serialNo()),
 248                         (JType.Function) jt);
 249                 if (cf != null) {
 250                     cf.addType(JType2.bind(jt, t, null), null);
 251                 }
 252                 break;
 253             default:
 254                 throw new UnsupportedOperationException("Type kind not supported: " + t.kind());
 255         }
 256 
 257         final JType finalJt = jt;
 258         logger.config(() -> "Type " + t.spelling() + " defined as " + finalJt.getSignature());
 259         return (jt instanceof JType2) ? jt : JType2.bind(jt, t, t.getDeclarationCursor());
 260     }
 261 }