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 }