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 }