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