langtools/src/jdk.compiler/share/classes/com/sun/tools/javap/JavapTask.java
changeset 30846 2b3f379840f0
parent 30845 43ddd58a5a56
child 30847 9385b9c8506b
equal deleted inserted replaced
30845:43ddd58a5a56 30846:2b3f379840f0
     1 /*
       
     2  * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package com.sun.tools.javap;
       
    27 
       
    28 import java.io.EOFException;
       
    29 import java.io.FileNotFoundException;
       
    30 import java.io.FilterInputStream;
       
    31 import java.io.InputStream;
       
    32 import java.io.IOException;
       
    33 import java.io.OutputStream;
       
    34 import java.io.PrintWriter;
       
    35 import java.io.Reader;
       
    36 import java.io.StringWriter;
       
    37 import java.io.Writer;
       
    38 import java.net.URI;
       
    39 import java.net.URISyntaxException;
       
    40 import java.net.URL;
       
    41 import java.net.URLConnection;
       
    42 import java.nio.file.NoSuchFileException;
       
    43 import java.security.DigestInputStream;
       
    44 import java.security.MessageDigest;
       
    45 import java.security.NoSuchAlgorithmException;
       
    46 import java.text.MessageFormat;
       
    47 import java.util.ArrayList;
       
    48 import java.util.Arrays;
       
    49 import java.util.EnumSet;
       
    50 import java.util.HashMap;
       
    51 import java.util.Iterator;
       
    52 import java.util.List;
       
    53 import java.util.Locale;
       
    54 import java.util.Map;
       
    55 import java.util.MissingResourceException;
       
    56 import java.util.Objects;
       
    57 import java.util.ResourceBundle;
       
    58 
       
    59 import javax.lang.model.element.Modifier;
       
    60 import javax.lang.model.element.NestingKind;
       
    61 import javax.tools.Diagnostic;
       
    62 import javax.tools.DiagnosticListener;
       
    63 import javax.tools.JavaFileManager;
       
    64 import javax.tools.JavaFileObject;
       
    65 import javax.tools.StandardJavaFileManager;
       
    66 import javax.tools.StandardLocation;
       
    67 
       
    68 import com.sun.tools.classfile.*;
       
    69 import com.sun.tools.javac.util.DefinedBy;
       
    70 import com.sun.tools.javac.util.DefinedBy.Api;
       
    71 
       
    72 /**
       
    73  *  "Main" class for javap, normally accessed from the command line
       
    74  *  via Main, or from JSR199 via DisassemblerTool.
       
    75  *
       
    76  *  <p><b>This is NOT part of any supported API.
       
    77  *  If you write code that depends on this, you do so at your own risk.
       
    78  *  This code and its internal interfaces are subject to change or
       
    79  *  deletion without notice.</b>
       
    80  */
       
    81 public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
       
    82     public class BadArgs extends Exception {
       
    83         static final long serialVersionUID = 8765093759964640721L;
       
    84         BadArgs(String key, Object... args) {
       
    85             super(JavapTask.this.getMessage(key, args));
       
    86             this.key = key;
       
    87             this.args = args;
       
    88         }
       
    89 
       
    90         BadArgs showUsage(boolean b) {
       
    91             showUsage = b;
       
    92             return this;
       
    93         }
       
    94 
       
    95         final String key;
       
    96         final Object[] args;
       
    97         boolean showUsage;
       
    98     }
       
    99 
       
   100     static abstract class Option {
       
   101         Option(boolean hasArg, String... aliases) {
       
   102             this.hasArg = hasArg;
       
   103             this.aliases = aliases;
       
   104         }
       
   105 
       
   106         boolean matches(String opt) {
       
   107             for (String a: aliases) {
       
   108                 if (a.equals(opt))
       
   109                     return true;
       
   110             }
       
   111             return false;
       
   112         }
       
   113 
       
   114         boolean ignoreRest() {
       
   115             return false;
       
   116         }
       
   117 
       
   118         abstract void process(JavapTask task, String opt, String arg) throws BadArgs;
       
   119 
       
   120         final boolean hasArg;
       
   121         final String[] aliases;
       
   122     }
       
   123 
       
   124     static final Option[] recognizedOptions = {
       
   125 
       
   126         new Option(false, "-help", "--help", "-?") {
       
   127             void process(JavapTask task, String opt, String arg) {
       
   128                 task.options.help = true;
       
   129             }
       
   130         },
       
   131 
       
   132         new Option(false, "-version") {
       
   133             void process(JavapTask task, String opt, String arg) {
       
   134                 task.options.version = true;
       
   135             }
       
   136         },
       
   137 
       
   138         new Option(false, "-fullversion") {
       
   139             void process(JavapTask task, String opt, String arg) {
       
   140                 task.options.fullVersion = true;
       
   141             }
       
   142         },
       
   143 
       
   144         new Option(false, "-v", "-verbose", "-all") {
       
   145             void process(JavapTask task, String opt, String arg) {
       
   146                 task.options.verbose = true;
       
   147                 task.options.showDescriptors = true;
       
   148                 task.options.showFlags = true;
       
   149                 task.options.showAllAttrs = true;
       
   150             }
       
   151         },
       
   152 
       
   153         new Option(false, "-l") {
       
   154             void process(JavapTask task, String opt, String arg) {
       
   155                 task.options.showLineAndLocalVariableTables = true;
       
   156             }
       
   157         },
       
   158 
       
   159         new Option(false, "-public") {
       
   160             void process(JavapTask task, String opt, String arg) {
       
   161                 task.options.accessOptions.add(opt);
       
   162                 task.options.showAccess = AccessFlags.ACC_PUBLIC;
       
   163             }
       
   164         },
       
   165 
       
   166         new Option(false, "-protected") {
       
   167             void process(JavapTask task, String opt, String arg) {
       
   168                 task.options.accessOptions.add(opt);
       
   169                 task.options.showAccess = AccessFlags.ACC_PROTECTED;
       
   170             }
       
   171         },
       
   172 
       
   173         new Option(false, "-package") {
       
   174             void process(JavapTask task, String opt, String arg) {
       
   175                 task.options.accessOptions.add(opt);
       
   176                 task.options.showAccess = 0;
       
   177             }
       
   178         },
       
   179 
       
   180         new Option(false, "-p", "-private") {
       
   181             void process(JavapTask task, String opt, String arg) {
       
   182                 if (!task.options.accessOptions.contains("-p") &&
       
   183                         !task.options.accessOptions.contains("-private")) {
       
   184                     task.options.accessOptions.add(opt);
       
   185                 }
       
   186                 task.options.showAccess = AccessFlags.ACC_PRIVATE;
       
   187             }
       
   188         },
       
   189 
       
   190         new Option(false, "-c") {
       
   191             void process(JavapTask task, String opt, String arg) {
       
   192                 task.options.showDisassembled = true;
       
   193             }
       
   194         },
       
   195 
       
   196         new Option(false, "-s") {
       
   197             void process(JavapTask task, String opt, String arg) {
       
   198                 task.options.showDescriptors = true;
       
   199             }
       
   200         },
       
   201 
       
   202         new Option(false, "-sysinfo") {
       
   203             void process(JavapTask task, String opt, String arg) {
       
   204                 task.options.sysInfo = true;
       
   205             }
       
   206         },
       
   207 
       
   208         new Option(false, "-XDdetails") {
       
   209             void process(JavapTask task, String opt, String arg) {
       
   210                 task.options.details = EnumSet.allOf(InstructionDetailWriter.Kind.class);
       
   211             }
       
   212 
       
   213         },
       
   214 
       
   215         new Option(false, "-XDdetails:") {
       
   216             @Override
       
   217             boolean matches(String opt) {
       
   218                 int sep = opt.indexOf(":");
       
   219                 return sep != -1 && super.matches(opt.substring(0, sep + 1));
       
   220             }
       
   221 
       
   222             void process(JavapTask task, String opt, String arg) throws BadArgs {
       
   223                 int sep = opt.indexOf(":");
       
   224                 for (String v: opt.substring(sep + 1).split("[,: ]+")) {
       
   225                     if (!handleArg(task, v))
       
   226                         throw task.new BadArgs("err.invalid.arg.for.option", v);
       
   227                 }
       
   228             }
       
   229 
       
   230             boolean handleArg(JavapTask task, String arg) {
       
   231                 if (arg.length() == 0)
       
   232                     return true;
       
   233 
       
   234                 if (arg.equals("all")) {
       
   235                     task.options.details = EnumSet.allOf(InstructionDetailWriter.Kind.class);
       
   236                     return true;
       
   237                 }
       
   238 
       
   239                 boolean on = true;
       
   240                 if (arg.startsWith("-")) {
       
   241                     on = false;
       
   242                     arg = arg.substring(1);
       
   243                 }
       
   244 
       
   245                 for (InstructionDetailWriter.Kind k: InstructionDetailWriter.Kind.values()) {
       
   246                     if (arg.equalsIgnoreCase(k.option)) {
       
   247                         if (on)
       
   248                             task.options.details.add(k);
       
   249                         else
       
   250                             task.options.details.remove(k);
       
   251                         return true;
       
   252                     }
       
   253                 }
       
   254                 return false;
       
   255             }
       
   256         },
       
   257 
       
   258         new Option(false, "-constants") {
       
   259             void process(JavapTask task, String opt, String arg) {
       
   260                 task.options.showConstants = true;
       
   261             }
       
   262         },
       
   263 
       
   264         new Option(false, "-XDinner") {
       
   265             void process(JavapTask task, String opt, String arg) {
       
   266                 task.options.showInnerClasses = true;
       
   267             }
       
   268         },
       
   269 
       
   270         new Option(false, "-XDindent:") {
       
   271             @Override
       
   272             boolean matches(String opt) {
       
   273                 int sep = opt.indexOf(":");
       
   274                 return sep != -1 && super.matches(opt.substring(0, sep + 1));
       
   275             }
       
   276 
       
   277             void process(JavapTask task, String opt, String arg) throws BadArgs {
       
   278                 int sep = opt.indexOf(":");
       
   279                 try {
       
   280                     int i = Integer.valueOf(opt.substring(sep + 1));
       
   281                     if (i > 0) // silently ignore invalid values
       
   282                         task.options.indentWidth = i;
       
   283                 } catch (NumberFormatException e) {
       
   284                 }
       
   285             }
       
   286         },
       
   287 
       
   288         new Option(false, "-XDtab:") {
       
   289             @Override
       
   290             boolean matches(String opt) {
       
   291                 int sep = opt.indexOf(":");
       
   292                 return sep != -1 && super.matches(opt.substring(0, sep + 1));
       
   293             }
       
   294 
       
   295             void process(JavapTask task, String opt, String arg) throws BadArgs {
       
   296                 int sep = opt.indexOf(":");
       
   297                 try {
       
   298                     int i = Integer.valueOf(opt.substring(sep + 1));
       
   299                     if (i > 0) // silently ignore invalid values
       
   300                         task.options.tabColumn = i;
       
   301                 } catch (NumberFormatException e) {
       
   302                 }
       
   303             }
       
   304         }
       
   305 
       
   306     };
       
   307 
       
   308     public JavapTask() {
       
   309         context = new Context();
       
   310         context.put(Messages.class, this);
       
   311         options = Options.instance(context);
       
   312         attributeFactory = new Attribute.Factory();
       
   313     }
       
   314 
       
   315     public JavapTask(Writer out,
       
   316             JavaFileManager fileManager,
       
   317             DiagnosticListener<? super JavaFileObject> diagnosticListener) {
       
   318         this();
       
   319         this.log = getPrintWriterForWriter(out);
       
   320         this.fileManager = fileManager;
       
   321         this.diagnosticListener = diagnosticListener;
       
   322     }
       
   323 
       
   324     public JavapTask(Writer out,
       
   325             JavaFileManager fileManager,
       
   326             DiagnosticListener<? super JavaFileObject> diagnosticListener,
       
   327             Iterable<String> options,
       
   328             Iterable<String> classes) {
       
   329         this(out, fileManager, diagnosticListener);
       
   330 
       
   331         this.classes = new ArrayList<>();
       
   332         for (String classname: classes) {
       
   333             Objects.requireNonNull(classname);
       
   334             this.classes.add(classname);
       
   335         }
       
   336 
       
   337         try {
       
   338             if (options != null)
       
   339                 handleOptions(options, false);
       
   340         } catch (BadArgs e) {
       
   341             throw new IllegalArgumentException(e.getMessage());
       
   342         }
       
   343     }
       
   344 
       
   345     public void setLocale(Locale locale) {
       
   346         if (locale == null)
       
   347             locale = Locale.getDefault();
       
   348         task_locale = locale;
       
   349     }
       
   350 
       
   351     public void setLog(Writer log) {
       
   352         this.log = getPrintWriterForWriter(log);
       
   353     }
       
   354 
       
   355     public void setLog(OutputStream s) {
       
   356         setLog(getPrintWriterForStream(s));
       
   357     }
       
   358 
       
   359     private static PrintWriter getPrintWriterForStream(OutputStream s) {
       
   360         return new PrintWriter(s == null ? System.err : s, true);
       
   361     }
       
   362 
       
   363     private static PrintWriter getPrintWriterForWriter(Writer w) {
       
   364         if (w == null)
       
   365             return getPrintWriterForStream(null);
       
   366         else if (w instanceof PrintWriter)
       
   367             return (PrintWriter) w;
       
   368         else
       
   369             return new PrintWriter(w, true);
       
   370     }
       
   371 
       
   372     public void setDiagnosticListener(DiagnosticListener<? super JavaFileObject> dl) {
       
   373         diagnosticListener = dl;
       
   374     }
       
   375 
       
   376     public void setDiagnosticListener(OutputStream s) {
       
   377         setDiagnosticListener(getDiagnosticListenerForStream(s));
       
   378     }
       
   379 
       
   380     private DiagnosticListener<JavaFileObject> getDiagnosticListenerForStream(OutputStream s) {
       
   381         return getDiagnosticListenerForWriter(getPrintWriterForStream(s));
       
   382     }
       
   383 
       
   384     private DiagnosticListener<JavaFileObject> getDiagnosticListenerForWriter(Writer w) {
       
   385         final PrintWriter pw = getPrintWriterForWriter(w);
       
   386         return new DiagnosticListener<JavaFileObject> () {
       
   387             @DefinedBy(Api.COMPILER)
       
   388             public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
       
   389                 switch (diagnostic.getKind()) {
       
   390                     case ERROR:
       
   391                         pw.print(getMessage("err.prefix"));
       
   392                         break;
       
   393                     case WARNING:
       
   394                         pw.print(getMessage("warn.prefix"));
       
   395                         break;
       
   396                     case NOTE:
       
   397                         pw.print(getMessage("note.prefix"));
       
   398                         break;
       
   399                 }
       
   400                 pw.print(" ");
       
   401                 pw.println(diagnostic.getMessage(null));
       
   402             }
       
   403         };
       
   404     }
       
   405 
       
   406     /** Result codes.
       
   407      */
       
   408     static final int
       
   409         EXIT_OK = 0,        // Compilation completed with no errors.
       
   410         EXIT_ERROR = 1,     // Completed but reported errors.
       
   411         EXIT_CMDERR = 2,    // Bad command-line arguments
       
   412         EXIT_SYSERR = 3,    // System error or resource exhaustion.
       
   413         EXIT_ABNORMAL = 4;  // Compiler terminated abnormally
       
   414 
       
   415     int run(String[] args) {
       
   416         try {
       
   417             try {
       
   418                 handleOptions(args);
       
   419 
       
   420                 // the following gives consistent behavior with javac
       
   421                 if (classes == null || classes.size() == 0) {
       
   422                     if (options.help || options.version || options.fullVersion)
       
   423                         return EXIT_OK;
       
   424                     else
       
   425                         return EXIT_CMDERR;
       
   426                 }
       
   427 
       
   428                 return run();
       
   429             } finally {
       
   430                 if (defaultFileManager != null) {
       
   431                     try {
       
   432                         defaultFileManager.close();
       
   433                         defaultFileManager = null;
       
   434                     } catch (IOException e) {
       
   435                         throw new InternalError(e);
       
   436                     }
       
   437                 }
       
   438             }
       
   439         } catch (BadArgs e) {
       
   440             reportError(e.key, e.args);
       
   441             if (e.showUsage) {
       
   442                 printLines(getMessage("main.usage.summary", progname));
       
   443             }
       
   444             return EXIT_CMDERR;
       
   445         } catch (InternalError e) {
       
   446             Object[] e_args;
       
   447             if (e.getCause() == null)
       
   448                 e_args = e.args;
       
   449             else {
       
   450                 e_args = new Object[e.args.length + 1];
       
   451                 e_args[0] = e.getCause();
       
   452                 System.arraycopy(e.args, 0, e_args, 1, e.args.length);
       
   453             }
       
   454             reportError("err.internal.error", e_args);
       
   455             return EXIT_ABNORMAL;
       
   456         } finally {
       
   457             log.flush();
       
   458         }
       
   459     }
       
   460 
       
   461     public void handleOptions(String[] args) throws BadArgs {
       
   462         handleOptions(Arrays.asList(args), true);
       
   463     }
       
   464 
       
   465     private void handleOptions(Iterable<String> args, boolean allowClasses) throws BadArgs {
       
   466         if (log == null) {
       
   467             log = getPrintWriterForStream(System.out);
       
   468             if (diagnosticListener == null)
       
   469               diagnosticListener = getDiagnosticListenerForStream(System.err);
       
   470         } else {
       
   471             if (diagnosticListener == null)
       
   472               diagnosticListener = getDiagnosticListenerForWriter(log);
       
   473         }
       
   474 
       
   475 
       
   476         if (fileManager == null)
       
   477             fileManager = getDefaultFileManager(diagnosticListener, log);
       
   478 
       
   479         Iterator<String> iter = args.iterator();
       
   480         boolean noArgs = !iter.hasNext();
       
   481 
       
   482         while (iter.hasNext()) {
       
   483             String arg = iter.next();
       
   484             if (arg.startsWith("-"))
       
   485                 handleOption(arg, iter);
       
   486             else if (allowClasses) {
       
   487                 if (classes == null)
       
   488                     classes = new ArrayList<>();
       
   489                 classes.add(arg);
       
   490                 while (iter.hasNext())
       
   491                     classes.add(iter.next());
       
   492             } else
       
   493                 throw new BadArgs("err.unknown.option", arg).showUsage(true);
       
   494         }
       
   495 
       
   496         if (options.accessOptions.size() > 1) {
       
   497             StringBuilder sb = new StringBuilder();
       
   498             for (String opt: options.accessOptions) {
       
   499                 if (sb.length() > 0)
       
   500                     sb.append(" ");
       
   501                 sb.append(opt);
       
   502             }
       
   503             throw new BadArgs("err.incompatible.options", sb);
       
   504         }
       
   505 
       
   506         if ((classes == null || classes.size() == 0) &&
       
   507                 !(noArgs || options.help || options.version || options.fullVersion)) {
       
   508             throw new BadArgs("err.no.classes.specified");
       
   509         }
       
   510 
       
   511         if (noArgs || options.help)
       
   512             showHelp();
       
   513 
       
   514         if (options.version || options.fullVersion)
       
   515             showVersion(options.fullVersion);
       
   516     }
       
   517 
       
   518     private void handleOption(String name, Iterator<String> rest) throws BadArgs {
       
   519         for (Option o: recognizedOptions) {
       
   520             if (o.matches(name)) {
       
   521                 if (o.hasArg) {
       
   522                     if (rest.hasNext())
       
   523                         o.process(this, name, rest.next());
       
   524                     else
       
   525                         throw new BadArgs("err.missing.arg", name).showUsage(true);
       
   526                 } else
       
   527                     o.process(this, name, null);
       
   528 
       
   529                 if (o.ignoreRest()) {
       
   530                     while (rest.hasNext())
       
   531                         rest.next();
       
   532                 }
       
   533                 return;
       
   534             }
       
   535         }
       
   536 
       
   537         try {
       
   538             if (fileManager.handleOption(name, rest))
       
   539                 return;
       
   540         } catch (IllegalArgumentException e) {
       
   541             throw new BadArgs("err.invalid.use.of.option", name).showUsage(true);
       
   542         }
       
   543 
       
   544         throw new BadArgs("err.unknown.option", name).showUsage(true);
       
   545     }
       
   546 
       
   547     public Boolean call() {
       
   548         return run() == 0;
       
   549     }
       
   550 
       
   551     public int run() {
       
   552         if (classes == null || classes.isEmpty()) {
       
   553             return EXIT_ERROR;
       
   554         }
       
   555 
       
   556         context.put(PrintWriter.class, log);
       
   557         ClassWriter classWriter = ClassWriter.instance(context);
       
   558         SourceWriter sourceWriter = SourceWriter.instance(context);
       
   559         sourceWriter.setFileManager(fileManager);
       
   560 
       
   561         int result = EXIT_OK;
       
   562 
       
   563         for (String className: classes) {
       
   564             try {
       
   565                 result = writeClass(classWriter, className);
       
   566             } catch (ConstantPoolException e) {
       
   567                 reportError("err.bad.constant.pool", className, e.getLocalizedMessage());
       
   568                 result = EXIT_ERROR;
       
   569             } catch (EOFException e) {
       
   570                 reportError("err.end.of.file", className);
       
   571                 result = EXIT_ERROR;
       
   572             } catch (FileNotFoundException | NoSuchFileException e) {
       
   573                 reportError("err.file.not.found", e.getLocalizedMessage());
       
   574                 result = EXIT_ERROR;
       
   575             } catch (IOException e) {
       
   576                 //e.printStackTrace();
       
   577                 Object msg = e.getLocalizedMessage();
       
   578                 if (msg == null) {
       
   579                     msg = e;
       
   580                 }
       
   581                 reportError("err.ioerror", className, msg);
       
   582                 result = EXIT_ERROR;
       
   583             } catch (OutOfMemoryError e) {
       
   584                 reportError("err.nomem");
       
   585                 result = EXIT_ERROR;
       
   586             } catch (Throwable t) {
       
   587                 StringWriter sw = new StringWriter();
       
   588                 PrintWriter pw = new PrintWriter(sw);
       
   589                 t.printStackTrace(pw);
       
   590                 pw.close();
       
   591                 reportError("err.crash", t.toString(), sw.toString());
       
   592                 result = EXIT_ABNORMAL;
       
   593             }
       
   594         }
       
   595 
       
   596         return result;
       
   597     }
       
   598 
       
   599     protected int writeClass(ClassWriter classWriter, String className)
       
   600             throws IOException, ConstantPoolException {
       
   601         JavaFileObject fo = open(className);
       
   602         if (fo == null) {
       
   603             reportError("err.class.not.found", className);
       
   604             return EXIT_ERROR;
       
   605         }
       
   606 
       
   607         ClassFileInfo cfInfo = read(fo);
       
   608         if (!className.endsWith(".class")) {
       
   609             String cfName = cfInfo.cf.getName();
       
   610             if (!cfName.replaceAll("[/$]", ".").equals(className.replaceAll("[/$]", "."))) {
       
   611                 reportWarning("warn.unexpected.class", className, cfName.replace('/', '.'));
       
   612             }
       
   613         }
       
   614         write(cfInfo);
       
   615 
       
   616         if (options.showInnerClasses) {
       
   617             ClassFile cf = cfInfo.cf;
       
   618             Attribute a = cf.getAttribute(Attribute.InnerClasses);
       
   619             if (a instanceof InnerClasses_attribute) {
       
   620                 InnerClasses_attribute inners = (InnerClasses_attribute) a;
       
   621                 try {
       
   622                     int result = EXIT_OK;
       
   623                     for (int i = 0; i < inners.classes.length; i++) {
       
   624                         int outerIndex = inners.classes[i].outer_class_info_index;
       
   625                         ConstantPool.CONSTANT_Class_info outerClassInfo = cf.constant_pool.getClassInfo(outerIndex);
       
   626                         String outerClassName = outerClassInfo.getName();
       
   627                         if (outerClassName.equals(cf.getName())) {
       
   628                             int innerIndex = inners.classes[i].inner_class_info_index;
       
   629                             ConstantPool.CONSTANT_Class_info innerClassInfo = cf.constant_pool.getClassInfo(innerIndex);
       
   630                             String innerClassName = innerClassInfo.getName();
       
   631                             classWriter.println("// inner class " + innerClassName.replaceAll("[/$]", "."));
       
   632                             classWriter.println();
       
   633                             result = writeClass(classWriter, innerClassName);
       
   634                             if (result != EXIT_OK) return result;
       
   635                         }
       
   636                     }
       
   637                     return result;
       
   638                 } catch (ConstantPoolException e) {
       
   639                     reportError("err.bad.innerclasses.attribute", className);
       
   640                     return EXIT_ERROR;
       
   641                 }
       
   642             } else if (a != null) {
       
   643                 reportError("err.bad.innerclasses.attribute", className);
       
   644                 return EXIT_ERROR;
       
   645             }
       
   646         }
       
   647 
       
   648         return EXIT_OK;
       
   649     }
       
   650 
       
   651     protected JavaFileObject open(String className) throws IOException {
       
   652         // for compatibility, first see if it is a class name
       
   653         JavaFileObject fo = getClassFileObject(className);
       
   654         if (fo != null)
       
   655             return fo;
       
   656 
       
   657         // see if it is an inner class, by replacing dots to $, starting from the right
       
   658         String cn = className;
       
   659         int lastDot;
       
   660         while ((lastDot = cn.lastIndexOf(".")) != -1) {
       
   661             cn = cn.substring(0, lastDot) + "$" + cn.substring(lastDot + 1);
       
   662             fo = getClassFileObject(cn);
       
   663             if (fo != null)
       
   664                 return fo;
       
   665         }
       
   666 
       
   667         if (!className.endsWith(".class"))
       
   668             return null;
       
   669 
       
   670         if (fileManager instanceof StandardJavaFileManager) {
       
   671             StandardJavaFileManager sfm = (StandardJavaFileManager) fileManager;
       
   672             try {
       
   673                 fo = sfm.getJavaFileObjects(className).iterator().next();
       
   674                 if (fo != null && fo.getLastModified() != 0) {
       
   675                     return fo;
       
   676                 }
       
   677             } catch (IllegalArgumentException ignore) {
       
   678             }
       
   679         }
       
   680 
       
   681         // see if it is a URL, and if so, wrap it in just enough of a JavaFileObject
       
   682         // to suit javap's needs
       
   683         if (className.matches("^[A-Za-z]+:.*")) {
       
   684             try {
       
   685                 final URI uri = new URI(className);
       
   686                 final URL url = uri.toURL();
       
   687                 final URLConnection conn = url.openConnection();
       
   688                 conn.setUseCaches(false);
       
   689                 return new JavaFileObject() {
       
   690                     @DefinedBy(Api.COMPILER)
       
   691                     public Kind getKind() {
       
   692                         return JavaFileObject.Kind.CLASS;
       
   693                     }
       
   694 
       
   695                     @DefinedBy(Api.COMPILER)
       
   696                     public boolean isNameCompatible(String simpleName, Kind kind) {
       
   697                         throw new UnsupportedOperationException();
       
   698                     }
       
   699 
       
   700                     @DefinedBy(Api.COMPILER)
       
   701                     public NestingKind getNestingKind() {
       
   702                         throw new UnsupportedOperationException();
       
   703                     }
       
   704 
       
   705                     @DefinedBy(Api.COMPILER)
       
   706                     public Modifier getAccessLevel() {
       
   707                         throw new UnsupportedOperationException();
       
   708                     }
       
   709 
       
   710                     @DefinedBy(Api.COMPILER)
       
   711                     public URI toUri() {
       
   712                         return uri;
       
   713                     }
       
   714 
       
   715                     @DefinedBy(Api.COMPILER)
       
   716                     public String getName() {
       
   717                         return uri.toString();
       
   718                     }
       
   719 
       
   720                     @DefinedBy(Api.COMPILER)
       
   721                     public InputStream openInputStream() throws IOException {
       
   722                         return conn.getInputStream();
       
   723                     }
       
   724 
       
   725                     @DefinedBy(Api.COMPILER)
       
   726                     public OutputStream openOutputStream() throws IOException {
       
   727                         throw new UnsupportedOperationException();
       
   728                     }
       
   729 
       
   730                     @DefinedBy(Api.COMPILER)
       
   731                     public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
       
   732                         throw new UnsupportedOperationException();
       
   733                     }
       
   734 
       
   735                     @DefinedBy(Api.COMPILER)
       
   736                     public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
       
   737                         throw new UnsupportedOperationException();
       
   738                     }
       
   739 
       
   740                     @DefinedBy(Api.COMPILER)
       
   741                     public Writer openWriter() throws IOException {
       
   742                         throw new UnsupportedOperationException();
       
   743                     }
       
   744 
       
   745                     @DefinedBy(Api.COMPILER)
       
   746                     public long getLastModified() {
       
   747                         return conn.getLastModified();
       
   748                     }
       
   749 
       
   750                     @DefinedBy(Api.COMPILER)
       
   751                     public boolean delete() {
       
   752                         throw new UnsupportedOperationException();
       
   753                     }
       
   754 
       
   755                 };
       
   756             } catch (URISyntaxException | IOException ignore) {
       
   757             }
       
   758         }
       
   759 
       
   760         return null;
       
   761     }
       
   762 
       
   763     public static class ClassFileInfo {
       
   764         ClassFileInfo(JavaFileObject fo, ClassFile cf, byte[] digest, int size) {
       
   765             this.fo = fo;
       
   766             this.cf = cf;
       
   767             this.digest = digest;
       
   768             this.size = size;
       
   769         }
       
   770         public final JavaFileObject fo;
       
   771         public final ClassFile cf;
       
   772         public final byte[] digest;
       
   773         public final int size;
       
   774     }
       
   775 
       
   776     public ClassFileInfo read(JavaFileObject fo) throws IOException, ConstantPoolException {
       
   777         InputStream in = fo.openInputStream();
       
   778         try {
       
   779             SizeInputStream sizeIn = null;
       
   780             MessageDigest md  = null;
       
   781             if (options.sysInfo || options.verbose) {
       
   782                 try {
       
   783                     md = MessageDigest.getInstance("MD5");
       
   784                 } catch (NoSuchAlgorithmException ignore) {
       
   785                 }
       
   786                 in = new DigestInputStream(in, md);
       
   787                 in = sizeIn = new SizeInputStream(in);
       
   788             }
       
   789 
       
   790             ClassFile cf = ClassFile.read(in, attributeFactory);
       
   791             byte[] digest = (md == null) ? null : md.digest();
       
   792             int size = (sizeIn == null) ? -1 : sizeIn.size();
       
   793             return new ClassFileInfo(fo, cf, digest, size);
       
   794         } finally {
       
   795             in.close();
       
   796         }
       
   797     }
       
   798 
       
   799     public void write(ClassFileInfo info) {
       
   800         ClassWriter classWriter = ClassWriter.instance(context);
       
   801         if (options.sysInfo || options.verbose) {
       
   802             classWriter.setFile(info.fo.toUri());
       
   803             classWriter.setLastModified(info.fo.getLastModified());
       
   804             classWriter.setDigest("MD5", info.digest);
       
   805             classWriter.setFileSize(info.size);
       
   806         }
       
   807 
       
   808         classWriter.write(info.cf);
       
   809     }
       
   810 
       
   811     protected void setClassFile(ClassFile classFile) {
       
   812         ClassWriter classWriter = ClassWriter.instance(context);
       
   813         classWriter.setClassFile(classFile);
       
   814     }
       
   815 
       
   816     protected void setMethod(Method enclosingMethod) {
       
   817         ClassWriter classWriter = ClassWriter.instance(context);
       
   818         classWriter.setMethod(enclosingMethod);
       
   819     }
       
   820 
       
   821     protected void write(Attribute value) {
       
   822         AttributeWriter attrWriter = AttributeWriter.instance(context);
       
   823         ClassWriter classWriter = ClassWriter.instance(context);
       
   824         ClassFile cf = classWriter.getClassFile();
       
   825         attrWriter.write(cf, value, cf.constant_pool);
       
   826     }
       
   827 
       
   828     protected void write(Attributes attrs) {
       
   829         AttributeWriter attrWriter = AttributeWriter.instance(context);
       
   830         ClassWriter classWriter = ClassWriter.instance(context);
       
   831         ClassFile cf = classWriter.getClassFile();
       
   832         attrWriter.write(cf, attrs, cf.constant_pool);
       
   833     }
       
   834 
       
   835     protected void write(ConstantPool constant_pool) {
       
   836         ConstantWriter constantWriter = ConstantWriter.instance(context);
       
   837         constantWriter.writeConstantPool(constant_pool);
       
   838     }
       
   839 
       
   840     protected void write(ConstantPool constant_pool, int value) {
       
   841         ConstantWriter constantWriter = ConstantWriter.instance(context);
       
   842         constantWriter.write(value);
       
   843     }
       
   844 
       
   845     protected void write(ConstantPool.CPInfo value) {
       
   846         ConstantWriter constantWriter = ConstantWriter.instance(context);
       
   847         constantWriter.println(value);
       
   848     }
       
   849 
       
   850     protected void write(Field value) {
       
   851         ClassWriter classWriter = ClassWriter.instance(context);
       
   852         classWriter.writeField(value);
       
   853     }
       
   854 
       
   855     protected void write(Method value) {
       
   856         ClassWriter classWriter = ClassWriter.instance(context);
       
   857         classWriter.writeMethod(value);
       
   858     }
       
   859 
       
   860     private JavaFileManager getDefaultFileManager(final DiagnosticListener<? super JavaFileObject> dl, PrintWriter log) {
       
   861         if (defaultFileManager == null)
       
   862             defaultFileManager = JavapFileManager.create(dl, log);
       
   863         return defaultFileManager;
       
   864     }
       
   865 
       
   866     private JavaFileObject getClassFileObject(String className) throws IOException {
       
   867         try {
       
   868             JavaFileObject fo;
       
   869             fo = fileManager.getJavaFileForInput(StandardLocation.PLATFORM_CLASS_PATH, className, JavaFileObject.Kind.CLASS);
       
   870             if (fo == null)
       
   871                 fo = fileManager.getJavaFileForInput(StandardLocation.CLASS_PATH, className, JavaFileObject.Kind.CLASS);
       
   872             return fo;
       
   873         } catch (IllegalArgumentException e) {
       
   874             return null;
       
   875         }
       
   876     }
       
   877 
       
   878     private void showHelp() {
       
   879         printLines(getMessage("main.usage", progname));
       
   880         for (Option o: recognizedOptions) {
       
   881             String name = o.aliases[0].substring(1); // there must always be at least one name
       
   882             if (name.startsWith("X") || name.equals("fullversion") || name.equals("h") || name.equals("verify"))
       
   883                 continue;
       
   884             printLines(getMessage("main.opt." + name));
       
   885         }
       
   886         String[] fmOptions = { "-classpath", "-cp", "-bootclasspath" };
       
   887         for (String o: fmOptions) {
       
   888             if (fileManager.isSupportedOption(o) == -1)
       
   889                 continue;
       
   890             String name = o.substring(1);
       
   891             printLines(getMessage("main.opt." + name));
       
   892         }
       
   893 
       
   894     }
       
   895 
       
   896     private void showVersion(boolean full) {
       
   897         printLines(version(full ? "full" : "release"));
       
   898     }
       
   899 
       
   900     private void printLines(String msg) {
       
   901         log.println(msg.replace("\n", nl));
       
   902     }
       
   903 
       
   904     private static final String nl = System.getProperty("line.separator");
       
   905 
       
   906     private static final String versionRBName = "com.sun.tools.javap.resources.version";
       
   907     private static ResourceBundle versionRB;
       
   908 
       
   909     private String version(String key) {
       
   910         // key=version:  mm.nn.oo[-milestone]
       
   911         // key=full:     mm.mm.oo[-milestone]-build
       
   912         if (versionRB == null) {
       
   913             try {
       
   914                 versionRB = ResourceBundle.getBundle(versionRBName);
       
   915             } catch (MissingResourceException e) {
       
   916                 return getMessage("version.resource.missing", System.getProperty("java.version"));
       
   917             }
       
   918         }
       
   919         try {
       
   920             return versionRB.getString(key);
       
   921         }
       
   922         catch (MissingResourceException e) {
       
   923             return getMessage("version.unknown", System.getProperty("java.version"));
       
   924         }
       
   925     }
       
   926 
       
   927     private void reportError(String key, Object... args) {
       
   928         diagnosticListener.report(createDiagnostic(Diagnostic.Kind.ERROR, key, args));
       
   929     }
       
   930 
       
   931     private void reportNote(String key, Object... args) {
       
   932         diagnosticListener.report(createDiagnostic(Diagnostic.Kind.NOTE, key, args));
       
   933     }
       
   934 
       
   935     private void reportWarning(String key, Object... args) {
       
   936         diagnosticListener.report(createDiagnostic(Diagnostic.Kind.WARNING, key, args));
       
   937     }
       
   938 
       
   939     private Diagnostic<JavaFileObject> createDiagnostic(
       
   940             final Diagnostic.Kind kind, final String key, final Object... args) {
       
   941         return new Diagnostic<JavaFileObject>() {
       
   942             @DefinedBy(Api.COMPILER)
       
   943             public Kind getKind() {
       
   944                 return kind;
       
   945             }
       
   946 
       
   947             @DefinedBy(Api.COMPILER)
       
   948             public JavaFileObject getSource() {
       
   949                 return null;
       
   950             }
       
   951 
       
   952             @DefinedBy(Api.COMPILER)
       
   953             public long getPosition() {
       
   954                 return Diagnostic.NOPOS;
       
   955             }
       
   956 
       
   957             @DefinedBy(Api.COMPILER)
       
   958             public long getStartPosition() {
       
   959                 return Diagnostic.NOPOS;
       
   960             }
       
   961 
       
   962             @DefinedBy(Api.COMPILER)
       
   963             public long getEndPosition() {
       
   964                 return Diagnostic.NOPOS;
       
   965             }
       
   966 
       
   967             @DefinedBy(Api.COMPILER)
       
   968             public long getLineNumber() {
       
   969                 return Diagnostic.NOPOS;
       
   970             }
       
   971 
       
   972             @DefinedBy(Api.COMPILER)
       
   973             public long getColumnNumber() {
       
   974                 return Diagnostic.NOPOS;
       
   975             }
       
   976 
       
   977             @DefinedBy(Api.COMPILER)
       
   978             public String getCode() {
       
   979                 return key;
       
   980             }
       
   981 
       
   982             @DefinedBy(Api.COMPILER)
       
   983             public String getMessage(Locale locale) {
       
   984                 return JavapTask.this.getMessage(locale, key, args);
       
   985             }
       
   986 
       
   987             @Override
       
   988             public String toString() {
       
   989                 return getClass().getName() + "[key=" + key + ",args=" + Arrays.asList(args) + "]";
       
   990             }
       
   991 
       
   992         };
       
   993 
       
   994     }
       
   995 
       
   996     public String getMessage(String key, Object... args) {
       
   997         return getMessage(task_locale, key, args);
       
   998     }
       
   999 
       
  1000     public String getMessage(Locale locale, String key, Object... args) {
       
  1001         if (bundles == null) {
       
  1002             // could make this a HashMap<Locale,SoftReference<ResourceBundle>>
       
  1003             // and for efficiency, keep a hard reference to the bundle for the task
       
  1004             // locale
       
  1005             bundles = new HashMap<>();
       
  1006         }
       
  1007 
       
  1008         if (locale == null)
       
  1009             locale = Locale.getDefault();
       
  1010 
       
  1011         ResourceBundle b = bundles.get(locale);
       
  1012         if (b == null) {
       
  1013             try {
       
  1014                 b = ResourceBundle.getBundle("com.sun.tools.javap.resources.javap", locale);
       
  1015                 bundles.put(locale, b);
       
  1016             } catch (MissingResourceException e) {
       
  1017                 throw new InternalError("Cannot find javap resource bundle for locale " + locale);
       
  1018             }
       
  1019         }
       
  1020 
       
  1021         try {
       
  1022             return MessageFormat.format(b.getString(key), args);
       
  1023         } catch (MissingResourceException e) {
       
  1024             throw new InternalError(e, key);
       
  1025         }
       
  1026     }
       
  1027 
       
  1028     protected Context context;
       
  1029     JavaFileManager fileManager;
       
  1030     JavaFileManager defaultFileManager;
       
  1031     PrintWriter log;
       
  1032     DiagnosticListener<? super JavaFileObject> diagnosticListener;
       
  1033     List<String> classes;
       
  1034     Options options;
       
  1035     //ResourceBundle bundle;
       
  1036     Locale task_locale;
       
  1037     Map<Locale, ResourceBundle> bundles;
       
  1038     protected Attribute.Factory attributeFactory;
       
  1039 
       
  1040     private static final String progname = "javap";
       
  1041 
       
  1042     private static class SizeInputStream extends FilterInputStream {
       
  1043         SizeInputStream(InputStream in) {
       
  1044             super(in);
       
  1045         }
       
  1046 
       
  1047         int size() {
       
  1048             return size;
       
  1049         }
       
  1050 
       
  1051         @Override
       
  1052         public int read(byte[] buf, int offset, int length) throws IOException {
       
  1053             int n = super.read(buf, offset, length);
       
  1054             if (n > 0)
       
  1055                 size += n;
       
  1056             return n;
       
  1057         }
       
  1058 
       
  1059         @Override
       
  1060         public int read() throws IOException {
       
  1061             int b = super.read();
       
  1062             size += 1;
       
  1063             return b;
       
  1064         }
       
  1065 
       
  1066         private int size;
       
  1067     }
       
  1068 }