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