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 jdk.internal.clang.Cursor;
  27 import jdk.internal.clang.Type;
  28 import jdk.internal.clang.TypeKind;
  29 
  30 import java.io.ByteArrayOutputStream;
  31 import java.io.File;
  32 import java.io.IOException;
  33 import java.math.BigDecimal;
  34 import java.math.BigInteger;
  35 import java.nicl.metadata.NativeType;
  36 import java.nio.file.Files;
  37 import java.nio.file.Path;
  38 import java.util.HashMap;
  39 import java.util.Map;
  40 import java.util.concurrent.atomic.AtomicInteger;
  41 import java.util.function.Function;
  42 import java.util.jar.JarEntry;
  43 import java.util.jar.JarInputStream;
  44 import java.util.logging.Logger;
  45 import java.util.stream.Stream;
  46 
  47 import static java.nio.file.StandardOpenOption.READ;
  48 
  49 /**
  50  * A dictionary that find Java type for a given native type.
  51  * Each instance of TypeDictionary presents types for a given java package.
  52  */
  53 final class TypeDictionary {
  54     private final Logger logger = Logger.getLogger(getClass().getPackage().getName());
  55     private Context ctx;
  56     private final String pkgName;
  57     // clang.Type.spelling() to java type
  58     private final Map<String, JType> typeMap;
  59     private final AtomicInteger serialNo;
  60 
  61     private int serialNo() {
  62         return serialNo.incrementAndGet();
  63     }
  64 
  65     TypeDictionary(Context ctx, String pkg) {
  66         this.ctx = ctx;
  67         this.pkgName = pkg;
  68         this.typeMap = new HashMap<>();
  69         this.serialNo = new AtomicInteger();
  70     }
  71 
  72 /*
  73     public static boolean addType(Context ctx, Type t, Class<?> cls) {
  74         if (cls.isAnnotation() || cls.isArray()) {
  75             throw new IllegalArgumentException();
  76         }
  77 
  78         String pkg = cls.getPackage().getName();
  79         TypeDictionary dict = ctx.typeDictionaryFor(pkg);
  80 
  81         String type = t.spelling();
  82         return dict.addWithClass(type, cls);
  83     }
  84 
  85     boolean addType(String type, Class<?> cls) {
  86         String pkg = cls.getPackage().getName();
  87         // Class must be in this or sub packages
  88         if (! pkg.startsWith(pkgName)) {
  89             return false;
  90         }
  91         return addWithClass(type, cls);
  92     }
  93 
  94     private boolean addWithClass(String type, Class<?> cls) {
  95         assert(! cls.isArray());
  96         assert(! cls.isAnnotation());
  97 
  98         return (null == typeMap.putIfAbsent(type, JType.of(cls)));
  99     }
 100 */
 101 
 102     static class JarClassStreamer extends ClassLoader {
 103         private final Context ctx;
 104         private final HashMap<String, byte[]> bytecodes = new HashMap<>();
 105 
 106         JarClassStreamer(Context ctx, Path jar) {
 107             this.ctx = ctx;
 108             try (JarInputStream jis = new JarInputStream(Files.newInputStream(jar, READ))) {
 109                 // List all classes in the jar
 110                 for (JarEntry e = jis.getNextJarEntry(); e != null; e = jis.getNextJarEntry()) {
 111                     if (e.isDirectory()) {
 112                         jis.closeEntry();
 113                         continue;
 114                     }
 115                     String name = e.getName();
 116                     if (! name.endsWith(".class")) {
 117                         // Should not have file not class files
 118                         ctx.err.println("Warning: unexpected file " + name);
 119                     }
 120                     name = name.substring(0, name.length() - 6);
 121                     byte[] buf = new byte[4096];
 122                     ByteArrayOutputStream bos = new ByteArrayOutputStream();
 123                     int n;
 124                     while ((n = jis.read(buf)) > -1) {
 125                         bos.write(buf, 0, n);
 126                     }
 127                     bytecodes.put(name.replace(File.separatorChar, '.'), bos.toByteArray());
 128                 }
 129             } catch (IOException ioe) {
 130                 ctx.err.println("Failed to load types from jar file: " + jar.toString());
 131             }
 132         }
 133 
 134         public Stream<Class<?>> stream() {
 135             return bytecodes.entrySet().stream()
 136                     .map(e -> {
 137                         byte[] bytecode = e.getValue();
 138                         return defineClass(e.getKey(), bytecode, 0, bytecode.length);
 139                     });
 140         }
 141     }
 142 
 143     void loadJar(Path jar) {
 144         JarClassStreamer cl = new JarClassStreamer(ctx, jar);
 145         cl.stream().forEach(cls -> {
 146             Package pkg = cls.getPackage();
 147             TypeDictionary dict = ctx.typeDictionaryFor(pkg.getName());
 148             NativeType nt = cls.getAnnotation(NativeType.class);
 149             dict.typeMap.putIfAbsent(nt.ctype(), JType.of(cls));
 150         });
 151     }
 152 
 153     private static JType checkPrimitive(Type t) {
 154         switch (t.kind()) {
 155             case Void:
 156                 return JType.Void;
 157             // signed integer
 158             case Char_S:
 159             case SChar:
 160             case Short:
 161             case Int:
 162             case Long:
 163             case LongLong:
 164             case Int128:
 165             // unsigned integer, size-compatible at groveller level
 166             // accomodate value-range needed at civilizer level
 167             case UShort:
 168             case UInt:
 169             case UInt128:
 170             case ULong:
 171             case ULongLong:
 172             case Char_U:
 173             case UChar:
 174                 long size = t.size();
 175                 if (size <= 1) {
 176                     return JType.Byte;
 177                 } else if (size <= 2) {
 178                     return JType.Short;
 179                 } else if (size <= 4) {
 180                     return JType.Int;
 181                 } else if (size <= 8) {
 182                     return JType.Long;
 183                 } else {
 184                     return JType.of(BigInteger.class);
 185                 }
 186             case WChar:
 187             case Char16:
 188                 return JType.Char;
 189             case Bool:
 190                 return JType.Bool;
 191             case Double:
 192                 return JType.Double;
 193             case Float:
 194                 return JType.Float;
 195             case LongDouble:
 196                 return JType.of(BigDecimal.class);
 197         }
 198         return null;
 199     }
 200 
 201     private JType exist(Type t) {
 202         JType jt = checkPrimitive(t);
 203         if (jt == null) {
 204             jt = typeMap.get(t.spelling());
 205         }
 206         return jt;
 207     }
 208 
 209     /**
 210      * @param t
 211      * @return
 212      */
 213     final JType get(Type t) {
 214         JType jt;
 215 
 216         switch(t.kind()) {
 217             case Unexposed:
 218                 // Always look at canonical type
 219                 jt = get(t.canonicalType());
 220                 break;
 221             case ConstantArray:
 222                 // Array of element type
 223                 jt = get(t.getElementType());
 224                 if (jt != null) {
 225                     jt = new JType.Array(jt);
 226                 }
 227                 break;
 228             case IncompleteArray:
 229                 jt = get(t.getElementType());
 230                 if (jt != null) {
 231                     jt = new PointerType(jt);
 232                 }
 233                 break;
 234             case Pointer:
 235                 // Pointer type to non-primitive type need to be added to pointee dictionary explicitly
 236                 // Pointee type can be from other dictionary
 237                 jt = exist(t);
 238                 if (jt == null) {
 239                     Type pointee = t.getPointeeType();
 240                     jt = checkPrimitive(pointee);
 241                     if (jt != null) {
 242                         jt = new PointerType(jt);
 243                     }
 244                     // leave out non-primitive type for caller to define
 245                 }
 246                 break;
 247             case Vector:
 248                 switch ((int) t.size()) {
 249                     case 8:
 250                         jt = JType.Long;
 251                         break;
 252                     case 16:
 253                     case 32:
 254                     case 64:
 255                     default:
 256                         throw new UnsupportedOperationException("Support for vector size: " + t.size());
 257                 }
 258                 break;
 259             default:
 260                 jt = exist(t);
 261         }
 262 
 263         if (null == jt) {
 264             logger.fine(() -> "Cannot find type for " + t.spelling());
 265         } else {
 266             final JType finalJt = jt;
 267             logger.fine(() -> "Found type " + finalJt.getDescriptor() + " for " + t.spelling());
 268             jt = (jt instanceof JType2) ? jt : JType2.bind(jt, t, t.getDeclarationCursor());
 269         }
 270         return jt;
 271     }
 272 
 273     final JType computeIfAbsent(Type t, Function<Type, JType> fn) {
 274         JType jt = get(t);
 275         if (jt != null) {
 276             return jt;
 277         }
 278 
 279         // avoid nested call of computeAbsent as fn is likely to define a type
 280         jt = fn.apply(t);
 281         JType rv = typeMap.putIfAbsent(t.spelling(), jt);
 282         // should we be alert in this situation?
 283         return (rv == null) ? jt : rv;
 284     }
 285 
 286     /**
 287      * Look up a type in this instance first, if cannot find it, try to
 288      * look into the origin(declaring) TypeDictionary.
 289      * @param t
 290      * @return
 291      * @throws com.sun.tools.jextract.TypeDictionary.NotDeclaredException
 292      */
 293     final JType lookup(Type t) throws NotDeclaredException {
 294         JType jt = get(t);
 295         if (jt == null && t.kind() != TypeKind.Pointer) {
 296             // Pointer type need to check with pointee type, as the declaration
 297             // might still be in same TypeDictionary
 298             Cursor c = t.getDeclarationCursor();
 299             if (c.isInvalid()) {
 300                 logger.info(() -> "Type " + t.spelling() + " has invalid declaration cursor.");
 301                 logger.fine(() -> Printer.Stringifier(p -> p.dumpType(t)));
 302                 throw new NotDeclaredException(t);
 303             }
 304             jt = ctx.getJType(t.getDeclarationCursor());
 305         }
 306         return jt;
 307     }
 308 
 309     static class NotDeclaredException extends RuntimeException {
 310         private static final long serialVersionUID = -1L;
 311 
 312         private final Type type;
 313 
 314         public NotDeclaredException(Type t) {
 315             type = t;
 316         }
 317 
 318         public final Type getType() { return type; }
 319     }
 320 }