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