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