langtools/src/share/classes/com/sun/tools/javap/JavapTask.java
changeset 727 cb50c1ae7bab
child 733 b76c4357c649
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/com/sun/tools/javap/JavapTask.java	Tue Jun 03 13:26:47 2008 -0700
@@ -0,0 +1,624 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  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.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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-15301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.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.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+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.ResourceBundle;
+
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticListener;
+import javax.tools.JavaFileManager;
+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 API supported by Sun Microsystems.  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 {
+    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 Option[] recognizedOptions = {
+
+        new Option(false, "-help", "--help", "-?") {
+            void process(JavapTask task, String opt, String arg) {
+                task.options.help = true;
+            }
+        },
+
+        new Option(false, "-version") {
+            void process(JavapTask task, String opt, String arg) {
+                task.options.version = true;
+            }
+        },
+
+        new Option(false, "-fullversion") {
+            void process(JavapTask task, String opt, String arg) {
+                task.options.fullVersion = true;
+            }
+        },
+
+        new Option(false, "-v", "-verbose", "-all") {
+            void process(JavapTask task, String opt, String arg) {
+                task.options.verbose = true;
+                task.options.showFlags = true;
+                task.options.showAllAttrs = true;
+            }
+        },
+
+        new Option(false, "-l") {
+            void process(JavapTask task, String opt, String arg) {
+                task.options.showLineAndLocalVariableTables = true;
+            }
+        },
+
+        new Option(false, "-public") {
+            void process(JavapTask task, String opt, String arg) {
+                task.options.showAccess = AccessFlags.ACC_PUBLIC;
+            }
+        },
+
+        new Option(false, "-protected") {
+            void process(JavapTask task, String opt, String arg) {
+                task.options.showAccess = AccessFlags.ACC_PROTECTED;
+            }
+        },
+
+        new Option(false, "-package") {
+            void process(JavapTask task, String opt, String arg) {
+                task.options.showAccess = 0;
+            }
+        },
+
+        new Option(false, "-p", "-private") {
+            void process(JavapTask task, String opt, String arg) {
+                task.options.showAccess = AccessFlags.ACC_PRIVATE;
+            }
+        },
+
+        new Option(false, "-c") {
+            void process(JavapTask task, String opt, String arg) {
+                task.options.showDisassembled = true;
+            }
+        },
+
+        new Option(false, "-s") {
+            void process(JavapTask task, String opt, String arg) {
+                task.options.showInternalSignatures = true;
+            }
+        },
+
+//        new Option(false, "-all") {
+//            void process(JavapTask task, String opt, String arg) {
+//                task.options.showAllAttrs = true;
+//            }
+//        },
+
+        new Option(false, "-h") {
+            void process(JavapTask task, String opt, String arg) throws BadArgs {
+                throw task.new BadArgs("err.h.not.supported");
+            }
+        },
+
+        new Option(false, "-verify", "-verify-verbose") {
+            void process(JavapTask task, String opt, String arg) throws BadArgs {
+                throw task.new BadArgs("err.verify.not.supported");
+            }
+        },
+
+        new Option(false, "-Xold") {
+            void process(JavapTask task, String opt, String arg) throws BadArgs {
+                // -Xold is only supported as first arg when invoked from
+                // command line; this is handled in Main,main
+                throw task.new BadArgs("err.Xold.not.supported.here");
+            }
+        },
+
+        new Option(false, "-Xnew") {
+            void process(JavapTask task, String opt, String arg) throws BadArgs {
+                // ignore: this _is_ the new version
+            }
+        },
+
+        new Option(false, "-XDcompat") {
+            void process(JavapTask task, String opt, String arg) {
+                task.options.compat = true;
+            }
+        },
+
+        new Option(false, "-XDjsr277") {
+            void process(JavapTask task, String opt, String arg) {
+                task.options.jsr277 = true;
+            }
+        },
+
+        new Option(false, "-XDignore.symbol.file") {
+            void process(JavapTask task, String opt, String arg) {
+                task.options.ignoreSymbolFile = true;
+            }
+        }
+
+    };
+
+    JavapTask() {
+        context = new Context();
+        options = Options.instance(context);
+    }
+
+    JavapTask(Writer out,
+            JavaFileManager fileManager,
+            DiagnosticListener<? super JavaFileObject> diagnosticListener,
+            Iterable<String> options,
+            Iterable<String> classes) {
+        this();
+        this.log = getPrintWriterForWriter(out);
+        this.fileManager = fileManager;
+        this.diagnosticListener = diagnosticListener;
+
+        try {
+            handleOptions(options, false);
+        } catch (BadArgs e) {
+            throw new IllegalArgumentException(e.getMessage());
+        }
+
+        this.classes = new ArrayList<String>();
+        for (String classname: classes) {
+            classname.getClass(); // null-check
+            this.classes.add(classname);
+        }
+    }
+
+    public void setLocale(Locale locale) {
+        if (locale == null)
+            locale = Locale.getDefault();
+        task_locale = locale;
+    }
+
+    public void setLog(PrintWriter log) {
+        this.log = log;
+    }
+
+    public void setLog(OutputStream s) {
+        setLog(getPrintWriterForStream(s));
+    }
+
+    private static PrintWriter getPrintWriterForStream(OutputStream s) {
+        return new PrintWriter(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 new DiagnosticListener<JavaFileObject> () {
+            public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
+                if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
+                    pw.print(getMessage("err.prefix"));
+                    pw.print(" ");
+                }
+                pw.println(diagnostic.getMessage(null));
+            }
+        };
+    }
+
+    int run(String[] args) {
+        try {
+            handleOptions(args);
+            boolean ok = run();
+            return ok ? 0 : 1;
+        } catch (BadArgs e) {
+            diagnosticListener.report(createDiagnostic(e.key, e.args));
+            return 1;
+        } 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);
+            }
+            diagnosticListener.report(createDiagnostic("err.internal.error", e_args));
+            return 1;
+        } 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();
+        if (!iter.hasNext())
+            options.help = true;
+
+        while (iter.hasNext()) {
+            String arg = iter.next();
+            if (arg.startsWith("-"))
+                handleOption(arg, iter);
+            else if (allowClasses) {
+                if (classes == null)
+                    classes = new ArrayList<String>();
+                classes.add(arg);
+                while (iter.hasNext())
+                    classes.add(iter.next());
+            } else
+                throw new BadArgs("err.unknown.option", arg).showUsage(true);
+        }
+
+        if (options.ignoreSymbolFile && fileManager instanceof JavapFileManager)
+            ((JavapFileManager) fileManager).setIgnoreSymbolFile(true);
+
+        if ((classes == null || classes.size() == 0) &&
+                !(options.help || options.version || options.fullVersion)) {
+            throw new BadArgs("err.no.classes.specified");
+        }
+    }
+
+    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;
+            }
+        }
+
+        if (fileManager.handleOption(name, rest))
+            return;
+
+        throw new BadArgs("err.unknown.option", name).showUsage(true);
+    }
+
+    public Boolean call() {
+        return run();
+    }
+
+    public boolean run() {
+        if (options.help)
+            showHelp();
+
+        if (options.version || options.fullVersion)
+            showVersion(options.fullVersion);
+
+        if (classes == null || classes.size() == 0)
+            return true;
+
+        context.put(PrintWriter.class, log);
+        ClassWriter classWriter = ClassWriter.instance(context);
+
+        boolean ok = true;
+
+        for (String className: classes) {
+            JavaFileObject fo;
+            try {
+                if (className.endsWith(".class")) {
+                    if (fileManager instanceof StandardJavaFileManager) {
+                        StandardJavaFileManager sfm = (StandardJavaFileManager) fileManager;
+                        fo = sfm.getJavaFileObjects(className).iterator().next();
+                    } else {
+                       diagnosticListener.report(createDiagnostic("err.not.standard.file.manager", className));
+                       ok = false;
+                       continue;
+                    }
+                } else {
+                    fo = getClassFileObject(className);
+                    if (fo == null) {
+                        // see if it is an inner class, by replacing dots to $, starting from the right
+                        String cn = className;
+                        int lastDot;
+                        while (fo == null && (lastDot = cn.lastIndexOf(".")) != -1) {
+                            cn = cn.substring(0, lastDot) + "$" + cn.substring(lastDot + 1);
+                            fo = getClassFileObject(cn);
+                        }
+                    }
+                    if (fo == null) {
+                       diagnosticListener.report(createDiagnostic("err.class.not.found", className));
+                       ok = false;
+                       continue;
+                    }
+                }
+                Attribute.Factory attributeFactory = new Attribute.Factory();
+                attributeFactory.setCompat(options.compat);
+                attributeFactory.setJSR277(options.jsr277);
+                ClassFile cf = ClassFile.read(fo.openInputStream(), attributeFactory);
+                classWriter.write(cf);
+            } catch (ConstantPoolException e) {
+                diagnosticListener.report(createDiagnostic("err.bad.constant.pool", className, e.getLocalizedMessage()));
+                ok = false;
+            } catch (EOFException e) {
+                diagnosticListener.report(createDiagnostic("err.end.of.file", className));
+                ok = false;
+            } catch (FileNotFoundException e) {
+                diagnosticListener.report(createDiagnostic("err.file.not.found", e.getLocalizedMessage()));
+                ok = false;
+            } catch (IOException e) {
+                //e.printStackTrace();
+                Object msg = e.getLocalizedMessage();
+                if (msg == null)
+                    msg = e;
+                diagnosticListener.report(createDiagnostic("err.ioerror", className, msg));
+                ok = false;
+            } catch (Throwable t) {
+                StringWriter sw = new StringWriter();
+                PrintWriter pw = new PrintWriter(sw);
+                t.printStackTrace(pw);
+                pw.close();
+                diagnosticListener.report(createDiagnostic("err.crash", t.toString(), sw.toString()));
+            }
+        }
+
+        return ok;
+    }
+
+    private JavaFileManager getDefaultFileManager(final DiagnosticListener<? super JavaFileObject> dl, PrintWriter log) {
+        return JavapFileManager.create(dl, log, options);
+    }
+
+    private JavaFileObject getClassFileObject(String className) throws IOException {
+        JavaFileObject fo;
+        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;
+    }
+
+    private void showHelp() {
+        log.println(getMessage("main.usage", progname));
+        for (Option o: recognizedOptions) {
+            String name = o.aliases[0].substring(1); // there must always be at least one name
+            if (name.startsWith("X") || name.equals("fullversion") || name.equals("h") || name.equals("verify"))
+                continue;
+            log.println(getMessage("main.opt." + name));
+        }
+        String[] fmOptions = { "-classpath", "-bootclasspath" };
+        for (String o: fmOptions) {
+            if (fileManager.isSupportedOption(o) == -1)
+                continue;
+            String name = o.substring(1);
+            log.println(getMessage("main.opt." + name));
+        }
+
+    }
+
+    private void showVersion(boolean full) {
+        log.println(version(full ? "full" : "release"));
+    }
+
+    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 Diagnostic<JavaFileObject> createDiagnostic(final String key, final Object... args) {
+        return new Diagnostic<JavaFileObject>() {
+            public Kind getKind() {
+                return Diagnostic.Kind.ERROR;
+            }
+
+            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);
+            }
+
+        };
+
+    }
+
+    private String getMessage(String key, Object... args) {
+        return getMessage(task_locale, key, args);
+    }
+
+    private 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<Locale, ResourceBundle>();
+        }
+
+        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);
+        }
+    }
+
+    Context context;
+    JavaFileManager fileManager;
+    PrintWriter log;
+    DiagnosticListener<? super JavaFileObject> diagnosticListener;
+    List<String> classes;
+    Options options;
+    //ResourceBundle bundle;
+    Locale task_locale;
+    Map<Locale, ResourceBundle> bundles;
+
+    private static final String progname = "javap";
+}