1 /*
2 * Copyright (c) 2014, 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
24 package com.sun.tools.jextract;
25
26 import java.foreign.layout.Address;
27 import java.foreign.layout.Function;
28 import java.foreign.layout.Group;
29 import java.foreign.layout.Layout;
30 import java.foreign.layout.Padding;
31 import java.foreign.layout.Sequence;
32 import java.foreign.layout.Unresolved;
33 import java.foreign.layout.Value;
34 import java.util.ArrayList;
35 import java.util.List;
36 import java.util.function.BiFunction;
37 import java.util.stream.Collectors;
38 import java.util.stream.Stream;
39 import javax.lang.model.SourceVersion;
40 import jdk.internal.clang.Cursor;
41 import jdk.internal.clang.CursorKind;
42 import jdk.internal.clang.SourceLocation;
43 import jdk.internal.clang.Type;
44 import jdk.internal.clang.TypeKind;
45 import jdk.internal.foreign.memory.Types;
46
47 /**
48 * General utility functions
49 */
50 public class Utils {
51 public static String toJavaIdentifier(String str) {
52 final int size = str.length();
53 StringBuilder sb = new StringBuilder(size);
54 if (! Character.isJavaIdentifierStart(str.charAt(0))) {
55 sb.append('_');
56 }
57 for (int i = 0; i < size; i++) {
58 char ch = str.charAt(i);
59 if (Character.isJavaIdentifierPart(ch)) {
60 sb.append(ch);
61 } else {
62 sb.append('_');
63 }
64 }
65 return sb.toString();
66 }
67
68 public static String toClassName(String cname) {
69 StringBuilder sb = new StringBuilder(cname.length());
70 cname = toJavaIdentifier(cname);
71 sb.append(cname);
72 if (SourceVersion.isKeyword(cname)) {
73 sb.append("$");
74 }
75 return sb.toString();
76 }
77
78 public static String toInternalName(String pkg, String name, String... nested) {
79 if ((pkg == null || pkg.isEmpty()) && nested == null) {
80 return name;
81 }
82
83 StringBuilder sb = new StringBuilder();
84 if (pkg != null && ! pkg.isEmpty()) {
85 sb.append(pkg.replace('.', '/'));
86 if (sb.charAt(sb.length() - 1) != '/') {
87 sb.append('/');
88 }
89 }
90 sb.append(name);
91 for (String n: nested) {
92 sb.append('$');
93 sb.append(n);
94 }
95 return sb.toString();
96 }
97
98 public static String getIdentifier(Type type) {
99 Cursor c = type.getDeclarationCursor();
100 if (c.isInvalid()) {
101 return type.spelling();
102 }
103 return getIdentifier(c);
104 }
105
106 public static String getIdentifier(Cursor cursor) {
107 // Use cursor name instead of type name, this way we don't have struct
108 // or enum prefix
109 String nativeName = cursor.spelling();
110 if (nativeName.isEmpty()) {
111 // This happens when a typedef an anonymous struct, i.e., typedef struct {} type;
112 Type t = cursor.type();
113 nativeName = t.spelling();
114 if (nativeName.contains("::")) {
115 SourceLocation.Location loc = cursor.getSourceLocation().getFileLocation();
116 return "anon$"
117 + loc.path().getFileName().toString().replaceAll("\\.", "_")
118 + "$" + loc.offset();
119 }
120 }
121
122 return nativeName;
123 }
124
125 public static String ClassToDescriptor(Class<?> cls) {
126 if (cls.isArray()) {
127 return cls.getName();
128 }
129 if (cls.isPrimitive()) {
130 switch (cls.getTypeName()) {
131 case "int":
132 return "I";
133 case "long":
134 return "J";
135 case "byte":
136 return "B";
137 case "char":
138 return "C";
139 case "float":
140 return "F";
141 case "double":
142 return "D";
143 case "short":
144 return "S";
145 case "boolean":
146 return "Z";
147 case "void":
148 return "V";
149 }
150 }
151 // assuming reference
152 return "L" + cls.getName() + ";";
153 }
154
155 public static String DescriptorToBinaryName(String descriptor) {
156 final char[] ar = descriptor.trim().toCharArray();
157 switch (ar[0]) {
158 case '(':
159 throw new IllegalArgumentException("Method descriptor is not allowed");
160 case 'B':
161 return "byte";
162 case 'C':
163 return "char";
164 case 'D':
165 return "double";
166 case 'F':
167 return "float";
168 case 'I':
169 return "int";
170 case 'J':
171 return "long";
172 case 'S':
173 return "short";
174 case 'Z':
175 return "boolean";
176 }
177
178 StringBuilder sb = new StringBuilder();
179 if (ar[0] == 'L') {
180 for (int i = 1; i < ar.length && ar[i] != ';'; i++) {
181 if (ar[i] == '/') {
182 sb.append('.');
183 }
184 if (!Character.isJavaIdentifierPart(ar[i])) {
185 throw new IllegalArgumentException("Malformed descriptor");
186 }
187 sb.append(ar[i]);
188 }
189 return sb.toString();
190 }
191
192 if (ar[0] == '[') {
193 int depth = 1;
194 while (ar[depth] == '[') depth++;
195 sb.append(DescriptorToBinaryName(descriptor.substring(depth)));
196 for (int i = 0; i < depth; i++) {
197 sb.append("[]");
198 }
199 return sb.toString();
200 }
201
202 throw new IllegalArgumentException("Malformed descriptor");
203 }
204
205 public static Layout getLayout(Cursor clang_cursor) {
206 return getLayout(clang_cursor.type());
207 }
208
209 public static Function getFunction(Cursor clang_cursor) {
210 return getFunction(clang_cursor.type());
211 }
212
213 public static boolean isFunction(Type clang_type) {
214 switch (clang_type.kind()) {
215 case Unexposed:
216 case Typedef:
217 case Elaborated:
218 return isFunction(clang_type.canonicalType());
219 case FunctionProto:
220 case FunctionNoProto:
221 return true;
222 default:
223 return false;
224 }
225 }
226
227 public static Function getFunction(Type t) {
228 switch (t.kind()) {
229 case Unexposed:
230 case Typedef:
231 case Elaborated:
232 return parseFunctionInternal(t.canonicalType());
233 case FunctionProto:
234 case FunctionNoProto:
235 return parseFunctionInternal(t);
236 default:
237 throw new IllegalArgumentException(
238 "Unsupported type kind: " + t.kind());
239 }
240 }
241
242 private static Function parseFunctionInternal(Type t) {
243 final int argSize = t.numberOfArgs();
244 Layout[] args = new Layout[argSize];
245 for (int i = 0; i < argSize; i++) {
246 args[i] = getLayout(t.argType(i));
247 }
248 if (t.resultType().kind() == TypeKind.Void) {
249 return Function.ofVoid(t.isVariadic(), args);
250 } else {
251 return Function.of(getLayout(t.resultType()), t.isVariadic(), args);
252 }
253 }
254
255 public static Layout getLayout(Type t) {
256 switch(t.kind()) {
257 case Bool:
258 return Types.BOOLEAN;
259 case Int:
260 return Types.INT;
261 case UInt:
262 return Types.UNSIGNED.INT;
263 case Int128:
264 return Types.INT128;
265 case UInt128:
266 return Types.UNSIGNED.INT128;
267 case Short:
268 return Types.SHORT;
269 case UShort:
270 return Types.UNSIGNED.SHORT;
271 case Long:
272 return Types.LONG;
273 case ULong:
274 return Types.UNSIGNED.LONG;
275 case LongLong:
276 return Types.LONG_LONG;
277 case ULongLong:
278 return Types.UNSIGNED.LONG_LONG;
279 case SChar:
280 return Types.BYTE;
281 case Char_S:
282 case Char_U:
283 case UChar:
284 return Types.UNSIGNED.BYTE;
285 case Float:
286 return Types.FLOAT;
287 case Double:
288 return Types.DOUBLE;
289 case LongDouble:
290 return Types.LONG_DOUBLE;
291 case Record:
292 return getRecordReferenceLayout(t);
293 case Enum:
294 return Types.INT;
295 case ConstantArray:
296 return Sequence.of(t.getNumberOfElements(), getLayout(t.getElementType()));
297 case IncompleteArray:
298 return Sequence.of(0L, getLayout(t.getElementType()));
299 case Unexposed:
300 case Typedef:
301 case Elaborated:
302 return getLayout(t.canonicalType());
303 case Pointer:
304 case BlockPointer:
305 return parsePointerInternal(t.getPointeeType());
306 default:
307 throw new IllegalArgumentException(
308 "Unsupported type kind: " + t.kind());
309 }
310 }
311
312 private static Address parsePointerInternal(Type pointeeType) {
313 switch (pointeeType.kind()) {
314 case Unexposed:
315 case Typedef:
316 case Elaborated:
317 return parsePointerInternal(pointeeType.canonicalType());
318 case FunctionProto:
319 case FunctionNoProto:
320 return Address.ofFunction(64, parseFunctionInternal(pointeeType));
321 case Void:
322 return Address.ofVoid(64);
323 default:
324 return Address.ofLayout(64, getLayout(pointeeType));
325 }
326 }
327
328 private static Layout getRecordReferenceLayout(Type t) {
329 //symbolic reference
330 return Unresolved.of()
331 .withAnnotation(Layout.NAME, getIdentifier(t.canonicalType()));
332 }
333
334 public static Layout getRecordLayout(Type t, BiFunction<Cursor, Layout, Layout> fieldMapper) {
335 return getRecordLayoutInternal(0, t, t, fieldMapper);
336 }
337
338 static Layout getRecordLayoutInternal(long offset, Type parent, Type t, BiFunction<Cursor, Layout, Layout> fieldMapper) {
339 Cursor cu = t.getDeclarationCursor().getDefinition();
340 if (cu.isInvalid()) {
341 return getRecordReferenceLayout(t);
342 }
343 final boolean isUnion = cu.kind() == CursorKind.UnionDecl;
344 Stream<Cursor> fieldTypes = cu.children()
345 .filter(cx -> cx.isAnonymousStruct() || cx.kind() == CursorKind.FieldDecl);
346 List<Layout> fieldLayouts = new ArrayList<>();
347 int pendingBitfieldStart = -1;
348 long actualSize = 0L;
349 for (Cursor c : fieldTypes.collect(Collectors.toList())) {
350 boolean isBitfield = c.isBitField();
351 if (isBitfield && c.getBitFieldWidth() == 0) continue;
352 long expectedOffset = offsetOf(parent, c);
353 if (expectedOffset > offset) {
354 if (isUnion) {
355 throw new IllegalStateException("No padding in union elements!");
356 }
357 fieldLayouts.add(Padding.of(expectedOffset - offset));
358 actualSize += (expectedOffset - offset);
359 offset = expectedOffset;
360 }
361 if (isBitfield && !isUnion && pendingBitfieldStart == -1) {
362 pendingBitfieldStart = fieldLayouts.size();
363 }
364 if (!isBitfield && pendingBitfieldStart >= 0) {
365 //emit/replace bitfields
366 replaceBitfields(fieldLayouts, pendingBitfieldStart);
367 pendingBitfieldStart = -1;
368 }
369 Layout fieldLayout = (c.isAnonymous()) ?
370 getRecordLayoutInternal(offset, parent, c.type(), fieldMapper) :
371 fieldLayout(isUnion, c, fieldMapper);
372 fieldLayouts.add(fieldLayout);
373 long size = fieldSize(isUnion, c);
374 if (isUnion) {
375 actualSize = Math.max(actualSize, size);
376 } else {
377 offset += size;
378 actualSize += size;
379 }
380 }
381 long expectedSize = t.size() * 8;
382 if (actualSize < expectedSize) {
383 fieldLayouts.add(Padding.of(expectedSize - actualSize));
384 }
385 if (pendingBitfieldStart >= 0) {
386 //emit/replace bitfields
387 replaceBitfields(fieldLayouts, pendingBitfieldStart);
388 }
389 Layout[] fields = fieldLayouts.toArray(new Layout[0]);
390 Group g = isUnion ?
391 Group.union(fields) : Group.struct(fields);
392 return g.withAnnotation(Layout.NAME, getIdentifier(cu));
393 }
394
395 static Layout fieldLayout(boolean isUnion, Cursor c, BiFunction<Cursor, Layout, Layout> fieldMapper) {
396 Layout layout = getLayout(c.type());
397 if (c.isBitField()) {
398 boolean isSigned = ((Value)layout).kind() == Value.Kind.INTEGRAL_SIGNED;
399 Layout sublayout = isSigned ?
400 Value.ofSignedInt(c.getBitFieldWidth()) :
401 Value.ofUnsignedInt(c.getBitFieldWidth());
402 sublayout = fieldMapper.apply(c, sublayout);
403 return isUnion ?
404 bitfield((Value)layout, List.of(sublayout)) :
405 sublayout;
406 } else {
407 return fieldMapper.apply(c, layout);
408 }
409 }
410
411 static long fieldSize(boolean isUnion, Cursor c) {
412 if (!c.isBitField() || isUnion) {
413 return c.type().size() * 8;
414 } else {
415 return c.getBitFieldWidth();
416 }
417 }
418
419 static void replaceBitfields(List<Layout> layouts, int pendingBitfieldsStart) {
420 long storageSize = storageSize(layouts);
421 long offset = 0L;
422 List<Layout> newFields = new ArrayList<>();
423 List<Layout> pendingFields = new ArrayList<>();
424 while (layouts.size() > pendingBitfieldsStart) {
425 Layout l = layouts.remove(pendingBitfieldsStart);
426 offset += l.bitsSize();
427 pendingFields.add(l);
428 if (!pendingFields.isEmpty() &&
429 offset == storageSize) {
430 //emit new
431 newFields.add(bitfield(Value.ofUnsignedInt(storageSize), pendingFields));
432 pendingFields.clear();
433 offset = 0L;
434 } else if (offset > storageSize) {
435 throw new IllegalStateException("Crossing storage unit boundaries");
436 }
437 }
438 if (!pendingFields.isEmpty()) {
439 throw new IllegalStateException("Partially used storage unit");
440 }
441 //add back new fields
442 newFields.forEach(layouts::add);
443 }
444
445 static long storageSize(List<Layout> layouts) {
446 long size = layouts.stream().mapToLong(Layout::bitsSize).sum();
447 int[] sizes = { 64, 32, 16, 8 };
448 for (int s : sizes) {
449 if (size % s == 0) {
450 return s;
451 }
452 }
453 throw new IllegalStateException("Cannot infer storage size");
454 }
455
456 static Value bitfield(Value v, List<Layout> sublayouts) {
457 return v.withContents(Group.struct(sublayouts.toArray(new Layout[0])));
458 }
459
460 static long offsetOf(Type parent, Cursor c) {
461 if (c.kind() == CursorKind.FieldDecl) {
462 return parent.getOffsetOf(c.spelling());
463 } else {
464 return c.children()
465 .mapToLong(child -> offsetOf(parent, child))
466 .findFirst().orElseThrow(IllegalStateException::new);
467 }
468 }
469 }
--- EOF ---