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 ---