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