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