JavapTask.java revision 3827:44bdefe64114
1/*
2 * Copyright (c) 2007, 2016, 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
26package com.sun.tools.javap;
27
28import java.io.EOFException;
29import java.io.FileNotFoundException;
30import java.io.FilterInputStream;
31import java.io.InputStream;
32import java.io.IOException;
33import java.io.OutputStream;
34import java.io.PrintWriter;
35import java.io.Reader;
36import java.io.StringWriter;
37import java.io.Writer;
38import java.net.URI;
39import java.net.URISyntaxException;
40import java.net.URL;
41import java.net.URLConnection;
42import java.nio.file.NoSuchFileException;
43import java.security.DigestInputStream;
44import java.security.MessageDigest;
45import java.security.NoSuchAlgorithmException;
46import java.text.MessageFormat;
47import java.util.ArrayList;
48import java.util.Arrays;
49import java.util.EnumSet;
50import java.util.HashMap;
51import java.util.Iterator;
52import java.util.List;
53import java.util.Locale;
54import java.util.Map;
55import java.util.MissingResourceException;
56import java.util.Objects;
57import java.util.ResourceBundle;
58import java.util.Set;
59
60import javax.lang.model.element.Modifier;
61import javax.lang.model.element.NestingKind;
62import javax.tools.Diagnostic;
63import javax.tools.DiagnosticListener;
64import javax.tools.JavaFileManager;
65import javax.tools.JavaFileManager.Location;
66import javax.tools.JavaFileObject;
67import javax.tools.StandardJavaFileManager;
68import javax.tools.StandardLocation;
69
70import com.sun.tools.classfile.*;
71
72/**
73 *  "Main" class for javap, normally accessed from the command line
74 *  via Main, or from JSR199 via DisassemblerTool.
75 *
76 *  <p><b>This is NOT part of any supported API.
77 *  If you write code that depends on this, you do so at your own risk.
78 *  This code and its internal interfaces are subject to change or
79 *  deletion without notice.</b>
80 */
81public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
82    public class BadArgs extends Exception {
83        static final long serialVersionUID = 8765093759964640721L;
84        BadArgs(String key, Object... args) {
85            super(JavapTask.this.getMessage(key, args));
86            this.key = key;
87            this.args = args;
88        }
89
90        BadArgs showUsage(boolean b) {
91            showUsage = b;
92            return this;
93        }
94
95        final String key;
96        final Object[] args;
97        boolean showUsage;
98    }
99
100    static abstract class Option {
101        Option(boolean hasArg, String... aliases) {
102            this.hasArg = hasArg;
103            this.aliases = aliases;
104        }
105
106        boolean matches(String opt) {
107            for (String a: aliases) {
108                if (a.equals(opt))
109                    return true;
110            }
111            return false;
112        }
113
114        boolean ignoreRest() {
115            return false;
116        }
117
118        abstract void process(JavapTask task, String opt, String arg) throws BadArgs;
119
120        final boolean hasArg;
121        final String[] aliases;
122    }
123
124    static final Option[] recognizedOptions = {
125
126        new Option(false, "-help", "--help", "-?") {
127            @Override
128            void process(JavapTask task, String opt, String arg) {
129                task.options.help = true;
130            }
131        },
132
133        new Option(false, "-version") {
134            @Override
135            void process(JavapTask task, String opt, String arg) {
136                task.options.version = true;
137            }
138        },
139
140        new Option(false, "-fullversion") {
141            @Override
142            void process(JavapTask task, String opt, String arg) {
143                task.options.fullVersion = true;
144            }
145        },
146
147        new Option(false, "-v", "-verbose", "-all") {
148            @Override
149            void process(JavapTask task, String opt, String arg) {
150                task.options.verbose = true;
151                task.options.showDescriptors = true;
152                task.options.showFlags = true;
153                task.options.showAllAttrs = true;
154            }
155        },
156
157        new Option(false, "-l") {
158            @Override
159            void process(JavapTask task, String opt, String arg) {
160                task.options.showLineAndLocalVariableTables = true;
161            }
162        },
163
164        new Option(false, "-public") {
165            @Override
166            void process(JavapTask task, String opt, String arg) {
167                task.options.accessOptions.add(opt);
168                task.options.showAccess = AccessFlags.ACC_PUBLIC;
169            }
170        },
171
172        new Option(false, "-protected") {
173            @Override
174            void process(JavapTask task, String opt, String arg) {
175                task.options.accessOptions.add(opt);
176                task.options.showAccess = AccessFlags.ACC_PROTECTED;
177            }
178        },
179
180        new Option(false, "-package") {
181            @Override
182            void process(JavapTask task, String opt, String arg) {
183                task.options.accessOptions.add(opt);
184                task.options.showAccess = 0;
185            }
186        },
187
188        new Option(false, "-p", "-private") {
189            @Override
190            void process(JavapTask task, String opt, String arg) {
191                if (!task.options.accessOptions.contains("-p") &&
192                        !task.options.accessOptions.contains("-private")) {
193                    task.options.accessOptions.add(opt);
194                }
195                task.options.showAccess = AccessFlags.ACC_PRIVATE;
196            }
197        },
198
199        new Option(false, "-c") {
200            @Override
201            void process(JavapTask task, String opt, String arg) {
202                task.options.showDisassembled = true;
203            }
204        },
205
206        new Option(false, "-s") {
207            @Override
208            void process(JavapTask task, String opt, String arg) {
209                task.options.showDescriptors = true;
210            }
211        },
212
213        new Option(false, "-sysinfo") {
214            @Override
215            void process(JavapTask task, String opt, String arg) {
216                task.options.sysInfo = true;
217            }
218        },
219
220        new Option(false, "-XDdetails") {
221            @Override
222            void process(JavapTask task, String opt, String arg) {
223                task.options.details = EnumSet.allOf(InstructionDetailWriter.Kind.class);
224            }
225
226        },
227
228        new Option(false, "-XDdetails:") {
229            @Override
230            boolean matches(String opt) {
231                int sep = opt.indexOf(":");
232                return sep != -1 && super.matches(opt.substring(0, sep + 1));
233            }
234
235            @Override
236            void process(JavapTask task, String opt, String arg) throws BadArgs {
237                int sep = opt.indexOf(":");
238                for (String v: opt.substring(sep + 1).split("[,: ]+")) {
239                    if (!handleArg(task, v))
240                        throw task.new BadArgs("err.invalid.arg.for.option", v);
241                }
242            }
243
244            boolean handleArg(JavapTask task, String arg) {
245                if (arg.length() == 0)
246                    return true;
247
248                if (arg.equals("all")) {
249                    task.options.details = EnumSet.allOf(InstructionDetailWriter.Kind.class);
250                    return true;
251                }
252
253                boolean on = true;
254                if (arg.startsWith("-")) {
255                    on = false;
256                    arg = arg.substring(1);
257                }
258
259                for (InstructionDetailWriter.Kind k: InstructionDetailWriter.Kind.values()) {
260                    if (arg.equalsIgnoreCase(k.option)) {
261                        if (on)
262                            task.options.details.add(k);
263                        else
264                            task.options.details.remove(k);
265                        return true;
266                    }
267                }
268                return false;
269            }
270        },
271
272        new Option(false, "-constants") {
273            @Override
274            void process(JavapTask task, String opt, String arg) {
275                task.options.showConstants = true;
276            }
277        },
278
279        new Option(false, "-XDinner") {
280            @Override
281            void process(JavapTask task, String opt, String arg) {
282                task.options.showInnerClasses = true;
283            }
284        },
285
286        new Option(false, "-XDindent:") {
287            @Override
288            boolean matches(String opt) {
289                int sep = opt.indexOf(":");
290                return sep != -1 && super.matches(opt.substring(0, sep + 1));
291            }
292
293            @Override
294            void process(JavapTask task, String opt, String arg) throws BadArgs {
295                int sep = opt.indexOf(":");
296                try {
297                    int i = Integer.valueOf(opt.substring(sep + 1));
298                    if (i > 0) // silently ignore invalid values
299                        task.options.indentWidth = i;
300                } catch (NumberFormatException e) {
301                }
302            }
303        },
304
305        new Option(false, "-XDtab:") {
306            @Override
307            boolean matches(String opt) {
308                int sep = opt.indexOf(":");
309                return sep != -1 && super.matches(opt.substring(0, sep + 1));
310            }
311
312            @Override
313            void process(JavapTask task, String opt, String arg) throws BadArgs {
314                int sep = opt.indexOf(":");
315                try {
316                    int i = Integer.valueOf(opt.substring(sep + 1));
317                    if (i > 0) // silently ignore invalid values
318                        task.options.tabColumn = i;
319                } catch (NumberFormatException e) {
320                }
321            }
322        },
323
324        new Option(true, "--module", "-m") {
325            @Override
326            void process(JavapTask task, String opt, String arg) throws BadArgs {
327                task.options.moduleName = arg;
328            }
329        }
330
331    };
332
333    public JavapTask() {
334        context = new Context();
335        context.put(Messages.class, this);
336        options = Options.instance(context);
337        attributeFactory = new Attribute.Factory();
338    }
339
340    public JavapTask(Writer out,
341            JavaFileManager fileManager,
342            DiagnosticListener<? super JavaFileObject> diagnosticListener) {
343        this();
344        this.log = getPrintWriterForWriter(out);
345        this.fileManager = fileManager;
346        this.diagnosticListener = diagnosticListener;
347    }
348
349    public JavapTask(Writer out,
350            JavaFileManager fileManager,
351            DiagnosticListener<? super JavaFileObject> diagnosticListener,
352            Iterable<String> options,
353            Iterable<String> classes) {
354        this(out, fileManager, diagnosticListener);
355
356        this.classes = new ArrayList<>();
357        for (String classname: classes) {
358            Objects.requireNonNull(classname);
359            this.classes.add(classname);
360        }
361
362        try {
363            if (options != null)
364                handleOptions(options, false);
365        } catch (BadArgs e) {
366            throw new IllegalArgumentException(e.getMessage());
367        }
368    }
369
370    public void setLocale(Locale locale) {
371        if (locale == null)
372            locale = Locale.getDefault();
373        task_locale = locale;
374    }
375
376    public void setLog(Writer log) {
377        this.log = getPrintWriterForWriter(log);
378    }
379
380    public void setLog(OutputStream s) {
381        setLog(getPrintWriterForStream(s));
382    }
383
384    private static PrintWriter getPrintWriterForStream(OutputStream s) {
385        return new PrintWriter(s == null ? System.err : s, true);
386    }
387
388    private static PrintWriter getPrintWriterForWriter(Writer w) {
389        if (w == null)
390            return getPrintWriterForStream(null);
391        else if (w instanceof PrintWriter)
392            return (PrintWriter) w;
393        else
394            return new PrintWriter(w, true);
395    }
396
397    public void setDiagnosticListener(DiagnosticListener<? super JavaFileObject> dl) {
398        diagnosticListener = dl;
399    }
400
401    public void setDiagnosticListener(OutputStream s) {
402        setDiagnosticListener(getDiagnosticListenerForStream(s));
403    }
404
405    private DiagnosticListener<JavaFileObject> getDiagnosticListenerForStream(OutputStream s) {
406        return getDiagnosticListenerForWriter(getPrintWriterForStream(s));
407    }
408
409    private DiagnosticListener<JavaFileObject> getDiagnosticListenerForWriter(Writer w) {
410        final PrintWriter pw = getPrintWriterForWriter(w);
411        return diagnostic -> {
412            switch (diagnostic.getKind()) {
413                case ERROR:
414                    pw.print(getMessage("err.prefix"));
415                    break;
416                case WARNING:
417                    pw.print(getMessage("warn.prefix"));
418                    break;
419                case NOTE:
420                    pw.print(getMessage("note.prefix"));
421                    break;
422            }
423            pw.print(" ");
424            pw.println(diagnostic.getMessage(null));
425        };
426    }
427
428    /** Result codes.
429     */
430    static final int
431        EXIT_OK = 0,        // Compilation completed with no errors.
432        EXIT_ERROR = 1,     // Completed but reported errors.
433        EXIT_CMDERR = 2,    // Bad command-line arguments
434        EXIT_SYSERR = 3,    // System error or resource exhaustion.
435        EXIT_ABNORMAL = 4;  // Compiler terminated abnormally
436
437    int run(String[] args) {
438        try {
439            try {
440                handleOptions(args);
441
442                // the following gives consistent behavior with javac
443                if (classes == null || classes.size() == 0) {
444                    if (options.help || options.version || options.fullVersion)
445                        return EXIT_OK;
446                    else
447                        return EXIT_CMDERR;
448                }
449
450                return run();
451            } finally {
452                if (defaultFileManager != null) {
453                    try {
454                        defaultFileManager.close();
455                        defaultFileManager = null;
456                    } catch (IOException e) {
457                        throw new InternalError(e);
458                    }
459                }
460            }
461        } catch (BadArgs e) {
462            reportError(e.key, e.args);
463            if (e.showUsage) {
464                printLines(getMessage("main.usage.summary", progname));
465            }
466            return EXIT_CMDERR;
467        } catch (InternalError e) {
468            Object[] e_args;
469            if (e.getCause() == null)
470                e_args = e.args;
471            else {
472                e_args = new Object[e.args.length + 1];
473                e_args[0] = e.getCause();
474                System.arraycopy(e.args, 0, e_args, 1, e.args.length);
475            }
476            reportError("err.internal.error", e_args);
477            return EXIT_ABNORMAL;
478        } finally {
479            log.flush();
480        }
481    }
482
483    public void handleOptions(String[] args) throws BadArgs {
484        handleOptions(Arrays.asList(args), true);
485    }
486
487    private void handleOptions(Iterable<String> args, boolean allowClasses) throws BadArgs {
488        if (log == null) {
489            log = getPrintWriterForStream(System.out);
490            if (diagnosticListener == null)
491              diagnosticListener = getDiagnosticListenerForStream(System.err);
492        } else {
493            if (diagnosticListener == null)
494              diagnosticListener = getDiagnosticListenerForWriter(log);
495        }
496
497
498        if (fileManager == null)
499            fileManager = getDefaultFileManager(diagnosticListener, log);
500
501        Iterator<String> iter = args.iterator();
502        boolean noArgs = !iter.hasNext();
503
504        while (iter.hasNext()) {
505            String arg = iter.next();
506            if (arg.startsWith("-"))
507                handleOption(arg, iter);
508            else if (allowClasses) {
509                if (classes == null)
510                    classes = new ArrayList<>();
511                classes.add(arg);
512                while (iter.hasNext())
513                    classes.add(iter.next());
514            } else
515                throw new BadArgs("err.unknown.option", arg).showUsage(true);
516        }
517
518        if (options.accessOptions.size() > 1) {
519            StringBuilder sb = new StringBuilder();
520            for (String opt: options.accessOptions) {
521                if (sb.length() > 0)
522                    sb.append(" ");
523                sb.append(opt);
524            }
525            throw new BadArgs("err.incompatible.options", sb);
526        }
527
528        if ((classes == null || classes.size() == 0) &&
529                !(noArgs || options.help || options.version || options.fullVersion)) {
530            throw new BadArgs("err.no.classes.specified");
531        }
532
533        if (noArgs || options.help)
534            showHelp();
535
536        if (options.version || options.fullVersion)
537            showVersion(options.fullVersion);
538    }
539
540    private void handleOption(String name, Iterator<String> rest) throws BadArgs {
541        for (Option o: recognizedOptions) {
542            if (o.matches(name)) {
543                if (o.hasArg) {
544                    if (rest.hasNext())
545                        o.process(this, name, rest.next());
546                    else
547                        throw new BadArgs("err.missing.arg", name).showUsage(true);
548                } else
549                    o.process(this, name, null);
550
551                if (o.ignoreRest()) {
552                    while (rest.hasNext())
553                        rest.next();
554                }
555                return;
556            }
557        }
558
559        try {
560            if (fileManager.handleOption(name, rest))
561                return;
562        } catch (IllegalArgumentException e) {
563            throw new BadArgs("err.invalid.use.of.option", name).showUsage(true);
564        }
565
566        throw new BadArgs("err.unknown.option", name).showUsage(true);
567    }
568
569    public Boolean call() {
570        return run() == 0;
571    }
572
573    public int run() {
574        if (classes == null || classes.isEmpty()) {
575            return EXIT_ERROR;
576        }
577
578        context.put(PrintWriter.class, log);
579        ClassWriter classWriter = ClassWriter.instance(context);
580        SourceWriter sourceWriter = SourceWriter.instance(context);
581        sourceWriter.setFileManager(fileManager);
582
583        if (options.moduleName != null) {
584            try {
585                moduleLocation = findModule(options.moduleName);
586                if (moduleLocation == null) {
587                    reportError("err.cant.find.module", options.moduleName);
588                    return EXIT_ERROR;
589                }
590            } catch (IOException e) {
591                reportError("err.cant.find.module.ex", options.moduleName, e);
592                return EXIT_ERROR;
593            }
594        }
595
596        int result = EXIT_OK;
597
598        for (String className: classes) {
599            try {
600                result = writeClass(classWriter, className);
601            } catch (ConstantPoolException e) {
602                reportError("err.bad.constant.pool", className, e.getLocalizedMessage());
603                result = EXIT_ERROR;
604            } catch (EOFException e) {
605                reportError("err.end.of.file", className);
606                result = EXIT_ERROR;
607            } catch (FileNotFoundException | NoSuchFileException e) {
608                reportError("err.file.not.found", e.getLocalizedMessage());
609                result = EXIT_ERROR;
610            } catch (IOException e) {
611                //e.printStackTrace();
612                Object msg = e.getLocalizedMessage();
613                if (msg == null) {
614                    msg = e;
615                }
616                reportError("err.ioerror", className, msg);
617                result = EXIT_ERROR;
618            } catch (OutOfMemoryError e) {
619                reportError("err.nomem");
620                result = EXIT_ERROR;
621            } catch (Throwable t) {
622                StringWriter sw = new StringWriter();
623                PrintWriter pw = new PrintWriter(sw);
624                t.printStackTrace(pw);
625                pw.close();
626                reportError("err.crash", t.toString(), sw.toString());
627                result = EXIT_ABNORMAL;
628            }
629        }
630
631        return result;
632    }
633
634    protected int writeClass(ClassWriter classWriter, String className)
635            throws IOException, ConstantPoolException {
636        JavaFileObject fo = open(className);
637        if (fo == null) {
638            reportError("err.class.not.found", className);
639            return EXIT_ERROR;
640        }
641
642        ClassFileInfo cfInfo = read(fo);
643        if (!className.endsWith(".class")) {
644            if (cfInfo.cf.this_class == 0) {
645                if (!className.equals("module-info")) {
646                    reportWarning("warn.unexpected.class", fo.getName(), className);
647                }
648            } else {
649                String cfName = cfInfo.cf.getName();
650                if (!cfName.replaceAll("[/$]", ".").equals(className.replaceAll("[/$]", "."))) {
651                    reportWarning("warn.unexpected.class", fo.getName(), className);
652                }
653            }
654        }
655        write(cfInfo);
656
657        if (options.showInnerClasses) {
658            ClassFile cf = cfInfo.cf;
659            Attribute a = cf.getAttribute(Attribute.InnerClasses);
660            if (a instanceof InnerClasses_attribute) {
661                InnerClasses_attribute inners = (InnerClasses_attribute) a;
662                try {
663                    int result = EXIT_OK;
664                    for (int i = 0; i < inners.classes.length; i++) {
665                        int outerIndex = inners.classes[i].outer_class_info_index;
666                        ConstantPool.CONSTANT_Class_info outerClassInfo = cf.constant_pool.getClassInfo(outerIndex);
667                        String outerClassName = outerClassInfo.getName();
668                        if (outerClassName.equals(cf.getName())) {
669                            int innerIndex = inners.classes[i].inner_class_info_index;
670                            ConstantPool.CONSTANT_Class_info innerClassInfo = cf.constant_pool.getClassInfo(innerIndex);
671                            String innerClassName = innerClassInfo.getName();
672                            classWriter.println("// inner class " + innerClassName.replaceAll("[/$]", "."));
673                            classWriter.println();
674                            result = writeClass(classWriter, innerClassName);
675                            if (result != EXIT_OK) return result;
676                        }
677                    }
678                    return result;
679                } catch (ConstantPoolException e) {
680                    reportError("err.bad.innerclasses.attribute", className);
681                    return EXIT_ERROR;
682                }
683            } else if (a != null) {
684                reportError("err.bad.innerclasses.attribute", className);
685                return EXIT_ERROR;
686            }
687        }
688
689        return EXIT_OK;
690    }
691
692    protected JavaFileObject open(String className) throws IOException {
693        // for compatibility, first see if it is a class name
694        JavaFileObject fo = getClassFileObject(className);
695        if (fo != null)
696            return fo;
697
698        // see if it is an inner class, by replacing dots to $, starting from the right
699        String cn = className;
700        int lastDot;
701        while ((lastDot = cn.lastIndexOf(".")) != -1) {
702            cn = cn.substring(0, lastDot) + "$" + cn.substring(lastDot + 1);
703            fo = getClassFileObject(cn);
704            if (fo != null)
705                return fo;
706        }
707
708        if (!className.endsWith(".class"))
709            return null;
710
711        if (fileManager instanceof StandardJavaFileManager) {
712            StandardJavaFileManager sfm = (StandardJavaFileManager) fileManager;
713            try {
714                fo = sfm.getJavaFileObjects(className).iterator().next();
715                if (fo != null && fo.getLastModified() != 0) {
716                    return fo;
717                }
718            } catch (IllegalArgumentException ignore) {
719            }
720        }
721
722        // see if it is a URL, and if so, wrap it in just enough of a JavaFileObject
723        // to suit javap's needs
724        if (className.matches("^[A-Za-z]+:.*")) {
725            try {
726                final URI uri = new URI(className);
727                final URL url = uri.toURL();
728                final URLConnection conn = url.openConnection();
729                conn.setUseCaches(false);
730                return new JavaFileObject() {
731                    public Kind getKind() {
732                        return JavaFileObject.Kind.CLASS;
733                    }
734
735                    public boolean isNameCompatible(String simpleName, Kind kind) {
736                        throw new UnsupportedOperationException();
737                    }
738
739                    public NestingKind getNestingKind() {
740                        throw new UnsupportedOperationException();
741                    }
742
743                    public Modifier getAccessLevel() {
744                        throw new UnsupportedOperationException();
745                    }
746
747                    public URI toUri() {
748                        return uri;
749                    }
750
751                    public String getName() {
752                        return uri.toString();
753                    }
754
755                    public InputStream openInputStream() throws IOException {
756                        return conn.getInputStream();
757                    }
758
759                    public OutputStream openOutputStream() throws IOException {
760                        throw new UnsupportedOperationException();
761                    }
762
763                    public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
764                        throw new UnsupportedOperationException();
765                    }
766
767                    public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
768                        throw new UnsupportedOperationException();
769                    }
770
771                    public Writer openWriter() throws IOException {
772                        throw new UnsupportedOperationException();
773                    }
774
775                    public long getLastModified() {
776                        return conn.getLastModified();
777                    }
778
779                    public boolean delete() {
780                        throw new UnsupportedOperationException();
781                    }
782
783                };
784            } catch (URISyntaxException | IOException ignore) {
785            }
786        }
787
788        return null;
789    }
790
791    public static class ClassFileInfo {
792        ClassFileInfo(JavaFileObject fo, ClassFile cf, byte[] digest, int size) {
793            this.fo = fo;
794            this.cf = cf;
795            this.digest = digest;
796            this.size = size;
797        }
798        public final JavaFileObject fo;
799        public final ClassFile cf;
800        public final byte[] digest;
801        public final int size;
802    }
803
804    public ClassFileInfo read(JavaFileObject fo) throws IOException, ConstantPoolException {
805        InputStream in = fo.openInputStream();
806        try {
807            SizeInputStream sizeIn = null;
808            MessageDigest md  = null;
809            if (options.sysInfo || options.verbose) {
810                try {
811                    md = MessageDigest.getInstance("MD5");
812                } catch (NoSuchAlgorithmException ignore) {
813                }
814                in = new DigestInputStream(in, md);
815                in = sizeIn = new SizeInputStream(in);
816            }
817
818            ClassFile cf = ClassFile.read(in, attributeFactory);
819            byte[] digest = (md == null) ? null : md.digest();
820            int size = (sizeIn == null) ? -1 : sizeIn.size();
821            return new ClassFileInfo(fo, cf, digest, size);
822        } finally {
823            in.close();
824        }
825    }
826
827    public void write(ClassFileInfo info) {
828        ClassWriter classWriter = ClassWriter.instance(context);
829        if (options.sysInfo || options.verbose) {
830            classWriter.setFile(info.fo.toUri());
831            classWriter.setLastModified(info.fo.getLastModified());
832            classWriter.setDigest("MD5", info.digest);
833            classWriter.setFileSize(info.size);
834        }
835
836        classWriter.write(info.cf);
837    }
838
839    protected void setClassFile(ClassFile classFile) {
840        ClassWriter classWriter = ClassWriter.instance(context);
841        classWriter.setClassFile(classFile);
842    }
843
844    protected void setMethod(Method enclosingMethod) {
845        ClassWriter classWriter = ClassWriter.instance(context);
846        classWriter.setMethod(enclosingMethod);
847    }
848
849    protected void write(Attribute value) {
850        AttributeWriter attrWriter = AttributeWriter.instance(context);
851        ClassWriter classWriter = ClassWriter.instance(context);
852        ClassFile cf = classWriter.getClassFile();
853        attrWriter.write(cf, value, cf.constant_pool);
854    }
855
856    protected void write(Attributes attrs) {
857        AttributeWriter attrWriter = AttributeWriter.instance(context);
858        ClassWriter classWriter = ClassWriter.instance(context);
859        ClassFile cf = classWriter.getClassFile();
860        attrWriter.write(cf, attrs, cf.constant_pool);
861    }
862
863    protected void write(ConstantPool constant_pool) {
864        ConstantWriter constantWriter = ConstantWriter.instance(context);
865        constantWriter.writeConstantPool(constant_pool);
866    }
867
868    protected void write(ConstantPool constant_pool, int value) {
869        ConstantWriter constantWriter = ConstantWriter.instance(context);
870        constantWriter.write(value);
871    }
872
873    protected void write(ConstantPool.CPInfo value) {
874        ConstantWriter constantWriter = ConstantWriter.instance(context);
875        constantWriter.println(value);
876    }
877
878    protected void write(Field value) {
879        ClassWriter classWriter = ClassWriter.instance(context);
880        classWriter.writeField(value);
881    }
882
883    protected void write(Method value) {
884        ClassWriter classWriter = ClassWriter.instance(context);
885        classWriter.writeMethod(value);
886    }
887
888    private JavaFileManager getDefaultFileManager(final DiagnosticListener<? super JavaFileObject> dl, PrintWriter log) {
889        if (defaultFileManager == null)
890            defaultFileManager = JavapFileManager.create(dl, log);
891        return defaultFileManager;
892    }
893
894    private JavaFileObject getClassFileObject(String className) throws IOException {
895        try {
896            JavaFileObject fo;
897            if (moduleLocation != null) {
898                fo = fileManager.getJavaFileForInput(moduleLocation, className, JavaFileObject.Kind.CLASS);
899            } else {
900                fo = fileManager.getJavaFileForInput(StandardLocation.PLATFORM_CLASS_PATH, className, JavaFileObject.Kind.CLASS);
901                if (fo == null)
902                    fo = fileManager.getJavaFileForInput(StandardLocation.CLASS_PATH, className, JavaFileObject.Kind.CLASS);
903            }
904            return fo;
905        } catch (IllegalArgumentException e) {
906            return null;
907        }
908    }
909
910    private Location findModule(String moduleName) throws IOException {
911        Location[] locns = {
912            StandardLocation.UPGRADE_MODULE_PATH,
913            StandardLocation.SYSTEM_MODULES,
914            StandardLocation.MODULE_PATH
915        };
916        for (Location segment: locns) {
917            for (Set<Location> set: fileManager.listLocationsForModules(segment)) {
918                Location result = null;
919                for (Location l: set) {
920                    String name = fileManager.inferModuleName(l);
921                    if (name.equals(moduleName)) {
922                        if (result == null)
923                            result = l;
924                        else
925                            throw new IOException("multiple definitions found for " + moduleName);
926                    }
927                }
928                if (result != null)
929                    return result;
930            }
931        }
932        return null;
933    }
934
935    private void showHelp() {
936        printLines(getMessage("main.usage", progname));
937        for (Option o: recognizedOptions) {
938            String name = o.aliases[0].replaceAll("^-+", "").replaceAll("-+", "_"); // there must always be at least one name
939            if (name.startsWith("X") || name.equals("fullversion") || name.equals("h") || name.equals("verify"))
940                continue;
941            printLines(getMessage("main.opt." + name));
942        }
943
944        String[] fmOptions = {
945            "--module-path", "--system",
946            "--class-path", "-classpath", "-cp",
947            "-bootclasspath"
948        };
949
950        for (String o: fmOptions) {
951            if (fileManager.isSupportedOption(o) == -1)
952                continue;
953            String name = o.replaceAll("^-+", "").replaceAll("-+", "_");
954            printLines(getMessage("main.opt." + name));
955        }
956
957        printLines(getMessage("main.usage.foot"));
958    }
959
960    private void showVersion(boolean full) {
961        printLines(version(full ? "full" : "release"));
962    }
963
964    private void printLines(String msg) {
965        log.println(msg.replace("\n", nl));
966    }
967
968    private static final String nl = System.getProperty("line.separator");
969
970    private static final String versionRBName = "com.sun.tools.javap.resources.version";
971    private static ResourceBundle versionRB;
972
973    private String version(String key) {
974        // key=version:  mm.nn.oo[-milestone]
975        // key=full:     mm.mm.oo[-milestone]-build
976        if (versionRB == null) {
977            try {
978                versionRB = ResourceBundle.getBundle(versionRBName);
979            } catch (MissingResourceException e) {
980                return getMessage("version.resource.missing", System.getProperty("java.version"));
981            }
982        }
983        try {
984            return versionRB.getString(key);
985        }
986        catch (MissingResourceException e) {
987            return getMessage("version.unknown", System.getProperty("java.version"));
988        }
989    }
990
991    private void reportError(String key, Object... args) {
992        diagnosticListener.report(createDiagnostic(Diagnostic.Kind.ERROR, key, args));
993    }
994
995    private void reportNote(String key, Object... args) {
996        diagnosticListener.report(createDiagnostic(Diagnostic.Kind.NOTE, key, args));
997    }
998
999    private void reportWarning(String key, Object... args) {
1000        diagnosticListener.report(createDiagnostic(Diagnostic.Kind.WARNING, key, args));
1001    }
1002
1003    private Diagnostic<JavaFileObject> createDiagnostic(
1004            final Diagnostic.Kind kind, final String key, final Object... args) {
1005        return new Diagnostic<JavaFileObject>() {
1006            public Kind getKind() {
1007                return kind;
1008            }
1009
1010            public JavaFileObject getSource() {
1011                return null;
1012            }
1013
1014            public long getPosition() {
1015                return Diagnostic.NOPOS;
1016            }
1017
1018            public long getStartPosition() {
1019                return Diagnostic.NOPOS;
1020            }
1021
1022            public long getEndPosition() {
1023                return Diagnostic.NOPOS;
1024            }
1025
1026            public long getLineNumber() {
1027                return Diagnostic.NOPOS;
1028            }
1029
1030            public long getColumnNumber() {
1031                return Diagnostic.NOPOS;
1032            }
1033
1034            public String getCode() {
1035                return key;
1036            }
1037
1038            public String getMessage(Locale locale) {
1039                return JavapTask.this.getMessage(locale, key, args);
1040            }
1041
1042            @Override
1043            public String toString() {
1044                return getClass().getName() + "[key=" + key + ",args=" + Arrays.asList(args) + "]";
1045            }
1046
1047        };
1048
1049    }
1050
1051    public String getMessage(String key, Object... args) {
1052        return getMessage(task_locale, key, args);
1053    }
1054
1055    public String getMessage(Locale locale, String key, Object... args) {
1056        if (bundles == null) {
1057            // could make this a HashMap<Locale,SoftReference<ResourceBundle>>
1058            // and for efficiency, keep a hard reference to the bundle for the task
1059            // locale
1060            bundles = new HashMap<>();
1061        }
1062
1063        if (locale == null)
1064            locale = Locale.getDefault();
1065
1066        ResourceBundle b = bundles.get(locale);
1067        if (b == null) {
1068            try {
1069                b = ResourceBundle.getBundle("com.sun.tools.javap.resources.javap", locale);
1070                bundles.put(locale, b);
1071            } catch (MissingResourceException e) {
1072                throw new InternalError("Cannot find javap resource bundle for locale " + locale);
1073            }
1074        }
1075
1076        try {
1077            return MessageFormat.format(b.getString(key), args);
1078        } catch (MissingResourceException e) {
1079            throw new InternalError(e, key);
1080        }
1081    }
1082
1083    protected Context context;
1084    JavaFileManager fileManager;
1085    JavaFileManager defaultFileManager;
1086    PrintWriter log;
1087    DiagnosticListener<? super JavaFileObject> diagnosticListener;
1088    List<String> classes;
1089    Location moduleLocation;
1090    Options options;
1091    //ResourceBundle bundle;
1092    Locale task_locale;
1093    Map<Locale, ResourceBundle> bundles;
1094    protected Attribute.Factory attributeFactory;
1095
1096    private static final String progname = "javap";
1097
1098    private static class SizeInputStream extends FilterInputStream {
1099        SizeInputStream(InputStream in) {
1100            super(in);
1101        }
1102
1103        int size() {
1104            return size;
1105        }
1106
1107        @Override
1108        public int read(byte[] buf, int offset, int length) throws IOException {
1109            int n = super.read(buf, offset, length);
1110            if (n > 0)
1111                size += n;
1112            return n;
1113        }
1114
1115        @Override
1116        public int read() throws IOException {
1117            int b = super.read();
1118            size += 1;
1119            return b;
1120        }
1121
1122        private int size;
1123    }
1124}
1125