Tue, 13 Oct 2009 15:26:30 -0700
6891079: Compiler allows invalid binary literals 0b and oBL
Reviewed-by: darcy
1 /*
2 * Copyright 2002-2009 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
26 package com.sun.tools.javah;
28 import java.io.File;
29 import java.io.IOException;
30 import java.io.OutputStream;
31 import java.io.PrintWriter;
32 import java.io.Writer;
33 import java.text.MessageFormat;
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.Collections;
37 import java.util.HashMap;
38 import java.util.Iterator;
39 import java.util.LinkedHashSet;
40 import java.util.List;
41 import java.util.Locale;
42 import java.util.Map;
43 import java.util.MissingResourceException;
44 import java.util.ResourceBundle;
45 import java.util.Set;
47 import javax.annotation.processing.AbstractProcessor;
48 import javax.annotation.processing.Messager;
49 import javax.annotation.processing.RoundEnvironment;
50 import javax.annotation.processing.SupportedAnnotationTypes;
51 import javax.annotation.processing.SupportedSourceVersion;
53 import javax.lang.model.SourceVersion;
54 import javax.lang.model.element.ExecutableElement;
55 import javax.lang.model.element.TypeElement;
56 import javax.lang.model.element.VariableElement;
57 import javax.lang.model.type.ArrayType;
58 import javax.lang.model.type.DeclaredType;
59 import javax.lang.model.type.TypeMirror;
60 import javax.lang.model.type.TypeVisitor;
61 import javax.lang.model.util.ElementFilter;
62 import javax.lang.model.util.SimpleTypeVisitor6;
63 import javax.lang.model.util.Types;
65 import javax.tools.Diagnostic;
66 import javax.tools.DiagnosticListener;
67 import javax.tools.JavaCompiler;
68 import javax.tools.JavaCompiler.CompilationTask;
69 import javax.tools.JavaFileManager;
70 import javax.tools.JavaFileObject;
71 import javax.tools.StandardJavaFileManager;
72 import javax.tools.StandardLocation;
73 import javax.tools.ToolProvider;
75 /**
76 * Javah generates support files for native methods.
77 * Parse commandline options & Invokes javadoc to execute those commands.
78 *
79 * <p><b>This is NOT part of any API supported by Sun Microsystems.
80 * If you write code that depends on this, you do so at your own
81 * risk. This code and its internal interfaces are subject to change
82 * or deletion without notice.</b></p>
83 *
84 * @author Sucheta Dambalkar
85 * @author Jonathan Gibbons
86 */
87 public class JavahTask implements NativeHeaderTool.NativeHeaderTask {
88 public class BadArgs extends Exception {
89 private static final long serialVersionUID = 1479361270874789045L;
90 BadArgs(String key, Object... args) {
91 super(JavahTask.this.getMessage(key, args));
92 this.key = key;
93 this.args = args;
94 }
96 BadArgs showUsage(boolean b) {
97 showUsage = b;
98 return this;
99 }
101 final String key;
102 final Object[] args;
103 boolean showUsage;
104 }
106 static abstract class Option {
107 Option(boolean hasArg, String... aliases) {
108 this.hasArg = hasArg;
109 this.aliases = aliases;
110 }
112 boolean isHidden() {
113 return false;
114 }
116 boolean matches(String opt) {
117 for (String a: aliases) {
118 if (a.equals(opt))
119 return true;
120 }
121 return false;
122 }
124 boolean ignoreRest() {
125 return false;
126 }
128 abstract void process(JavahTask task, String opt, String arg) throws BadArgs;
130 final boolean hasArg;
131 final String[] aliases;
132 }
134 static abstract class HiddenOption extends Option {
135 HiddenOption(boolean hasArg, String... aliases) {
136 super(hasArg, aliases);
137 }
139 @Override
140 boolean isHidden() {
141 return true;
142 }
143 }
145 static Option[] recognizedOptions = {
146 new Option(true, "-o") {
147 void process(JavahTask task, String opt, String arg) {
148 task.ofile = new File(arg);
149 }
150 },
152 new Option(true, "-d") {
153 void process(JavahTask task, String opt, String arg) {
154 task.odir = new File(arg);
155 }
156 },
158 new HiddenOption(true, "-td") {
159 void process(JavahTask task, String opt, String arg) {
160 // ignored; for backwards compatibility
161 }
162 },
164 new HiddenOption(false, "-stubs") {
165 void process(JavahTask task, String opt, String arg) {
166 // ignored; for backwards compatibility
167 }
168 },
170 new Option(false, "-v", "-verbose") {
171 void process(JavahTask task, String opt, String arg) {
172 task.verbose = true;
173 }
174 },
176 new Option(false, "-help", "--help", "-?") {
177 void process(JavahTask task, String opt, String arg) {
178 task.help = true;
179 }
180 },
182 new HiddenOption(false, "-trace") {
183 void process(JavahTask task, String opt, String arg) {
184 task.trace = true;
185 }
186 },
188 new Option(false, "-version") {
189 void process(JavahTask task, String opt, String arg) {
190 task.version = true;
191 }
192 },
194 new HiddenOption(false, "-fullversion") {
195 void process(JavahTask task, String opt, String arg) {
196 task.fullVersion = true;
197 }
198 },
200 new Option(false, "-jni") {
201 void process(JavahTask task, String opt, String arg) {
202 task.jni = true;
203 }
204 },
206 new Option(false, "-force") {
207 void process(JavahTask task, String opt, String arg) {
208 task.force = true;
209 }
210 },
212 new HiddenOption(false, "-Xnew") {
213 void process(JavahTask task, String opt, String arg) {
214 // we're already using the new javah
215 }
216 },
218 new HiddenOption(false, "-old") {
219 void process(JavahTask task, String opt, String arg) {
220 task.old = true;
221 }
222 },
224 new HiddenOption(false, "-llni", "-Xllni") {
225 void process(JavahTask task, String opt, String arg) {
226 task.llni = true;
227 }
228 },
230 new HiddenOption(false, "-llnidouble") {
231 void process(JavahTask task, String opt, String arg) {
232 task.llni = true;
233 task.doubleAlign = true;
234 }
235 },
236 };
238 JavahTask() {
239 }
241 JavahTask(Writer out,
242 JavaFileManager fileManager,
243 DiagnosticListener<? super JavaFileObject> diagnosticListener,
244 Iterable<String> options,
245 Iterable<String> classes) {
246 this();
247 this.log = getPrintWriterForWriter(out);
248 this.fileManager = fileManager;
249 this.diagnosticListener = diagnosticListener;
251 try {
252 handleOptions(options, false);
253 } catch (BadArgs e) {
254 throw new IllegalArgumentException(e.getMessage());
255 }
257 this.classes = new ArrayList<String>();
258 for (String classname: classes) {
259 classname.getClass(); // null-check
260 this.classes.add(classname);
261 }
262 }
264 public void setLocale(Locale locale) {
265 if (locale == null)
266 locale = Locale.getDefault();
267 task_locale = locale;
268 }
270 public void setLog(PrintWriter log) {
271 this.log = log;
272 }
274 public void setLog(OutputStream s) {
275 setLog(getPrintWriterForStream(s));
276 }
278 static PrintWriter getPrintWriterForStream(OutputStream s) {
279 return new PrintWriter(s, true);
280 }
282 static PrintWriter getPrintWriterForWriter(Writer w) {
283 if (w == null)
284 return getPrintWriterForStream(null);
285 else if (w instanceof PrintWriter)
286 return (PrintWriter) w;
287 else
288 return new PrintWriter(w, true);
289 }
291 public void setDiagnosticListener(DiagnosticListener<? super JavaFileObject> dl) {
292 diagnosticListener = dl;
293 }
295 public void setDiagnosticListener(OutputStream s) {
296 setDiagnosticListener(getDiagnosticListenerForStream(s));
297 }
299 private DiagnosticListener<JavaFileObject> getDiagnosticListenerForStream(OutputStream s) {
300 return getDiagnosticListenerForWriter(getPrintWriterForStream(s));
301 }
303 private DiagnosticListener<JavaFileObject> getDiagnosticListenerForWriter(Writer w) {
304 final PrintWriter pw = getPrintWriterForWriter(w);
305 return new DiagnosticListener<JavaFileObject> () {
306 public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
307 if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
308 pw.print(getMessage("err.prefix"));
309 pw.print(" ");
310 }
311 pw.println(diagnostic.getMessage(null));
312 }
313 };
314 }
316 int run(String[] args) {
317 try {
318 handleOptions(args);
319 boolean ok = run();
320 return ok ? 0 : 1;
321 } catch (BadArgs e) {
322 diagnosticListener.report(createDiagnostic(e.key, e.args));
323 return 1;
324 } catch (InternalError e) {
325 diagnosticListener.report(createDiagnostic("err.internal.error", e.getMessage()));
326 return 1;
327 } finally {
328 log.flush();
329 }
330 }
332 public void handleOptions(String[] args) throws BadArgs {
333 handleOptions(Arrays.asList(args), true);
334 }
336 private void handleOptions(Iterable<String> args, boolean allowClasses) throws BadArgs {
337 if (log == null) {
338 log = getPrintWriterForStream(System.out);
339 if (diagnosticListener == null)
340 diagnosticListener = getDiagnosticListenerForStream(System.err);
341 } else {
342 if (diagnosticListener == null)
343 diagnosticListener = getDiagnosticListenerForWriter(log);
344 }
346 if (fileManager == null)
347 fileManager = getDefaultFileManager(diagnosticListener, log);
349 Iterator<String> iter = args.iterator();
350 if (!iter.hasNext())
351 help = true;
353 while (iter.hasNext()) {
354 String arg = iter.next();
355 if (arg.startsWith("-"))
356 handleOption(arg, iter);
357 else if (allowClasses) {
358 if (classes == null)
359 classes = new ArrayList<String>();
360 classes.add(arg);
361 while (iter.hasNext())
362 classes.add(iter.next());
363 } else
364 throw new BadArgs("err.unknown.option", arg).showUsage(true);
365 }
367 if ((classes == null || classes.size() == 0) &&
368 !(help || version || fullVersion)) {
369 throw new BadArgs("err.no.classes.specified");
370 }
372 if (jni && llni)
373 throw new BadArgs("jni.llni.mixed");
375 if (odir != null && ofile != null)
376 throw new BadArgs("dir.file.mixed");
377 }
379 private void handleOption(String name, Iterator<String> rest) throws BadArgs {
380 for (Option o: recognizedOptions) {
381 if (o.matches(name)) {
382 if (o.hasArg) {
383 if (rest.hasNext())
384 o.process(this, name, rest.next());
385 else
386 throw new BadArgs("err.missing.arg", name).showUsage(true);
387 } else
388 o.process(this, name, null);
390 if (o.ignoreRest()) {
391 while (rest.hasNext())
392 rest.next();
393 }
394 return;
395 }
396 }
398 if (fileManager.handleOption(name, rest))
399 return;
401 throw new BadArgs("err.unknown.option", name).showUsage(true);
402 }
404 public Boolean call() {
405 return run();
406 }
408 public boolean run() throws Util.Exit {
410 Util util = new Util(log, diagnosticListener);
412 if (help) {
413 showHelp();
414 return true;
415 }
417 if (version || fullVersion) {
418 showVersion(fullVersion);
419 return true;
420 }
422 util.verbose = verbose;
424 Gen g;
426 if (llni)
427 g = new LLNI(doubleAlign, util);
428 else {
429 // if (stubs)
430 // throw new BadArgs("jni.no.stubs");
431 g = new JNI(util);
432 }
434 if (ofile != null) {
435 if (!(fileManager instanceof StandardJavaFileManager)) {
436 diagnosticListener.report(createDiagnostic("err.cant.use.option.for.fm", "-o"));
437 return false;
438 }
439 Iterable<? extends JavaFileObject> iter =
440 ((StandardJavaFileManager) fileManager).getJavaFileObjectsFromFiles(Collections.singleton(ofile));
441 JavaFileObject fo = iter.iterator().next();
442 g.setOutFile(fo);
443 } else {
444 if (odir != null) {
445 if (!(fileManager instanceof StandardJavaFileManager)) {
446 diagnosticListener.report(createDiagnostic("err.cant.use.option.for.fm", "-d"));
447 return false;
448 }
450 if (!odir.exists())
451 if (!odir.mkdirs())
452 util.error("cant.create.dir", odir.toString());
453 try {
454 ((StandardJavaFileManager) fileManager).setLocation(StandardLocation.CLASS_OUTPUT, Collections.singleton(odir));
455 } catch (IOException e) {
456 Object msg = e.getLocalizedMessage();
457 if (msg == null) {
458 msg = e;
459 }
460 diagnosticListener.report(createDiagnostic("err.ioerror", odir, msg));
461 return false;
462 }
463 }
464 g.setFileManager(fileManager);
465 }
467 /*
468 * Force set to false will turn off smarts about checking file
469 * content before writing.
470 */
471 g.setForce(force);
473 if (fileManager instanceof JavahFileManager)
474 ((JavahFileManager) fileManager).setIgnoreSymbolFile(true);
476 JavaCompiler c = ToolProvider.getSystemJavaCompiler();
477 List<String> opts = Arrays.asList("-proc:only");
478 CompilationTask t = c.getTask(log, fileManager, diagnosticListener, opts, internalize(classes), null);
479 JavahProcessor p = new JavahProcessor(g);
480 t.setProcessors(Collections.singleton(p));
482 boolean ok = t.call();
483 if (p.exit != null)
484 throw new Util.Exit(p.exit);
485 return ok;
486 }
488 private List<String> internalize(List<String> classes) {
489 List<String> l = new ArrayList<String>();
490 for (String c: classes) {
491 l.add(c.replace('$', '.'));
492 }
493 return l;
494 }
496 private List<File> pathToFiles(String path) {
497 List<File> files = new ArrayList<File>();
498 for (String f: path.split(File.pathSeparator)) {
499 if (f.length() > 0)
500 files.add(new File(f));
501 }
502 return files;
503 }
505 static StandardJavaFileManager getDefaultFileManager(final DiagnosticListener<? super JavaFileObject> dl, PrintWriter log) {
506 return JavahFileManager.create(dl, log);
507 }
509 private void showHelp() {
510 log.println(getMessage("main.usage", progname));
511 for (Option o: recognizedOptions) {
512 if (o.isHidden())
513 continue;
514 String name = o.aliases[0].substring(1); // there must always be at least one name
515 log.println(getMessage("main.opt." + name));
516 }
517 String[] fmOptions = { "-classpath", "-bootclasspath" };
518 for (String o: fmOptions) {
519 if (fileManager.isSupportedOption(o) == -1)
520 continue;
521 String name = o.substring(1);
522 log.println(getMessage("main.opt." + name));
523 }
524 log.println(getMessage("main.usage.foot"));
525 }
527 private void showVersion(boolean full) {
528 log.println(version(full ? "full" : "release"));
529 }
531 private static final String versionRBName = "com.sun.tools.javah.resources.version";
532 private static ResourceBundle versionRB;
534 private String version(String key) {
535 // key=version: mm.nn.oo[-milestone]
536 // key=full: mm.mm.oo[-milestone]-build
537 if (versionRB == null) {
538 try {
539 versionRB = ResourceBundle.getBundle(versionRBName);
540 } catch (MissingResourceException e) {
541 return getMessage("version.resource.missing", System.getProperty("java.version"));
542 }
543 }
544 try {
545 return versionRB.getString(key);
546 }
547 catch (MissingResourceException e) {
548 return getMessage("version.unknown", System.getProperty("java.version"));
549 }
550 }
552 private Diagnostic<JavaFileObject> createDiagnostic(final String key, final Object... args) {
553 return new Diagnostic<JavaFileObject>() {
554 public Kind getKind() {
555 return Diagnostic.Kind.ERROR;
556 }
558 public JavaFileObject getSource() {
559 return null;
560 }
562 public long getPosition() {
563 return Diagnostic.NOPOS;
564 }
566 public long getStartPosition() {
567 return Diagnostic.NOPOS;
568 }
570 public long getEndPosition() {
571 return Diagnostic.NOPOS;
572 }
574 public long getLineNumber() {
575 return Diagnostic.NOPOS;
576 }
578 public long getColumnNumber() {
579 return Diagnostic.NOPOS;
580 }
582 public String getCode() {
583 return key;
584 }
586 public String getMessage(Locale locale) {
587 return JavahTask.this.getMessage(locale, key, args);
588 }
590 };
592 }
593 private String getMessage(String key, Object... args) {
594 return getMessage(task_locale, key, args);
595 }
597 private String getMessage(Locale locale, String key, Object... args) {
598 if (bundles == null) {
599 // could make this a HashMap<Locale,SoftReference<ResourceBundle>>
600 // and for efficiency, keep a hard reference to the bundle for the task
601 // locale
602 bundles = new HashMap<Locale, ResourceBundle>();
603 }
605 if (locale == null)
606 locale = Locale.getDefault();
608 ResourceBundle b = bundles.get(locale);
609 if (b == null) {
610 try {
611 b = ResourceBundle.getBundle("com.sun.tools.javah.resources.l10n", locale);
612 bundles.put(locale, b);
613 } catch (MissingResourceException e) {
614 throw new InternalError("Cannot find javah resource bundle for locale " + locale, e);
615 }
616 }
618 try {
619 return MessageFormat.format(b.getString(key), args);
620 } catch (MissingResourceException e) {
621 return key;
622 //throw new InternalError(e, key);
623 }
624 }
626 File ofile;
627 File odir;
628 String bootcp;
629 String usercp;
630 List<String> classes;
631 boolean verbose;
632 boolean help;
633 boolean trace;
634 boolean version;
635 boolean fullVersion;
636 boolean jni;
637 boolean llni;
638 boolean doubleAlign;
639 boolean force;
640 boolean old;
642 PrintWriter log;
643 JavaFileManager fileManager;
644 DiagnosticListener<? super JavaFileObject> diagnosticListener;
645 Locale task_locale;
646 Map<Locale, ResourceBundle> bundles;
648 private static final String progname = "javah";
650 @SupportedAnnotationTypes("*")
651 @SupportedSourceVersion(SourceVersion.RELEASE_7)
652 class JavahProcessor extends AbstractProcessor {
653 JavahProcessor(Gen g) {
654 this.g = g;
655 }
657 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
658 Messager messager = processingEnv.getMessager();
659 Set<TypeElement> classes = getAllClasses(ElementFilter.typesIn(roundEnv.getRootElements()));
660 if (classes.size() > 0) {
661 checkMethodParameters(classes);
662 g.setProcessingEnvironment(processingEnv);
663 g.setClasses(classes);
665 try {
666 g.run();
667 } catch (ClassNotFoundException cnfe) {
668 messager.printMessage(Diagnostic.Kind.ERROR, getMessage("class.not.found", cnfe.getMessage()));
669 } catch (IOException ioe) {
670 messager.printMessage(Diagnostic.Kind.ERROR, getMessage("io.exception", ioe.getMessage()));
671 } catch (Util.Exit e) {
672 exit = e;
673 }
674 }
675 return true;
676 }
678 private Set<TypeElement> getAllClasses(Set<? extends TypeElement> classes) {
679 Set<TypeElement> allClasses = new LinkedHashSet<TypeElement>();
680 getAllClasses0(classes, allClasses);
681 return allClasses;
682 }
684 private void getAllClasses0(Iterable<? extends TypeElement> classes, Set<TypeElement> allClasses) {
685 for (TypeElement c: classes) {
686 allClasses.add(c);
687 getAllClasses0(ElementFilter.typesIn(c.getEnclosedElements()), allClasses);
688 }
689 }
691 // 4942232:
692 // check that classes exist for all the parameters of native methods
693 private void checkMethodParameters(Set<TypeElement> classes) {
694 Types types = processingEnv.getTypeUtils();
695 for (TypeElement te: classes) {
696 for (ExecutableElement ee: ElementFilter.methodsIn(te.getEnclosedElements())) {
697 for (VariableElement ve: ee.getParameters()) {
698 TypeMirror tm = ve.asType();
699 checkMethodParametersVisitor.visit(tm, types);
700 }
701 }
702 }
703 }
705 private TypeVisitor<Void,Types> checkMethodParametersVisitor =
706 new SimpleTypeVisitor6<Void,Types>() {
707 @Override
708 public Void visitArray(ArrayType t, Types types) {
709 visit(t.getComponentType(), types);
710 return null;
711 }
712 @Override
713 public Void visitDeclared(DeclaredType t, Types types) {
714 t.asElement().getKind(); // ensure class exists
715 for (TypeMirror st: types.directSupertypes(t))
716 visit(st, types);
717 return null;
718 }
719 };
721 private Gen g;
722 private Util.Exit exit;
723 }
724 }