jjg@46: /* jjg@1321: * Copyright (c) 2007, 2012, 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@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@46: task.options.showInternalSignatures = 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 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 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 { jjg@46: 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 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 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 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 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: }