001: /*
002: * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.tools.javah;
027:
028: import java.io.File;
029: import java.io.OutputStream;
030: import java.io.PrintWriter;
031: import java.util.Hashtable;
032: import com.sun.javadoc.*;
033:
034: /*
035: * @author Sucheta Dambalkar(Revised)
036: */
037: public class LLNI extends Gen {
038:
039: protected final char pathChar = File.separatorChar;
040: protected final char innerDelim = '$'; /* For inner classes */
041: protected Hashtable doneHandleTypes;
042: MemberDoc[] fields;
043: MemberDoc[] methods;
044: private boolean doubleAlign;
045: private int padFieldNum = 0;
046:
047: LLNI(boolean doubleAlign, RootDoc root) {
048: super (root);
049: this .doubleAlign = doubleAlign;
050: }
051:
052: protected String getIncludes() {
053: return "";
054: }
055:
056: protected void write(OutputStream o, ClassDoc clazz)
057: throws ClassNotFoundException {
058: String cname = mangleClassName(clazz.qualifiedName());
059: PrintWriter pw = wrapWriter(o);
060: fields = clazz.fields();
061: methods = clazz.methods();
062: generateDeclsForClass(pw, clazz, cname);
063: }
064:
065: protected void generateDeclsForClass(PrintWriter pw,
066: ClassDoc clazz, String cname) throws ClassNotFoundException {
067: doneHandleTypes = new Hashtable();
068: /* The following handle types are predefined in "typedefs.h". Suppress
069: inclusion in the output by generating them "into the blue" here. */
070: genHandleType(null, "java.lang.Class");
071: genHandleType(null, "java.lang.ClassLoader");
072: genHandleType(null, "java.lang.Object");
073: genHandleType(null, "java.lang.String");
074: genHandleType(null, "java.lang.Thread");
075: genHandleType(null, "java.lang.ThreadGroup");
076: genHandleType(null, "java.lang.Throwable");
077:
078: pw.println("/* LLNI Header for class " + clazz.qualifiedName()
079: + " */" + lineSep);
080: pw.println("#ifndef _Included_" + cname);
081: pw.println("#define _Included_" + cname);
082: pw.println("#include \"typedefs.h\"");
083: pw.println("#include \"llni.h\"");
084: pw.println("#include \"jni.h\"" + lineSep);
085:
086: forwardDecls(pw, clazz);
087: structSectionForClass(pw, clazz, cname);
088: methodSectionForClass(pw, clazz, cname);
089: pw.println("#endif");
090: }
091:
092: protected void genHandleType(PrintWriter pw, String clazzname) {
093: String cname = mangleClassName(clazzname);
094: if (!doneHandleTypes.containsKey(cname)) {
095: doneHandleTypes.put(cname, cname);
096: if (pw != null) {
097: pw.println("#ifndef DEFINED_" + cname);
098: pw.println(" #define DEFINED_" + cname);
099: pw.println(" GEN_HANDLE_TYPES(" + cname + ");");
100: pw.println("#endif" + lineSep);
101: }
102: }
103: }
104:
105: protected String mangleClassName(String s) {
106: return s.replace('.', '_').replace(pathChar, '_').replace(
107: innerDelim, '_');
108: }
109:
110: protected void forwardDecls(PrintWriter pw, ClassDoc clazz)
111: throws ClassNotFoundException {
112: ClassDoc clazzfield = null;
113:
114: if (clazz.qualifiedName().equals("java.lang.Object"))
115: return;
116: genHandleType(pw, clazz.qualifiedName());
117: ClassDoc super Class = clazz.super class();
118:
119: if (super Class != null) {
120: String super ClassName = super Class.qualifiedName();
121: forwardDecls(pw, super Class);
122: }
123:
124: for (int i = 0; i < fields.length; i++) {
125: FieldDoc field = (FieldDoc) fields[i];
126:
127: if (!field.isStatic()) {
128: Type t = field.type();
129: String tname = t.qualifiedTypeName();
130: TypeSignature newTypeSig = new TypeSignature(root);
131: String sig = newTypeSig.getTypeSignature(tname);
132:
133: if (sig.charAt(0) != '[')
134: forwardDeclsFromSig(pw, sig);
135: }
136: }
137:
138: for (int i = 0; i < methods.length; i++) {
139: MethodDoc method = (MethodDoc) methods[i];
140:
141: if (method.isNative()) {
142: Type retType = method.returnType();
143: String typesig = method.signature();
144: TypeSignature newTypeSig = new TypeSignature(root);
145: String sig = newTypeSig.getTypeSignature(typesig,
146: retType);
147:
148: if (sig.charAt(0) != '[')
149: forwardDeclsFromSig(pw, sig);
150:
151: }
152: }
153: }
154:
155: protected void forwardDeclsFromSig(PrintWriter pw, String sig) {
156: int len = sig.length();
157: int i = sig.charAt(0) == '(' ? 1 : 0;
158:
159: /* Skip the initial "(". */
160: while (i < len) {
161: if (sig.charAt(i) == 'L') {
162: int j = i + 1;
163: while (sig.charAt(j) != ';')
164: j++;
165: genHandleType(pw, sig.substring(i + 1, j));
166: i = j + 1;
167: } else {
168: i++;
169: }
170: }
171: }
172:
173: protected void structSectionForClass(PrintWriter pw,
174: ClassDoc jclazz, String cname)
175: throws ClassNotFoundException {
176:
177: String jname = jclazz.qualifiedName();
178:
179: if (cname.equals("java_lang_Object")) {
180: pw
181: .println("/* struct java_lang_Object is defined in typedefs.h. */");
182: pw.println();
183: return;
184: }
185: pw.println("#if !defined(__i386)");
186: pw.println("#pragma pack(4)");
187: pw.println("#endif");
188: pw.println();
189: pw.println("struct " + cname + " {");
190: pw.println(" ObjHeader h;");
191: pw.print(fieldDefs(jclazz, cname));
192:
193: if (jname.equals("java.lang.Class"))
194: pw.println(" Class *LLNI_mask(cClass);"
195: + " /* Fake field; don't access (see oobj.h) */");
196: pw.println("};" + lineSep + lineSep + "#pragma pack()");
197: pw.println();
198: return;
199: }
200:
201: private static class FieldDefsRes {
202: public String className; /* Name of the current class. */
203: public FieldDefsRes parent;
204: public String s;
205: public int byteSize;
206: public boolean bottomMost;
207: public boolean printedOne = false;
208:
209: FieldDefsRes(ClassDoc clazz, FieldDefsRes parent,
210: boolean bottomMost) {
211: this .className = clazz.qualifiedName();
212: this .parent = parent;
213: this .bottomMost = bottomMost;
214: int byteSize = 0;
215: if (parent == null)
216: this .s = "";
217: else
218: this .s = parent.s;
219: }
220: }
221:
222: /* Returns "true" iff added a field. */
223: private boolean doField(FieldDefsRes res, FieldDoc field,
224: String cname, boolean padWord)
225: throws ClassNotFoundException {
226:
227: String fieldDef = addStructMember(field, cname, padWord);
228: if (fieldDef != null) {
229: if (!res.printedOne) { /* add separator */
230: if (res.bottomMost) {
231: if (res.s.length() != 0)
232: res.s = res.s + " /* local members: */"
233: + lineSep;
234: } else {
235: res.s = res.s + " /* inherited members from "
236: + res.className + ": */" + lineSep;
237: }
238: res.printedOne = true;
239: }
240: res.s = res.s + fieldDef;
241: return true;
242: }
243:
244: // Otherwise.
245: return false;
246: }
247:
248: private int doTwoWordFields(FieldDefsRes res, ClassDoc clazz,
249: int offset, String cname, boolean padWord)
250: throws ClassNotFoundException {
251: boolean first = true;
252: FieldDoc[] fields = clazz.fields();
253:
254: for (int i = 0; i < fields.length; i++) {
255: FieldDoc field = fields[i];
256: String tc = field.type().typeName();
257: boolean twoWords = (tc.equals("long") || tc
258: .equals("double"));
259: if (twoWords
260: && doField(res, field, cname, first && padWord)) {
261: offset += 8;
262: first = false;
263: }
264: }
265: return offset;
266: }
267:
268: protected String fieldDefs(ClassDoc clazz, String cname)
269: throws ClassNotFoundException {
270: FieldDefsRes res = fieldDefs(clazz, cname, true);
271: return res.s;
272: }
273:
274: protected FieldDefsRes fieldDefs(ClassDoc clazz, String cname,
275: boolean bottomMost) throws ClassNotFoundException {
276: FieldDefsRes res;
277: int offset;
278: boolean didTwoWordFields = false;
279: ClassDoc super clazz = clazz.super class();
280:
281: if (super clazz != null) {
282: String super name = super clazz.qualifiedName();
283: res = new FieldDefsRes(clazz, fieldDefs(super clazz, cname,
284: false), bottomMost);
285: offset = res.parent.byteSize;
286: } else {
287: res = new FieldDefsRes(clazz, null, bottomMost);
288: offset = 0;
289: }
290:
291: FieldDoc[] fields = clazz.fields();
292:
293: for (int i = 0; i < fields.length; i++) {
294: FieldDoc field = fields[i];
295:
296: if (doubleAlign && !didTwoWordFields && (offset % 8) == 0) {
297: offset = doTwoWordFields(res, clazz, offset, cname,
298: false);
299: didTwoWordFields = true;
300: }
301:
302: String tc = field.type().typeName();
303: boolean twoWords = (tc.equals("long") || tc
304: .equals("double"));
305:
306: if (!doubleAlign || !twoWords) {
307: if (doField(res, field, cname, false))
308: offset += 4;
309: }
310:
311: }
312:
313: if (doubleAlign && !didTwoWordFields) {
314: if ((offset % 8) != 0)
315: offset += 4;
316: offset = doTwoWordFields(res, clazz, offset, cname, true);
317: }
318:
319: res.byteSize = offset;
320: return res;
321: }
322:
323: /* OVERRIDE: This method handles instance fields */
324: protected String addStructMember(FieldDoc member, String cname,
325: boolean padWord) throws ClassNotFoundException {
326: String res = null;
327:
328: if (member.isStatic()) {
329: res = addStaticStructMember(member, cname);
330: // if (res == null) /* JNI didn't handle it, print comment. */
331: // res = " /* Inaccessible static: " + member + " */" + lineSep;
332: } else {
333: if (padWord)
334: res = " java_int padWord" + padFieldNum++ + ";"
335: + lineSep;
336: res = " " + llniType(member.type(), false, false) + " "
337: + llniFieldName(member);
338: if (isLongOrDouble(member.type()))
339: res = res + "[2]";
340: res = res + ";" + lineSep;
341: }
342: return res;
343: }
344:
345: static private final boolean isWindows = System.getProperty(
346: "os.name").startsWith("Windows");
347:
348: /*
349: * This method only handles static final fields.
350: */
351: protected String addStaticStructMember(FieldDoc field, String cname)
352: throws ClassNotFoundException {
353: String res = null;
354: Object exp = null;
355:
356: if (!field.isStatic())
357: return res;
358: if (!field.isFinal())
359: return res;
360:
361: exp = field.constantValue();
362:
363: if (exp != null) {
364: /* Constant. */
365:
366: String cn = cname + "_" + field.name();
367: String suffix = null;
368: long val = 0;
369: /* Can only handle int, long, float, and double fields. */
370: if (exp instanceof Integer) {
371: suffix = "L";
372: val = ((Integer) exp).intValue();
373: }
374: if (exp instanceof Long) {
375: // Visual C++ supports the i64 suffix, not LL
376: suffix = isWindows ? "i64" : "LL";
377: val = ((Long) exp).longValue();
378: }
379: if (exp instanceof Float)
380: suffix = "f";
381: if (exp instanceof Double)
382: suffix = "";
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 + " (" + (val + 1)
391: + suffix + "-1)" + lineSep;
392: } else {
393: res = " #undef " + cn + lineSep
394: + " #define " + cn + " "
395: + exp.toString() + suffix + lineSep;
396: }
397: }
398: }
399: return res;
400: }
401:
402: protected void methodSectionForClass(PrintWriter pw,
403: ClassDoc clazz, String cname) throws ClassNotFoundException {
404: String methods = methodDecls(clazz, cname);
405:
406: if (methods.length() != 0) {
407: pw.println("/* Native method declarations: */" + lineSep);
408: pw.println("#ifdef __cplusplus");
409: pw.println("extern \"C\" {");
410: pw.println("#endif" + lineSep);
411: pw.println(methods);
412: pw.println("#ifdef __cplusplus");
413: pw.println("}");
414: pw.println("#endif");
415: }
416: }
417:
418: protected String methodDecls(ClassDoc clazz, String cname)
419: throws ClassNotFoundException {
420:
421: String res = "";
422: for (int i = 0; i < methods.length; i++) {
423: MethodDoc method = (MethodDoc) methods[i];
424: if (method.isNative())
425: res = res + methodDecl(method, clazz, cname);
426: }
427: return res;
428: }
429:
430: protected String methodDecl(MethodDoc method, ClassDoc clazz,
431: String cname) throws ClassNotFoundException {
432: String res = null;
433:
434: Type retType = method.returnType();
435: String typesig = method.signature();
436: TypeSignature newTypeSig = new TypeSignature(root);
437: String sig = newTypeSig.getTypeSignature(typesig, retType);
438: boolean longName = needLongName(method, clazz);
439:
440: if (sig.charAt(0) != '(')
441: Util.error("invalid.method.signature", sig);
442:
443: res = "JNIEXPORT " + jniType(retType) + " JNICALL" + lineSep
444: + jniMethodName(method, cname, longName)
445: + "(JNIEnv *, " + cRcvrDecl(method, cname);
446: Parameter[] params = method.parameters();
447: Type argTypes[] = new Type[params.length];
448: for (int p = 0; p < params.length; p++) {
449: argTypes[p] = params[p].type();
450: }
451:
452: /* It would have been nice to include the argument names in the
453: declaration, but there seems to be a bug in the "BinaryField"
454: class, causing the getArguments() method to return "null" for
455: most (non-constructor) methods. */
456: for (int i = 0; i < argTypes.length; i++)
457: res = res + ", " + jniType(argTypes[i]);
458: res = res + ");" + lineSep;
459: return res;
460: }
461:
462: protected final boolean needLongName(MethodDoc method,
463: ClassDoc clazz) throws ClassNotFoundException {
464: String methodName = method.name();
465: for (int i = 0; i < methods.length; i++) {
466: MethodDoc memberMethod = (MethodDoc) methods[i];
467: if ((memberMethod != method) && memberMethod.isNative()
468: && (methodName == memberMethod.name()))
469: return true;
470: }
471: return false;
472: }
473:
474: protected final String jniMethodName(MethodDoc method,
475: String cname, boolean longName) {
476: String res = "Java_" + cname + "_" + method.name();
477:
478: if (longName) {
479: Type mType = method.returnType();
480: Parameter[] params = method.parameters();
481: Type argTypes[] = new Type[params.length];
482: for (int p = 0; p < params.length; p++) {
483: argTypes[p] = params[p].type();
484: }
485:
486: res = res + "__";
487: for (int i = 0; i < argTypes.length; i++) {
488: Type t = argTypes[i];
489: String tname = t.typeName();
490: TypeSignature newTypeSig = new TypeSignature(root);
491: String sig = newTypeSig.getTypeSignature(tname);
492: res = res + nameToIdentifier(sig);
493: }
494: }
495: return res;
496: }
497:
498: protected final String jniType(Type t) {
499: String elmT = t.typeName();
500: if (t.dimension().indexOf("[]") != -1) {
501: if (elmT.equals("boolean"))
502: return "jbooleanArray";
503: else if (elmT.equals("byte"))
504: return "jbyteArray";
505: else if (elmT.equals("char"))
506: return "jcharArray";
507: else if (elmT.equals("short"))
508: return "jshortArray";
509: else if (elmT.equals("int"))
510: return "jintArray";
511: else if (elmT.equals("long"))
512: return "jlongArray";
513: else if (elmT.equals("float"))
514: return "jfloatArray";
515: else if (elmT.equals("double"))
516: return "jdoubleArray";
517: else if ((t.dimension().indexOf("[][]") != -1)
518: || (t.asClassDoc() != null))
519: return "jobjectArray";
520: } else {
521: if (elmT.equals("void"))
522: return "void";
523: else if (elmT.equals("boolean"))
524: return "jboolean";
525: else if (elmT.equals("byte"))
526: return "jbyte";
527: else if (elmT.equals("char"))
528: return "jchar";
529: else if (elmT.equals("short"))
530: return "jshort";
531: else if (elmT.equals("int"))
532: return "jint";
533: else if (elmT.equals("long"))
534: return "jlong";
535: else if (elmT.equals("float"))
536: return "jfloat";
537: else if (elmT.equals("double"))
538: return "jdouble";
539: else if (t.asClassDoc() != null) {
540: if (elmT.equals("String"))
541: return "jstring";
542: else if (t.asClassDoc().subclassOf(
543: root.classNamed("java.lang.Class")))
544: return "jclass";
545: else
546: return "jobject";
547: }
548: }
549: Util.bug("jni.unknown.type");
550: return null; /* dead code. */
551: }
552:
553: protected String llniType(Type t, boolean handleize,
554: boolean longDoubleOK) {
555: String res = null;
556: String elmt = t.typeName();
557: if (t.dimension().indexOf("[]") != -1) {
558: if ((t.dimension().indexOf("[][]") != -1)
559: || (t.asClassDoc() != null))
560: res = "IArrayOfRef";
561: else if (elmt.equals("boolean"))
562: res = "IArrayOfBoolean";
563: else if (elmt.equals("byte"))
564: res = "IArrayOfByte";
565: else if (elmt.equals("char"))
566: res = "IArrayOfChar";
567: else if (elmt.equals("int"))
568: res = "IArrayOfInt";
569: else if (elmt.equals("long"))
570: res = "IArrayOfLong";
571: else if (elmt.equals("float"))
572: res = "IArrayOfFloat";
573: else if (elmt.equals("double"))
574: res = "IArrayOfDouble";
575: if (!handleize)
576: res = "DEREFERENCED_" + res;
577: } else {
578: if (elmt.equals("void"))
579: res = "void";
580: else if ((elmt.equals("boolean")) || (elmt.equals("byte"))
581: || (elmt.equals("char")) || (elmt.equals("short"))
582: || (elmt.equals("int")))
583: res = "java_int";
584: else if (elmt.equals("long"))
585: res = longDoubleOK ? "java_long"
586: : "val32 /* java_long */";
587: else if (elmt.equals("float"))
588: res = "java_float";
589: else if (elmt.equals("double"))
590: res = res = longDoubleOK ? "java_double"
591: : "val32 /* java_double */";
592: else if (t.asClassDoc() != null) {
593: res = "I"
594: + mangleClassName(t.asClassDoc()
595: .qualifiedName());
596: if (!handleize)
597: res = "DEREFERENCED_" + res;
598: }
599: }
600: return res;
601: }
602:
603: protected final String cRcvrDecl(MemberDoc field, String cname) {
604: return (field.isStatic() ? "jclass" : "jobject");
605: }
606:
607: protected String maskName(String s) {
608: return "LLNI_mask(" + s + ")";
609: }
610:
611: protected String llniFieldName(MemberDoc field) {
612: return maskName(field.name());
613: }
614:
615: protected final boolean isLongOrDouble(Type t) {
616: String tc = t.typeName();
617: return (tc.equals("long") || tc.equals("double"));
618: }
619:
620: /* Do unicode to ansi C identifier conversion.
621: %%% This may not be right, but should be called more often. */
622: protected final String nameToIdentifier(String name) {
623: int len = name.length();
624: StringBuffer buf = new StringBuffer(len);
625: for (int i = 0; i < len; i++) {
626: char c = name.charAt(i);
627: if (isASCIILetterOrDigit(c))
628: buf.append(c);
629: else if (c == '/')
630: buf.append('_');
631: else if (c == '.')
632: buf.append('_');
633: else if (c == '_')
634: buf.append("_1");
635: else if (c == ';')
636: buf.append("_2");
637: else if (c == '[')
638: buf.append("_3");
639: else
640: buf.append("_0" + ((int) c));
641: }
642: return new String(buf);
643: }
644:
645: protected final boolean isASCIILetterOrDigit(char c) {
646: if (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z'))
647: || ((c >= '0') && (c <= '9')))
648: return true;
649: else
650: return false;
651: }
652: }
|