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