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