1 /*
   2  * Copyright (c) 2002, 2014, 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.SimpleTypeVisitor9;
  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 and 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 final 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, "-llni", "-Xllni") {
 224             void process(JavahTask task, String opt, String arg) {
 225                 task.llni = true;
 226             }
 227         },
 228 
 229         new HiddenOption(false, "-llnidouble") {
 230             void process(JavahTask task, String opt, String arg) {
 231                 task.llni = true;
 232                 task.doubleAlign = true;
 233             }
 234         },
 235 
 236         new HiddenOption(false) {
 237             boolean matches(String opt) {
 238                 return opt.startsWith("-XD");
 239             }
 240             void process(JavahTask task, String opt, String arg) {
 241                 task.javac_extras.add(opt);
 242             }
 243         },
 244     };
 245 
 246     JavahTask() {
 247     }
 248 
 249     JavahTask(Writer out,
 250             JavaFileManager fileManager,
 251             DiagnosticListener<? super JavaFileObject> diagnosticListener,
 252             Iterable<String> options,
 253             Iterable<String> classes) {
 254         this();
 255         this.log = getPrintWriterForWriter(out);
 256         this.fileManager = fileManager;
 257         this.diagnosticListener = diagnosticListener;
 258 
 259         try {
 260             handleOptions(options, false);
 261         } catch (BadArgs e) {
 262             throw new IllegalArgumentException(e.getMessage());
 263         }
 264 
 265         this.classes = new ArrayList<>();
 266         if (classes != null) {
 267             for (String classname: classes) {
 268                 classname.getClass(); // null-check
 269                 this.classes.add(classname);
 270             }
 271         }
 272     }
 273 
 274     public void setLocale(Locale locale) {
 275         if (locale == null)
 276             locale = Locale.getDefault();
 277         task_locale = locale;
 278     }
 279 
 280     public void setLog(PrintWriter log) {
 281         this.log = log;
 282     }
 283 
 284     public void setLog(OutputStream s) {
 285         setLog(getPrintWriterForStream(s));
 286     }
 287 
 288     static PrintWriter getPrintWriterForStream(OutputStream s) {
 289         return new PrintWriter(s, true);
 290     }
 291 
 292     static PrintWriter getPrintWriterForWriter(Writer w) {
 293         if (w == null)
 294             return getPrintWriterForStream(null);
 295         else if (w instanceof PrintWriter)
 296             return (PrintWriter) w;
 297         else
 298             return new PrintWriter(w, true);
 299     }
 300 
 301     public void setDiagnosticListener(DiagnosticListener<? super JavaFileObject> dl) {
 302         diagnosticListener = dl;
 303     }
 304 
 305     public void setDiagnosticListener(OutputStream s) {
 306         setDiagnosticListener(getDiagnosticListenerForStream(s));
 307     }
 308 
 309     private DiagnosticListener<JavaFileObject> getDiagnosticListenerForStream(OutputStream s) {
 310         return getDiagnosticListenerForWriter(getPrintWriterForStream(s));
 311     }
 312 
 313     private DiagnosticListener<JavaFileObject> getDiagnosticListenerForWriter(Writer w) {
 314         final PrintWriter pw = getPrintWriterForWriter(w);
 315         return new DiagnosticListener<JavaFileObject> () {
 316             public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
 317                 if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
 318                     pw.print(getMessage("err.prefix"));
 319                     pw.print(" ");
 320                 }
 321                 pw.println(diagnostic.getMessage(null));
 322             }
 323         };
 324     }
 325 
 326     int run(String[] args) {
 327         try {
 328             handleOptions(args);
 329             boolean ok = run();
 330             return ok ? 0 : 1;
 331         } catch (BadArgs e) {
 332             diagnosticListener.report(createDiagnostic(e.key, e.args));
 333             return 1;
 334         } catch (InternalError e) {
 335             diagnosticListener.report(createDiagnostic("err.internal.error", e.getMessage()));
 336             return 1;
 337         } catch (Util.Exit e) {
 338             return e.exitValue;
 339         } finally {
 340             log.flush();
 341         }
 342     }
 343 
 344     public void handleOptions(String[] args) throws BadArgs {
 345         handleOptions(Arrays.asList(args), true);
 346     }
 347 
 348     private void handleOptions(Iterable<String> args, boolean allowClasses) throws BadArgs {
 349         if (log == null) {
 350             log = getPrintWriterForStream(System.out);
 351             if (diagnosticListener == null)
 352               diagnosticListener = getDiagnosticListenerForStream(System.err);
 353         } else {
 354             if (diagnosticListener == null)
 355               diagnosticListener = getDiagnosticListenerForWriter(log);
 356         }
 357 
 358         if (fileManager == null)
 359             fileManager = getDefaultFileManager(diagnosticListener, log);
 360 
 361         Iterator<String> iter = expandAtArgs(args).iterator();
 362         noArgs = !iter.hasNext();
 363 
 364         while (iter.hasNext()) {
 365             String arg = iter.next();
 366             if (arg.startsWith("-"))
 367                 handleOption(arg, iter);
 368             else if (allowClasses) {
 369                 if (classes == null)
 370                     classes = new ArrayList<>();
 371                 classes.add(arg);
 372                 while (iter.hasNext())
 373                     classes.add(iter.next());
 374             } else
 375                 throw new BadArgs("err.unknown.option", arg).showUsage(true);
 376         }
 377 
 378         if ((classes == null || classes.size() == 0) &&
 379                 !(noArgs || help || version || fullVersion)) {
 380             throw new BadArgs("err.no.classes.specified");
 381         }
 382 
 383         if (jni && llni)
 384             throw new BadArgs("jni.llni.mixed");
 385 
 386         if (odir != null && ofile != null)
 387             throw new BadArgs("dir.file.mixed");
 388     }
 389 
 390     private void handleOption(String name, Iterator<String> rest) throws BadArgs {
 391         for (Option o: recognizedOptions) {
 392             if (o.matches(name)) {
 393                 if (o.hasArg) {
 394                     if (rest.hasNext())
 395                         o.process(this, name, rest.next());
 396                     else
 397                         throw new BadArgs("err.missing.arg", name).showUsage(true);
 398                 } else
 399                     o.process(this, name, null);
 400 
 401                 if (o.ignoreRest()) {
 402                     while (rest.hasNext())
 403                         rest.next();
 404                 }
 405                 return;
 406             }
 407         }
 408 
 409         if (fileManager.handleOption(name, rest))
 410             return;
 411 
 412         throw new BadArgs("err.unknown.option", name).showUsage(true);
 413     }
 414 
 415     private Iterable<String> expandAtArgs(Iterable<String> args) throws BadArgs {
 416         try {
 417             List<String> l = new ArrayList<>();
 418             for (String arg: args) l.add(arg);
 419             return Arrays.asList(CommandLine.parse(l.toArray(new String[l.size()])));
 420         } catch (FileNotFoundException e) {
 421             throw new BadArgs("at.args.file.not.found", e.getLocalizedMessage());
 422         } catch (IOException e) {
 423             throw new BadArgs("at.args.io.exception", e.getLocalizedMessage());
 424         }
 425     }
 426 
 427     public Boolean call() {
 428         return run();
 429     }
 430 
 431     public boolean run() throws Util.Exit {
 432 
 433         Util util = new Util(log, diagnosticListener);
 434 
 435         if (noArgs || help) {
 436             showHelp();
 437             return help; // treat noArgs as an error for purposes of exit code
 438         }
 439 
 440         if (version || fullVersion) {
 441             showVersion(fullVersion);
 442             return true;
 443         }
 444 
 445         util.verbose = verbose;
 446 
 447         Gen g;
 448 
 449         if (llni)
 450             g = new LLNI(doubleAlign, util);
 451         else {
 452 //            if (stubs)
 453 //                throw new BadArgs("jni.no.stubs");
 454             g = new JNI(util);
 455         }
 456 
 457         if (ofile != null) {
 458             if (!(fileManager instanceof StandardJavaFileManager)) {
 459                 diagnosticListener.report(createDiagnostic("err.cant.use.option.for.fm", "-o"));
 460                 return false;
 461             }
 462             Iterable<? extends JavaFileObject> iter =
 463                     ((StandardJavaFileManager) fileManager).getJavaFileObjectsFromFiles(Collections.singleton(ofile));
 464             JavaFileObject fo = iter.iterator().next();
 465             g.setOutFile(fo);
 466         } else {
 467             if (odir != null) {
 468                 if (!(fileManager instanceof StandardJavaFileManager)) {
 469                     diagnosticListener.report(createDiagnostic("err.cant.use.option.for.fm", "-d"));
 470                     return false;
 471                 }
 472 
 473                 if (!odir.exists())
 474                     if (!odir.mkdirs())
 475                         util.error("cant.create.dir", odir.toString());
 476                 try {
 477                     ((StandardJavaFileManager) fileManager).setLocation(StandardLocation.CLASS_OUTPUT, Collections.singleton(odir));
 478                 } catch (IOException e) {
 479                     Object msg = e.getLocalizedMessage();
 480                     if (msg == null) {
 481                         msg = e;
 482                     }
 483                     diagnosticListener.report(createDiagnostic("err.ioerror", odir, msg));
 484                     return false;
 485                 }
 486             }
 487             g.setFileManager(fileManager);
 488         }
 489 
 490         /*
 491          * Force set to false will turn off smarts about checking file
 492          * content before writing.
 493          */
 494         g.setForce(force);
 495 
 496         if (fileManager instanceof JavahFileManager)
 497             ((JavahFileManager) fileManager).setSymbolFileEnabled(false);
 498 
 499         JavaCompiler c = ToolProvider.getSystemJavaCompiler();
 500         List<String> opts = new ArrayList<>();
 501         opts.add("-proc:only");
 502         opts.addAll(javac_extras);
 503         CompilationTask t = c.getTask(log, fileManager, diagnosticListener, opts, classes, null);
 504         JavahProcessor p = new JavahProcessor(g);
 505         t.setProcessors(Collections.singleton(p));
 506 
 507         boolean ok = t.call();
 508         if (p.exit != null)
 509             throw new Util.Exit(p.exit);
 510         return ok;
 511     }
 512 
 513     private List<File> pathToFiles(String path) {
 514         List<File> files = new ArrayList<>();
 515         for (String f: path.split(File.pathSeparator)) {
 516             if (f.length() > 0)
 517                 files.add(new File(f));
 518         }
 519         return files;
 520     }
 521 
 522     static StandardJavaFileManager getDefaultFileManager(final DiagnosticListener<? super JavaFileObject> dl, PrintWriter log) {
 523         return JavahFileManager.create(dl, log);
 524     }
 525 
 526     private void showHelp() {
 527         log.println(getMessage("main.usage", progname));
 528         for (Option o: recognizedOptions) {
 529             if (o.isHidden())
 530                 continue;
 531             String name = o.aliases[0].substring(1); // there must always be at least one name
 532             log.println(getMessage("main.opt." + name));
 533         }
 534         String[] fmOptions = { "-classpath", "-cp", "-bootclasspath" };
 535         for (String o: fmOptions) {
 536             if (fileManager.isSupportedOption(o) == -1)
 537                 continue;
 538             String name = o.substring(1);
 539             log.println(getMessage("main.opt." + name));
 540         }
 541         log.println(getMessage("main.usage.foot"));
 542     }
 543 
 544     private void showVersion(boolean full) {
 545         log.println(version(full));
 546     }
 547 
 548     private static final String versionRBName = "com.sun.tools.javah.resources.version";
 549     private static ResourceBundle versionRB;
 550 
 551     private String version(boolean full) {
 552         String msgKey = (full ? "javah.fullVersion" : "javah.version");
 553         String versionKey = (full ? "full" : "release");
 554         // versionKey=product:  mm.nn.oo[-milestone]
 555         // versionKey=full:     mm.mm.oo[-milestone]-build
 556         if (versionRB == null) {
 557             try {
 558                 versionRB = ResourceBundle.getBundle(versionRBName);
 559             } catch (MissingResourceException e) {
 560                 return getMessage("version.resource.missing", System.getProperty("java.version"));
 561             }
 562         }
 563         try {
 564             return getMessage(msgKey, "javah", versionRB.getString(versionKey));
 565         }
 566         catch (MissingResourceException e) {
 567             return getMessage("version.unknown", System.getProperty("java.version"));
 568         }
 569     }
 570 
 571     private Diagnostic<JavaFileObject> createDiagnostic(final String key, final Object... args) {
 572         return new Diagnostic<JavaFileObject>() {
 573             public Kind getKind() {
 574                 return Diagnostic.Kind.ERROR;
 575             }
 576 
 577             public JavaFileObject getSource() {
 578                 return null;
 579             }
 580 
 581             public long getPosition() {
 582                 return Diagnostic.NOPOS;
 583             }
 584 
 585             public long getStartPosition() {
 586                 return Diagnostic.NOPOS;
 587             }
 588 
 589             public long getEndPosition() {
 590                 return Diagnostic.NOPOS;
 591             }
 592 
 593             public long getLineNumber() {
 594                 return Diagnostic.NOPOS;
 595             }
 596 
 597             public long getColumnNumber() {
 598                 return Diagnostic.NOPOS;
 599             }
 600 
 601             public String getCode() {
 602                 return key;
 603             }
 604 
 605             public String getMessage(Locale locale) {
 606                 return JavahTask.this.getMessage(locale, key, args);
 607             }
 608 
 609         };
 610     }
 611 
 612     private String getMessage(String key, Object... args) {
 613         return getMessage(task_locale, key, args);
 614     }
 615 
 616     private String getMessage(Locale locale, String key, Object... args) {
 617         if (bundles == null) {
 618             // could make this a HashMap<Locale,SoftReference<ResourceBundle>>
 619             // and for efficiency, keep a hard reference to the bundle for the task
 620             // locale
 621             bundles = new HashMap<>();
 622         }
 623 
 624         if (locale == null)
 625             locale = Locale.getDefault();
 626 
 627         ResourceBundle b = bundles.get(locale);
 628         if (b == null) {
 629             try {
 630                 b = ResourceBundle.getBundle("com.sun.tools.javah.resources.l10n", locale);
 631                 bundles.put(locale, b);
 632             } catch (MissingResourceException e) {
 633                 throw new InternalError("Cannot find javah resource bundle for locale " + locale, e);
 634             }
 635         }
 636 
 637         try {
 638             return MessageFormat.format(b.getString(key), args);
 639         } catch (MissingResourceException e) {
 640             return key;
 641             //throw new InternalError(e, key);
 642         }
 643     }
 644 
 645     File ofile;
 646     File odir;
 647     String bootcp;
 648     String usercp;
 649     List<String> classes;
 650     boolean verbose;
 651     boolean noArgs;
 652     boolean help;
 653     boolean trace;
 654     boolean version;
 655     boolean fullVersion;
 656     boolean jni;
 657     boolean llni;
 658     boolean doubleAlign;
 659     boolean force;
 660     Set<String> javac_extras = new LinkedHashSet<>();
 661 
 662     PrintWriter log;
 663     JavaFileManager fileManager;
 664     DiagnosticListener<? super JavaFileObject> diagnosticListener;
 665     Locale task_locale;
 666     Map<Locale, ResourceBundle> bundles;
 667 
 668     private static final String progname = "javah";
 669 
 670     @SupportedAnnotationTypes("*")
 671     class JavahProcessor extends AbstractProcessor {
 672         private Messager messager;
 673 
 674         JavahProcessor(Gen g) {
 675             this.g = g;
 676         }
 677 
 678         @Override
 679         public SourceVersion getSupportedSourceVersion() {
 680             // since this is co-bundled with javac, we can assume it supports
 681             // the latest source version
 682             return SourceVersion.latest();
 683         }
 684 
 685         @Override
 686         public void init(ProcessingEnvironment pEnv) {
 687             super.init(pEnv);
 688             messager  = processingEnv.getMessager();
 689         }
 690 
 691         public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 692             try {
 693                 Set<TypeElement> classes = getAllClasses(ElementFilter.typesIn(roundEnv.getRootElements()));
 694                 if (classes.size() > 0) {
 695                     checkMethodParameters(classes);
 696                     g.setProcessingEnvironment(processingEnv);
 697                     g.setClasses(classes);
 698                     g.run();
 699                 }
 700             } catch (CompletionFailure cf) {
 701                 messager.printMessage(ERROR, getMessage("class.not.found", cf.sym.getQualifiedName().toString()));
 702             } catch (ClassNotFoundException cnfe) {
 703                 messager.printMessage(ERROR, getMessage("class.not.found", cnfe.getMessage()));
 704             } catch (IOException ioe) {
 705                 messager.printMessage(ERROR, getMessage("io.exception", ioe.getMessage()));
 706             } catch (Util.Exit e) {
 707                 exit = e;
 708             }
 709 
 710             return true;
 711         }
 712 
 713         private Set<TypeElement> getAllClasses(Set<? extends TypeElement> classes) {
 714             Set<TypeElement> allClasses = new LinkedHashSet<>();
 715             getAllClasses0(classes, allClasses);
 716             return allClasses;
 717         }
 718 
 719         private void getAllClasses0(Iterable<? extends TypeElement> classes, Set<TypeElement> allClasses) {
 720             for (TypeElement c: classes) {
 721                 allClasses.add(c);
 722                 getAllClasses0(ElementFilter.typesIn(c.getEnclosedElements()), allClasses);
 723             }
 724         }
 725 
 726         // 4942232:
 727         // check that classes exist for all the parameters of native methods
 728         private void checkMethodParameters(Set<TypeElement> classes) {
 729             Types types = processingEnv.getTypeUtils();
 730             for (TypeElement te: classes) {
 731                 for (ExecutableElement ee: ElementFilter.methodsIn(te.getEnclosedElements())) {
 732                     for (VariableElement ve: ee.getParameters()) {
 733                         TypeMirror tm = ve.asType();
 734                         checkMethodParametersVisitor.visit(tm, types);
 735                     }
 736                 }
 737             }
 738         }
 739 
 740         private TypeVisitor<Void,Types> checkMethodParametersVisitor =
 741                 new SimpleTypeVisitor9<Void,Types>() {
 742             @Override
 743             public Void visitArray(ArrayType t, Types types) {
 744                 visit(t.getComponentType(), types);
 745                 return null;
 746             }
 747             @Override
 748             public Void visitDeclared(DeclaredType t, Types types) {
 749                 t.asElement().getKind(); // ensure class exists
 750                 for (TypeMirror st: types.directSupertypes(t))
 751                     visit(st, types);
 752                 return null;
 753             }
 754         };
 755 
 756         private Gen g;
 757         private Util.Exit exit;
 758     }
 759 }