25 |
25 |
26 package com.sun.tools.javah; |
26 package com.sun.tools.javah; |
27 |
27 |
28 import java.io.UnsupportedEncodingException; |
28 import java.io.UnsupportedEncodingException; |
29 import java.io.ByteArrayOutputStream; |
29 import java.io.ByteArrayOutputStream; |
|
30 import java.io.FileNotFoundException; |
30 import java.io.IOException; |
31 import java.io.IOException; |
|
32 import java.io.InputStream; |
31 import java.io.OutputStream; |
33 import java.io.OutputStream; |
|
34 import java.io.OutputStreamWriter; |
32 import java.io.PrintWriter; |
35 import java.io.PrintWriter; |
33 import com.sun.javadoc.*; |
36 import java.util.ArrayList; |
34 import java.io.*; |
37 import java.util.Arrays; |
|
38 import java.util.List; |
|
39 import java.util.Set; |
35 import java.util.Stack; |
40 import java.util.Stack; |
36 import java.util.Vector; |
41 |
37 import java.util.Arrays; |
42 import javax.annotation.processing.ProcessingEnvironment; |
38 |
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; |
39 |
56 |
40 /** |
57 /** |
41 * An abstraction for generating support files required by native methods. |
58 * An abstraction for generating support files required by native methods. |
42 * Subclasses are for specific native interfaces. At the time of its |
59 * Subclasses are for specific native interfaces. At the time of its |
43 * original writing, this interface is rich enough to support JNI and the |
60 * original writing, this interface is rich enough to support JNI and the |
44 * old 1.0-style native method interface. |
61 * old 1.0-style native method interface. |
45 * |
62 * |
|
63 * <p><b>This is NOT part of any API supported by Sun Microsystems. |
|
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 * |
46 * @author Sucheta Dambalkar(Revised) |
68 * @author Sucheta Dambalkar(Revised) |
47 */ |
69 */ |
48 |
|
49 |
|
50 public abstract class Gen { |
70 public abstract class Gen { |
51 protected String lineSep = System.getProperty("line.separator"); |
71 protected String lineSep = System.getProperty("line.separator"); |
52 |
72 |
53 RootDoc root; |
73 protected ProcessingEnvironment processingEnvironment; |
|
74 protected Types types; |
|
75 protected Elements elems; |
|
76 protected Mangle mangler; |
|
77 protected Util util; |
|
78 |
|
79 protected Gen(Util util) { |
|
80 this.util = util; |
|
81 } |
|
82 |
54 /* |
83 /* |
55 * List of classes for which we must generate output. |
84 * List of classes for which we must generate output. |
56 */ |
85 */ |
57 protected ClassDoc[] classes; |
86 protected Set<TypeElement> classes; |
58 static private final boolean isWindows = |
87 static private final boolean isWindows = |
59 System.getProperty("os.name").startsWith("Windows"); |
88 System.getProperty("os.name").startsWith("Windows"); |
60 |
89 |
61 public Gen(RootDoc root){ |
|
62 this.root = root; |
|
63 } |
|
64 |
90 |
65 /** |
91 /** |
66 * Override this abstract method, generating content for the named |
92 * Override this abstract method, generating content for the named |
67 * class into the outputstream. |
93 * class into the outputstream. |
68 */ |
94 */ |
69 protected abstract void write(OutputStream o, ClassDoc clazz) |
95 protected abstract void write(OutputStream o, TypeElement clazz) throws Util.Exit; |
70 throws ClassNotFoundException; |
|
71 |
96 |
72 /** |
97 /** |
73 * Override this method to provide a list of #include statements |
98 * Override this method to provide a list of #include statements |
74 * required by the native interface. |
99 * required by the native interface. |
75 */ |
100 */ |
76 protected abstract String getIncludes(); |
101 protected abstract String getIncludes(); |
77 |
102 |
78 /* |
103 /* |
79 * Output location. |
104 * Output location. |
80 */ |
105 */ |
81 protected String outDir; |
106 protected JavaFileManager fileManager; |
82 protected String outFile; |
107 protected JavaFileObject outFile; |
83 |
108 |
84 public void setOutDir(String outDir) { |
109 public void setFileManager(JavaFileManager fm) { |
85 /* Check important, otherwise concatenation of two null strings |
110 fileManager = fm; |
86 * produces the "nullnull" String. |
111 } |
87 */ |
112 |
88 if (outDir != null) { |
113 public void setOutFile(JavaFileObject outFile) { |
89 this.outDir = outDir + System.getProperty("file.separator"); |
|
90 File d = new File(outDir); |
|
91 if (!d.exists()) |
|
92 if (!d.mkdirs()) |
|
93 Util.error("cant.create.dir", d.toString()); |
|
94 } |
|
95 } |
|
96 |
|
97 public void setOutFile(String outFile) { |
|
98 this.outFile = outFile; |
114 this.outFile = outFile; |
99 } |
115 } |
100 |
116 |
101 |
117 |
102 public void setClasses(ClassDoc[] classes) { |
118 public void setClasses(Set<TypeElement> classes) { |
103 this.classes = classes; |
119 this.classes = classes; |
|
120 } |
|
121 |
|
122 void setProcessingEnvironment(ProcessingEnvironment pEnv) { |
|
123 processingEnvironment = pEnv; |
|
124 elems = pEnv.getElementUtils(); |
|
125 types = pEnv.getTypeUtils(); |
|
126 mangler = new Mangle(elems, types); |
104 } |
127 } |
105 |
128 |
106 /* |
129 /* |
107 * Smartness with generated files. |
130 * Smartness with generated files. |
108 */ |
131 */ |
131 * processing. |
153 * processing. |
132 * |
154 * |
133 * Buffer size chosen as an approximation from a single sampling of: |
155 * Buffer size chosen as an approximation from a single sampling of: |
134 * expr `du -sk` / `ls *.h | wc -l` |
156 * expr `du -sk` / `ls *.h | wc -l` |
135 */ |
157 */ |
136 public void run() throws IOException, ClassNotFoundException { |
158 public void run() throws IOException, ClassNotFoundException, Util.Exit { |
137 int i = 0; |
159 int i = 0; |
138 if (outFile != null) { |
160 if (outFile != null) { |
139 /* Everything goes to one big file... */ |
161 /* Everything goes to one big file... */ |
140 ByteArrayOutputStream bout = new ByteArrayOutputStream(8192); |
162 ByteArrayOutputStream bout = new ByteArrayOutputStream(8192); |
141 writeFileTop(bout); /* only once */ |
163 writeFileTop(bout); /* only once */ |
142 |
164 |
143 for (i = 0; i < classes.length; i++) { |
165 for (TypeElement t: classes) { |
144 write(bout, classes[i]); |
166 write(bout, t); |
145 } |
167 } |
146 |
168 |
147 writeIfChanged(bout.toByteArray(), outFile); |
169 writeIfChanged(bout.toByteArray(), outFile); |
148 } else { |
170 } else { |
149 /* Each class goes to its own file... */ |
171 /* Each class goes to its own file... */ |
150 for (i = 0; i < classes.length; i++) { |
172 for (TypeElement t: classes) { |
151 ByteArrayOutputStream bout = new ByteArrayOutputStream(8192); |
173 ByteArrayOutputStream bout = new ByteArrayOutputStream(8192); |
152 writeFileTop(bout); |
174 writeFileTop(bout); |
153 ClassDoc clazz = classes[i]; |
175 write(bout, t); |
154 write(bout, clazz); |
176 writeIfChanged(bout.toByteArray(), getFileObject(t.getQualifiedName())); |
155 writeIfChanged(bout.toByteArray(), getFileName(clazz.qualifiedName())); |
|
156 } |
177 } |
157 } |
178 } |
158 } |
179 } |
159 |
180 |
160 /* |
181 /* |
161 * Write the contents of byte[] b to a file named file. Writing |
182 * Write the contents of byte[] b to a file named file. Writing |
162 * is done if either the file doesn't exist or if the contents are |
183 * is done if either the file doesn't exist or if the contents are |
163 * different. |
184 * different. |
164 */ |
185 */ |
165 private void writeIfChanged(byte[] b, String file) throws IOException { |
186 private void writeIfChanged(byte[] b, FileObject file) throws IOException { |
166 File f = new File(file); |
|
167 boolean mustWrite = false; |
187 boolean mustWrite = false; |
168 String event = "[No need to update file "; |
188 String event = "[No need to update file "; |
169 |
189 |
170 if (force) { |
190 if (force) { |
171 mustWrite = true; |
191 mustWrite = true; |
172 event = "[Forcefully writing file "; |
192 event = "[Forcefully writing file "; |
173 } else { |
193 } else { |
174 if (!f.exists()) { |
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) { |
175 mustWrite = true; |
208 mustWrite = true; |
176 event = "[Creating file "; |
209 event = "[Creating file "; |
177 } else { |
210 } |
178 int l = (int)f.length(); |
211 } |
179 if (b.length != l) { |
212 |
180 mustWrite = true; |
213 if (util.verbose) |
181 event = "[Overwriting file "; |
214 util.log(event + file + "]"); |
182 } else { |
215 |
183 /* Lengths are equal, so read it. */ |
|
184 byte[] a = new byte[l]; |
|
185 FileInputStream in = new FileInputStream(f); |
|
186 if (in.read(a) != l) { |
|
187 in.close(); |
|
188 /* This can't happen, we already checked the length. */ |
|
189 Util.error("not.enough.bytes", Integer.toString(l), |
|
190 f.toString()); |
|
191 } |
|
192 in.close(); |
|
193 while (--l >= 0) { |
|
194 if (a[l] != b[l]) { |
|
195 mustWrite = true; |
|
196 event = "[Overwriting file "; |
|
197 } |
|
198 } |
|
199 } |
|
200 } |
|
201 } |
|
202 if (Util.verbose) |
|
203 Util.log(event + file + "]"); |
|
204 if (mustWrite) { |
216 if (mustWrite) { |
205 OutputStream out = new FileOutputStream(file); |
217 OutputStream out = file.openOutputStream(); |
206 out.write(b); /* No buffering, just one big write! */ |
218 out.write(b); /* No buffering, just one big write! */ |
207 out.close(); |
219 out.close(); |
208 } |
220 } |
209 } |
221 } |
210 |
222 |
211 protected String defineForStatic(ClassDoc c, FieldDoc f){ |
223 protected byte[] readBytes(InputStream in) throws IOException { |
212 |
224 try { |
213 String cnamedoc = c.qualifiedName(); |
225 byte[] array = new byte[in.available() + 1]; |
214 String fnamedoc = f.name(); |
226 int offset = 0; |
215 |
227 int n; |
216 String cname = Mangle.mangle(cnamedoc, Mangle.Type.CLASS); |
228 while ((n = in.read(array, offset, array.length - offset)) != -1) { |
217 String fname = Mangle.mangle(fnamedoc, Mangle.Type.FIELDSTUB); |
229 offset += n; |
218 |
230 if (offset == array.length) |
219 if (!f.isStatic()) |
231 array = Arrays.copyOf(array, array.length * 2); |
220 Util.bug("tried.to.define.non.static"); |
232 } |
221 |
233 |
222 if (f.isFinal()) { |
234 return Arrays.copyOf(array, offset); |
|
235 } finally { |
|
236 in.close(); |
|
237 } |
|
238 } |
|
239 |
|
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)) { |
223 Object value = null; |
252 Object value = null; |
224 |
253 |
225 value = f.constantValue(); |
254 value = f.getConstantValue(); |
226 |
255 |
227 if (value != null) { /* so it is a ConstantExpression */ |
256 if (value != null) { /* so it is a ConstantExpression */ |
228 String constString = null; |
257 String constString = null; |
229 if ((value instanceof Integer) |
258 if ((value instanceof Integer) |
230 || (value instanceof Byte) |
259 || (value instanceof Byte) |
231 || (value instanceof Character) |
260 || (value instanceof Short)) { |
232 || (value instanceof Short) |
261 /* covers byte, short, int */ |
233 || (value instanceof Boolean)) { |
262 constString = value.toString() + "L"; |
234 /* covers byte, boolean, char, short, int */ |
263 } else if (value instanceof Boolean) { |
235 if(value instanceof Boolean) |
264 constString = ((Boolean) value) ? "1L" : "0L"; |
236 constString = (value.toString() == "true") ? "1L" : "0L"; |
265 } else if (value instanceof Character) { |
237 else |
266 Character ch = (Character) value; |
238 constString = value.toString() + "L"; |
267 constString = String.valueOf(((int) ch) & 0xffff) + "L"; |
239 } else if (value instanceof Long) { |
268 } else if (value instanceof Long) { |
240 // Visual C++ supports the i64 suffix, not LL. |
269 // Visual C++ supports the i64 suffix, not LL. |
241 if (isWindows) |
270 if (isWindows) |
242 constString = value.toString() + "i64"; |
271 constString = value.toString() + "i64"; |
243 else |
272 else |
292 } |
321 } |
293 |
322 |
294 /* |
323 /* |
295 * File name and file preamble related operations. |
324 * File name and file preamble related operations. |
296 */ |
325 */ |
297 protected void writeFileTop(OutputStream o) { |
326 protected void writeFileTop(OutputStream o) throws Util.Exit { |
298 PrintWriter pw = wrapWriter(o); |
327 PrintWriter pw = wrapWriter(o); |
299 pw.println("/* DO NOT EDIT THIS FILE - it is machine generated */" + lineSep + |
328 pw.println("/* DO NOT EDIT THIS FILE - it is machine generated */" + lineSep + |
300 getIncludes()); |
329 getIncludes()); |
301 } |
330 } |
302 |
331 |
303 protected String baseFileName(String clazz) { |
332 protected String baseFileName(CharSequence className) { |
304 StringBuffer f = |
333 return mangler.mangle(className, Mangle.Type.CLASS); |
305 new StringBuffer(Mangle.mangle(clazz, |
334 } |
306 Mangle.Type.CLASS)); |
335 |
307 if (outDir != null) { |
336 protected FileObject getFileObject(CharSequence className) throws IOException { |
308 f.insert(0, outDir); |
337 String name = baseFileName(className) + getFileSuffix(); |
309 } |
338 return fileManager.getFileForOutput(StandardLocation.SOURCE_OUTPUT, "", name, null); |
310 return f.toString(); |
|
311 } |
|
312 |
|
313 protected String getFileName(String clazz) { |
|
314 return baseFileName(clazz) + getFileSuffix(); |
|
315 } |
339 } |
316 |
340 |
317 protected String getFileSuffix() { |
341 protected String getFileSuffix() { |
318 return ".h"; |
342 return ".h"; |
319 } |
343 } |
320 |
344 |
321 /** |
345 /** |
322 * Including super classes' fields. |
346 * Including super classes' fields. |
323 */ |
347 */ |
324 |
348 |
325 FieldDoc[] getAllFields(ClassDoc subclazz) |
349 List<VariableElement> getAllFields(TypeElement subclazz) { |
326 throws ClassNotFoundException { |
350 List<VariableElement> fields = new ArrayList<VariableElement>(); |
327 Vector<FieldDoc> fields = new Vector<FieldDoc>(); |
351 TypeElement cd = null; |
328 ClassDoc cd = null; |
352 Stack<TypeElement> s = new Stack<TypeElement>(); |
329 Stack<Object> s = new Stack<Object>(); |
|
330 |
353 |
331 cd = subclazz; |
354 cd = subclazz; |
332 while (true) { |
355 while (true) { |
333 s.push(cd); |
356 s.push(cd); |
334 ClassDoc c = cd.superclass(); |
357 TypeElement c = (TypeElement) (types.asElement(cd.getSuperclass())); |
335 if (c == null) |
358 if (c == null) |
336 break; |
359 break; |
337 cd = c; |
360 cd = c; |
338 } |
361 } |
339 |
362 |
340 while (!s.empty()) { |
363 while (!s.empty()) { |
341 cd = (ClassDoc)s.pop(); |
364 cd = s.pop(); |
342 fields.addAll(Arrays.asList(cd.fields())); |
365 fields.addAll(ElementFilter.fieldsIn(cd.getEnclosedElements())); |
343 } |
366 } |
344 |
367 |
345 return fields.toArray(new FieldDoc[fields.size()]); |
368 return fields; |
|
369 } |
|
370 |
|
371 // c.f. MethodDoc.signature |
|
372 String signature(ExecutableElement e) { |
|
373 StringBuffer sb = new StringBuffer("("); |
|
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(); |
346 } |
382 } |
347 } |
383 } |
|
384 |