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