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.ArrayList; 27 import java.util.Collections; 28 import java.util.List; 29 import java.util.Objects; 30 import java.util.concurrent.atomic.AtomicInteger; 31 import java.util.logging.Logger; 32 import java.util.stream.Stream; 33 import jdk.internal.clang.Cursor; 34 import jdk.internal.clang.CursorKind; 35 import jdk.internal.clang.Type; 36 import jdk.internal.clang.TypeKind; 37 38 /** 39 * This class represent a native code header file 40 */ 41 public final class HeaderFile { 42 private final Context ctx; 43 final Path path; 44 final String pkgName; 45 final String clsName; 46 private final TypeDictionary dict; 47 // The top header file cause this file to be parsed 48 private HeaderFile main; 49 // files included that are from the same directory 50 private final List<HeaderFile> includedFiles; 51 private AsmCodeFactory cf; 52 List<String> libraries; // immutable 53 List<String> libraryPaths; // immutable 54 55 private final AtomicInteger serialNo; 56 57 private final Logger logger = Logger.getLogger(getClass().getPackage().getName()); 58 59 HeaderFile(Context ctx, Path path, String pkgName, String clsName, HeaderFile main) { 60 this.ctx = ctx; 61 this.path = path; 62 this.pkgName = pkgName; 63 this.clsName = clsName; 64 this.dict = ctx.typeDictionaryFor(pkgName); 65 this.serialNo = new AtomicInteger(); 66 this.main = main == null ? this : main; 67 this.includedFiles = new ArrayList<>(); 68 } 69 70 Stream<HeaderFile> getIncludedFiles() { 71 return includedFiles.stream(); 72 } 73 74 void useLibraries(List<String> libraries, List<String> libraryPaths) { 75 this.libraries = Collections.unmodifiableList(libraries); 76 this.libraryPaths = Collections.unmodifiableList(libraryPaths); 77 } 78 79 AsmCodeFactory getCodeFactory() { 80 return cf; 81 } 82 83 /** 84 * Call this function to enable code generation for this HeaderFile. 85 * This function should only be called once to turn on code generation and before process any cursor. 86 * @param cf The CodeFactory used to generate code 87 */ 88 void useCodeFactory(AsmCodeFactory cf) { 89 if (null != this.cf) { 90 logger.config(() -> "CodeFactory had been initialized for " + path); 91 // Diagnosis code 92 if (Main.DEBUG) { 93 new Throwable().printStackTrace(ctx.err); 94 } 95 } else { 96 this.cf = cf; 97 // The 'main' header interface inherits from included header interfaces 98 // only if the included file is assigned to the same package and is from 99 // the same directory. 100 if (this != main && Objects.equals(pkgName, main.pkgName) && 101 Objects.equals(path.getParent(), main.path.getParent())) { 102 main.includedFiles.add(this); 103 } 104 } 105 } 106 107 @Override 108 public String toString() { 109 return "HeaderFile(path=" + path + ")"; 110 } 111 112 private int serialNo() { 113 return serialNo.incrementAndGet(); 114 } 115 116 void processCursor(Cursor c, HeaderFile main, boolean isBuiltIn) { 117 if (c.isDeclaration()) { 118 logger.finest(() -> "Looking at cursor " + c.spelling() + " of kind " + c.kind()); 119 if (c.kind() == CursorKind.UnexposedDecl || 120 c.kind() == CursorKind.Namespace) { 121 c.children() 122 .filter(c1 -> c1.isDeclaration()) 123 .peek(c1 -> logger.finest( 124 () -> "Cursor: " + c1.spelling() + "@" + c1.USR() + "?" + c1.isDeclaration())) 125 .forEach(c1 -> processCursor(c1, main, isBuiltIn)); 126 return; 127 } 128 Type t = c.type(); 129 if (t.kind() == TypeKind.FunctionProto || 130 t.kind() == TypeKind.FunctionNoProto) { 131 String name = c.spelling(); 132 133 if (ctx.isSymbolExcluded(name)) { 134 return; 135 } 136 137 if (!ctx.isSymbolFound(name)) { 138 ctx.err.println(Main.format("warn.symbol.not.found", name)); 139 } 140 } 141 142 // C structs and unions can have nested structs, unions and enums. 143 // And nested types are hoisted to the containing scope - i.e., nested 144 // structs/unions/enums are not scoped types. We recursively visit all 145 // nested types. 146 if (c.kind() == CursorKind.StructDecl || 147 c.kind() == CursorKind.UnionDecl) { 148 c.children() 149 .filter(c1 -> c1.isDeclaration() && 150 (c1.kind() == CursorKind.StructDecl || 151 c1.kind() == CursorKind.UnionDecl || 152 c1.kind() == CursorKind.EnumDecl)) 153 .peek(c1 -> logger.finest( 154 () -> "Cursor: " + c1.spelling() + "@" + c1.USR() + "?" + c1.isDeclaration())) 155 .forEach(c1 -> processCursor(c1, main, isBuiltIn)); 156 } 157 158 // generate nothing for anonymous structs/unions. Fields are generated in the 159 // containing struct or union 160 if (c.isAnonymousStruct()) { 161 return; 162 } 163 164 JType jt = dict.computeIfAbsent(t, type -> { 165 logger.fine(() -> "PH: Compute type for " + type.spelling()); 166 return define(type); 167 }); 168 assert (jt instanceof JType2); 169 170 if (t.kind() == TypeKind.Typedef && 171 t.canonicalType().kind() == TypeKind.Enum && 172 t.spelling().equals(t.canonicalType().getDeclarationCursor().spelling())) 173 { 174 logger.fine("Skip redundant typedef " + t.spelling()); 175 return; 176 } 177 178 // Only main file can define interface 179 if (cf != null && this.main == main) { 180 cf.addType(jt, c); 181 } 182 } else if (c.isPreprocessing()) { 183 if (c.kind() == CursorKind.MacroDefinition && !isBuiltIn) { 184 ctx.defineMacro(c); 185 } 186 } 187 } 188 189 JType globalLookup(Type type) { 190 JType jt; 191 try { 192 jt = dict.lookup(type); 193 if (null == jt) { 194 jt = dict.computeIfAbsent(type, this::define); 195 } 196 } catch (TypeDictionary.NotDeclaredException ex) { 197 // The type has no declaration, consider it local defined 198 jt = dict.computeIfAbsent(type, this::define); 199 } 200 return jt; 201 } 202 203 /** 204 * Local lookup, the type is assumed to be locally defined. Use 205 * TypeDictionary.lookup(Type) for a global lookup or Context.getJType(Cursor) 206 * 207 * @param type 208 * @return 209 * @see TypeDictionary#lookup(Type) 210 */ 211 JType localLookup(Type type) { 212 return dict.computeIfAbsent(type, this::define); 213 } 214 215 private JType doRecord(Type t) { 216 assert(t.kind() == TypeKind.Record); 217 String name = Utils.toClassName(Utils.getIdentifier(t)); 218 Cursor dcl = t.getDeclarationCursor(); 219 // Define record locally but not declared in this file, likely a built-in type. 220 // __builtin_va_list is such a type. 221 boolean gen_code = (cf != null) && (dcl.getSourceLocation().getFileLocation().path() == null); 222 JType2 jt; 223 // case of #typedef struct Foo Bar, struct Foo is a Record type 224 // as no definition found, we consider it an annotation 225 Cursor defC = dcl.getDefinition(); 226 jt = JType2.bind( 227 new JType.InnerType(Utils.toInternalName(pkgName, clsName), name), 228 t, defC.isInvalid() ? dcl : defC); 229 if (gen_code) { 230 cf.addType(jt, defC); 231 } 232 return jt; 233 } 234 235 // Use of dict.lookup() and lookup() is tricky, if a type should have being 236 // declared earlier, use dict.lookup(); otherwise use lookup() for potentially 237 // local declaration of a type. 238 JType define(Type t) { 239 JType jt; 240 JType2 jt2; 241 logger.fine("Define " + t.kind() + ":" + t.spelling() + " for TD " + pkgName); 242 switch (t.kind()) { 243 case Unexposed: 244 case Elaborated: 245 jt = define(t.canonicalType()); 246 break; 247 case ConstantArray: 248 case IncompleteArray: 249 jt = JType.ofArray(globalLookup(t.getElementType())); 250 break; 251 case FunctionProto: 252 case FunctionNoProto: 253 JType[] args = new JType[t.numberOfArgs()]; 254 for (int i = 0; i < args.length; i++) { 255 // argument could be function pointer declared locally 256 args[i] = globalLookup(t.argType(i)); 257 } 258 jt = new JType.Function(Utils.getFunction(t), t.isVariadic(), globalLookup(t.resultType()), args); 259 break; 260 case Enum: 261 String name = Utils.toInternalName(pkgName, clsName, 262 Utils.toClassName(Utils.getIdentifier(t))); 263 jt = TypeAlias.of(name, JType.Int); 264 break; 265 case Invalid: 266 throw new IllegalArgumentException("Invalid type"); 267 case Record: 268 jt = doRecord(t); 269 break; 270 case Pointer: 271 Type pointee = t.getPointeeType().canonicalType(); 272 jt2 = (JType2) globalLookup(pointee); 273 jt = jt2.getDelegate(); 274 if (jt instanceof JType.Function) { 275 jt = new JType.FnIf(new JType.InnerType( 276 Utils.toInternalName(pkgName, clsName), 277 "FI" + serialNo()), 278 (JType.Function) jt); 279 if (cf != null) { 280 cf.addType(JType2.bind(jt, t, null), null); 281 } 282 } else { 283 jt = new PointerType(jt); 284 } 285 break; 286 case Typedef: 287 Type truetype = t.canonicalType(); 288 logger.fine(() -> "Typedef " + t.spelling() + " as " + truetype.spelling()); 289 name = Utils.toInternalName(pkgName, clsName, 290 Utils.toClassName(t.spelling())); 291 jt = TypeAlias.of(name, globalLookup(truetype)); 292 break; 293 case BlockPointer: 294 // FIXME: what is BlockPointer? A FunctionalPointer as this is closure 295 pointee = t.getPointeeType(); 296 jt2 = (JType2) globalLookup(pointee); 297 jt = jt2.getDelegate(); 298 jt = new JType.FnIf(new JType.InnerType( 299 Utils.toInternalName(pkgName, clsName), 300 "FI" + serialNo()), 301 (JType.Function) jt); 302 if (cf != null) { 303 cf.addType(JType2.bind(jt, t, null), null); 304 } 305 break; 306 default: 307 throw new UnsupportedOperationException("Type kind not supported: " + t.kind()); 308 } 309 310 final JType finalJt = jt; 311 logger.config(() -> "Type " + t.spelling() + " defined as " + finalJt.getSignature()); 312 return (jt instanceof JType2) ? jt : JType2.bind(jt, t, t.getDeclarationCursor()); 313 } 314 }