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