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