Mon, 26 Mar 2012 15:27:51 +0100
7151580: Separate DA/DU logic from exception checking logic in Flow.java
Summary: DA/DU analysis and exception checking analysis should live in two separate tree visitors
Reviewed-by: gafter, dlsmith, jjg
1 /*
2 * Copyright (c) 2002, 2011, 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 */
26 package com.sun.tools.javah;
28 import java.io.File;
29 import java.io.FileNotFoundException;
30 import java.io.IOException;
31 import java.io.OutputStream;
32 import java.io.PrintWriter;
33 import java.io.Writer;
34 import java.text.MessageFormat;
35 import java.util.ArrayList;
36 import java.util.Arrays;
37 import java.util.Collections;
38 import java.util.HashMap;
39 import java.util.Iterator;
40 import java.util.LinkedHashSet;
41 import java.util.List;
42 import java.util.Locale;
43 import java.util.Map;
44 import java.util.MissingResourceException;
45 import java.util.ResourceBundle;
46 import java.util.Set;
48 import javax.annotation.processing.AbstractProcessor;
49 import javax.annotation.processing.Messager;
50 import javax.annotation.processing.ProcessingEnvironment;
51 import javax.annotation.processing.RoundEnvironment;
52 import javax.annotation.processing.SupportedAnnotationTypes;
54 import javax.lang.model.SourceVersion;
55 import javax.lang.model.element.ExecutableElement;
56 import javax.lang.model.element.TypeElement;
57 import javax.lang.model.element.VariableElement;
58 import javax.lang.model.type.ArrayType;
59 import javax.lang.model.type.DeclaredType;
60 import javax.lang.model.type.TypeMirror;
61 import javax.lang.model.type.TypeVisitor;
62 import javax.lang.model.util.ElementFilter;
63 import javax.lang.model.util.SimpleTypeVisitor8;
64 import javax.lang.model.util.Types;
66 import javax.tools.Diagnostic;
67 import javax.tools.DiagnosticListener;
68 import javax.tools.JavaCompiler;
69 import javax.tools.JavaCompiler.CompilationTask;
70 import javax.tools.JavaFileManager;
71 import javax.tools.JavaFileObject;
72 import javax.tools.StandardJavaFileManager;
73 import javax.tools.StandardLocation;
74 import javax.tools.ToolProvider;
75 import static javax.tools.Diagnostic.Kind.*;
77 import com.sun.tools.javac.code.Symbol.CompletionFailure;
78 import com.sun.tools.javac.main.CommandLine;
80 /**
81 * Javah generates support files for native methods.
82 * Parse commandline options & Invokes javadoc to execute those commands.
83 *
84 * <p><b>This is NOT part of any supported API.
85 * If you write code that depends on this, you do so at your own
86 * risk. This code and its internal interfaces are subject to change
87 * or deletion without notice.</b></p>
88 *
89 * @author Sucheta Dambalkar
90 * @author Jonathan Gibbons
91 */
92 public class JavahTask implements NativeHeaderTool.NativeHeaderTask {
93 public class BadArgs extends Exception {
94 private static final long serialVersionUID = 1479361270874789045L;
95 BadArgs(String key, Object... args) {
96 super(JavahTask.this.getMessage(key, args));
97 this.key = key;
98 this.args = args;
99 }
101 BadArgs showUsage(boolean b) {
102 showUsage = b;
103 return this;
104 }
106 final String key;
107 final Object[] args;
108 boolean showUsage;
109 }
111 static abstract class Option {
112 Option(boolean hasArg, String... aliases) {
113 this.hasArg = hasArg;
114 this.aliases = aliases;
115 }
117 boolean isHidden() {
118 return false;
119 }
121 boolean matches(String opt) {
122 for (String a: aliases) {
123 if (a.equals(opt))
124 return true;
125 }
126 return false;
127 }
129 boolean ignoreRest() {
130 return false;
131 }
133 abstract void process(JavahTask task, String opt, String arg) throws BadArgs;
135 final boolean hasArg;
136 final String[] aliases;
137 }
139 static abstract class HiddenOption extends Option {
140 HiddenOption(boolean hasArg, String... aliases) {
141 super(hasArg, aliases);
142 }
144 @Override
145 boolean isHidden() {
146 return true;
147 }
148 }
150 static Option[] recognizedOptions = {
151 new Option(true, "-o") {
152 void process(JavahTask task, String opt, String arg) {
153 task.ofile = new File(arg);
154 }
155 },
157 new Option(true, "-d") {
158 void process(JavahTask task, String opt, String arg) {
159 task.odir = new File(arg);
160 }
161 },
163 new HiddenOption(true, "-td") {
164 void process(JavahTask task, String opt, String arg) {
165 // ignored; for backwards compatibility
166 }
167 },
169 new HiddenOption(false, "-stubs") {
170 void process(JavahTask task, String opt, String arg) {
171 // ignored; for backwards compatibility
172 }
173 },
175 new Option(false, "-v", "-verbose") {
176 void process(JavahTask task, String opt, String arg) {
177 task.verbose = true;
178 }
179 },
181 new Option(false, "-h", "-help", "--help", "-?") {
182 void process(JavahTask task, String opt, String arg) {
183 task.help = true;
184 }
185 },
187 new HiddenOption(false, "-trace") {
188 void process(JavahTask task, String opt, String arg) {
189 task.trace = true;
190 }
191 },
193 new Option(false, "-version") {
194 void process(JavahTask task, String opt, String arg) {
195 task.version = true;
196 }
197 },
199 new HiddenOption(false, "-fullversion") {
200 void process(JavahTask task, String opt, String arg) {
201 task.fullVersion = true;
202 }
203 },
205 new Option(false, "-jni") {
206 void process(JavahTask task, String opt, String arg) {
207 task.jni = true;
208 }
209 },
211 new Option(false, "-force") {
212 void process(JavahTask task, String opt, String arg) {
213 task.force = true;
214 }
215 },
217 new HiddenOption(false, "-Xnew") {
218 void process(JavahTask task, String opt, String arg) {
219 // we're already using the new javah
220 }
221 },
223 new HiddenOption(false, "-old") {
224 void process(JavahTask task, String opt, String arg) {
225 task.old = true;
226 }
227 },
229 new HiddenOption(false, "-llni", "-Xllni") {
230 void process(JavahTask task, String opt, String arg) {
231 task.llni = true;
232 }
233 },
235 new HiddenOption(false, "-llnidouble") {
236 void process(JavahTask task, String opt, String arg) {
237 task.llni = true;
238 task.doubleAlign = true;
239 }
240 },
242 new HiddenOption(false) {
243 boolean matches(String opt) {
244 return opt.startsWith("-XD");
245 }
246 void process(JavahTask task, String opt, String arg) {
247 task.javac_extras.add(opt);
248 }
249 },
250 };
252 JavahTask() {
253 }
255 JavahTask(Writer out,
256 JavaFileManager fileManager,
257 DiagnosticListener<? super JavaFileObject> diagnosticListener,
258 Iterable<String> options,
259 Iterable<String> classes) {
260 this();
261 this.log = getPrintWriterForWriter(out);
262 this.fileManager = fileManager;
263 this.diagnosticListener = diagnosticListener;
265 try {
266 handleOptions(options, false);
267 } catch (BadArgs e) {
268 throw new IllegalArgumentException(e.getMessage());
269 }
271 this.classes = new ArrayList<String>();
272 if (classes != null) {
273 for (String classname: classes) {
274 classname.getClass(); // null-check
275 this.classes.add(classname);
276 }
277 }
278 }
280 public void setLocale(Locale locale) {
281 if (locale == null)
282 locale = Locale.getDefault();
283 task_locale = locale;
284 }
286 public void setLog(PrintWriter log) {
287 this.log = log;
288 }
290 public void setLog(OutputStream s) {
291 setLog(getPrintWriterForStream(s));
292 }
294 static PrintWriter getPrintWriterForStream(OutputStream s) {
295 return new PrintWriter(s, true);
296 }
298 static PrintWriter getPrintWriterForWriter(Writer w) {
299 if (w == null)
300 return getPrintWriterForStream(null);
301 else if (w instanceof PrintWriter)
302 return (PrintWriter) w;
303 else
304 return new PrintWriter(w, true);
305 }
307 public void setDiagnosticListener(DiagnosticListener<? super JavaFileObject> dl) {
308 diagnosticListener = dl;
309 }
311 public void setDiagnosticListener(OutputStream s) {
312 setDiagnosticListener(getDiagnosticListenerForStream(s));
313 }
315 private DiagnosticListener<JavaFileObject> getDiagnosticListenerForStream(OutputStream s) {
316 return getDiagnosticListenerForWriter(getPrintWriterForStream(s));
317 }
319 private DiagnosticListener<JavaFileObject> getDiagnosticListenerForWriter(Writer w) {
320 final PrintWriter pw = getPrintWriterForWriter(w);
321 return new DiagnosticListener<JavaFileObject> () {
322 public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
323 if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
324 pw.print(getMessage("err.prefix"));
325 pw.print(" ");
326 }
327 pw.println(diagnostic.getMessage(null));
328 }
329 };
330 }
332 int run(String[] args) {
333 try {
334 handleOptions(args);
335 boolean ok = run();
336 return ok ? 0 : 1;
337 } catch (BadArgs e) {
338 diagnosticListener.report(createDiagnostic(e.key, e.args));
339 return 1;
340 } catch (InternalError e) {
341 diagnosticListener.report(createDiagnostic("err.internal.error", e.getMessage()));
342 return 1;
343 } catch (Util.Exit e) {
344 return e.exitValue;
345 } finally {
346 log.flush();
347 }
348 }
350 public void handleOptions(String[] args) throws BadArgs {
351 handleOptions(Arrays.asList(args), true);
352 }
354 private void handleOptions(Iterable<String> args, boolean allowClasses) throws BadArgs {
355 if (log == null) {
356 log = getPrintWriterForStream(System.out);
357 if (diagnosticListener == null)
358 diagnosticListener = getDiagnosticListenerForStream(System.err);
359 } else {
360 if (diagnosticListener == null)
361 diagnosticListener = getDiagnosticListenerForWriter(log);
362 }
364 if (fileManager == null)
365 fileManager = getDefaultFileManager(diagnosticListener, log);
367 Iterator<String> iter = expandAtArgs(args).iterator();
368 noArgs = !iter.hasNext();
370 while (iter.hasNext()) {
371 String arg = iter.next();
372 if (arg.startsWith("-"))
373 handleOption(arg, iter);
374 else if (allowClasses) {
375 if (classes == null)
376 classes = new ArrayList<String>();
377 classes.add(arg);
378 while (iter.hasNext())
379 classes.add(iter.next());
380 } else
381 throw new BadArgs("err.unknown.option", arg).showUsage(true);
382 }
384 if ((classes == null || classes.size() == 0) &&
385 !(noArgs || help || version || fullVersion)) {
386 throw new BadArgs("err.no.classes.specified");
387 }
389 if (jni && llni)
390 throw new BadArgs("jni.llni.mixed");
392 if (odir != null && ofile != null)
393 throw new BadArgs("dir.file.mixed");
394 }
396 private void handleOption(String name, Iterator<String> rest) throws BadArgs {
397 for (Option o: recognizedOptions) {
398 if (o.matches(name)) {
399 if (o.hasArg) {
400 if (rest.hasNext())
401 o.process(this, name, rest.next());
402 else
403 throw new BadArgs("err.missing.arg", name).showUsage(true);
404 } else
405 o.process(this, name, null);
407 if (o.ignoreRest()) {
408 while (rest.hasNext())
409 rest.next();
410 }
411 return;
412 }
413 }
415 if (fileManager.handleOption(name, rest))
416 return;
418 throw new BadArgs("err.unknown.option", name).showUsage(true);
419 }
421 private Iterable<String> expandAtArgs(Iterable<String> args) throws BadArgs {
422 try {
423 List<String> l = new ArrayList<String>();
424 for (String arg: args) l.add(arg);
425 return Arrays.asList(CommandLine.parse(l.toArray(new String[l.size()])));
426 } catch (FileNotFoundException e) {
427 throw new BadArgs("at.args.file.not.found", e.getLocalizedMessage());
428 } catch (IOException e) {
429 throw new BadArgs("at.args.io.exception", e.getLocalizedMessage());
430 }
431 }
433 public Boolean call() {
434 return run();
435 }
437 public boolean run() throws Util.Exit {
439 Util util = new Util(log, diagnosticListener);
441 if (noArgs || help) {
442 showHelp();
443 return help; // treat noArgs as an error for purposes of exit code
444 }
446 if (version || fullVersion) {
447 showVersion(fullVersion);
448 return true;
449 }
451 util.verbose = verbose;
453 Gen g;
455 if (llni)
456 g = new LLNI(doubleAlign, util);
457 else {
458 // if (stubs)
459 // throw new BadArgs("jni.no.stubs");
460 g = new JNI(util);
461 }
463 if (ofile != null) {
464 if (!(fileManager instanceof StandardJavaFileManager)) {
465 diagnosticListener.report(createDiagnostic("err.cant.use.option.for.fm", "-o"));
466 return false;
467 }
468 Iterable<? extends JavaFileObject> iter =
469 ((StandardJavaFileManager) fileManager).getJavaFileObjectsFromFiles(Collections.singleton(ofile));
470 JavaFileObject fo = iter.iterator().next();
471 g.setOutFile(fo);
472 } else {
473 if (odir != null) {
474 if (!(fileManager instanceof StandardJavaFileManager)) {
475 diagnosticListener.report(createDiagnostic("err.cant.use.option.for.fm", "-d"));
476 return false;
477 }
479 if (!odir.exists())
480 if (!odir.mkdirs())
481 util.error("cant.create.dir", odir.toString());
482 try {
483 ((StandardJavaFileManager) fileManager).setLocation(StandardLocation.CLASS_OUTPUT, Collections.singleton(odir));
484 } catch (IOException e) {
485 Object msg = e.getLocalizedMessage();
486 if (msg == null) {
487 msg = e;
488 }
489 diagnosticListener.report(createDiagnostic("err.ioerror", odir, msg));
490 return false;
491 }
492 }
493 g.setFileManager(fileManager);
494 }
496 /*
497 * Force set to false will turn off smarts about checking file
498 * content before writing.
499 */
500 g.setForce(force);
502 if (fileManager instanceof JavahFileManager)
503 ((JavahFileManager) fileManager).setIgnoreSymbolFile(true);
505 JavaCompiler c = ToolProvider.getSystemJavaCompiler();
506 List<String> opts = new ArrayList<String>();
507 opts.add("-proc:only");
508 opts.addAll(javac_extras);
509 CompilationTask t = c.getTask(log, fileManager, diagnosticListener, opts, internalize(classes), null);
510 JavahProcessor p = new JavahProcessor(g);
511 t.setProcessors(Collections.singleton(p));
513 boolean ok = t.call();
514 if (p.exit != null)
515 throw new Util.Exit(p.exit);
516 return ok;
517 }
519 private List<String> internalize(List<String> classes) {
520 List<String> l = new ArrayList<String>();
521 for (String c: classes) {
522 l.add(c.replace('$', '.'));
523 }
524 return l;
525 }
527 private List<File> pathToFiles(String path) {
528 List<File> files = new ArrayList<File>();
529 for (String f: path.split(File.pathSeparator)) {
530 if (f.length() > 0)
531 files.add(new File(f));
532 }
533 return files;
534 }
536 static StandardJavaFileManager getDefaultFileManager(final DiagnosticListener<? super JavaFileObject> dl, PrintWriter log) {
537 return JavahFileManager.create(dl, log);
538 }
540 private void showHelp() {
541 log.println(getMessage("main.usage", progname));
542 for (Option o: recognizedOptions) {
543 if (o.isHidden())
544 continue;
545 String name = o.aliases[0].substring(1); // there must always be at least one name
546 log.println(getMessage("main.opt." + name));
547 }
548 String[] fmOptions = { "-classpath", "-bootclasspath" };
549 for (String o: fmOptions) {
550 if (fileManager.isSupportedOption(o) == -1)
551 continue;
552 String name = o.substring(1);
553 log.println(getMessage("main.opt." + name));
554 }
555 log.println(getMessage("main.usage.foot"));
556 }
558 private void showVersion(boolean full) {
559 log.println(version(full));
560 }
562 private static final String versionRBName = "com.sun.tools.javah.resources.version";
563 private static ResourceBundle versionRB;
565 private String version(boolean full) {
566 String msgKey = (full ? "javah.fullVersion" : "javah.version");
567 String versionKey = (full ? "full" : "release");
568 // versionKey=product: mm.nn.oo[-milestone]
569 // versionKey=full: mm.mm.oo[-milestone]-build
570 if (versionRB == null) {
571 try {
572 versionRB = ResourceBundle.getBundle(versionRBName);
573 } catch (MissingResourceException e) {
574 return getMessage("version.resource.missing", System.getProperty("java.version"));
575 }
576 }
577 try {
578 return getMessage(msgKey, "javah", versionRB.getString(versionKey));
579 }
580 catch (MissingResourceException e) {
581 return getMessage("version.unknown", System.getProperty("java.version"));
582 }
583 }
585 private Diagnostic<JavaFileObject> createDiagnostic(final String key, final Object... args) {
586 return new Diagnostic<JavaFileObject>() {
587 public Kind getKind() {
588 return Diagnostic.Kind.ERROR;
589 }
591 public JavaFileObject getSource() {
592 return null;
593 }
595 public long getPosition() {
596 return Diagnostic.NOPOS;
597 }
599 public long getStartPosition() {
600 return Diagnostic.NOPOS;
601 }
603 public long getEndPosition() {
604 return Diagnostic.NOPOS;
605 }
607 public long getLineNumber() {
608 return Diagnostic.NOPOS;
609 }
611 public long getColumnNumber() {
612 return Diagnostic.NOPOS;
613 }
615 public String getCode() {
616 return key;
617 }
619 public String getMessage(Locale locale) {
620 return JavahTask.this.getMessage(locale, key, args);
621 }
623 };
624 }
626 private String getMessage(String key, Object... args) {
627 return getMessage(task_locale, key, args);
628 }
630 private String getMessage(Locale locale, String key, Object... args) {
631 if (bundles == null) {
632 // could make this a HashMap<Locale,SoftReference<ResourceBundle>>
633 // and for efficiency, keep a hard reference to the bundle for the task
634 // locale
635 bundles = new HashMap<Locale, ResourceBundle>();
636 }
638 if (locale == null)
639 locale = Locale.getDefault();
641 ResourceBundle b = bundles.get(locale);
642 if (b == null) {
643 try {
644 b = ResourceBundle.getBundle("com.sun.tools.javah.resources.l10n", locale);
645 bundles.put(locale, b);
646 } catch (MissingResourceException e) {
647 throw new InternalError("Cannot find javah resource bundle for locale " + locale, e);
648 }
649 }
651 try {
652 return MessageFormat.format(b.getString(key), args);
653 } catch (MissingResourceException e) {
654 return key;
655 //throw new InternalError(e, key);
656 }
657 }
659 File ofile;
660 File odir;
661 String bootcp;
662 String usercp;
663 List<String> classes;
664 boolean verbose;
665 boolean noArgs;
666 boolean help;
667 boolean trace;
668 boolean version;
669 boolean fullVersion;
670 boolean jni;
671 boolean llni;
672 boolean doubleAlign;
673 boolean force;
674 boolean old;
675 Set<String> javac_extras = new LinkedHashSet<String>();
677 PrintWriter log;
678 JavaFileManager fileManager;
679 DiagnosticListener<? super JavaFileObject> diagnosticListener;
680 Locale task_locale;
681 Map<Locale, ResourceBundle> bundles;
683 private static final String progname = "javah";
685 @SupportedAnnotationTypes("*")
686 class JavahProcessor extends AbstractProcessor {
687 private Messager messager;
689 JavahProcessor(Gen g) {
690 this.g = g;
691 }
693 @Override
694 public SourceVersion getSupportedSourceVersion() {
695 // since this is co-bundled with javac, we can assume it supports
696 // the latest source version
697 return SourceVersion.latest();
698 }
700 @Override
701 public void init(ProcessingEnvironment pEnv) {
702 super.init(pEnv);
703 messager = processingEnv.getMessager();
704 }
706 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
707 try {
708 Set<TypeElement> classes = getAllClasses(ElementFilter.typesIn(roundEnv.getRootElements()));
709 if (classes.size() > 0) {
710 checkMethodParameters(classes);
711 g.setProcessingEnvironment(processingEnv);
712 g.setClasses(classes);
713 g.run();
714 }
715 } catch (CompletionFailure cf) {
716 messager.printMessage(ERROR, getMessage("class.not.found", cf.sym.getQualifiedName().toString()));
717 } catch (ClassNotFoundException cnfe) {
718 messager.printMessage(ERROR, getMessage("class.not.found", cnfe.getMessage()));
719 } catch (IOException ioe) {
720 messager.printMessage(ERROR, getMessage("io.exception", ioe.getMessage()));
721 } catch (Util.Exit e) {
722 exit = e;
723 }
725 return true;
726 }
728 private Set<TypeElement> getAllClasses(Set<? extends TypeElement> classes) {
729 Set<TypeElement> allClasses = new LinkedHashSet<TypeElement>();
730 getAllClasses0(classes, allClasses);
731 return allClasses;
732 }
734 private void getAllClasses0(Iterable<? extends TypeElement> classes, Set<TypeElement> allClasses) {
735 for (TypeElement c: classes) {
736 allClasses.add(c);
737 getAllClasses0(ElementFilter.typesIn(c.getEnclosedElements()), allClasses);
738 }
739 }
741 // 4942232:
742 // check that classes exist for all the parameters of native methods
743 private void checkMethodParameters(Set<TypeElement> classes) {
744 Types types = processingEnv.getTypeUtils();
745 for (TypeElement te: classes) {
746 for (ExecutableElement ee: ElementFilter.methodsIn(te.getEnclosedElements())) {
747 for (VariableElement ve: ee.getParameters()) {
748 TypeMirror tm = ve.asType();
749 checkMethodParametersVisitor.visit(tm, types);
750 }
751 }
752 }
753 }
755 private TypeVisitor<Void,Types> checkMethodParametersVisitor =
756 new SimpleTypeVisitor8<Void,Types>() {
757 @Override
758 public Void visitArray(ArrayType t, Types types) {
759 visit(t.getComponentType(), types);
760 return null;
761 }
762 @Override
763 public Void visitDeclared(DeclaredType t, Types types) {
764 t.asElement().getKind(); // ensure class exists
765 for (TypeMirror st: types.directSupertypes(t))
766 visit(st, types);
767 return null;
768 }
769 };
771 private Gen g;
772 private Util.Exit exit;
773 }
774 }