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