JNIWriter.java revision 2571:10fc81ac75b4
1/* 2 * Copyright (c) 1999, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package com.sun.tools.javac.jvm; 27 28import java.io.IOException; 29import java.io.PrintWriter; 30import java.util.ArrayList; 31import java.util.Collections; 32import java.util.List; 33 34import javax.tools.FileObject; 35import javax.tools.JavaFileManager; 36import javax.tools.StandardLocation; 37 38import com.sun.tools.javac.code.Attribute; 39import com.sun.tools.javac.code.Flags; 40import com.sun.tools.javac.code.Symbol; 41import com.sun.tools.javac.code.Symbol.ClassSymbol; 42import com.sun.tools.javac.code.Symbol.VarSymbol; 43import com.sun.tools.javac.code.Symtab; 44import com.sun.tools.javac.code.Type; 45import com.sun.tools.javac.code.Types; 46import com.sun.tools.javac.model.JavacElements; 47import com.sun.tools.javac.util.Assert; 48import com.sun.tools.javac.util.Context; 49import com.sun.tools.javac.util.Log; 50import com.sun.tools.javac.util.Options; 51import com.sun.tools.javac.util.Pair; 52 53import static com.sun.tools.javac.main.Option.*; 54import static com.sun.tools.javac.code.Kinds.*; 55import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE; 56 57/** This class provides operations to write native header files for classes. 58 * 59 * <p><b>This is NOT part of any supported API. 60 * If you write code that depends on this, you do so at your own risk. 61 * This code and its internal interfaces are subject to change or 62 * deletion without notice.</b> 63 */ 64public class JNIWriter { 65 protected static final Context.Key<JNIWriter> jniWriterKey = new Context.Key<>(); 66 67 /** Access to files. */ 68 private final JavaFileManager fileManager; 69 70 Types types; 71 Symtab syms; 72 73 /** The log to use for verbose output. 74 */ 75 private final Log log; 76 77 /** Switch: verbose output. 78 */ 79 private boolean verbose; 80 81 /** Switch: check all nested classes of top level class 82 */ 83 private boolean checkAll; 84 85 private Context context; 86 87 private static final boolean isWindows = 88 System.getProperty("os.name").startsWith("Windows"); 89 90 /** Get the ClassWriter instance for this context. */ 91 public static JNIWriter instance(Context context) { 92 JNIWriter instance = context.get(jniWriterKey); 93 if (instance == null) 94 instance = new JNIWriter(context); 95 return instance; 96 } 97 98 /** Construct a class writer, given an options table. 99 */ 100 private JNIWriter(Context context) { 101 context.put(jniWriterKey, this); 102 fileManager = context.get(JavaFileManager.class); 103 log = Log.instance(context); 104 105 Options options = Options.instance(context); 106 verbose = options.isSet(VERBOSE); 107 checkAll = options.isSet("javah:full"); 108 109 this.context = context; // for lazyInit() 110 } 111 112 private void lazyInit() { 113 if (types == null) 114 types = Types.instance(context); 115 if (syms == null) 116 syms = Symtab.instance(context); 117 118 } 119 120 static boolean isSynthetic(Symbol s) { 121 return hasFlag(s, Flags.SYNTHETIC); 122 } 123 static boolean isStatic(Symbol s) { 124 return hasFlag(s, Flags.STATIC); 125 } 126 static boolean isFinal(Symbol s) { 127 return hasFlag(s, Flags.FINAL); 128 } 129 static boolean isNative(Symbol s) { 130 return hasFlag(s, Flags.NATIVE); 131 } 132 static private boolean hasFlag(Symbol m, int flag) { 133 return (m.flags() & flag) != 0; 134 } 135 136 public boolean needsHeader(ClassSymbol c) { 137 lazyInit(); 138 if (c.isLocal() || isSynthetic(c)) 139 return false; 140 return (checkAll) 141 ? needsHeader(c.outermostClass(), true) 142 : needsHeader(c, false); 143 } 144 145 private boolean needsHeader(ClassSymbol c, boolean checkNestedClasses) { 146 if (c.isLocal() || isSynthetic(c)) 147 return false; 148 149 for (Symbol sym : c.members_field.getSymbols(NON_RECURSIVE)) { 150 if (sym.kind == MTH && isNative(sym)) 151 return true; 152 for (Attribute.Compound a: sym.getDeclarationAttributes()) { 153 if (a.type.tsym == syms.nativeHeaderType.tsym) 154 return true; 155 } 156 } 157 if (checkNestedClasses) { 158 for (Symbol sym : c.members_field.getSymbols(NON_RECURSIVE)) { 159 if ((sym.kind == TYP) && needsHeader(((ClassSymbol) sym), true)) 160 return true; 161 } 162 } 163 return false; 164 } 165 166 /** Emit a class file for a given class. 167 * @param c The class from which a class file is generated. 168 */ 169 public FileObject write(ClassSymbol c) throws IOException { 170 String className = c.flatName().toString(); 171 FileObject outFile 172 = fileManager.getFileForOutput(StandardLocation.NATIVE_HEADER_OUTPUT, 173 "", className.replaceAll("[.$]", "_") + ".h", null); 174 PrintWriter out = new PrintWriter(outFile.openWriter()); 175 try { 176 write(out, c); 177 if (verbose) 178 log.printVerbose("wrote.file", outFile); 179 out.close(); 180 out = null; 181 } finally { 182 if (out != null) { 183 // if we are propogating an exception, delete the file 184 out.close(); 185 outFile.delete(); 186 outFile = null; 187 } 188 } 189 return outFile; // may be null if write failed 190 } 191 192 public void write(PrintWriter out, ClassSymbol sym) throws IOException { 193 lazyInit(); 194 try { 195 String cname = encode(sym.fullname, EncoderType.CLASS); 196 fileTop(out); 197 includes(out); 198 guardBegin(out, cname); 199 cppGuardBegin(out); 200 201 writeStatics(out, sym); 202 writeMethods(out, sym, cname); 203 204 cppGuardEnd(out); 205 guardEnd(out); 206 } catch (TypeSignature.SignatureException e) { 207 throw new IOException(e); 208 } 209 } 210 protected void writeStatics(PrintWriter out, ClassSymbol sym) throws IOException { 211 List<ClassSymbol> clist = new ArrayList<>(); 212 for (ClassSymbol cd = sym; cd != null; 213 cd = (ClassSymbol) cd.getSuperclass().tsym) { 214 clist.add(cd); 215 } 216 /* 217 * list needs to be super-class, base-class1, base-class2 and so on, 218 * so we reverse class hierarchy 219 */ 220 Collections.reverse(clist); 221 for (ClassSymbol cd : clist) { 222 for (Symbol i : cd.getEnclosedElements()) { 223 // consider only final, static and fields with ConstantExpressions 224 if (isFinal(i) && i.isStatic() && i.kind == VAR) { 225 VarSymbol v = (VarSymbol) i; 226 if (v.getConstantValue() != null) { 227 Pair<ClassSymbol, VarSymbol> p = new Pair<>(sym, v); 228 printStaticDefines(out, p); 229 } 230 } 231 } 232 } 233 } 234 static void printStaticDefines(PrintWriter out, Pair<ClassSymbol, VarSymbol> p) { 235 ClassSymbol cls = p.fst; 236 VarSymbol f = p.snd; 237 Object value = f.getConstantValue(); 238 String valueStr = null; 239 switch (f.asType().getKind()) { 240 case BOOLEAN: 241 valueStr = (((Boolean) value) ? "1L" : "0L"); 242 break; 243 case BYTE: case SHORT: case INT: 244 valueStr = value.toString() + "L"; 245 break; 246 case LONG: 247 // Visual C++ supports the i64 suffix, not LL. 248 valueStr = value.toString() + ((isWindows) ? "i64" : "LL"); 249 break; 250 case CHAR: 251 Character ch = (Character) value; 252 valueStr = String.valueOf(((int) ch) & 0xffff) + "L"; 253 break; 254 case FLOAT: 255 // bug compatible 256 float fv = ((Float) value).floatValue(); 257 valueStr = (Float.isInfinite(fv)) 258 ? ((fv < 0) ? "-" : "") + "Inff" 259 : value.toString() + "f"; 260 break; 261 case DOUBLE: 262 // bug compatible 263 double d = ((Double) value).doubleValue(); 264 valueStr = (Double.isInfinite(d)) 265 ? ((d < 0) ? "-" : "") + "InfD" 266 : value.toString(); 267 break; 268 default: 269 valueStr = null; 270 } 271 if (valueStr != null) { 272 out.print("#undef "); 273 String cname = encode(cls.getQualifiedName(), EncoderType.CLASS); 274 String fname = encode(f.getSimpleName(), EncoderType.FIELDSTUB); 275 out.println(cname + "_" + fname); 276 out.print("#define " + cname + "_"); 277 out.println(fname + " " + valueStr); 278 } 279 } 280 protected void writeMethods(PrintWriter out, ClassSymbol sym, String cname) 281 throws IOException, TypeSignature.SignatureException { 282 List<Symbol> classmethods = sym.getEnclosedElements(); 283 for (Symbol md : classmethods) { 284 if (isNative(md)) { 285 TypeSignature newtypesig = new TypeSignature(types); 286 CharSequence methodName = md.getSimpleName(); 287 boolean isOverloaded = false; 288 for (Symbol md2 : classmethods) { 289 if ((md2 != md) 290 && (methodName.equals(md2.getSimpleName())) 291 && isNative(md2)) { 292 isOverloaded = true; 293 } 294 } 295 out.println("/*"); 296 out.println(" * Class: " + cname); 297 out.println(" * Method: " + encode(methodName, EncoderType.FIELDSTUB)); 298 out.println(" * Signature: " + newtypesig.getSignature(md.type)); 299 out.println(" */"); 300 out.println("JNIEXPORT " + jniType(types.erasure(md.type.getReturnType())) 301 + " JNICALL " + encodeMethod(md, sym, isOverloaded)); 302 out.print(" (JNIEnv *, "); 303 out.print((md.isStatic()) 304 ? "jclass" 305 : "jobject"); 306 for (Type arg : types.erasure(md.type.getParameterTypes())) { 307 out.print(", "); 308 out.print(jniType(arg)); 309 } 310 out.println(");"); 311 out.println(); 312 } 313 } 314 } 315 @SuppressWarnings("fallthrough") 316 protected final String jniType(Type t) { 317 switch (t.getKind()) { 318 case ARRAY: { 319 Type ct = ((Type.ArrayType)t).getComponentType(); 320 switch (ct.getKind()) { 321 case BOOLEAN: return "jbooleanArray"; 322 case BYTE: return "jbyteArray"; 323 case CHAR: return "jcharArray"; 324 case SHORT: return "jshortArray"; 325 case INT: return "jintArray"; 326 case LONG: return "jlongArray"; 327 case FLOAT: return "jfloatArray"; 328 case DOUBLE: return "jdoubleArray"; 329 case ARRAY: 330 case DECLARED: return "jobjectArray"; 331 default: throw new Error(ct.toString()); 332 } 333 } 334 335 case VOID: return "void"; 336 case BOOLEAN: return "jboolean"; 337 case BYTE: return "jbyte"; 338 case CHAR: return "jchar"; 339 case SHORT: return "jshort"; 340 case INT: return "jint"; 341 case LONG: return "jlong"; 342 case FLOAT: return "jfloat"; 343 case DOUBLE: return "jdouble"; 344 case DECLARED: { 345 if (t.tsym.type == syms.stringType) { 346 return "jstring"; 347 } else if (types.isAssignable(t, syms.throwableType)) { 348 return "jthrowable"; 349 } else if (types.isAssignable(t, syms.classType)) { 350 return "jclass"; 351 } else { 352 return "jobject"; 353 } 354 } 355 } 356 357 Assert.check(false, "jni unknown type"); 358 return null; /* dead code. */ 359 } 360 361 protected void fileTop(PrintWriter out) { 362 out.println("/* DO NOT EDIT THIS FILE - it is machine generated */"); 363 } 364 365 protected void includes(PrintWriter out) { 366 out.println("#include <jni.h>"); 367 } 368 369 /* 370 * Deal with the C pre-processor. 371 */ 372 protected void cppGuardBegin(PrintWriter out) { 373 out.println("#ifdef __cplusplus"); 374 out.println("extern \"C\" {"); 375 out.println("#endif"); 376 } 377 378 protected void cppGuardEnd(PrintWriter out) { 379 out.println("#ifdef __cplusplus"); 380 out.println("}"); 381 out.println("#endif"); 382 } 383 384 protected void guardBegin(PrintWriter out, String cname) { 385 out.println("/* Header for class " + cname + " */"); 386 out.println(); 387 out.println("#ifndef _Included_" + cname); 388 out.println("#define _Included_" + cname); 389 } 390 391 protected void guardEnd(PrintWriter out) { 392 out.println("#endif"); 393 } 394 395 String encodeMethod(Symbol msym, ClassSymbol clazz, 396 boolean isOverloaded) throws TypeSignature.SignatureException { 397 StringBuilder result = new StringBuilder(100); 398 result.append("Java_"); 399 /* JNI */ 400 result.append(encode(clazz.flatname.toString(), EncoderType.JNI)); 401 result.append('_'); 402 result.append(encode(msym.getSimpleName(), EncoderType.JNI)); 403 if (isOverloaded) { 404 TypeSignature typeSig = new TypeSignature(types); 405 StringBuilder sig = typeSig.getParameterSignature(msym.type); 406 result.append("__").append(encode(sig, EncoderType.JNI)); 407 } 408 return result.toString(); 409 } 410 411 static enum EncoderType { 412 CLASS, 413 FIELDSTUB, 414 FIELD, 415 JNI, 416 SIGNATURE 417 } 418 @SuppressWarnings("fallthrough") 419 static String encode(CharSequence name, EncoderType mtype) { 420 StringBuilder result = new StringBuilder(100); 421 int length = name.length(); 422 423 for (int i = 0; i < length; i++) { 424 char ch = name.charAt(i); 425 if (isalnum(ch)) { 426 result.append(ch); 427 continue; 428 } 429 switch (mtype) { 430 case CLASS: 431 switch (ch) { 432 case '.': 433 case '_': 434 result.append("_"); 435 break; 436 case '$': 437 result.append("__"); 438 break; 439 default: 440 result.append(encodeChar(ch)); 441 } 442 break; 443 case JNI: 444 switch (ch) { 445 case '/': 446 case '.': 447 result.append("_"); 448 break; 449 case '_': 450 result.append("_1"); 451 break; 452 case ';': 453 result.append("_2"); 454 break; 455 case '[': 456 result.append("_3"); 457 break; 458 default: 459 result.append(encodeChar(ch)); 460 } 461 break; 462 case SIGNATURE: 463 result.append(isprint(ch) ? ch : encodeChar(ch)); 464 break; 465 case FIELDSTUB: 466 result.append(ch == '_' ? ch : encodeChar(ch)); 467 break; 468 default: 469 result.append(encodeChar(ch)); 470 } 471 } 472 return result.toString(); 473 } 474 475 static String encodeChar(char ch) { 476 String s = Integer.toHexString(ch); 477 int nzeros = 5 - s.length(); 478 char[] result = new char[6]; 479 result[0] = '_'; 480 for (int i = 1; i <= nzeros; i++) { 481 result[i] = '0'; 482 } 483 for (int i = nzeros + 1, j = 0; i < 6; i++, j++) { 484 result[i] = s.charAt(j); 485 } 486 return new String(result); 487 } 488 489 /* Warning: Intentional ASCII operation. */ 490 private static boolean isalnum(char ch) { 491 return ch <= 0x7f && /* quick test */ 492 ((ch >= 'A' && ch <= 'Z') || 493 (ch >= 'a' && ch <= 'z') || 494 (ch >= '0' && ch <= '9')); 495 } 496 497 /* Warning: Intentional ASCII operation. */ 498 private static boolean isprint(char ch) { 499 return ch >= 32 && ch <= 126; 500 } 501 502 private static class TypeSignature { 503 static class SignatureException extends Exception { 504 private static final long serialVersionUID = 1L; 505 SignatureException(String reason) { 506 super(reason); 507 } 508 } 509 510 JavacElements elems; 511 Types types; 512 513 /* Signature Characters */ 514 private static final String SIG_VOID = "V"; 515 private static final String SIG_BOOLEAN = "Z"; 516 private static final String SIG_BYTE = "B"; 517 private static final String SIG_CHAR = "C"; 518 private static final String SIG_SHORT = "S"; 519 private static final String SIG_INT = "I"; 520 private static final String SIG_LONG = "J"; 521 private static final String SIG_FLOAT = "F"; 522 private static final String SIG_DOUBLE = "D"; 523 private static final String SIG_ARRAY = "["; 524 private static final String SIG_CLASS = "L"; 525 526 public TypeSignature(Types types) { 527 this.types = types; 528 } 529 530 StringBuilder getParameterSignature(Type mType) 531 throws SignatureException { 532 StringBuilder result = new StringBuilder(); 533 for (Type pType : mType.getParameterTypes()) { 534 result.append(getJvmSignature(pType)); 535 } 536 return result; 537 } 538 539 StringBuilder getReturnSignature(Type mType) 540 throws SignatureException { 541 return getJvmSignature(mType.getReturnType()); 542 } 543 544 StringBuilder getSignature(Type mType) throws SignatureException { 545 StringBuilder sb = new StringBuilder(); 546 sb.append("(").append(getParameterSignature(mType)).append(")"); 547 sb.append(getReturnSignature(mType)); 548 return sb; 549 } 550 551 /* 552 * Returns jvm internal signature. 553 */ 554 static class JvmTypeVisitor extends JNIWriter.SimpleTypeVisitor<Type, StringBuilder> { 555 556 @Override 557 public Type visitClassType(Type.ClassType t, StringBuilder s) { 558 setDeclaredType(t, s); 559 return null; 560 } 561 562 @Override 563 public Type visitArrayType(Type.ArrayType t, StringBuilder s) { 564 s.append("["); 565 return t.getComponentType().accept(this, s); 566 } 567 568 @Override 569 public Type visitType(Type t, StringBuilder s) { 570 if (t.isPrimitiveOrVoid()) { 571 s.append(getJvmPrimitiveSignature(t)); 572 return null; 573 } 574 return t.accept(this, s); 575 } 576 private void setDeclaredType(Type t, StringBuilder s) { 577 String classname = t.tsym.getQualifiedName().toString(); 578 classname = classname.replace('.', '/'); 579 s.append("L").append(classname).append(";"); 580 } 581 private String getJvmPrimitiveSignature(Type t) { 582 switch (t.getKind()) { 583 case VOID: return SIG_VOID; 584 case BOOLEAN: return SIG_BOOLEAN; 585 case BYTE: return SIG_BYTE; 586 case CHAR: return SIG_CHAR; 587 case SHORT: return SIG_SHORT; 588 case INT: return SIG_INT; 589 case LONG: return SIG_LONG; 590 case FLOAT: return SIG_FLOAT; 591 case DOUBLE: return SIG_DOUBLE; 592 default: 593 Assert.error("unknown type: should not happen"); 594 } 595 return null; 596 } 597 } 598 599 StringBuilder getJvmSignature(Type type) { 600 Type t = types.erasure(type); 601 StringBuilder sig = new StringBuilder(); 602 JvmTypeVisitor jv = new JvmTypeVisitor(); 603 jv.visitType(t, sig); 604 return sig; 605 } 606 } 607 608 static class SimpleTypeVisitor<R, P> implements Type.Visitor<R, P> { 609 610 protected final R DEFAULT_VALUE; 611 612 protected SimpleTypeVisitor() { 613 DEFAULT_VALUE = null; 614 } 615 616 protected SimpleTypeVisitor(R defaultValue) { 617 DEFAULT_VALUE = defaultValue; 618 } 619 620 protected R defaultAction(Type t, P p) { 621 return DEFAULT_VALUE; 622 } 623 624 @Override 625 public R visitClassType(Type.ClassType t, P p) { 626 return defaultAction(t, p); 627 } 628 629 @Override 630 public R visitWildcardType(Type.WildcardType t, P p) { 631 return defaultAction(t, p); 632 } 633 634 @Override 635 public R visitArrayType(Type.ArrayType t, P p) { 636 return defaultAction(t, p); 637 } 638 639 @Override 640 public R visitMethodType(Type.MethodType t, P p) { 641 return defaultAction(t, p); 642 } 643 644 @Override 645 public R visitPackageType(Type.PackageType t, P p) { 646 return defaultAction(t, p); 647 } 648 649 @Override 650 public R visitTypeVar(Type.TypeVar t, P p) { 651 return defaultAction(t, p); 652 } 653 654 @Override 655 public R visitCapturedType(Type.CapturedType t, P p) { 656 return defaultAction(t, p); 657 } 658 659 @Override 660 public R visitForAll(Type.ForAll t, P p) { 661 return defaultAction(t, p); 662 } 663 664 @Override 665 public R visitUndetVar(Type.UndetVar t, P p) { 666 return defaultAction(t, p); 667 } 668 669 @Override 670 public R visitErrorType(Type.ErrorType t, P p) { 671 return defaultAction(t, p); 672 } 673 674 @Override 675 public R visitType(Type t, P p) { 676 return defaultAction(t, p); 677 } 678 } 679} 680