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