Mon, 26 Mar 2012 15:27:51 +0100
7151580: Separate DA/DU logic from exception checking logic in Flow.java
Summary: DA/DU analysis and exception checking analysis should live in two separate tree visitors
Reviewed-by: gafter, dlsmith, jjg
1 /*
2 * Copyright (c) 2002, 2011, 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 */
27 package com.sun.tools.javah;
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;
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.SimpleTypeVisitor8;
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 {
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;
67 LLNI(boolean doubleAlign, Util util) {
68 super(util);
69 this.doubleAlign = doubleAlign;
70 }
72 protected String getIncludes() {
73 return "";
74 }
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 }
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");
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);
110 forwardDecls(pw, clazz);
111 structSectionForClass(pw, clazz, cname);
112 methodSectionForClass(pw, clazz, cname);
113 pw.println("#endif");
114 }
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 }
129 protected String mangleClassName(String s) {
130 return s.replace('.', '_')
131 .replace('/', '_')
132 .replace(innerDelim, '_');
133 }
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;
141 genHandleType(pw, clazz.getQualifiedName().toString());
142 TypeElement superClass = (TypeElement) (types.asElement(clazz.getSuperclass()));
144 if (superClass != null) {
145 String superClassName = superClass.getQualifiedName().toString();
146 forwardDecls(pw, superClass);
147 }
149 for (VariableElement field: fields) {
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);
157 if (sig.charAt(0) != '[')
158 forwardDeclsFromSig(pw, sig);
159 }
160 }
162 for (ExecutableElement method: methods) {
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);
170 if (sig.charAt(0) != '[')
171 forwardDeclsFromSig(pw, sig);
173 }
174 }
175 }
177 protected void forwardDeclsFromSig(PrintWriter pw, String sig) {
178 int len = sig.length();
179 int i = sig.charAt(0) == '(' ? 1 : 0;
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 }
194 protected void structSectionForClass(PrintWriter pw,
195 TypeElement jclazz, String cname) {
197 String jname = jclazz.getQualifiedName().toString();
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));
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 }
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;
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 }
238 /* Returns "true" iff added a field. */
239 private boolean doField(FieldDefsRes res, VariableElement field,
240 String cname, boolean padWord) {
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 }
258 // Otherwise.
259 return false;
260 }
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());
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 }
277 String fieldDefs(TypeElement clazz, String cname) {
278 FieldDefsRes res = fieldDefs(clazz, cname, true);
279 return res.s;
280 }
282 FieldDefsRes fieldDefs(TypeElement clazz, String cname,
283 boolean bottomMost){
284 FieldDefsRes res;
285 int offset;
286 boolean didTwoWordFields = false;
288 TypeElement superclazz = (TypeElement) types.asElement(clazz.getSuperclass());
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 }
301 List<VariableElement> fields = ElementFilter.fieldsIn(clazz.getEnclosedElements());
303 for (VariableElement field: fields) {
305 if (doubleAlign && !didTwoWordFields && (offset % 8) == 0) {
306 offset = doTwoWordFields(res, clazz, offset, cname, false);
307 didTwoWordFields = true;
308 }
310 TypeKind tk = field.asType().getKind();
311 boolean twoWords = (tk == TypeKind.LONG || tk == TypeKind.DOUBLE);
313 if (!doubleAlign || !twoWords) {
314 if (doField(res, field, cname, false)) offset += 4;
315 }
317 }
319 if (doubleAlign && !didTwoWordFields) {
320 if ((offset % 8) != 0) offset += 4;
321 offset = doTwoWordFields(res, clazz, offset, cname, true);
322 }
324 res.byteSize = offset;
325 return res;
326 }
328 /* OVERRIDE: This method handles instance fields */
329 protected String addStructMember(VariableElement member, String cname,
330 boolean padWord) {
331 String res = null;
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 }
347 static private final boolean isWindows =
348 System.getProperty("os.name").startsWith("Windows");
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;
357 if (!field.getModifiers().contains(Modifier.STATIC))
358 return res;
359 if (!field.getModifiers().contains(Modifier.FINAL))
360 return res;
362 exp = field.getConstantValue();
364 if (exp != null) {
365 /* Constant. */
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 }
410 protected void methodSectionForClass(PrintWriter pw,
411 TypeElement clazz, String cname)
412 throws TypeSignature.SignatureException, Util.Exit {
413 String methods = methodDecls(clazz, cname);
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 }
427 protected String methodDecls(TypeElement clazz, String cname)
428 throws TypeSignature.SignatureException, Util.Exit {
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 }
438 protected String methodDecl(ExecutableElement method,
439 TypeElement clazz, String cname)
440 throws TypeSignature.SignatureException, Util.Exit {
441 String res = null;
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);
449 if (sig.charAt(0) != '(')
450 util.error("invalid.method.signature", sig);
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 }
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 }
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 }
483 protected final String jniMethodName(ExecutableElement method, String cname,
484 boolean longName)
485 throws TypeSignature.SignatureException {
486 String res = "Java_" + cname + "_" + method.getSimpleName();
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 }
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 }
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);
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 }
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";
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 }
554 util.bug("jni.unknown.type");
555 return null; /* dead code. */
556 }
558 protected String llniType(TypeMirror t, boolean handleize, boolean longDoubleOK) {
559 String res = null;
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 }
581 case VOID:
582 res = "void";
583 break;
585 case BOOLEAN:
586 case BYTE:
587 case CHAR:
588 case SHORT:
589 case INT:
590 res = "java_int" ;
591 break;
593 case LONG:
594 res = longDoubleOK ? "java_long" : "val32 /* java_long */";
595 break;
597 case FLOAT:
598 res = "java_float";
599 break;
601 case DOUBLE:
602 res = longDoubleOK ? "java_double" : "val32 /* java_double */";
603 break;
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;
611 default:
612 throw new Error(t.getKind() + " " + t); // FIXME
613 }
615 return res;
616 }
618 protected final String cRcvrDecl(Element field, String cname) {
619 return (field.getModifiers().contains(Modifier.STATIC) ? "jclass" : "jobject");
620 }
622 protected String maskName(String s) {
623 return "LLNI_mask(" + s + ")";
624 }
626 protected String llniFieldName(VariableElement field) {
627 return maskName(field.getSimpleName().toString());
628 }
630 protected final boolean isLongOrDouble(TypeMirror t) {
631 TypeVisitor<Boolean,Void> v = new SimpleTypeVisitor8<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 }
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 }
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 }