src/jdk.jdeps/share/classes/com/sun/tools/javap/JavapTask.java
changeset 47216 71c04702a3d5
parent 42827 36468b5fa7f4
child 48543 7067fe4e054e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/javap/JavapTask.java	Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,1124 @@
+/*
+ * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.tools.javap;
+
+import java.io.EOFException;
+import java.io.FileNotFoundException;
+import java.io.FilterInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.file.NoSuchFileException;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.Objects;
+import java.util.ResourceBundle;
+import java.util.Set;
+
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.NestingKind;
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticListener;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileManager.Location;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+
+import com.sun.tools.classfile.*;
+
+/**
+ *  "Main" class for javap, normally accessed from the command line
+ *  via Main, or from JSR199 via DisassemblerTool.
+ *
+ *  <p><b>This is NOT part of any supported API.
+ *  If you write code that depends on this, you do so at your own risk.
+ *  This code and its internal interfaces are subject to change or
+ *  deletion without notice.</b>
+ */
+public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
+    public class BadArgs extends Exception {
+        static final long serialVersionUID = 8765093759964640721L;
+        BadArgs(String key, Object... args) {
+            super(JavapTask.this.getMessage(key, args));
+            this.key = key;
+            this.args = args;
+        }
+
+        BadArgs showUsage(boolean b) {
+            showUsage = b;
+            return this;
+        }
+
+        final String key;
+        final Object[] args;
+        boolean showUsage;
+    }
+
+    static abstract class Option {
+        Option(boolean hasArg, String... aliases) {
+            this.hasArg = hasArg;
+            this.aliases = aliases;
+        }
+
+        boolean matches(String opt) {
+            for (String a: aliases) {
+                if (a.equals(opt))
+                    return true;
+            }
+            return false;
+        }
+
+        boolean ignoreRest() {
+            return false;
+        }
+
+        abstract void process(JavapTask task, String opt, String arg) throws BadArgs;
+
+        final boolean hasArg;
+        final String[] aliases;
+    }
+
+    static final Option[] recognizedOptions = {
+
+        new Option(false, "-help", "--help", "-?") {
+            @Override
+            void process(JavapTask task, String opt, String arg) {
+                task.options.help = true;
+            }
+        },
+
+        new Option(false, "-version") {
+            @Override
+            void process(JavapTask task, String opt, String arg) {
+                task.options.version = true;
+            }
+        },
+
+        new Option(false, "-fullversion") {
+            @Override
+            void process(JavapTask task, String opt, String arg) {
+                task.options.fullVersion = true;
+            }
+        },
+
+        new Option(false, "-v", "-verbose", "-all") {
+            @Override
+            void process(JavapTask task, String opt, String arg) {
+                task.options.verbose = true;
+                task.options.showDescriptors = true;
+                task.options.showFlags = true;
+                task.options.showAllAttrs = true;
+            }
+        },
+
+        new Option(false, "-l") {
+            @Override
+            void process(JavapTask task, String opt, String arg) {
+                task.options.showLineAndLocalVariableTables = true;
+            }
+        },
+
+        new Option(false, "-public") {
+            @Override
+            void process(JavapTask task, String opt, String arg) {
+                task.options.accessOptions.add(opt);
+                task.options.showAccess = AccessFlags.ACC_PUBLIC;
+            }
+        },
+
+        new Option(false, "-protected") {
+            @Override
+            void process(JavapTask task, String opt, String arg) {
+                task.options.accessOptions.add(opt);
+                task.options.showAccess = AccessFlags.ACC_PROTECTED;
+            }
+        },
+
+        new Option(false, "-package") {
+            @Override
+            void process(JavapTask task, String opt, String arg) {
+                task.options.accessOptions.add(opt);
+                task.options.showAccess = 0;
+            }
+        },
+
+        new Option(false, "-p", "-private") {
+            @Override
+            void process(JavapTask task, String opt, String arg) {
+                if (!task.options.accessOptions.contains("-p") &&
+                        !task.options.accessOptions.contains("-private")) {
+                    task.options.accessOptions.add(opt);
+                }
+                task.options.showAccess = AccessFlags.ACC_PRIVATE;
+            }
+        },
+
+        new Option(false, "-c") {
+            @Override
+            void process(JavapTask task, String opt, String arg) {
+                task.options.showDisassembled = true;
+            }
+        },
+
+        new Option(false, "-s") {
+            @Override
+            void process(JavapTask task, String opt, String arg) {
+                task.options.showDescriptors = true;
+            }
+        },
+
+        new Option(false, "-sysinfo") {
+            @Override
+            void process(JavapTask task, String opt, String arg) {
+                task.options.sysInfo = true;
+            }
+        },
+
+        new Option(false, "-XDdetails") {
+            @Override
+            void process(JavapTask task, String opt, String arg) {
+                task.options.details = EnumSet.allOf(InstructionDetailWriter.Kind.class);
+            }
+
+        },
+
+        new Option(false, "-XDdetails:") {
+            @Override
+            boolean matches(String opt) {
+                int sep = opt.indexOf(":");
+                return sep != -1 && super.matches(opt.substring(0, sep + 1));
+            }
+
+            @Override
+            void process(JavapTask task, String opt, String arg) throws BadArgs {
+                int sep = opt.indexOf(":");
+                for (String v: opt.substring(sep + 1).split("[,: ]+")) {
+                    if (!handleArg(task, v))
+                        throw task.new BadArgs("err.invalid.arg.for.option", v);
+                }
+            }
+
+            boolean handleArg(JavapTask task, String arg) {
+                if (arg.length() == 0)
+                    return true;
+
+                if (arg.equals("all")) {
+                    task.options.details = EnumSet.allOf(InstructionDetailWriter.Kind.class);
+                    return true;
+                }
+
+                boolean on = true;
+                if (arg.startsWith("-")) {
+                    on = false;
+                    arg = arg.substring(1);
+                }
+
+                for (InstructionDetailWriter.Kind k: InstructionDetailWriter.Kind.values()) {
+                    if (arg.equalsIgnoreCase(k.option)) {
+                        if (on)
+                            task.options.details.add(k);
+                        else
+                            task.options.details.remove(k);
+                        return true;
+                    }
+                }
+                return false;
+            }
+        },
+
+        new Option(false, "-constants") {
+            @Override
+            void process(JavapTask task, String opt, String arg) {
+                task.options.showConstants = true;
+            }
+        },
+
+        new Option(false, "-XDinner") {
+            @Override
+            void process(JavapTask task, String opt, String arg) {
+                task.options.showInnerClasses = true;
+            }
+        },
+
+        new Option(false, "-XDindent:") {
+            @Override
+            boolean matches(String opt) {
+                int sep = opt.indexOf(":");
+                return sep != -1 && super.matches(opt.substring(0, sep + 1));
+            }
+
+            @Override
+            void process(JavapTask task, String opt, String arg) throws BadArgs {
+                int sep = opt.indexOf(":");
+                try {
+                    int i = Integer.valueOf(opt.substring(sep + 1));
+                    if (i > 0) // silently ignore invalid values
+                        task.options.indentWidth = i;
+                } catch (NumberFormatException e) {
+                }
+            }
+        },
+
+        new Option(false, "-XDtab:") {
+            @Override
+            boolean matches(String opt) {
+                int sep = opt.indexOf(":");
+                return sep != -1 && super.matches(opt.substring(0, sep + 1));
+            }
+
+            @Override
+            void process(JavapTask task, String opt, String arg) throws BadArgs {
+                int sep = opt.indexOf(":");
+                try {
+                    int i = Integer.valueOf(opt.substring(sep + 1));
+                    if (i > 0) // silently ignore invalid values
+                        task.options.tabColumn = i;
+                } catch (NumberFormatException e) {
+                }
+            }
+        },
+
+        new Option(true, "--module", "-m") {
+            @Override
+            void process(JavapTask task, String opt, String arg) throws BadArgs {
+                task.options.moduleName = arg;
+            }
+        }
+
+    };
+
+    public JavapTask() {
+        context = new Context();
+        context.put(Messages.class, this);
+        options = Options.instance(context);
+        attributeFactory = new Attribute.Factory();
+    }
+
+    public JavapTask(Writer out,
+            JavaFileManager fileManager,
+            DiagnosticListener<? super JavaFileObject> diagnosticListener) {
+        this();
+        this.log = getPrintWriterForWriter(out);
+        this.fileManager = fileManager;
+        this.diagnosticListener = diagnosticListener;
+    }
+
+    public JavapTask(Writer out,
+            JavaFileManager fileManager,
+            DiagnosticListener<? super JavaFileObject> diagnosticListener,
+            Iterable<String> options,
+            Iterable<String> classes) {
+        this(out, fileManager, diagnosticListener);
+
+        this.classes = new ArrayList<>();
+        for (String classname: classes) {
+            Objects.requireNonNull(classname);
+            this.classes.add(classname);
+        }
+
+        try {
+            if (options != null)
+                handleOptions(options, false);
+        } catch (BadArgs e) {
+            throw new IllegalArgumentException(e.getMessage());
+        }
+    }
+
+    public void setLocale(Locale locale) {
+        if (locale == null)
+            locale = Locale.getDefault();
+        task_locale = locale;
+    }
+
+    public void setLog(Writer log) {
+        this.log = getPrintWriterForWriter(log);
+    }
+
+    public void setLog(OutputStream s) {
+        setLog(getPrintWriterForStream(s));
+    }
+
+    private static PrintWriter getPrintWriterForStream(OutputStream s) {
+        return new PrintWriter(s == null ? System.err : s, true);
+    }
+
+    private static PrintWriter getPrintWriterForWriter(Writer w) {
+        if (w == null)
+            return getPrintWriterForStream(null);
+        else if (w instanceof PrintWriter)
+            return (PrintWriter) w;
+        else
+            return new PrintWriter(w, true);
+    }
+
+    public void setDiagnosticListener(DiagnosticListener<? super JavaFileObject> dl) {
+        diagnosticListener = dl;
+    }
+
+    public void setDiagnosticListener(OutputStream s) {
+        setDiagnosticListener(getDiagnosticListenerForStream(s));
+    }
+
+    private DiagnosticListener<JavaFileObject> getDiagnosticListenerForStream(OutputStream s) {
+        return getDiagnosticListenerForWriter(getPrintWriterForStream(s));
+    }
+
+    private DiagnosticListener<JavaFileObject> getDiagnosticListenerForWriter(Writer w) {
+        final PrintWriter pw = getPrintWriterForWriter(w);
+        return diagnostic -> {
+            switch (diagnostic.getKind()) {
+                case ERROR:
+                    pw.print(getMessage("err.prefix"));
+                    break;
+                case WARNING:
+                    pw.print(getMessage("warn.prefix"));
+                    break;
+                case NOTE:
+                    pw.print(getMessage("note.prefix"));
+                    break;
+            }
+            pw.print(" ");
+            pw.println(diagnostic.getMessage(null));
+        };
+    }
+
+    /** Result codes.
+     */
+    static final int
+        EXIT_OK = 0,        // Compilation completed with no errors.
+        EXIT_ERROR = 1,     // Completed but reported errors.
+        EXIT_CMDERR = 2,    // Bad command-line arguments
+        EXIT_SYSERR = 3,    // System error or resource exhaustion.
+        EXIT_ABNORMAL = 4;  // Compiler terminated abnormally
+
+    int run(String[] args) {
+        try {
+            try {
+                handleOptions(args);
+
+                // the following gives consistent behavior with javac
+                if (classes == null || classes.size() == 0) {
+                    if (options.help || options.version || options.fullVersion)
+                        return EXIT_OK;
+                    else
+                        return EXIT_CMDERR;
+                }
+
+                return run();
+            } finally {
+                if (defaultFileManager != null) {
+                    try {
+                        defaultFileManager.close();
+                        defaultFileManager = null;
+                    } catch (IOException e) {
+                        throw new InternalError(e);
+                    }
+                }
+            }
+        } catch (BadArgs e) {
+            reportError(e.key, e.args);
+            if (e.showUsage) {
+                printLines(getMessage("main.usage.summary", progname));
+            }
+            return EXIT_CMDERR;
+        } catch (InternalError e) {
+            Object[] e_args;
+            if (e.getCause() == null)
+                e_args = e.args;
+            else {
+                e_args = new Object[e.args.length + 1];
+                e_args[0] = e.getCause();
+                System.arraycopy(e.args, 0, e_args, 1, e.args.length);
+            }
+            reportError("err.internal.error", e_args);
+            return EXIT_ABNORMAL;
+        } finally {
+            log.flush();
+        }
+    }
+
+    public void handleOptions(String[] args) throws BadArgs {
+        handleOptions(Arrays.asList(args), true);
+    }
+
+    private void handleOptions(Iterable<String> args, boolean allowClasses) throws BadArgs {
+        if (log == null) {
+            log = getPrintWriterForStream(System.out);
+            if (diagnosticListener == null)
+              diagnosticListener = getDiagnosticListenerForStream(System.err);
+        } else {
+            if (diagnosticListener == null)
+              diagnosticListener = getDiagnosticListenerForWriter(log);
+        }
+
+
+        if (fileManager == null)
+            fileManager = getDefaultFileManager(diagnosticListener, log);
+
+        Iterator<String> iter = args.iterator();
+        boolean noArgs = !iter.hasNext();
+
+        while (iter.hasNext()) {
+            String arg = iter.next();
+            if (arg.startsWith("-"))
+                handleOption(arg, iter);
+            else if (allowClasses) {
+                if (classes == null)
+                    classes = new ArrayList<>();
+                classes.add(arg);
+                while (iter.hasNext())
+                    classes.add(iter.next());
+            } else
+                throw new BadArgs("err.unknown.option", arg).showUsage(true);
+        }
+
+        if (options.accessOptions.size() > 1) {
+            StringBuilder sb = new StringBuilder();
+            for (String opt: options.accessOptions) {
+                if (sb.length() > 0)
+                    sb.append(" ");
+                sb.append(opt);
+            }
+            throw new BadArgs("err.incompatible.options", sb);
+        }
+
+        if ((classes == null || classes.size() == 0) &&
+                !(noArgs || options.help || options.version || options.fullVersion)) {
+            throw new BadArgs("err.no.classes.specified");
+        }
+
+        if (noArgs || options.help)
+            showHelp();
+
+        if (options.version || options.fullVersion)
+            showVersion(options.fullVersion);
+    }
+
+    private void handleOption(String name, Iterator<String> rest) throws BadArgs {
+        for (Option o: recognizedOptions) {
+            if (o.matches(name)) {
+                if (o.hasArg) {
+                    if (rest.hasNext())
+                        o.process(this, name, rest.next());
+                    else
+                        throw new BadArgs("err.missing.arg", name).showUsage(true);
+                } else
+                    o.process(this, name, null);
+
+                if (o.ignoreRest()) {
+                    while (rest.hasNext())
+                        rest.next();
+                }
+                return;
+            }
+        }
+
+        try {
+            if (fileManager.handleOption(name, rest))
+                return;
+        } catch (IllegalArgumentException e) {
+            throw new BadArgs("err.invalid.use.of.option", name).showUsage(true);
+        }
+
+        throw new BadArgs("err.unknown.option", name).showUsage(true);
+    }
+
+    public Boolean call() {
+        return run() == 0;
+    }
+
+    public int run() {
+        if (classes == null || classes.isEmpty()) {
+            return EXIT_ERROR;
+        }
+
+        context.put(PrintWriter.class, log);
+        ClassWriter classWriter = ClassWriter.instance(context);
+        SourceWriter sourceWriter = SourceWriter.instance(context);
+        sourceWriter.setFileManager(fileManager);
+
+        if (options.moduleName != null) {
+            try {
+                moduleLocation = findModule(options.moduleName);
+                if (moduleLocation == null) {
+                    reportError("err.cant.find.module", options.moduleName);
+                    return EXIT_ERROR;
+                }
+            } catch (IOException e) {
+                reportError("err.cant.find.module.ex", options.moduleName, e);
+                return EXIT_ERROR;
+            }
+        }
+
+        int result = EXIT_OK;
+
+        for (String className: classes) {
+            try {
+                result = writeClass(classWriter, className);
+            } catch (ConstantPoolException e) {
+                reportError("err.bad.constant.pool", className, e.getLocalizedMessage());
+                result = EXIT_ERROR;
+            } catch (EOFException e) {
+                reportError("err.end.of.file", className);
+                result = EXIT_ERROR;
+            } catch (FileNotFoundException | NoSuchFileException e) {
+                reportError("err.file.not.found", e.getLocalizedMessage());
+                result = EXIT_ERROR;
+            } catch (IOException e) {
+                //e.printStackTrace();
+                Object msg = e.getLocalizedMessage();
+                if (msg == null) {
+                    msg = e;
+                }
+                reportError("err.ioerror", className, msg);
+                result = EXIT_ERROR;
+            } catch (OutOfMemoryError e) {
+                reportError("err.nomem");
+                result = EXIT_ERROR;
+            } catch (Throwable t) {
+                StringWriter sw = new StringWriter();
+                PrintWriter pw = new PrintWriter(sw);
+                t.printStackTrace(pw);
+                pw.close();
+                reportError("err.crash", t.toString(), sw.toString());
+                result = EXIT_ABNORMAL;
+            }
+        }
+
+        return result;
+    }
+
+    protected int writeClass(ClassWriter classWriter, String className)
+            throws IOException, ConstantPoolException {
+        JavaFileObject fo = open(className);
+        if (fo == null) {
+            reportError("err.class.not.found", className);
+            return EXIT_ERROR;
+        }
+
+        ClassFileInfo cfInfo = read(fo);
+        if (!className.endsWith(".class")) {
+            if (cfInfo.cf.this_class == 0) {
+                if (!className.equals("module-info")) {
+                    reportWarning("warn.unexpected.class", fo.getName(), className);
+                }
+            } else {
+                String cfName = cfInfo.cf.getName();
+                if (!cfName.replaceAll("[/$]", ".").equals(className.replaceAll("[/$]", "."))) {
+                    reportWarning("warn.unexpected.class", fo.getName(), className);
+                }
+            }
+        }
+        write(cfInfo);
+
+        if (options.showInnerClasses) {
+            ClassFile cf = cfInfo.cf;
+            Attribute a = cf.getAttribute(Attribute.InnerClasses);
+            if (a instanceof InnerClasses_attribute) {
+                InnerClasses_attribute inners = (InnerClasses_attribute) a;
+                try {
+                    int result = EXIT_OK;
+                    for (int i = 0; i < inners.classes.length; i++) {
+                        int outerIndex = inners.classes[i].outer_class_info_index;
+                        ConstantPool.CONSTANT_Class_info outerClassInfo = cf.constant_pool.getClassInfo(outerIndex);
+                        String outerClassName = outerClassInfo.getName();
+                        if (outerClassName.equals(cf.getName())) {
+                            int innerIndex = inners.classes[i].inner_class_info_index;
+                            ConstantPool.CONSTANT_Class_info innerClassInfo = cf.constant_pool.getClassInfo(innerIndex);
+                            String innerClassName = innerClassInfo.getName();
+                            classWriter.println("// inner class " + innerClassName.replaceAll("[/$]", "."));
+                            classWriter.println();
+                            result = writeClass(classWriter, innerClassName);
+                            if (result != EXIT_OK) return result;
+                        }
+                    }
+                    return result;
+                } catch (ConstantPoolException e) {
+                    reportError("err.bad.innerclasses.attribute", className);
+                    return EXIT_ERROR;
+                }
+            } else if (a != null) {
+                reportError("err.bad.innerclasses.attribute", className);
+                return EXIT_ERROR;
+            }
+        }
+
+        return EXIT_OK;
+    }
+
+    protected JavaFileObject open(String className) throws IOException {
+        // for compatibility, first see if it is a class name
+        JavaFileObject fo = getClassFileObject(className);
+        if (fo != null)
+            return fo;
+
+        // see if it is an inner class, by replacing dots to $, starting from the right
+        String cn = className;
+        int lastDot;
+        while ((lastDot = cn.lastIndexOf(".")) != -1) {
+            cn = cn.substring(0, lastDot) + "$" + cn.substring(lastDot + 1);
+            fo = getClassFileObject(cn);
+            if (fo != null)
+                return fo;
+        }
+
+        if (!className.endsWith(".class"))
+            return null;
+
+        if (fileManager instanceof StandardJavaFileManager) {
+            StandardJavaFileManager sfm = (StandardJavaFileManager) fileManager;
+            try {
+                fo = sfm.getJavaFileObjects(className).iterator().next();
+                if (fo != null && fo.getLastModified() != 0) {
+                    return fo;
+                }
+            } catch (IllegalArgumentException ignore) {
+            }
+        }
+
+        // see if it is a URL, and if so, wrap it in just enough of a JavaFileObject
+        // to suit javap's needs
+        if (className.matches("^[A-Za-z]+:.*")) {
+            try {
+                final URI uri = new URI(className);
+                final URL url = uri.toURL();
+                final URLConnection conn = url.openConnection();
+                conn.setUseCaches(false);
+                return new JavaFileObject() {
+                    public Kind getKind() {
+                        return JavaFileObject.Kind.CLASS;
+                    }
+
+                    public boolean isNameCompatible(String simpleName, Kind kind) {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public NestingKind getNestingKind() {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public Modifier getAccessLevel() {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public URI toUri() {
+                        return uri;
+                    }
+
+                    public String getName() {
+                        return uri.toString();
+                    }
+
+                    public InputStream openInputStream() throws IOException {
+                        return conn.getInputStream();
+                    }
+
+                    public OutputStream openOutputStream() throws IOException {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public Writer openWriter() throws IOException {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public long getLastModified() {
+                        return conn.getLastModified();
+                    }
+
+                    public boolean delete() {
+                        throw new UnsupportedOperationException();
+                    }
+
+                };
+            } catch (URISyntaxException | IOException ignore) {
+            }
+        }
+
+        return null;
+    }
+
+    public static class ClassFileInfo {
+        ClassFileInfo(JavaFileObject fo, ClassFile cf, byte[] digest, int size) {
+            this.fo = fo;
+            this.cf = cf;
+            this.digest = digest;
+            this.size = size;
+        }
+        public final JavaFileObject fo;
+        public final ClassFile cf;
+        public final byte[] digest;
+        public final int size;
+    }
+
+    public ClassFileInfo read(JavaFileObject fo) throws IOException, ConstantPoolException {
+        InputStream in = fo.openInputStream();
+        try {
+            SizeInputStream sizeIn = null;
+            MessageDigest md  = null;
+            if (options.sysInfo || options.verbose) {
+                try {
+                    md = MessageDigest.getInstance("MD5");
+                } catch (NoSuchAlgorithmException ignore) {
+                }
+                in = new DigestInputStream(in, md);
+                in = sizeIn = new SizeInputStream(in);
+            }
+
+            ClassFile cf = ClassFile.read(in, attributeFactory);
+            byte[] digest = (md == null) ? null : md.digest();
+            int size = (sizeIn == null) ? -1 : sizeIn.size();
+            return new ClassFileInfo(fo, cf, digest, size);
+        } finally {
+            in.close();
+        }
+    }
+
+    public void write(ClassFileInfo info) {
+        ClassWriter classWriter = ClassWriter.instance(context);
+        if (options.sysInfo || options.verbose) {
+            classWriter.setFile(info.fo.toUri());
+            classWriter.setLastModified(info.fo.getLastModified());
+            classWriter.setDigest("MD5", info.digest);
+            classWriter.setFileSize(info.size);
+        }
+
+        classWriter.write(info.cf);
+    }
+
+    protected void setClassFile(ClassFile classFile) {
+        ClassWriter classWriter = ClassWriter.instance(context);
+        classWriter.setClassFile(classFile);
+    }
+
+    protected void setMethod(Method enclosingMethod) {
+        ClassWriter classWriter = ClassWriter.instance(context);
+        classWriter.setMethod(enclosingMethod);
+    }
+
+    protected void write(Attribute value) {
+        AttributeWriter attrWriter = AttributeWriter.instance(context);
+        ClassWriter classWriter = ClassWriter.instance(context);
+        ClassFile cf = classWriter.getClassFile();
+        attrWriter.write(cf, value, cf.constant_pool);
+    }
+
+    protected void write(Attributes attrs) {
+        AttributeWriter attrWriter = AttributeWriter.instance(context);
+        ClassWriter classWriter = ClassWriter.instance(context);
+        ClassFile cf = classWriter.getClassFile();
+        attrWriter.write(cf, attrs, cf.constant_pool);
+    }
+
+    protected void write(ConstantPool constant_pool) {
+        ConstantWriter constantWriter = ConstantWriter.instance(context);
+        constantWriter.writeConstantPool(constant_pool);
+    }
+
+    protected void write(ConstantPool constant_pool, int value) {
+        ConstantWriter constantWriter = ConstantWriter.instance(context);
+        constantWriter.write(value);
+    }
+
+    protected void write(ConstantPool.CPInfo value) {
+        ConstantWriter constantWriter = ConstantWriter.instance(context);
+        constantWriter.println(value);
+    }
+
+    protected void write(Field value) {
+        ClassWriter classWriter = ClassWriter.instance(context);
+        classWriter.writeField(value);
+    }
+
+    protected void write(Method value) {
+        ClassWriter classWriter = ClassWriter.instance(context);
+        classWriter.writeMethod(value);
+    }
+
+    private JavaFileManager getDefaultFileManager(final DiagnosticListener<? super JavaFileObject> dl, PrintWriter log) {
+        if (defaultFileManager == null)
+            defaultFileManager = JavapFileManager.create(dl, log);
+        return defaultFileManager;
+    }
+
+    private JavaFileObject getClassFileObject(String className) throws IOException {
+        try {
+            JavaFileObject fo;
+            if (moduleLocation != null) {
+                fo = fileManager.getJavaFileForInput(moduleLocation, className, JavaFileObject.Kind.CLASS);
+            } else {
+                fo = fileManager.getJavaFileForInput(StandardLocation.PLATFORM_CLASS_PATH, className, JavaFileObject.Kind.CLASS);
+                if (fo == null)
+                    fo = fileManager.getJavaFileForInput(StandardLocation.CLASS_PATH, className, JavaFileObject.Kind.CLASS);
+            }
+            return fo;
+        } catch (IllegalArgumentException e) {
+            return null;
+        }
+    }
+
+    private Location findModule(String moduleName) throws IOException {
+        Location[] locns = {
+            StandardLocation.UPGRADE_MODULE_PATH,
+            StandardLocation.SYSTEM_MODULES,
+            StandardLocation.MODULE_PATH
+        };
+        for (Location segment: locns) {
+            for (Set<Location> set: fileManager.listLocationsForModules(segment)) {
+                Location result = null;
+                for (Location l: set) {
+                    String name = fileManager.inferModuleName(l);
+                    if (name.equals(moduleName)) {
+                        if (result == null)
+                            result = l;
+                        else
+                            throw new IOException("multiple definitions found for " + moduleName);
+                    }
+                }
+                if (result != null)
+                    return result;
+            }
+        }
+        return null;
+    }
+
+    private void showHelp() {
+        printLines(getMessage("main.usage", progname));
+        for (Option o: recognizedOptions) {
+            String name = o.aliases[0].replaceAll("^-+", "").replaceAll("-+", "_"); // there must always be at least one name
+            if (name.startsWith("X") || name.equals("fullversion") || name.equals("h") || name.equals("verify"))
+                continue;
+            printLines(getMessage("main.opt." + name));
+        }
+
+        String[] fmOptions = {
+            "--module-path", "--system",
+            "--class-path", "-classpath", "-cp",
+            "-bootclasspath"
+        };
+
+        for (String o: fmOptions) {
+            if (fileManager.isSupportedOption(o) == -1)
+                continue;
+            String name = o.replaceAll("^-+", "").replaceAll("-+", "_");
+            printLines(getMessage("main.opt." + name));
+        }
+
+        printLines(getMessage("main.usage.foot"));
+    }
+
+    private void showVersion(boolean full) {
+        printLines(version(full ? "full" : "release"));
+    }
+
+    private void printLines(String msg) {
+        log.println(msg.replace("\n", nl));
+    }
+
+    private static final String nl = System.getProperty("line.separator");
+
+    private static final String versionRBName = "com.sun.tools.javap.resources.version";
+    private static ResourceBundle versionRB;
+
+    private String version(String key) {
+        // key=version:  mm.nn.oo[-milestone]
+        // key=full:     mm.mm.oo[-milestone]-build
+        if (versionRB == null) {
+            try {
+                versionRB = ResourceBundle.getBundle(versionRBName);
+            } catch (MissingResourceException e) {
+                return getMessage("version.resource.missing", System.getProperty("java.version"));
+            }
+        }
+        try {
+            return versionRB.getString(key);
+        }
+        catch (MissingResourceException e) {
+            return getMessage("version.unknown", System.getProperty("java.version"));
+        }
+    }
+
+    private void reportError(String key, Object... args) {
+        diagnosticListener.report(createDiagnostic(Diagnostic.Kind.ERROR, key, args));
+    }
+
+    private void reportNote(String key, Object... args) {
+        diagnosticListener.report(createDiagnostic(Diagnostic.Kind.NOTE, key, args));
+    }
+
+    private void reportWarning(String key, Object... args) {
+        diagnosticListener.report(createDiagnostic(Diagnostic.Kind.WARNING, key, args));
+    }
+
+    private Diagnostic<JavaFileObject> createDiagnostic(
+            final Diagnostic.Kind kind, final String key, final Object... args) {
+        return new Diagnostic<JavaFileObject>() {
+            public Kind getKind() {
+                return kind;
+            }
+
+            public JavaFileObject getSource() {
+                return null;
+            }
+
+            public long getPosition() {
+                return Diagnostic.NOPOS;
+            }
+
+            public long getStartPosition() {
+                return Diagnostic.NOPOS;
+            }
+
+            public long getEndPosition() {
+                return Diagnostic.NOPOS;
+            }
+
+            public long getLineNumber() {
+                return Diagnostic.NOPOS;
+            }
+
+            public long getColumnNumber() {
+                return Diagnostic.NOPOS;
+            }
+
+            public String getCode() {
+                return key;
+            }
+
+            public String getMessage(Locale locale) {
+                return JavapTask.this.getMessage(locale, key, args);
+            }
+
+            @Override
+            public String toString() {
+                return getClass().getName() + "[key=" + key + ",args=" + Arrays.asList(args) + "]";
+            }
+
+        };
+
+    }
+
+    public String getMessage(String key, Object... args) {
+        return getMessage(task_locale, key, args);
+    }
+
+    public String getMessage(Locale locale, String key, Object... args) {
+        if (bundles == null) {
+            // could make this a HashMap<Locale,SoftReference<ResourceBundle>>
+            // and for efficiency, keep a hard reference to the bundle for the task
+            // locale
+            bundles = new HashMap<>();
+        }
+
+        if (locale == null)
+            locale = Locale.getDefault();
+
+        ResourceBundle b = bundles.get(locale);
+        if (b == null) {
+            try {
+                b = ResourceBundle.getBundle("com.sun.tools.javap.resources.javap", locale);
+                bundles.put(locale, b);
+            } catch (MissingResourceException e) {
+                throw new InternalError("Cannot find javap resource bundle for locale " + locale);
+            }
+        }
+
+        try {
+            return MessageFormat.format(b.getString(key), args);
+        } catch (MissingResourceException e) {
+            throw new InternalError(e, key);
+        }
+    }
+
+    protected Context context;
+    JavaFileManager fileManager;
+    JavaFileManager defaultFileManager;
+    PrintWriter log;
+    DiagnosticListener<? super JavaFileObject> diagnosticListener;
+    List<String> classes;
+    Location moduleLocation;
+    Options options;
+    //ResourceBundle bundle;
+    Locale task_locale;
+    Map<Locale, ResourceBundle> bundles;
+    protected Attribute.Factory attributeFactory;
+
+    private static final String progname = "javap";
+
+    private static class SizeInputStream extends FilterInputStream {
+        SizeInputStream(InputStream in) {
+            super(in);
+        }
+
+        int size() {
+            return size;
+        }
+
+        @Override
+        public int read(byte[] buf, int offset, int length) throws IOException {
+            int n = super.read(buf, offset, length);
+            if (n > 0)
+                size += n;
+            return n;
+        }
+
+        @Override
+        public int read() throws IOException {
+            int b = super.read();
+            size += 1;
+            return b;
+        }
+
+        private int size;
+    }
+}