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