Gen.java revision 2674:e284f560acf6
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.ByteArrayOutputStream;
29import java.io.FileNotFoundException;
30import java.io.IOException;
31import java.io.InputStream;
32import java.io.OutputStream;
33import java.io.OutputStreamWriter;
34import java.io.PrintWriter;
35import java.io.UnsupportedEncodingException;
36import java.nio.file.NoSuchFileException;
37import java.util.ArrayList;
38import java.util.Arrays;
39import java.util.List;
40import java.util.Set;
41import java.util.Stack;
42
43import javax.annotation.processing.ProcessingEnvironment;
44import javax.lang.model.element.ExecutableElement;
45import javax.lang.model.element.Modifier;
46import javax.lang.model.element.TypeElement;
47import javax.lang.model.element.VariableElement;
48import javax.lang.model.util.ElementFilter;
49import javax.lang.model.util.Elements;
50import javax.lang.model.util.Types;
51import javax.tools.FileObject;
52import javax.tools.JavaFileManager;
53import javax.tools.JavaFileObject;
54import javax.tools.StandardLocation;
55
56/**
57 * An abstraction for generating support files required by native methods.
58 * Subclasses are for specific native interfaces. At the time of its
59 * original writing, this interface is rich enough to support JNI and the
60 * old 1.0-style native method interface.
61 *
62 * <p><b>This is NOT part of any supported API.
63 * If you write code that depends on this, you do so at your own
64 * risk.  This code and its internal interfaces are subject to change
65 * or deletion without notice.</b></p>
66 *
67 * @author  Sucheta Dambalkar(Revised)
68 */
69public abstract class Gen {
70    protected String lineSep = System.getProperty("line.separator");
71
72    protected ProcessingEnvironment processingEnvironment;
73    protected Types types;
74    protected Elements elems;
75    protected Mangle mangler;
76    protected Util util;
77
78    protected Gen(Util util) {
79        this.util = util;
80    }
81
82    /*
83     * List of classes for which we must generate output.
84     */
85    protected Set<TypeElement> classes;
86    static private final boolean isWindows =
87        System.getProperty("os.name").startsWith("Windows");
88
89
90    /**
91     * Override this abstract method, generating content for the named
92     * class into the outputstream.
93     */
94    protected abstract void write(OutputStream o, TypeElement clazz) throws Util.Exit;
95
96    /**
97     * Override this method to provide a list of #include statements
98     * required by the native interface.
99     */
100    protected abstract String getIncludes();
101
102    /*
103     * Output location.
104     */
105    protected JavaFileManager fileManager;
106    protected JavaFileObject outFile;
107
108    public void setFileManager(JavaFileManager fm) {
109        fileManager = fm;
110    }
111
112    public void setOutFile(JavaFileObject outFile) {
113        this.outFile = outFile;
114    }
115
116
117    public void setClasses(Set<TypeElement> classes) {
118        this.classes = classes;
119    }
120
121    void setProcessingEnvironment(ProcessingEnvironment pEnv) {
122        processingEnvironment = pEnv;
123        elems = pEnv.getElementUtils();
124        types = pEnv.getTypeUtils();
125        mangler = new Mangle(elems, types);
126    }
127
128    /*
129     * Smartness with generated files.
130     */
131    protected boolean force = false;
132
133    public void setForce(boolean state) {
134        force = state;
135    }
136
137    /**
138     * We explicitly need to write ASCII files because that is what C
139     * compilers understand.
140     */
141    protected PrintWriter wrapWriter(OutputStream o) throws Util.Exit {
142        try {
143            return new PrintWriter(new OutputStreamWriter(o, "ISO8859_1"), true);
144        } catch (UnsupportedEncodingException use) {
145            util.bug("encoding.iso8859_1.not.found");
146            return null; /* dead code */
147        }
148    }
149
150    /**
151     * After initializing state of an instance, use this method to start
152     * processing.
153     *
154     * Buffer size chosen as an approximation from a single sampling of:
155     *         expr `du -sk` / `ls *.h | wc -l`
156     */
157    public void run() throws IOException, ClassNotFoundException, Util.Exit {
158        int i = 0;
159        if (outFile != null) {
160            /* Everything goes to one big file... */
161            ByteArrayOutputStream bout = new ByteArrayOutputStream(8192);
162            writeFileTop(bout); /* only once */
163
164            for (TypeElement t: classes) {
165                write(bout, t);
166            }
167
168            writeIfChanged(bout.toByteArray(), outFile);
169        } else {
170            /* Each class goes to its own file... */
171            for (TypeElement t: classes) {
172                ByteArrayOutputStream bout = new ByteArrayOutputStream(8192);
173                writeFileTop(bout);
174                write(bout, t);
175                writeIfChanged(bout.toByteArray(), getFileObject(t.getQualifiedName()));
176            }
177        }
178    }
179
180    /*
181     * Write the contents of byte[] b to a file named file.  Writing
182     * is done if either the file doesn't exist or if the contents are
183     * different.
184     */
185    private void writeIfChanged(byte[] b, FileObject file) throws IOException {
186        boolean mustWrite = false;
187        String event = "[No need to update file ";
188
189        if (force) {
190            mustWrite = true;
191            event = "[Forcefully writing file ";
192        } else {
193            InputStream in;
194            byte[] a;
195            try {
196                // regrettably, there's no API to get the length in bytes
197                // for a FileObject, so we can't short-circuit reading the
198                // file here
199                in = file.openInputStream();
200                a = readBytes(in);
201                if (!Arrays.equals(a, b)) {
202                    mustWrite = true;
203                    event = "[Overwriting file ";
204
205                }
206            } catch (FileNotFoundException | NoSuchFileException e) {
207                mustWrite = true;
208                event = "[Creating file ";
209            }
210        }
211
212        if (util.verbose)
213            util.log(event + file + "]");
214
215        if (mustWrite) {
216            OutputStream out = file.openOutputStream();
217            out.write(b); /* No buffering, just one big write! */
218            out.close();
219        }
220    }
221
222    protected byte[] readBytes(InputStream in) throws IOException {
223        try {
224            byte[] array = new byte[in.available() + 1];
225            int offset = 0;
226            int n;
227            while ((n = in.read(array, offset, array.length - offset)) != -1) {
228                offset += n;
229                if (offset == array.length)
230                    array = Arrays.copyOf(array, array.length * 2);
231            }
232
233            return Arrays.copyOf(array, offset);
234        } finally {
235            in.close();
236        }
237    }
238
239    protected String defineForStatic(TypeElement c, VariableElement f)
240            throws Util.Exit {
241        CharSequence cnamedoc = c.getQualifiedName();
242        CharSequence fnamedoc = f.getSimpleName();
243
244        String cname = mangler.mangle(cnamedoc, Mangle.Type.CLASS);
245        String fname = mangler.mangle(fnamedoc, Mangle.Type.FIELDSTUB);
246
247        if (!f.getModifiers().contains(Modifier.STATIC))
248            util.bug("tried.to.define.non.static");
249
250        if (f.getModifiers().contains(Modifier.FINAL)) {
251            Object value = null;
252
253            value = f.getConstantValue();
254
255            if (value != null) { /* so it is a ConstantExpression */
256                String constString = null;
257                if ((value instanceof Integer)
258                    || (value instanceof Byte)
259                    || (value instanceof Short)) {
260                    /* covers byte, short, int */
261                    constString = value.toString() + "L";
262                } else if (value instanceof Boolean) {
263                    constString = ((Boolean) value) ? "1L" : "0L";
264                } else if (value instanceof Character) {
265                    Character ch = (Character) value;
266                    constString = String.valueOf(((int) ch) & 0xffff) + "L";
267                } else if (value instanceof Long) {
268                    // Visual C++ supports the i64 suffix, not LL.
269                    if (isWindows)
270                        constString = value.toString() + "i64";
271                    else
272                        constString = value.toString() + "LL";
273                } else if (value instanceof Float) {
274                    /* bug for bug */
275                    float fv = ((Float)value).floatValue();
276                    if (Float.isInfinite(fv))
277                        constString = ((fv < 0) ? "-" : "") + "Inff";
278                    else
279                        constString = value.toString() + "f";
280                } else if (value instanceof Double) {
281                    /* bug for bug */
282                    double d = ((Double)value).doubleValue();
283                    if (Double.isInfinite(d))
284                        constString = ((d < 0) ? "-" : "") + "InfD";
285                    else
286                        constString = value.toString();
287                }
288                if (constString != null) {
289                    StringBuilder s = new StringBuilder("#undef ");
290                    s.append(cname); s.append("_"); s.append(fname); s.append(lineSep);
291                    s.append("#define "); s.append(cname); s.append("_");
292                    s.append(fname); s.append(" "); s.append(constString);
293                    return s.toString();
294                }
295
296            }
297        }
298        return null;
299    }
300
301    /*
302     * Deal with the C pre-processor.
303     */
304    protected String cppGuardBegin() {
305        return "#ifdef __cplusplus" + lineSep + "extern \"C\" {" + lineSep + "#endif";
306    }
307
308    protected String cppGuardEnd() {
309        return "#ifdef __cplusplus" + lineSep + "}" + lineSep + "#endif";
310    }
311
312    protected String guardBegin(String cname) {
313        return "/* Header for class " + cname + " */" + lineSep + lineSep +
314            "#ifndef _Included_" + cname + lineSep +
315            "#define _Included_" + cname;
316    }
317
318    protected String guardEnd(String cname) {
319        return "#endif";
320    }
321
322    /*
323     * File name and file preamble related operations.
324     */
325    protected void writeFileTop(OutputStream o) throws Util.Exit {
326        PrintWriter pw = wrapWriter(o);
327        pw.println("/* DO NOT EDIT THIS FILE - it is machine generated */" + lineSep +
328                   getIncludes());
329    }
330
331    protected String baseFileName(CharSequence className) {
332        return mangler.mangle(className, Mangle.Type.CLASS);
333    }
334
335    protected FileObject getFileObject(CharSequence className) throws IOException {
336        String name = baseFileName(className) + getFileSuffix();
337        return fileManager.getFileForOutput(StandardLocation.SOURCE_OUTPUT, "", name, null);
338    }
339
340    protected String getFileSuffix() {
341        return ".h";
342    }
343
344    /**
345     * Including super classes' fields.
346     */
347
348    List<VariableElement> getAllFields(TypeElement subclazz) {
349        List<VariableElement> fields = new ArrayList<>();
350        TypeElement cd = null;
351        Stack<TypeElement> s = new Stack<>();
352
353        cd = subclazz;
354        while (true) {
355            s.push(cd);
356            TypeElement c = (TypeElement) (types.asElement(cd.getSuperclass()));
357            if (c == null)
358                break;
359            cd = c;
360        }
361
362        while (!s.empty()) {
363            cd = s.pop();
364            fields.addAll(ElementFilter.fieldsIn(cd.getEnclosedElements()));
365        }
366
367        return fields;
368    }
369
370    // c.f. MethodDoc.signature
371    String signature(ExecutableElement e) {
372        StringBuilder sb = new StringBuilder("(");
373        String sep = "";
374        for (VariableElement p: e.getParameters()) {
375            sb.append(sep);
376            sb.append(types.erasure(p.asType()).toString());
377            sep = ",";
378        }
379        sb.append(")");
380        return sb.toString();
381    }
382}
383
384