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 Layout l = getLayout(t.argType(i)); 247 args[i] = l instanceof Sequence? Address.ofLayout(64, ((Sequence)l).element()) : l; 248 } 249 if (t.resultType().kind() == TypeKind.Void) { 250 return Function.ofVoid(t.isVariadic(), args); 251 } else { 252 return Function.of(getLayout(t.resultType()), t.isVariadic(), args); 253 } 254 } 255 256 public static Layout getLayout(Type t) { 257 switch(t.kind()) { 258 case Bool: 259 return Types.BOOLEAN; 260 case Int: 261 return Types.INT; 262 case UInt: 263 return Types.UNSIGNED.INT; 264 case Int128: 265 return Types.INT128; 266 case UInt128: 267 return Types.UNSIGNED.INT128; 268 case Short: 269 return Types.SHORT; 270 case UShort: 271 return Types.UNSIGNED.SHORT; 272 case Long: 273 return Types.LONG; 274 case ULong: 275 return Types.UNSIGNED.LONG; 276 case LongLong: 277 return Types.LONG_LONG; 278 case ULongLong: 279 return Types.UNSIGNED.LONG_LONG; 280 case SChar: 281 return Types.BYTE; 282 case Char_S: 283 case Char_U: 284 case UChar: 285 return Types.UNSIGNED.BYTE; 286 case Float: 287 return Types.FLOAT; 288 case Double: 289 return Types.DOUBLE; 290 case LongDouble: 291 return Types.LONG_DOUBLE; 292 case Record: 293 return getRecordReferenceLayout(t); 294 case Enum: 295 return Types.INT; 296 case ConstantArray: 297 return Sequence.of(t.getNumberOfElements(), getLayout(t.getElementType())); 298 case IncompleteArray: 299 return Sequence.of(0L, getLayout(t.getElementType())); 300 case Unexposed: 301 case Typedef: 302 case Elaborated: 303 return getLayout(t.canonicalType()); 304 case Pointer: 305 case BlockPointer: 306 return parsePointerInternal(t.getPointeeType()); 307 default: 308 throw new IllegalArgumentException( 309 "Unsupported type kind: " + t.kind()); 310 } 311 } 312 313 private static Address parsePointerInternal(Type pointeeType) { 314 switch (pointeeType.kind()) { 315 case Unexposed: 316 case Typedef: 317 case Elaborated: 318 return parsePointerInternal(pointeeType.canonicalType()); 319 case FunctionProto: 320 case FunctionNoProto: 321 return Address.ofFunction(64, parseFunctionInternal(pointeeType)); 322 case Void: 323 return Address.ofVoid(64); 324 default: 325 return Address.ofLayout(64, getLayout(pointeeType)); 326 } 327 } 328 329 private static Layout getRecordReferenceLayout(Type t) { 330 //symbolic reference 331 return Unresolved.of() 332 .withAnnotation(Layout.NAME, getIdentifier(t.canonicalType())); 333 } 334 335 public static Layout getRecordLayout(Type t, BiFunction<Cursor, Layout, Layout> fieldMapper) { 336 return getRecordLayoutInternal(0, t, t, fieldMapper); 337 } 338 339 static Layout getRecordLayoutInternal(long offset, Type parent, Type t, BiFunction<Cursor, Layout, Layout> fieldMapper) { 340 Cursor cu = t.getDeclarationCursor().getDefinition(); 341 if (cu.isInvalid()) { 342 return getRecordReferenceLayout(t); 343 } 344 final boolean isUnion = cu.kind() == CursorKind.UnionDecl; 345 Stream<Cursor> fieldTypes = cu.children() 346 .filter(cx -> cx.isAnonymousStruct() || cx.kind() == CursorKind.FieldDecl); 347 List<Layout> fieldLayouts = new ArrayList<>(); 348 int pendingBitfieldStart = -1; 349 long actualSize = 0L; 350 for (Cursor c : fieldTypes.collect(Collectors.toList())) { 351 boolean isBitfield = c.isBitField(); 352 if (isBitfield && c.getBitFieldWidth() == 0) continue; 353 long expectedOffset = offsetOf(parent, c); 354 if (expectedOffset > offset) { 355 if (isUnion) { 356 throw new IllegalStateException("No padding in union elements!"); 357 } 358 fieldLayouts.add(Padding.of(expectedOffset - offset)); 359 actualSize += (expectedOffset - offset); 360 offset = expectedOffset; 361 } 362 if (isBitfield && !isUnion && pendingBitfieldStart == -1) { 363 pendingBitfieldStart = fieldLayouts.size(); 364 } 365 if (!isBitfield && pendingBitfieldStart >= 0) { 366 //emit/replace bitfields 367 replaceBitfields(fieldLayouts, pendingBitfieldStart); 368 pendingBitfieldStart = -1; 369 } 370 Layout fieldLayout = (c.isAnonymous()) ? 371 getRecordLayoutInternal(offset, parent, c.type(), fieldMapper) : 372 fieldLayout(isUnion, c, fieldMapper); 373 fieldLayouts.add(fieldLayout); 374 long size = fieldSize(isUnion, c); 375 if (isUnion) { 376 actualSize = Math.max(actualSize, size); 377 } else { 378 offset += size; 379 actualSize += size; 380 } 381 } 382 long expectedSize = t.size() * 8; 383 if (actualSize < expectedSize) { 384 fieldLayouts.add(Padding.of(expectedSize - actualSize)); 385 } 386 if (pendingBitfieldStart >= 0) { 387 //emit/replace bitfields 388 replaceBitfields(fieldLayouts, pendingBitfieldStart); 389 } 390 Layout[] fields = fieldLayouts.toArray(new Layout[0]); 391 Group g = isUnion ? 392 Group.union(fields) : Group.struct(fields); 393 return g.withAnnotation(Layout.NAME, getIdentifier(cu)); 394 } 395 396 static Layout fieldLayout(boolean isUnion, Cursor c, BiFunction<Cursor, Layout, Layout> fieldMapper) { 397 Layout layout = getLayout(c.type()); 398 if (c.isBitField()) { 399 boolean isSigned = ((Value)layout).kind() == Value.Kind.INTEGRAL_SIGNED; 400 Layout sublayout = isSigned ? 401 Value.ofSignedInt(c.getBitFieldWidth()) : 402 Value.ofUnsignedInt(c.getBitFieldWidth()); 403 sublayout = fieldMapper.apply(c, sublayout); 404 return isUnion ? 405 bitfield((Value)layout, List.of(sublayout)) : 406 sublayout; 407 } else { 408 return fieldMapper.apply(c, layout); 409 } 410 } 411 412 static long fieldSize(boolean isUnion, Cursor c) { 413 if (!c.isBitField() || isUnion) { 414 return c.type().size() * 8; 415 } else { 416 return c.getBitFieldWidth(); 417 } 418 } 419 420 static void replaceBitfields(List<Layout> layouts, int pendingBitfieldsStart) { 421 long storageSize = storageSize(layouts); 422 long offset = 0L; 423 List<Layout> newFields = new ArrayList<>(); 424 List<Layout> pendingFields = new ArrayList<>(); 425 while (layouts.size() > pendingBitfieldsStart) { 426 Layout l = layouts.remove(pendingBitfieldsStart); 427 offset += l.bitsSize(); 428 pendingFields.add(l); 429 if (!pendingFields.isEmpty() && 430 offset == storageSize) { 431 //emit new 432 newFields.add(bitfield(Value.ofUnsignedInt(storageSize), pendingFields)); 433 pendingFields.clear(); 434 offset = 0L; 435 } else if (offset > storageSize) { 436 throw new IllegalStateException("Crossing storage unit boundaries"); 437 } 438 } 439 if (!pendingFields.isEmpty()) { 440 throw new IllegalStateException("Partially used storage unit"); 441 } 442 //add back new fields 443 newFields.forEach(layouts::add); 444 } 445 446 static long storageSize(List<Layout> layouts) { 447 long size = layouts.stream().mapToLong(Layout::bitsSize).sum(); 448 int[] sizes = { 64, 32, 16, 8 }; 449 for (int s : sizes) { 450 if (size % s == 0) { 451 return s; 452 } 453 } 454 throw new IllegalStateException("Cannot infer storage size"); 455 } 456 457 static Value bitfield(Value v, List<Layout> sublayouts) { 458 return v.withContents(Group.struct(sublayouts.toArray(new Layout[0]))); 459 } 460 461 static long offsetOf(Type parent, Cursor c) { 462 if (c.kind() == CursorKind.FieldDecl) { 463 return parent.getOffsetOf(c.spelling()); 464 } else { 465 return c.children() 466 .mapToLong(child -> offsetOf(parent, child)) 467 .findFirst().orElseThrow(IllegalStateException::new); 468 } 469 } 470 } --- EOF ---