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