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