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