JavapTask.java revision 3770:d813bfb238a9
1230557Sjimharris/* 2230557Sjimharris * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. 3230557Sjimharris * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4230557Sjimharris * 5230557Sjimharris * This code is free software; you can redistribute it and/or modify it 6230557Sjimharris * under the terms of the GNU General Public License version 2 only, as 7230557Sjimharris * published by the Free Software Foundation. Oracle designates this 8230557Sjimharris * particular file as subject to the "Classpath" exception as provided 9230557Sjimharris * by Oracle in the LICENSE file that accompanied this code. 10230557Sjimharris * 11230557Sjimharris * This code is distributed in the hope that it will be useful, but WITHOUT 12230557Sjimharris * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13230557Sjimharris * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14230557Sjimharris * version 2 for more details (a copy is included in the LICENSE file that 15230557Sjimharris * accompanied this code). 16230557Sjimharris * 17230557Sjimharris * You should have received a copy of the GNU General Public License version 18230557Sjimharris * 2 along with this work; if not, write to the Free Software Foundation, 19230557Sjimharris * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20230557Sjimharris * 21230557Sjimharris * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22230557Sjimharris * or visit www.oracle.com if you need additional information or have any 23230557Sjimharris * questions. 24230557Sjimharris */ 25230557Sjimharris 26230557Sjimharrispackage com.sun.tools.javap; 27230557Sjimharris 28230557Sjimharrisimport java.io.EOFException; 29230557Sjimharrisimport java.io.FileNotFoundException; 30230557Sjimharrisimport java.io.FilterInputStream; 31230557Sjimharrisimport java.io.InputStream; 32230557Sjimharrisimport java.io.IOException; 33230557Sjimharrisimport java.io.OutputStream; 34230557Sjimharrisimport java.io.PrintWriter; 35230557Sjimharrisimport java.io.Reader; 36230557Sjimharrisimport java.io.StringWriter; 37230557Sjimharrisimport java.io.Writer; 38230557Sjimharrisimport java.net.URI; 39230557Sjimharrisimport java.net.URISyntaxException; 40230557Sjimharrisimport java.net.URL; 41230557Sjimharrisimport java.net.URLConnection; 42230557Sjimharrisimport java.nio.file.NoSuchFileException; 43230557Sjimharrisimport java.security.DigestInputStream; 44230557Sjimharrisimport java.security.MessageDigest; 45230557Sjimharrisimport java.security.NoSuchAlgorithmException; 46230557Sjimharrisimport java.text.MessageFormat; 47230557Sjimharrisimport java.util.ArrayList; 48230557Sjimharrisimport java.util.Arrays; 49230557Sjimharrisimport java.util.EnumSet; 50230557Sjimharrisimport java.util.HashMap; 51230557Sjimharrisimport java.util.Iterator; 52230557Sjimharrisimport java.util.List; 53230557Sjimharrisimport java.util.Locale; 54230557Sjimharrisimport java.util.Map; 55230557Sjimharrisimport java.util.MissingResourceException; 56230557Sjimharrisimport java.util.Objects; 57230557Sjimharrisimport java.util.ResourceBundle; 58230557Sjimharrisimport java.util.Set; 59230557Sjimharris 60230557Sjimharrisimport javax.lang.model.element.Modifier; 61230557Sjimharrisimport javax.lang.model.element.NestingKind; 62230557Sjimharrisimport javax.tools.Diagnostic; 63230557Sjimharrisimport javax.tools.DiagnosticListener; 64230557Sjimharrisimport javax.tools.JavaFileManager; 65230557Sjimharrisimport javax.tools.JavaFileManager.Location; 66230557Sjimharrisimport javax.tools.JavaFileObject; 67230557Sjimharrisimport javax.tools.StandardJavaFileManager; 68230557Sjimharrisimport javax.tools.StandardLocation; 69230557Sjimharris 70230557Sjimharrisimport com.sun.tools.classfile.*; 71230557Sjimharris 72230557Sjimharris/** 73230557Sjimharris * "Main" class for javap, normally accessed from the command line 74230557Sjimharris * via Main, or from JSR199 via DisassemblerTool. 75230557Sjimharris * 76230557Sjimharris * <p><b>This is NOT part of any supported API. 77230557Sjimharris * If you write code that depends on this, you do so at your own risk. 78230557Sjimharris * This code and its internal interfaces are subject to change or 79230557Sjimharris * deletion without notice.</b> 80230557Sjimharris */ 81230557Sjimharrispublic class JavapTask implements DisassemblerTool.DisassemblerTask, Messages { 82230557Sjimharris public class BadArgs extends Exception { 83230557Sjimharris static final long serialVersionUID = 8765093759964640721L; 84230557Sjimharris BadArgs(String key, Object... args) { 85230557Sjimharris super(JavapTask.this.getMessage(key, args)); 86230557Sjimharris this.key = key; 87230557Sjimharris this.args = args; 88230557Sjimharris } 89230557Sjimharris 90230557Sjimharris BadArgs showUsage(boolean b) { 91230557Sjimharris showUsage = b; 92230557Sjimharris return this; 93230557Sjimharris } 94230557Sjimharris 95230557Sjimharris final String key; 96230557Sjimharris final Object[] args; 97230557Sjimharris boolean showUsage; 98230557Sjimharris } 99230557Sjimharris 100230557Sjimharris static abstract class Option { 101230557Sjimharris Option(boolean hasArg, String... aliases) { 102230557Sjimharris this.hasArg = hasArg; 103230557Sjimharris this.aliases = aliases; 104230557Sjimharris } 105230557Sjimharris 106230557Sjimharris boolean matches(String opt) { 107230557Sjimharris for (String a: aliases) { 108230557Sjimharris if (a.equals(opt)) 109230557Sjimharris return true; 110230557Sjimharris } 111230557Sjimharris return false; 112230557Sjimharris } 113230557Sjimharris 114230557Sjimharris boolean ignoreRest() { 115230557Sjimharris return false; 116230557Sjimharris } 117230557Sjimharris 118230557Sjimharris abstract void process(JavapTask task, String opt, String arg) throws BadArgs; 119230557Sjimharris 120230557Sjimharris final boolean hasArg; 121230557Sjimharris final String[] aliases; 122230557Sjimharris } 123230557Sjimharris 124230557Sjimharris static final Option[] recognizedOptions = { 125230557Sjimharris 126230557Sjimharris new Option(false, "-help", "--help", "-?") { 127230557Sjimharris @Override 128230557Sjimharris void process(JavapTask task, String opt, String arg) { 129230557Sjimharris task.options.help = true; 130230557Sjimharris } 131230557Sjimharris }, 132230557Sjimharris 133230557Sjimharris new Option(false, "-version") { 134230557Sjimharris @Override 135230557Sjimharris void process(JavapTask task, String opt, String arg) { 136230557Sjimharris task.options.version = true; 137230557Sjimharris } 138230557Sjimharris }, 139230557Sjimharris 140230557Sjimharris new Option(false, "-fullversion") { 141230557Sjimharris @Override 142230557Sjimharris void process(JavapTask task, String opt, String arg) { 143230557Sjimharris task.options.fullVersion = true; 144230557Sjimharris } 145230557Sjimharris }, 146230557Sjimharris 147230557Sjimharris new Option(false, "-v", "-verbose", "-all") { 148230557Sjimharris @Override 149230557Sjimharris void process(JavapTask task, String opt, String arg) { 150230557Sjimharris task.options.verbose = true; 151230557Sjimharris task.options.showDescriptors = true; 152230557Sjimharris task.options.showFlags = true; 153230557Sjimharris task.options.showAllAttrs = true; 154230557Sjimharris } 155230557Sjimharris }, 156230557Sjimharris 157230557Sjimharris new Option(false, "-l") { 158230557Sjimharris @Override 159230557Sjimharris void process(JavapTask task, String opt, String arg) { 160230557Sjimharris task.options.showLineAndLocalVariableTables = true; 161230557Sjimharris } 162230557Sjimharris }, 163230557Sjimharris 164230557Sjimharris new Option(false, "-public") { 165230557Sjimharris @Override 166230557Sjimharris void process(JavapTask task, String opt, String arg) { 167230557Sjimharris task.options.accessOptions.add(opt); 168230557Sjimharris task.options.showAccess = AccessFlags.ACC_PUBLIC; 169230557Sjimharris } 170230557Sjimharris }, 171230557Sjimharris 172230557Sjimharris new Option(false, "-protected") { 173230557Sjimharris @Override 174230557Sjimharris void process(JavapTask task, String opt, String arg) { 175230557Sjimharris task.options.accessOptions.add(opt); 176230557Sjimharris task.options.showAccess = AccessFlags.ACC_PROTECTED; 177230557Sjimharris } 178230557Sjimharris }, 179230557Sjimharris 180230557Sjimharris new Option(false, "-package") { 181230557Sjimharris @Override 182230557Sjimharris void process(JavapTask task, String opt, String arg) { 183230557Sjimharris task.options.accessOptions.add(opt); 184230557Sjimharris task.options.showAccess = 0; 185230557Sjimharris } 186230557Sjimharris }, 187230557Sjimharris 188230557Sjimharris new Option(false, "-p", "-private") { 189230557Sjimharris @Override 190230557Sjimharris void process(JavapTask task, String opt, String arg) { 191230557Sjimharris if (!task.options.accessOptions.contains("-p") && 192230557Sjimharris !task.options.accessOptions.contains("-private")) { 193230557Sjimharris task.options.accessOptions.add(opt); 194230557Sjimharris } 195230557Sjimharris task.options.showAccess = AccessFlags.ACC_PRIVATE; 196230557Sjimharris } 197230557Sjimharris }, 198230557Sjimharris 199230557Sjimharris new Option(false, "-c") { 200230557Sjimharris @Override 201230557Sjimharris void process(JavapTask task, String opt, String arg) { 202230557Sjimharris task.options.showDisassembled = true; 203230557Sjimharris } 204230557Sjimharris }, 205230557Sjimharris 206230557Sjimharris new Option(false, "-s") { 207230557Sjimharris @Override 208230557Sjimharris void process(JavapTask task, String opt, String arg) { 209230557Sjimharris task.options.showDescriptors = true; 210230557Sjimharris } 211230557Sjimharris }, 212230557Sjimharris 213230557Sjimharris new Option(false, "-sysinfo") { 214230557Sjimharris @Override 215230557Sjimharris void process(JavapTask task, String opt, String arg) { 216230557Sjimharris task.options.sysInfo = true; 217230557Sjimharris } 218230557Sjimharris }, 219230557Sjimharris 220230557Sjimharris new Option(false, "-XDdetails") { 221230557Sjimharris @Override 222230557Sjimharris void process(JavapTask task, String opt, String arg) { 223230557Sjimharris task.options.details = EnumSet.allOf(InstructionDetailWriter.Kind.class); 224230557Sjimharris } 225230557Sjimharris 226230557Sjimharris }, 227230557Sjimharris 228230557Sjimharris new Option(false, "-XDdetails:") { 229230557Sjimharris @Override 230230557Sjimharris boolean matches(String opt) { 231230557Sjimharris int sep = opt.indexOf(":"); 232230557Sjimharris return sep != -1 && super.matches(opt.substring(0, sep + 1)); 233230557Sjimharris } 234230557Sjimharris 235230557Sjimharris @Override 236230557Sjimharris void process(JavapTask task, String opt, String arg) throws BadArgs { 237230557Sjimharris int sep = opt.indexOf(":"); 238230557Sjimharris for (String v: opt.substring(sep + 1).split("[,: ]+")) { 239230557Sjimharris if (!handleArg(task, v)) 240230557Sjimharris throw task.new BadArgs("err.invalid.arg.for.option", v); 241230557Sjimharris } 242230557Sjimharris } 243230557Sjimharris 244230557Sjimharris boolean handleArg(JavapTask task, String arg) { 245230557Sjimharris if (arg.length() == 0) 246230557Sjimharris return true; 247230557Sjimharris 248230557Sjimharris if (arg.equals("all")) { 249230557Sjimharris task.options.details = EnumSet.allOf(InstructionDetailWriter.Kind.class); 250230557Sjimharris return true; 251230557Sjimharris } 252230557Sjimharris 253230557Sjimharris boolean on = true; 254230557Sjimharris if (arg.startsWith("-")) { 255230557Sjimharris on = false; 256230557Sjimharris arg = arg.substring(1); 257230557Sjimharris } 258230557Sjimharris 259230557Sjimharris for (InstructionDetailWriter.Kind k: InstructionDetailWriter.Kind.values()) { 260230557Sjimharris if (arg.equalsIgnoreCase(k.option)) { 261230557Sjimharris if (on) 262230557Sjimharris task.options.details.add(k); 263230557Sjimharris else 264230557Sjimharris task.options.details.remove(k); 265230557Sjimharris return true; 266230557Sjimharris } 267230557Sjimharris } 268230557Sjimharris return false; 269230557Sjimharris } 270230557Sjimharris }, 271230557Sjimharris 272230557Sjimharris new Option(false, "-constants") { 273230557Sjimharris @Override 274230557Sjimharris void process(JavapTask task, String opt, String arg) { 275230557Sjimharris 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 new DiagnosticListener<JavaFileObject> () { 412 public void report(Diagnostic<? extends JavaFileObject> diagnostic) { 413 switch (diagnostic.getKind()) { 414 case ERROR: 415 pw.print(getMessage("err.prefix")); 416 break; 417 case WARNING: 418 pw.print(getMessage("warn.prefix")); 419 break; 420 case NOTE: 421 pw.print(getMessage("note.prefix")); 422 break; 423 } 424 pw.print(" "); 425 pw.println(diagnostic.getMessage(null)); 426 } 427 }; 428 } 429 430 /** Result codes. 431 */ 432 static final int 433 EXIT_OK = 0, // Compilation completed with no errors. 434 EXIT_ERROR = 1, // Completed but reported errors. 435 EXIT_CMDERR = 2, // Bad command-line arguments 436 EXIT_SYSERR = 3, // System error or resource exhaustion. 437 EXIT_ABNORMAL = 4; // Compiler terminated abnormally 438 439 int run(String[] args) { 440 try { 441 try { 442 handleOptions(args); 443 444 // the following gives consistent behavior with javac 445 if (classes == null || classes.size() == 0) { 446 if (options.help || options.version || options.fullVersion) 447 return EXIT_OK; 448 else 449 return EXIT_CMDERR; 450 } 451 452 return run(); 453 } finally { 454 if (defaultFileManager != null) { 455 try { 456 defaultFileManager.close(); 457 defaultFileManager = null; 458 } catch (IOException e) { 459 throw new InternalError(e); 460 } 461 } 462 } 463 } catch (BadArgs e) { 464 reportError(e.key, e.args); 465 if (e.showUsage) { 466 printLines(getMessage("main.usage.summary", progname)); 467 } 468 return EXIT_CMDERR; 469 } catch (InternalError e) { 470 Object[] e_args; 471 if (e.getCause() == null) 472 e_args = e.args; 473 else { 474 e_args = new Object[e.args.length + 1]; 475 e_args[0] = e.getCause(); 476 System.arraycopy(e.args, 0, e_args, 1, e.args.length); 477 } 478 reportError("err.internal.error", e_args); 479 return EXIT_ABNORMAL; 480 } finally { 481 log.flush(); 482 } 483 } 484 485 public void handleOptions(String[] args) throws BadArgs { 486 handleOptions(Arrays.asList(args), true); 487 } 488 489 private void handleOptions(Iterable<String> args, boolean allowClasses) throws BadArgs { 490 if (log == null) { 491 log = getPrintWriterForStream(System.out); 492 if (diagnosticListener == null) 493 diagnosticListener = getDiagnosticListenerForStream(System.err); 494 } else { 495 if (diagnosticListener == null) 496 diagnosticListener = getDiagnosticListenerForWriter(log); 497 } 498 499 500 if (fileManager == null) 501 fileManager = getDefaultFileManager(diagnosticListener, log); 502 503 Iterator<String> iter = args.iterator(); 504 boolean noArgs = !iter.hasNext(); 505 506 while (iter.hasNext()) { 507 String arg = iter.next(); 508 if (arg.startsWith("-")) 509 handleOption(arg, iter); 510 else if (allowClasses) { 511 if (classes == null) 512 classes = new ArrayList<>(); 513 classes.add(arg); 514 while (iter.hasNext()) 515 classes.add(iter.next()); 516 } else 517 throw new BadArgs("err.unknown.option", arg).showUsage(true); 518 } 519 520 if (options.accessOptions.size() > 1) { 521 StringBuilder sb = new StringBuilder(); 522 for (String opt: options.accessOptions) { 523 if (sb.length() > 0) 524 sb.append(" "); 525 sb.append(opt); 526 } 527 throw new BadArgs("err.incompatible.options", sb); 528 } 529 530 if ((classes == null || classes.size() == 0) && 531 !(noArgs || options.help || options.version || options.fullVersion)) { 532 throw new BadArgs("err.no.classes.specified"); 533 } 534 535 if (noArgs || options.help) 536 showHelp(); 537 538 if (options.version || options.fullVersion) 539 showVersion(options.fullVersion); 540 } 541 542 private void handleOption(String name, Iterator<String> rest) throws BadArgs { 543 for (Option o: recognizedOptions) { 544 if (o.matches(name)) { 545 if (o.hasArg) { 546 if (rest.hasNext()) 547 o.process(this, name, rest.next()); 548 else 549 throw new BadArgs("err.missing.arg", name).showUsage(true); 550 } else 551 o.process(this, name, null); 552 553 if (o.ignoreRest()) { 554 while (rest.hasNext()) 555 rest.next(); 556 } 557 return; 558 } 559 } 560 561 try { 562 if (fileManager.handleOption(name, rest)) 563 return; 564 } catch (IllegalArgumentException e) { 565 throw new BadArgs("err.invalid.use.of.option", name).showUsage(true); 566 } 567 568 throw new BadArgs("err.unknown.option", name).showUsage(true); 569 } 570 571 public Boolean call() { 572 return run() == 0; 573 } 574 575 public int run() { 576 if (classes == null || classes.isEmpty()) { 577 return EXIT_ERROR; 578 } 579 580 context.put(PrintWriter.class, log); 581 ClassWriter classWriter = ClassWriter.instance(context); 582 SourceWriter sourceWriter = SourceWriter.instance(context); 583 sourceWriter.setFileManager(fileManager); 584 585 if (options.moduleName != null) { 586 try { 587 moduleLocation = findModule(options.moduleName); 588 if (moduleLocation == null) { 589 reportError("err.cant.find.module", options.moduleName); 590 return EXIT_ERROR; 591 } 592 } catch (IOException e) { 593 reportError("err.cant.find.module.ex", options.moduleName, e); 594 return EXIT_ERROR; 595 } 596 } 597 598 int result = EXIT_OK; 599 600 for (String className: classes) { 601 try { 602 result = writeClass(classWriter, className); 603 } catch (ConstantPoolException e) { 604 reportError("err.bad.constant.pool", className, e.getLocalizedMessage()); 605 result = EXIT_ERROR; 606 } catch (EOFException e) { 607 reportError("err.end.of.file", className); 608 result = EXIT_ERROR; 609 } catch (FileNotFoundException | NoSuchFileException e) { 610 reportError("err.file.not.found", e.getLocalizedMessage()); 611 result = EXIT_ERROR; 612 } catch (IOException e) { 613 //e.printStackTrace(); 614 Object msg = e.getLocalizedMessage(); 615 if (msg == null) { 616 msg = e; 617 } 618 reportError("err.ioerror", className, msg); 619 result = EXIT_ERROR; 620 } catch (OutOfMemoryError e) { 621 reportError("err.nomem"); 622 result = EXIT_ERROR; 623 } catch (Throwable t) { 624 StringWriter sw = new StringWriter(); 625 PrintWriter pw = new PrintWriter(sw); 626 t.printStackTrace(pw); 627 pw.close(); 628 reportError("err.crash", t.toString(), sw.toString()); 629 result = EXIT_ABNORMAL; 630 } 631 } 632 633 return result; 634 } 635 636 protected int writeClass(ClassWriter classWriter, String className) 637 throws IOException, ConstantPoolException { 638 JavaFileObject fo = open(className); 639 if (fo == null) { 640 reportError("err.class.not.found", className); 641 return EXIT_ERROR; 642 } 643 644 ClassFileInfo cfInfo = read(fo); 645 if (!className.endsWith(".class")) { 646 String cfName = cfInfo.cf.getName(); 647 if (!cfName.replaceAll("[/$]", ".").equals(className.replaceAll("[/$]", "."))) { 648 reportWarning("warn.unexpected.class", className, cfName.replace('/', '.')); 649 } 650 } 651 write(cfInfo); 652 653 if (options.showInnerClasses) { 654 ClassFile cf = cfInfo.cf; 655 Attribute a = cf.getAttribute(Attribute.InnerClasses); 656 if (a instanceof InnerClasses_attribute) { 657 InnerClasses_attribute inners = (InnerClasses_attribute) a; 658 try { 659 int result = EXIT_OK; 660 for (int i = 0; i < inners.classes.length; i++) { 661 int outerIndex = inners.classes[i].outer_class_info_index; 662 ConstantPool.CONSTANT_Class_info outerClassInfo = cf.constant_pool.getClassInfo(outerIndex); 663 String outerClassName = outerClassInfo.getName(); 664 if (outerClassName.equals(cf.getName())) { 665 int innerIndex = inners.classes[i].inner_class_info_index; 666 ConstantPool.CONSTANT_Class_info innerClassInfo = cf.constant_pool.getClassInfo(innerIndex); 667 String innerClassName = innerClassInfo.getName(); 668 classWriter.println("// inner class " + innerClassName.replaceAll("[/$]", ".")); 669 classWriter.println(); 670 result = writeClass(classWriter, innerClassName); 671 if (result != EXIT_OK) return result; 672 } 673 } 674 return result; 675 } catch (ConstantPoolException e) { 676 reportError("err.bad.innerclasses.attribute", className); 677 return EXIT_ERROR; 678 } 679 } else if (a != null) { 680 reportError("err.bad.innerclasses.attribute", className); 681 return EXIT_ERROR; 682 } 683 } 684 685 return EXIT_OK; 686 } 687 688 protected JavaFileObject open(String className) throws IOException { 689 // for compatibility, first see if it is a class name 690 JavaFileObject fo = getClassFileObject(className); 691 if (fo != null) 692 return fo; 693 694 // see if it is an inner class, by replacing dots to $, starting from the right 695 String cn = className; 696 int lastDot; 697 while ((lastDot = cn.lastIndexOf(".")) != -1) { 698 cn = cn.substring(0, lastDot) + "$" + cn.substring(lastDot + 1); 699 fo = getClassFileObject(cn); 700 if (fo != null) 701 return fo; 702 } 703 704 if (!className.endsWith(".class")) 705 return null; 706 707 if (fileManager instanceof StandardJavaFileManager) { 708 StandardJavaFileManager sfm = (StandardJavaFileManager) fileManager; 709 try { 710 fo = sfm.getJavaFileObjects(className).iterator().next(); 711 if (fo != null && fo.getLastModified() != 0) { 712 return fo; 713 } 714 } catch (IllegalArgumentException ignore) { 715 } 716 } 717 718 // see if it is a URL, and if so, wrap it in just enough of a JavaFileObject 719 // to suit javap's needs 720 if (className.matches("^[A-Za-z]+:.*")) { 721 try { 722 final URI uri = new URI(className); 723 final URL url = uri.toURL(); 724 final URLConnection conn = url.openConnection(); 725 conn.setUseCaches(false); 726 return new JavaFileObject() { 727 public Kind getKind() { 728 return JavaFileObject.Kind.CLASS; 729 } 730 731 public boolean isNameCompatible(String simpleName, Kind kind) { 732 throw new UnsupportedOperationException(); 733 } 734 735 public NestingKind getNestingKind() { 736 throw new UnsupportedOperationException(); 737 } 738 739 public Modifier getAccessLevel() { 740 throw new UnsupportedOperationException(); 741 } 742 743 public URI toUri() { 744 return uri; 745 } 746 747 public String getName() { 748 return uri.toString(); 749 } 750 751 public InputStream openInputStream() throws IOException { 752 return conn.getInputStream(); 753 } 754 755 public OutputStream openOutputStream() throws IOException { 756 throw new UnsupportedOperationException(); 757 } 758 759 public Reader openReader(boolean ignoreEncodingErrors) throws IOException { 760 throw new UnsupportedOperationException(); 761 } 762 763 public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { 764 throw new UnsupportedOperationException(); 765 } 766 767 public Writer openWriter() throws IOException { 768 throw new UnsupportedOperationException(); 769 } 770 771 public long getLastModified() { 772 return conn.getLastModified(); 773 } 774 775 public boolean delete() { 776 throw new UnsupportedOperationException(); 777 } 778 779 }; 780 } catch (URISyntaxException | IOException ignore) { 781 } 782 } 783 784 return null; 785 } 786 787 public static class ClassFileInfo { 788 ClassFileInfo(JavaFileObject fo, ClassFile cf, byte[] digest, int size) { 789 this.fo = fo; 790 this.cf = cf; 791 this.digest = digest; 792 this.size = size; 793 } 794 public final JavaFileObject fo; 795 public final ClassFile cf; 796 public final byte[] digest; 797 public final int size; 798 } 799 800 public ClassFileInfo read(JavaFileObject fo) throws IOException, ConstantPoolException { 801 InputStream in = fo.openInputStream(); 802 try { 803 SizeInputStream sizeIn = null; 804 MessageDigest md = null; 805 if (options.sysInfo || options.verbose) { 806 try { 807 md = MessageDigest.getInstance("MD5"); 808 } catch (NoSuchAlgorithmException ignore) { 809 } 810 in = new DigestInputStream(in, md); 811 in = sizeIn = new SizeInputStream(in); 812 } 813 814 ClassFile cf = ClassFile.read(in, attributeFactory); 815 byte[] digest = (md == null) ? null : md.digest(); 816 int size = (sizeIn == null) ? -1 : sizeIn.size(); 817 return new ClassFileInfo(fo, cf, digest, size); 818 } finally { 819 in.close(); 820 } 821 } 822 823 public void write(ClassFileInfo info) { 824 ClassWriter classWriter = ClassWriter.instance(context); 825 if (options.sysInfo || options.verbose) { 826 classWriter.setFile(info.fo.toUri()); 827 classWriter.setLastModified(info.fo.getLastModified()); 828 classWriter.setDigest("MD5", info.digest); 829 classWriter.setFileSize(info.size); 830 } 831 832 classWriter.write(info.cf); 833 } 834 835 protected void setClassFile(ClassFile classFile) { 836 ClassWriter classWriter = ClassWriter.instance(context); 837 classWriter.setClassFile(classFile); 838 } 839 840 protected void setMethod(Method enclosingMethod) { 841 ClassWriter classWriter = ClassWriter.instance(context); 842 classWriter.setMethod(enclosingMethod); 843 } 844 845 protected void write(Attribute value) { 846 AttributeWriter attrWriter = AttributeWriter.instance(context); 847 ClassWriter classWriter = ClassWriter.instance(context); 848 ClassFile cf = classWriter.getClassFile(); 849 attrWriter.write(cf, value, cf.constant_pool); 850 } 851 852 protected void write(Attributes attrs) { 853 AttributeWriter attrWriter = AttributeWriter.instance(context); 854 ClassWriter classWriter = ClassWriter.instance(context); 855 ClassFile cf = classWriter.getClassFile(); 856 attrWriter.write(cf, attrs, cf.constant_pool); 857 } 858 859 protected void write(ConstantPool constant_pool) { 860 ConstantWriter constantWriter = ConstantWriter.instance(context); 861 constantWriter.writeConstantPool(constant_pool); 862 } 863 864 protected void write(ConstantPool constant_pool, int value) { 865 ConstantWriter constantWriter = ConstantWriter.instance(context); 866 constantWriter.write(value); 867 } 868 869 protected void write(ConstantPool.CPInfo value) { 870 ConstantWriter constantWriter = ConstantWriter.instance(context); 871 constantWriter.println(value); 872 } 873 874 protected void write(Field value) { 875 ClassWriter classWriter = ClassWriter.instance(context); 876 classWriter.writeField(value); 877 } 878 879 protected void write(Method value) { 880 ClassWriter classWriter = ClassWriter.instance(context); 881 classWriter.writeMethod(value); 882 } 883 884 private JavaFileManager getDefaultFileManager(final DiagnosticListener<? super JavaFileObject> dl, PrintWriter log) { 885 if (defaultFileManager == null) 886 defaultFileManager = JavapFileManager.create(dl, log); 887 return defaultFileManager; 888 } 889 890 private JavaFileObject getClassFileObject(String className) throws IOException { 891 try { 892 JavaFileObject fo; 893 if (moduleLocation != null) { 894 fo = fileManager.getJavaFileForInput(moduleLocation, className, JavaFileObject.Kind.CLASS); 895 } else { 896 fo = fileManager.getJavaFileForInput(StandardLocation.PLATFORM_CLASS_PATH, className, JavaFileObject.Kind.CLASS); 897 if (fo == null) 898 fo = fileManager.getJavaFileForInput(StandardLocation.CLASS_PATH, className, JavaFileObject.Kind.CLASS); 899 } 900 return fo; 901 } catch (IllegalArgumentException e) { 902 return null; 903 } 904 } 905 906 private Location findModule(String moduleName) throws IOException { 907 Location[] locns = { 908 StandardLocation.UPGRADE_MODULE_PATH, 909 StandardLocation.SYSTEM_MODULES, 910 StandardLocation.MODULE_PATH 911 }; 912 for (Location segment: locns) { 913 for (Set<Location> set: fileManager.listLocationsForModules(segment)) { 914 Location result = null; 915 for (Location l: set) { 916 String name = fileManager.inferModuleName(l); 917 if (name.equals(moduleName)) { 918 if (result == null) 919 result = l; 920 else 921 throw new IOException("multiple definitions found for " + moduleName); 922 } 923 } 924 if (result != null) 925 return result; 926 } 927 } 928 return null; 929 } 930 931 private void showHelp() { 932 printLines(getMessage("main.usage", progname)); 933 for (Option o: recognizedOptions) { 934 String name = o.aliases[0].replaceAll("^-+", "").replaceAll("-+", "_"); // there must always be at least one name 935 if (name.startsWith("X") || name.equals("fullversion") || name.equals("h") || name.equals("verify")) 936 continue; 937 printLines(getMessage("main.opt." + name)); 938 } 939 940 String[] fmOptions = { 941 "--module-path", "--system", 942 "--class-path", "-classpath", "-cp", 943 "-bootclasspath" 944 }; 945 946 for (String o: fmOptions) { 947 if (fileManager.isSupportedOption(o) == -1) 948 continue; 949 String name = o.replaceAll("^-+", "").replaceAll("-+", "_"); 950 printLines(getMessage("main.opt." + name)); 951 } 952 953 printLines(getMessage("main.usage.foot")); 954 } 955 956 private void showVersion(boolean full) { 957 printLines(version(full ? "full" : "release")); 958 } 959 960 private void printLines(String msg) { 961 log.println(msg.replace("\n", nl)); 962 } 963 964 private static final String nl = System.getProperty("line.separator"); 965 966 private static final String versionRBName = "com.sun.tools.javap.resources.version"; 967 private static ResourceBundle versionRB; 968 969 private String version(String key) { 970 // key=version: mm.nn.oo[-milestone] 971 // key=full: mm.mm.oo[-milestone]-build 972 if (versionRB == null) { 973 try { 974 versionRB = ResourceBundle.getBundle(versionRBName); 975 } catch (MissingResourceException e) { 976 return getMessage("version.resource.missing", System.getProperty("java.version")); 977 } 978 } 979 try { 980 return versionRB.getString(key); 981 } 982 catch (MissingResourceException e) { 983 return getMessage("version.unknown", System.getProperty("java.version")); 984 } 985 } 986 987 private void reportError(String key, Object... args) { 988 diagnosticListener.report(createDiagnostic(Diagnostic.Kind.ERROR, key, args)); 989 } 990 991 private void reportNote(String key, Object... args) { 992 diagnosticListener.report(createDiagnostic(Diagnostic.Kind.NOTE, key, args)); 993 } 994 995 private void reportWarning(String key, Object... args) { 996 diagnosticListener.report(createDiagnostic(Diagnostic.Kind.WARNING, key, args)); 997 } 998 999 private Diagnostic<JavaFileObject> createDiagnostic( 1000 final Diagnostic.Kind kind, final String key, final Object... args) { 1001 return new Diagnostic<JavaFileObject>() { 1002 public Kind getKind() { 1003 return kind; 1004 } 1005 1006 public JavaFileObject getSource() { 1007 return null; 1008 } 1009 1010 public long getPosition() { 1011 return Diagnostic.NOPOS; 1012 } 1013 1014 public long getStartPosition() { 1015 return Diagnostic.NOPOS; 1016 } 1017 1018 public long getEndPosition() { 1019 return Diagnostic.NOPOS; 1020 } 1021 1022 public long getLineNumber() { 1023 return Diagnostic.NOPOS; 1024 } 1025 1026 public long getColumnNumber() { 1027 return Diagnostic.NOPOS; 1028 } 1029 1030 public String getCode() { 1031 return key; 1032 } 1033 1034 public String getMessage(Locale locale) { 1035 return JavapTask.this.getMessage(locale, key, args); 1036 } 1037 1038 @Override 1039 public String toString() { 1040 return getClass().getName() + "[key=" + key + ",args=" + Arrays.asList(args) + "]"; 1041 } 1042 1043 }; 1044 1045 } 1046 1047 public String getMessage(String key, Object... args) { 1048 return getMessage(task_locale, key, args); 1049 } 1050 1051 public String getMessage(Locale locale, String key, Object... args) { 1052 if (bundles == null) { 1053 // could make this a HashMap<Locale,SoftReference<ResourceBundle>> 1054 // and for efficiency, keep a hard reference to the bundle for the task 1055 // locale 1056 bundles = new HashMap<>(); 1057 } 1058 1059 if (locale == null) 1060 locale = Locale.getDefault(); 1061 1062 ResourceBundle b = bundles.get(locale); 1063 if (b == null) { 1064 try { 1065 b = ResourceBundle.getBundle("com.sun.tools.javap.resources.javap", locale); 1066 bundles.put(locale, b); 1067 } catch (MissingResourceException e) { 1068 throw new InternalError("Cannot find javap resource bundle for locale " + locale); 1069 } 1070 } 1071 1072 try { 1073 return MessageFormat.format(b.getString(key), args); 1074 } catch (MissingResourceException e) { 1075 throw new InternalError(e, key); 1076 } 1077 } 1078 1079 protected Context context; 1080 JavaFileManager fileManager; 1081 JavaFileManager defaultFileManager; 1082 PrintWriter log; 1083 DiagnosticListener<? super JavaFileObject> diagnosticListener; 1084 List<String> classes; 1085 Location moduleLocation; 1086 Options options; 1087 //ResourceBundle bundle; 1088 Locale task_locale; 1089 Map<Locale, ResourceBundle> bundles; 1090 protected Attribute.Factory attributeFactory; 1091 1092 private static final String progname = "javap"; 1093 1094 private static class SizeInputStream extends FilterInputStream { 1095 SizeInputStream(InputStream in) { 1096 super(in); 1097 } 1098 1099 int size() { 1100 return size; 1101 } 1102 1103 @Override 1104 public int read(byte[] buf, int offset, int length) throws IOException { 1105 int n = super.read(buf, offset, length); 1106 if (n > 0) 1107 size += n; 1108 return n; 1109 } 1110 1111 @Override 1112 public int read() throws IOException { 1113 int b = super.read(); 1114 size += 1; 1115 return b; 1116 } 1117 1118 private int size; 1119 } 1120} 1121