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  */
  25 
  26 package com.sun.tools.javah;
  27 
  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;
  47 
  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;
  53 
  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;
  65 
  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.*;
  76 
  77 import com.sun.tools.javac.code.Symbol.CompletionFailure;
  78 import com.sun.tools.javac.main.CommandLine;
  79 
  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         }
 100 
 101         BadArgs showUsage(boolean b) {
 102             showUsage = b;
 103             return this;
 104         }
 105 
 106         final String key;
 107         final Object[] args;
 108         boolean showUsage;
 109     }
 110 
 111     static abstract class Option {
 112         Option(boolean hasArg, String... aliases) {
 113             this.hasArg = hasArg;
 114             this.aliases = aliases;
 115         }
 116 
 117         boolean isHidden() {
 118             return false;
 119         }
 120 
 121         boolean matches(String opt) {
 122             for (String a: aliases) {
 123                 if (a.equals(opt))
 124                     return true;
 125             }
 126             return false;
 127         }
 128 
 129         boolean ignoreRest() {
 130             return false;
 131         }
 132 
 133         abstract void process(JavahTask task, String opt, String arg) throws BadArgs;
 134 
 135         final boolean hasArg;
 136         final String[] aliases;
 137     }
 138 
 139     static abstract class HiddenOption extends Option {
 140         HiddenOption(boolean hasArg, String... aliases) {
 141             super(hasArg, aliases);
 142         }
 143 
 144         @Override
 145         boolean isHidden() {
 146             return true;
 147         }
 148     }
 149 
 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         },
 156 
 157         new Option(true, "-d") {
 158             void process(JavahTask task, String opt, String arg) {
 159                 task.odir = new File(arg);
 160             }
 161         },
 162 
 163         new HiddenOption(true, "-td") {
 164             void process(JavahTask task, String opt, String arg) {
 165                  // ignored; for backwards compatibility
 166             }
 167         },
 168 
 169         new HiddenOption(false, "-stubs") {
 170             void process(JavahTask task, String opt, String arg) {
 171                  // ignored; for backwards compatibility
 172             }
 173         },
 174 
 175         new Option(false, "-v", "-verbose") {
 176             void process(JavahTask task, String opt, String arg) {
 177                 task.verbose = true;
 178             }
 179         },
 180 
 181         new Option(false, "-h", "-help", "--help", "-?") {
 182             void process(JavahTask task, String opt, String arg) {
 183                 task.help = true;
 184             }
 185         },
 186 
 187         new HiddenOption(false, "-trace") {
 188             void process(JavahTask task, String opt, String arg) {
 189                 task.trace = true;
 190             }
 191         },
 192 
 193         new Option(false, "-version") {
 194             void process(JavahTask task, String opt, String arg) {
 195                 task.version = true;
 196             }
 197         },
 198 
 199         new HiddenOption(false, "-fullversion") {
 200             void process(JavahTask task, String opt, String arg) {
 201                 task.fullVersion = true;
 202             }
 203         },
 204 
 205         new Option(false, "-jni") {
 206             void process(JavahTask task, String opt, String arg) {
 207                 task.jni = true;
 208             }
 209         },
 210 
 211         new Option(false, "-force") {
 212             void process(JavahTask task, String opt, String arg) {
 213                 task.force = true;
 214             }
 215         },
 216 
 217         new HiddenOption(false, "-Xnew") {
 218             void process(JavahTask task, String opt, String arg) {
 219                 // we're already using the new javah
 220             }
 221         },
 222 
 223         new HiddenOption(false, "-old") {
 224             void process(JavahTask task, String opt, String arg) {
 225                 task.old = true;
 226             }
 227         },
 228 
 229         new HiddenOption(false, "-llni", "-Xllni") {
 230             void process(JavahTask task, String opt, String arg) {
 231                 task.llni = true;
 232             }
 233         },
 234 
 235         new HiddenOption(false, "-llnidouble") {
 236             void process(JavahTask task, String opt, String arg) {
 237                 task.llni = true;
 238                 task.doubleAlign = true;
 239             }
 240         },
 241 
 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     };
 251 
 252     JavahTask() {
 253     }
 254 
 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;
 264 
 265         try {
 266             handleOptions(options, false);
 267         } catch (BadArgs e) {
 268             throw new IllegalArgumentException(e.getMessage());
 269         }
 270 
 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     }
 279 
 280     public void setLocale(Locale locale) {
 281         if (locale == null)
 282             locale = Locale.getDefault();
 283         task_locale = locale;
 284     }
 285 
 286     public void setLog(PrintWriter log) {
 287         this.log = log;
 288     }
 289 
 290     public void setLog(OutputStream s) {
 291         setLog(getPrintWriterForStream(s));
 292     }
 293 
 294     static PrintWriter getPrintWriterForStream(OutputStream s) {
 295         return new PrintWriter(s, true);
 296     }
 297 
 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     }
 306 
 307     public void setDiagnosticListener(DiagnosticListener<? super JavaFileObject> dl) {
 308         diagnosticListener = dl;
 309     }
 310 
 311     public void setDiagnosticListener(OutputStream s) {
 312         setDiagnosticListener(getDiagnosticListenerForStream(s));
 313     }
 314 
 315     private DiagnosticListener<JavaFileObject> getDiagnosticListenerForStream(OutputStream s) {
 316         return getDiagnosticListenerForWriter(getPrintWriterForStream(s));
 317     }
 318 
 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     }
 331 
 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     }
 349 
 350     public void handleOptions(String[] args) throws BadArgs {
 351         handleOptions(Arrays.asList(args), true);
 352     }
 353 
 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         }
 363 
 364         if (fileManager == null)
 365             fileManager = getDefaultFileManager(diagnosticListener, log);
 366 
 367         Iterator<String> iter = expandAtArgs(args).iterator();
 368         noArgs = !iter.hasNext();
 369 
 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         }
 383 
 384         if ((classes == null || classes.size() == 0) &&
 385                 !(noArgs || help || version || fullVersion)) {
 386             throw new BadArgs("err.no.classes.specified");
 387         }
 388 
 389         if (jni && llni)
 390             throw new BadArgs("jni.llni.mixed");
 391 
 392         if (odir != null && ofile != null)
 393             throw new BadArgs("dir.file.mixed");
 394     }
 395 
 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);
 406 
 407                 if (o.ignoreRest()) {
 408                     while (rest.hasNext())
 409                         rest.next();
 410                 }
 411                 return;
 412             }
 413         }
 414 
 415         if (fileManager.handleOption(name, rest))
 416             return;
 417 
 418         throw new BadArgs("err.unknown.option", name).showUsage(true);
 419     }
 420 
 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     }
 432 
 433     public Boolean call() {
 434         return run();
 435     }
 436 
 437     public boolean run() throws Util.Exit {
 438 
 439         Util util = new Util(log, diagnosticListener);
 440 
 441         if (noArgs || help) {
 442             showHelp();
 443             return help; // treat noArgs as an error for purposes of exit code
 444         }
 445 
 446         if (version || fullVersion) {
 447             showVersion(fullVersion);
 448             return true;
 449         }
 450 
 451         util.verbose = verbose;
 452 
 453         Gen g;
 454 
 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         }
 462 
 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                 }
 478 
 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         }
 495 
 496         /*
 497          * Force set to false will turn off smarts about checking file
 498          * content before writing.
 499          */
 500         g.setForce(force);
 501 
 502         if (fileManager instanceof JavahFileManager)
 503             ((JavahFileManager) fileManager).setIgnoreSymbolFile(true);
 504 
 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));
 512 
 513         boolean ok = t.call();
 514         if (p.exit != null)
 515             throw new Util.Exit(p.exit);
 516         return ok;
 517     }
 518 
 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     }
 526 
 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     }
 535 
 536     static StandardJavaFileManager getDefaultFileManager(final DiagnosticListener<? super JavaFileObject> dl, PrintWriter log) {
 537         return JavahFileManager.create(dl, log);
 538     }
 539 
 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     }
 557 
 558     private void showVersion(boolean full) {
 559         log.println(version(full));
 560     }
 561 
 562     private static final String versionRBName = "com.sun.tools.javah.resources.version";
 563     private static ResourceBundle versionRB;
 564 
 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     }
 584 
 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             }
 590 
 591             public JavaFileObject getSource() {
 592                 return null;
 593             }
 594 
 595             public long getPosition() {
 596                 return Diagnostic.NOPOS;
 597             }
 598 
 599             public long getStartPosition() {
 600                 return Diagnostic.NOPOS;
 601             }
 602 
 603             public long getEndPosition() {
 604                 return Diagnostic.NOPOS;
 605             }
 606 
 607             public long getLineNumber() {
 608                 return Diagnostic.NOPOS;
 609             }
 610 
 611             public long getColumnNumber() {
 612                 return Diagnostic.NOPOS;
 613             }
 614 
 615             public String getCode() {
 616                 return key;
 617             }
 618 
 619             public String getMessage(Locale locale) {
 620                 return JavahTask.this.getMessage(locale, key, args);
 621             }
 622 
 623         };
 624     }
 625 
 626     private String getMessage(String key, Object... args) {
 627         return getMessage(task_locale, key, args);
 628     }
 629 
 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         }
 637 
 638         if (locale == null)
 639             locale = Locale.getDefault();
 640 
 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         }
 650 
 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     }
 658 
 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>();
 676 
 677     PrintWriter log;
 678     JavaFileManager fileManager;
 679     DiagnosticListener<? super JavaFileObject> diagnosticListener;
 680     Locale task_locale;
 681     Map<Locale, ResourceBundle> bundles;
 682 
 683     private static final String progname = "javah";
 684 
 685     @SupportedAnnotationTypes("*")
 686     class JavahProcessor extends AbstractProcessor {
 687         private Messager messager;
 688 
 689         JavahProcessor(Gen g) {
 690             this.g = g;
 691         }
 692 
 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         }
 699 
 700         @Override
 701         public void init(ProcessingEnvironment pEnv) {
 702             super.init(pEnv);
 703             messager  = processingEnv.getMessager();
 704         }
 705 
 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             }
 724 
 725             return true;
 726         }
 727 
 728         private Set<TypeElement> getAllClasses(Set<? extends TypeElement> classes) {
 729             Set<TypeElement> allClasses = new LinkedHashSet<TypeElement>();
 730             getAllClasses0(classes, allClasses);
 731             return allClasses;
 732         }
 733 
 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         }
 740 
 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         }
 754 
 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         };
 770 
 771         private Gen g;
 772         private Util.Exit exit;
 773     }
 774 }