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