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 }