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 }