LLNI.java revision 2601:8e638f046bf0
1/* 2 * Copyright (c) 2002, 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 26 27package com.sun.tools.javah; 28 29import java.io.OutputStream; 30import java.io.PrintWriter; 31import java.util.ArrayList; 32import java.util.HashSet; 33import java.util.List; 34 35import java.util.Set; 36import javax.lang.model.element.Element; 37import javax.lang.model.element.ExecutableElement; 38import javax.lang.model.element.Modifier; 39import javax.lang.model.element.Name; 40import javax.lang.model.element.TypeElement; 41import javax.lang.model.element.VariableElement; 42import javax.lang.model.type.ArrayType; 43import javax.lang.model.type.PrimitiveType; 44import javax.lang.model.type.TypeKind; 45import javax.lang.model.type.TypeMirror; 46import javax.lang.model.type.TypeVisitor; 47import javax.lang.model.util.ElementFilter; 48import javax.lang.model.util.SimpleTypeVisitor9; 49 50import com.sun.tools.javac.util.DefinedBy; 51import com.sun.tools.javac.util.DefinedBy.Api; 52 53/* 54 * <p><b>This is NOT part of any supported API. 55 * If you write code that depends on this, you do so at your own 56 * risk. This code and its internal interfaces are subject to change 57 * or deletion without notice.</b></p> 58 * 59 * @author Sucheta Dambalkar(Revised) 60 */ 61public class LLNI extends Gen { 62 63 protected final char innerDelim = '$'; /* For inner classes */ 64 protected Set<String> doneHandleTypes; 65 List<VariableElement> fields; 66 List<ExecutableElement> methods; 67 private boolean doubleAlign; 68 private int padFieldNum = 0; 69 70 LLNI(boolean doubleAlign, Util util) { 71 super(util); 72 this.doubleAlign = doubleAlign; 73 } 74 75 protected String getIncludes() { 76 return ""; 77 } 78 79 protected void write(OutputStream o, TypeElement clazz) throws Util.Exit { 80 try { 81 String cname = mangleClassName(clazz.getQualifiedName().toString()); 82 PrintWriter pw = wrapWriter(o); 83 fields = ElementFilter.fieldsIn(clazz.getEnclosedElements()); 84 methods = ElementFilter.methodsIn(clazz.getEnclosedElements()); 85 generateDeclsForClass(pw, clazz, cname); 86 // FIXME check if errors occurred on the PrintWriter and throw exception if so 87 } catch (TypeSignature.SignatureException e) { 88 util.error("llni.sigerror", e.getMessage()); 89 } 90 } 91 92 protected void generateDeclsForClass(PrintWriter pw, 93 TypeElement clazz, String cname) 94 throws TypeSignature.SignatureException, Util.Exit { 95 doneHandleTypes = new HashSet<>(); 96 /* The following handle types are predefined in "typedefs.h". Suppress 97 inclusion in the output by generating them "into the blue" here. */ 98 genHandleType(null, "java.lang.Class"); 99 genHandleType(null, "java.lang.ClassLoader"); 100 genHandleType(null, "java.lang.Object"); 101 genHandleType(null, "java.lang.String"); 102 genHandleType(null, "java.lang.Thread"); 103 genHandleType(null, "java.lang.ThreadGroup"); 104 genHandleType(null, "java.lang.Throwable"); 105 106 pw.println("/* LLNI Header for class " + clazz.getQualifiedName() + " */" + lineSep); 107 pw.println("#ifndef _Included_" + cname); 108 pw.println("#define _Included_" + cname); 109 pw.println("#include \"typedefs.h\""); 110 pw.println("#include \"llni.h\""); 111 pw.println("#include \"jni.h\"" + lineSep); 112 113 forwardDecls(pw, clazz); 114 structSectionForClass(pw, clazz, cname); 115 methodSectionForClass(pw, clazz, cname); 116 pw.println("#endif"); 117 } 118 119 protected void genHandleType(PrintWriter pw, String clazzname) { 120 String cname = mangleClassName(clazzname); 121 if (!doneHandleTypes.contains(cname)) { 122 doneHandleTypes.add(cname); 123 if (pw != null) { 124 pw.println("#ifndef DEFINED_" + cname); 125 pw.println(" #define DEFINED_" + cname); 126 pw.println(" GEN_HANDLE_TYPES(" + cname + ");"); 127 pw.println("#endif" + lineSep); 128 } 129 } 130 } 131 132 protected String mangleClassName(String s) { 133 return s.replace('.', '_') 134 .replace('/', '_') 135 .replace(innerDelim, '_'); 136 } 137 138 protected void forwardDecls(PrintWriter pw, TypeElement clazz) 139 throws TypeSignature.SignatureException { 140 TypeElement object = elems.getTypeElement("java.lang.Object"); 141 if (clazz.equals(object)) 142 return; 143 144 genHandleType(pw, clazz.getQualifiedName().toString()); 145 TypeElement superClass = (TypeElement) (types.asElement(clazz.getSuperclass())); 146 147 if (superClass != null) { 148 String superClassName = superClass.getQualifiedName().toString(); 149 forwardDecls(pw, superClass); 150 } 151 152 for (VariableElement field: fields) { 153 154 if (!field.getModifiers().contains(Modifier.STATIC)) { 155 TypeMirror t = types.erasure(field.asType()); 156 TypeSignature newTypeSig = new TypeSignature(elems); 157 String tname = newTypeSig.qualifiedTypeName(t); 158 String sig = newTypeSig.getTypeSignature(tname); 159 160 if (sig.charAt(0) != '[') 161 forwardDeclsFromSig(pw, sig); 162 } 163 } 164 165 for (ExecutableElement method: methods) { 166 167 if (method.getModifiers().contains(Modifier.NATIVE)) { 168 TypeMirror retType = types.erasure(method.getReturnType()); 169 String typesig = signature(method); 170 TypeSignature newTypeSig = new TypeSignature(elems); 171 String sig = newTypeSig.getTypeSignature(typesig, retType); 172 173 if (sig.charAt(0) != '[') 174 forwardDeclsFromSig(pw, sig); 175 176 } 177 } 178 } 179 180 protected void forwardDeclsFromSig(PrintWriter pw, String sig) { 181 int len = sig.length(); 182 int i = sig.charAt(0) == '(' ? 1 : 0; 183 184 /* Skip the initial "(". */ 185 while (i < len) { 186 if (sig.charAt(i) == 'L') { 187 int j = i + 1; 188 while (sig.charAt(j) != ';') j++; 189 genHandleType(pw, sig.substring(i + 1, j)); 190 i = j + 1; 191 } else { 192 i++; 193 } 194 } 195 } 196 197 protected void structSectionForClass(PrintWriter pw, 198 TypeElement jclazz, String cname) { 199 200 String jname = jclazz.getQualifiedName().toString(); 201 202 if (cname.equals("java_lang_Object")) { 203 pw.println("/* struct java_lang_Object is defined in typedefs.h. */"); 204 pw.println(); 205 return; 206 } 207 pw.println("#if !defined(__i386)"); 208 pw.println("#pragma pack(4)"); 209 pw.println("#endif"); 210 pw.println(); 211 pw.println("struct " + cname + " {"); 212 pw.println(" ObjHeader h;"); 213 pw.print(fieldDefs(jclazz, cname)); 214 215 if (jname.equals("java.lang.Class")) 216 pw.println(" Class *LLNI_mask(cClass);" + 217 " /* Fake field; don't access (see oobj.h) */"); 218 pw.println("};" + lineSep + lineSep + "#pragma pack()"); 219 pw.println(); 220 return; 221 } 222 223 private static class FieldDefsRes { 224 public String className; /* Name of the current class. */ 225 public FieldDefsRes parent; 226 public String s; 227 public int byteSize; 228 public boolean bottomMost; 229 public boolean printedOne = false; 230 231 FieldDefsRes(TypeElement clazz, FieldDefsRes parent, boolean bottomMost) { 232 this.className = clazz.getQualifiedName().toString(); 233 this.parent = parent; 234 this.bottomMost = bottomMost; 235 int byteSize = 0; 236 if (parent == null) this.s = ""; 237 else this.s = parent.s; 238 } 239 } 240 241 /* Returns "true" iff added a field. */ 242 private boolean doField(FieldDefsRes res, VariableElement field, 243 String cname, boolean padWord) { 244 245 String fieldDef = addStructMember(field, cname, padWord); 246 if (fieldDef != null) { 247 if (!res.printedOne) { /* add separator */ 248 if (res.bottomMost) { 249 if (res.s.length() != 0) 250 res.s = res.s + " /* local members: */" + lineSep; 251 } else { 252 res.s = res.s + " /* inherited members from " + 253 res.className + ": */" + lineSep; 254 } 255 res.printedOne = true; 256 } 257 res.s = res.s + fieldDef; 258 return true; 259 } 260 261 // Otherwise. 262 return false; 263 } 264 265 private int doTwoWordFields(FieldDefsRes res, TypeElement clazz, 266 int offset, String cname, boolean padWord) { 267 boolean first = true; 268 List<VariableElement> fields = ElementFilter.fieldsIn(clazz.getEnclosedElements()); 269 270 for (VariableElement field: fields) { 271 TypeKind tk = field.asType().getKind(); 272 boolean twoWords = (tk == TypeKind.LONG || tk == TypeKind.DOUBLE); 273 if (twoWords && doField(res, field, cname, first && padWord)) { 274 offset += 8; first = false; 275 } 276 } 277 return offset; 278 } 279 280 String fieldDefs(TypeElement clazz, String cname) { 281 FieldDefsRes res = fieldDefs(clazz, cname, true); 282 return res.s; 283 } 284 285 FieldDefsRes fieldDefs(TypeElement clazz, String cname, 286 boolean bottomMost){ 287 FieldDefsRes res; 288 int offset; 289 boolean didTwoWordFields = false; 290 291 TypeElement superclazz = (TypeElement) types.asElement(clazz.getSuperclass()); 292 293 if (superclazz != null) { 294 String supername = superclazz.getQualifiedName().toString(); 295 res = new FieldDefsRes(clazz, 296 fieldDefs(superclazz, cname, false), 297 bottomMost); 298 offset = res.parent.byteSize; 299 } else { 300 res = new FieldDefsRes(clazz, null, bottomMost); 301 offset = 0; 302 } 303 304 List<VariableElement> fields = ElementFilter.fieldsIn(clazz.getEnclosedElements()); 305 306 for (VariableElement field: fields) { 307 308 if (doubleAlign && !didTwoWordFields && (offset % 8) == 0) { 309 offset = doTwoWordFields(res, clazz, offset, cname, false); 310 didTwoWordFields = true; 311 } 312 313 TypeKind tk = field.asType().getKind(); 314 boolean twoWords = (tk == TypeKind.LONG || tk == TypeKind.DOUBLE); 315 316 if (!doubleAlign || !twoWords) { 317 if (doField(res, field, cname, false)) offset += 4; 318 } 319 320 } 321 322 if (doubleAlign && !didTwoWordFields) { 323 if ((offset % 8) != 0) offset += 4; 324 offset = doTwoWordFields(res, clazz, offset, cname, true); 325 } 326 327 res.byteSize = offset; 328 return res; 329 } 330 331 /* OVERRIDE: This method handles instance fields */ 332 protected String addStructMember(VariableElement member, String cname, 333 boolean padWord) { 334 String res = null; 335 336 if (member.getModifiers().contains(Modifier.STATIC)) { 337 res = addStaticStructMember(member, cname); 338 // if (res == null) /* JNI didn't handle it, print comment. */ 339 // res = " /* Inaccessible static: " + member + " */" + lineSep; 340 } else { 341 TypeMirror mt = types.erasure(member.asType()); 342 if (padWord) res = " java_int padWord" + padFieldNum++ + ";" + lineSep; 343 res = " " + llniType(mt, false, false) + " " + llniFieldName(member); 344 if (isLongOrDouble(mt)) res = res + "[2]"; 345 res = res + ";" + lineSep; 346 } 347 return res; 348 } 349 350 static private final boolean isWindows = 351 System.getProperty("os.name").startsWith("Windows"); 352 353 /* 354 * This method only handles static final fields. 355 */ 356 protected String addStaticStructMember(VariableElement field, String cname) { 357 String res = null; 358 Object exp = null; 359 360 if (!field.getModifiers().contains(Modifier.STATIC)) 361 return res; 362 if (!field.getModifiers().contains(Modifier.FINAL)) 363 return res; 364 365 exp = field.getConstantValue(); 366 367 if (exp != null) { 368 /* Constant. */ 369 370 String cn = cname + "_" + field.getSimpleName(); 371 String suffix = null; 372 long val = 0; 373 /* Can only handle int, long, float, and double fields. */ 374 if (exp instanceof Byte 375 || exp instanceof Short 376 || exp instanceof Integer) { 377 suffix = "L"; 378 val = ((Number)exp).intValue(); 379 } 380 else if (exp instanceof Long) { 381 // Visual C++ supports the i64 suffix, not LL 382 suffix = isWindows ? "i64" : "LL"; 383 val = ((Long)exp).longValue(); 384 } 385 else if (exp instanceof Float) suffix = "f"; 386 else if (exp instanceof Double) suffix = ""; 387 else if (exp instanceof Character) { 388 suffix = "L"; 389 Character ch = (Character) exp; 390 val = ((int) ch) & 0xffff; 391 } 392 if (suffix != null) { 393 // Some compilers will generate a spurious warning 394 // for the integer constants for Integer.MIN_VALUE 395 // and Long.MIN_VALUE so we handle them specially. 396 if ((suffix.equals("L") && (val == Integer.MIN_VALUE)) || 397 (suffix.equals("LL") && (val == Long.MIN_VALUE))) { 398 res = " #undef " + cn + lineSep 399 + " #define " + cn 400 + " (" + (val + 1) + suffix + "-1)" + lineSep; 401 } else if (suffix.equals("L") || suffix.endsWith("LL")) { 402 res = " #undef " + cn + lineSep 403 + " #define " + cn + " " + val + suffix + lineSep; 404 } else { 405 res = " #undef " + cn + lineSep 406 + " #define " + cn + " " + exp + suffix + lineSep; 407 } 408 } 409 } 410 return res; 411 } 412 413 protected void methodSectionForClass(PrintWriter pw, 414 TypeElement clazz, String cname) 415 throws TypeSignature.SignatureException, Util.Exit { 416 String methods = methodDecls(clazz, cname); 417 418 if (methods.length() != 0) { 419 pw.println("/* Native method declarations: */" + lineSep); 420 pw.println("#ifdef __cplusplus"); 421 pw.println("extern \"C\" {"); 422 pw.println("#endif" + lineSep); 423 pw.println(methods); 424 pw.println("#ifdef __cplusplus"); 425 pw.println("}"); 426 pw.println("#endif"); 427 } 428 } 429 430 protected String methodDecls(TypeElement clazz, String cname) 431 throws TypeSignature.SignatureException, Util.Exit { 432 433 String res = ""; 434 for (ExecutableElement method: methods) { 435 if (method.getModifiers().contains(Modifier.NATIVE)) 436 res = res + methodDecl(method, clazz, cname); 437 } 438 return res; 439 } 440 441 protected String methodDecl(ExecutableElement method, 442 TypeElement clazz, String cname) 443 throws TypeSignature.SignatureException, Util.Exit { 444 String res = null; 445 446 TypeMirror retType = types.erasure(method.getReturnType()); 447 String typesig = signature(method); 448 TypeSignature newTypeSig = new TypeSignature(elems); 449 String sig = newTypeSig.getTypeSignature(typesig, retType); 450 boolean longName = needLongName(method, clazz); 451 452 if (sig.charAt(0) != '(') 453 util.error("invalid.method.signature", sig); 454 455 456 res = "JNIEXPORT " + jniType(retType) + " JNICALL" + lineSep + jniMethodName(method, cname, longName) 457 + "(JNIEnv *, " + cRcvrDecl(method, cname); 458 List<? extends VariableElement> params = method.getParameters(); 459 List<TypeMirror> argTypes = new ArrayList<>(); 460 for (VariableElement p: params){ 461 argTypes.add(types.erasure(p.asType())); 462 } 463 464 /* It would have been nice to include the argument names in the 465 declaration, but there seems to be a bug in the "BinaryField" 466 class, causing the getArguments() method to return "null" for 467 most (non-constructor) methods. */ 468 for (TypeMirror argType: argTypes) 469 res = res + ", " + jniType(argType); 470 res = res + ");" + lineSep; 471 return res; 472 } 473 474 protected final boolean needLongName(ExecutableElement method, 475 TypeElement clazz) { 476 Name methodName = method.getSimpleName(); 477 for (ExecutableElement memberMethod: methods) { 478 if ((memberMethod != method) && 479 memberMethod.getModifiers().contains(Modifier.NATIVE) && 480 (methodName.equals(memberMethod.getSimpleName()))) 481 return true; 482 } 483 return false; 484 } 485 486 protected final String jniMethodName(ExecutableElement method, String cname, 487 boolean longName) 488 throws TypeSignature.SignatureException { 489 String res = "Java_" + cname + "_" + method.getSimpleName(); 490 491 if (longName) { 492 TypeMirror mType = types.erasure(method.getReturnType()); 493 List<? extends VariableElement> params = method.getParameters(); 494 List<TypeMirror> argTypes = new ArrayList<>(); 495 for (VariableElement param: params) { 496 argTypes.add(types.erasure(param.asType())); 497 } 498 499 res = res + "__"; 500 for (TypeMirror t: argTypes) { 501 String tname = t.toString(); 502 TypeSignature newTypeSig = new TypeSignature(elems); 503 String sig = newTypeSig.getTypeSignature(tname); 504 res = res + nameToIdentifier(sig); 505 } 506 } 507 return res; 508 } 509 510 // copied from JNI.java 511 protected final String jniType(TypeMirror t) throws Util.Exit { 512 TypeElement throwable = elems.getTypeElement("java.lang.Throwable"); 513 TypeElement jClass = elems.getTypeElement("java.lang.Class"); 514 TypeElement jString = elems.getTypeElement("java.lang.String"); 515 Element tclassDoc = types.asElement(t); 516 517 switch (t.getKind()) { 518 case ARRAY: { 519 TypeMirror ct = ((ArrayType) t).getComponentType(); 520 switch (ct.getKind()) { 521 case BOOLEAN: return "jbooleanArray"; 522 case BYTE: return "jbyteArray"; 523 case CHAR: return "jcharArray"; 524 case SHORT: return "jshortArray"; 525 case INT: return "jintArray"; 526 case LONG: return "jlongArray"; 527 case FLOAT: return "jfloatArray"; 528 case DOUBLE: return "jdoubleArray"; 529 case ARRAY: 530 case DECLARED: return "jobjectArray"; 531 default: throw new Error(ct.toString()); 532 } 533 } 534 535 case VOID: return "void"; 536 case BOOLEAN: return "jboolean"; 537 case BYTE: return "jbyte"; 538 case CHAR: return "jchar"; 539 case SHORT: return "jshort"; 540 case INT: return "jint"; 541 case LONG: return "jlong"; 542 case FLOAT: return "jfloat"; 543 case DOUBLE: return "jdouble"; 544 545 case DECLARED: { 546 if (tclassDoc.equals(jString)) 547 return "jstring"; 548 else if (types.isAssignable(t, throwable.asType())) 549 return "jthrowable"; 550 else if (types.isAssignable(t, jClass.asType())) 551 return "jclass"; 552 else 553 return "jobject"; 554 } 555 } 556 557 util.bug("jni.unknown.type"); 558 return null; /* dead code. */ 559 } 560 561 protected String llniType(TypeMirror t, boolean handleize, boolean longDoubleOK) { 562 String res = null; 563 564 switch (t.getKind()) { 565 case ARRAY: { 566 TypeMirror ct = ((ArrayType) t).getComponentType(); 567 switch (ct.getKind()) { 568 case BOOLEAN: res = "IArrayOfBoolean"; break; 569 case BYTE: res = "IArrayOfByte"; break; 570 case CHAR: res = "IArrayOfChar"; break; 571 case SHORT: res = "IArrayOfShort"; break; 572 case INT: res = "IArrayOfInt"; break; 573 case LONG: res = "IArrayOfLong"; break; 574 case FLOAT: res = "IArrayOfFloat"; break; 575 case DOUBLE: res = "IArrayOfDouble"; break; 576 case ARRAY: 577 case DECLARED: res = "IArrayOfRef"; break; 578 default: throw new Error(ct.getKind() + " " + ct); 579 } 580 if (!handleize) res = "DEREFERENCED_" + res; 581 break; 582 } 583 584 case VOID: 585 res = "void"; 586 break; 587 588 case BOOLEAN: 589 case BYTE: 590 case CHAR: 591 case SHORT: 592 case INT: 593 res = "java_int" ; 594 break; 595 596 case LONG: 597 res = longDoubleOK ? "java_long" : "val32 /* java_long */"; 598 break; 599 600 case FLOAT: 601 res = "java_float"; 602 break; 603 604 case DOUBLE: 605 res = longDoubleOK ? "java_double" : "val32 /* java_double */"; 606 break; 607 608 case DECLARED: 609 TypeElement e = (TypeElement) types.asElement(t); 610 res = "I" + mangleClassName(e.getQualifiedName().toString()); 611 if (!handleize) res = "DEREFERENCED_" + res; 612 break; 613 614 default: 615 throw new Error(t.getKind() + " " + t); // FIXME 616 } 617 618 return res; 619 } 620 621 protected final String cRcvrDecl(Element field, String cname) { 622 return (field.getModifiers().contains(Modifier.STATIC) ? "jclass" : "jobject"); 623 } 624 625 protected String maskName(String s) { 626 return "LLNI_mask(" + s + ")"; 627 } 628 629 protected String llniFieldName(VariableElement field) { 630 return maskName(field.getSimpleName().toString()); 631 } 632 633 protected final boolean isLongOrDouble(TypeMirror t) { 634 TypeVisitor<Boolean,Void> v = new SimpleTypeVisitor9<Boolean,Void>() { 635 @DefinedBy(Api.LANGUAGE_MODEL) 636 public Boolean defaultAction(TypeMirror t, Void p){ 637 return false; 638 } 639 @DefinedBy(Api.LANGUAGE_MODEL) 640 public Boolean visitArray(ArrayType t, Void p) { 641 return visit(t.getComponentType(), p); 642 } 643 @DefinedBy(Api.LANGUAGE_MODEL) 644 public Boolean visitPrimitive(PrimitiveType t, Void p) { 645 TypeKind tk = t.getKind(); 646 return (tk == TypeKind.LONG || tk == TypeKind.DOUBLE); 647 } 648 }; 649 return v.visit(t, null); 650 } 651 652 /* Do unicode to ansi C identifier conversion. 653 %%% This may not be right, but should be called more often. */ 654 protected final String nameToIdentifier(String name) { 655 int len = name.length(); 656 StringBuilder buf = new StringBuilder(len); 657 for (int i = 0; i < len; i++) { 658 char c = name.charAt(i); 659 if (isASCIILetterOrDigit(c)) 660 buf.append(c); 661 else if (c == '/') 662 buf.append('_'); 663 else if (c == '.') 664 buf.append('_'); 665 else if (c == '_') 666 buf.append("_1"); 667 else if (c == ';') 668 buf.append("_2"); 669 else if (c == '[') 670 buf.append("_3"); 671 else 672 buf.append("_0" + ((int)c)); 673 } 674 return new String(buf); 675 } 676 677 protected final boolean isASCIILetterOrDigit(char c) { 678 if (((c >= 'A') && (c <= 'Z')) || 679 ((c >= 'a') && (c <= 'z')) || 680 ((c >= '0') && (c <= '9'))) 681 return true; 682 else 683 return false; 684 } 685} 686 687