1 /*
2  * Copyright (c) 2002, 2013, 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 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;
BadArgs(String key, Object... args)95         BadArgs(String key, Object... args) {
96             super(JavahTask.this.getMessage(key, args));
97             this.key = key;
98             this.args = args;
99         }
100 
showUsage(boolean b)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 {
Option(boolean hasArg, String... aliases)112         Option(boolean hasArg, String... aliases) {
113             this.hasArg = hasArg;
114             this.aliases = aliases;
115         }
116 
isHidden()117         boolean isHidden() {
118             return false;
119         }
120 
matches(String opt)121         boolean matches(String opt) {
122             for (String a: aliases) {
123                 if (a.equals(opt))
124                     return true;
125             }
126             return false;
127         }
128 
ignoreRest()129         boolean ignoreRest() {
130             return false;
131         }
132 
process(JavahTask task, String opt, String arg)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 {
HiddenOption(boolean hasArg, String... aliases)140         HiddenOption(boolean hasArg, String... aliases) {
141             super(hasArg, aliases);
142         }
143 
144         @Override
isHidden()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 
JavahTask()246     JavahTask() {
247     }
248 
JavahTask(Writer out, JavaFileManager fileManager, DiagnosticListener<? super JavaFileObject> diagnosticListener, Iterable<String> options, Iterable<String> classes)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<String>();
266         if (classes != null) {
267             for (String classname: classes) {
268                 classname.getClass(); // null-check
269                 this.classes.add(classname);
270             }
271         }
272     }
273 
setLocale(Locale locale)274     public void setLocale(Locale locale) {
275         if (locale == null)
276             locale = Locale.getDefault();
277         task_locale = locale;
278     }
279 
setLog(PrintWriter log)280     public void setLog(PrintWriter log) {
281         this.log = log;
282     }
283 
setLog(OutputStream s)284     public void setLog(OutputStream s) {
285         setLog(getPrintWriterForStream(s));
286     }
287 
getPrintWriterForStream(OutputStream s)288     static PrintWriter getPrintWriterForStream(OutputStream s) {
289         return new PrintWriter(s, true);
290     }
291 
getPrintWriterForWriter(Writer w)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 
setDiagnosticListener(DiagnosticListener<? super JavaFileObject> dl)301     public void setDiagnosticListener(DiagnosticListener<? super JavaFileObject> dl) {
302         diagnosticListener = dl;
303     }
304 
setDiagnosticListener(OutputStream s)305     public void setDiagnosticListener(OutputStream s) {
306         setDiagnosticListener(getDiagnosticListenerForStream(s));
307     }
308 
getDiagnosticListenerForStream(OutputStream s)309     private DiagnosticListener<JavaFileObject> getDiagnosticListenerForStream(OutputStream s) {
310         return getDiagnosticListenerForWriter(getPrintWriterForStream(s));
311     }
312 
getDiagnosticListenerForWriter(Writer w)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<String>();
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<String>();
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<String>();
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<File>();
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<Locale, ResourceBundle>();
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<String>();
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<TypeElement>();
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 SimpleTypeVisitor8<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 }
760