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