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 }