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