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