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