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