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 }