Tue, 19 May 2009 11:50:54 -0700
6824493: experimental support for additional info for instructions
Reviewed-by: mcimadamore
jjg@46 | 1 | /* |
xdono@54 | 2 | * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. |
jjg@46 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
jjg@46 | 4 | * |
jjg@46 | 5 | * This code is free software; you can redistribute it and/or modify it |
jjg@46 | 6 | * under the terms of the GNU General Public License version 2 only, as |
jjg@46 | 7 | * published by the Free Software Foundation. Sun designates this |
jjg@46 | 8 | * particular file as subject to the "Classpath" exception as provided |
jjg@46 | 9 | * by Sun in the LICENSE file that accompanied this code. |
jjg@46 | 10 | * |
jjg@46 | 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
jjg@46 | 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
jjg@46 | 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
jjg@46 | 14 | * version 2 for more details (a copy is included in the LICENSE file that |
jjg@46 | 15 | * accompanied this code). |
jjg@46 | 16 | * |
jjg@46 | 17 | * You should have received a copy of the GNU General Public License version |
jjg@46 | 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
jjg@90 | 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
jjg@46 | 20 | * |
jjg@46 | 21 | * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
jjg@46 | 22 | * CA 95054 USA or visit www.sun.com if you need additional information or |
jjg@46 | 23 | * have any questions. |
jjg@46 | 24 | */ |
jjg@46 | 25 | |
jjg@46 | 26 | package com.sun.tools.javap; |
jjg@46 | 27 | |
jjg@46 | 28 | import java.io.EOFException; |
jjg@46 | 29 | import java.io.FileNotFoundException; |
jjg@88 | 30 | import java.io.FilterInputStream; |
jjg@88 | 31 | import java.io.InputStream; |
jjg@46 | 32 | import java.io.IOException; |
jjg@46 | 33 | import java.io.OutputStream; |
jjg@46 | 34 | import java.io.PrintWriter; |
jjg@46 | 35 | import java.io.StringWriter; |
jjg@46 | 36 | import java.io.Writer; |
jjg@88 | 37 | import java.security.DigestInputStream; |
jjg@88 | 38 | import java.security.MessageDigest; |
jjg@46 | 39 | import java.text.MessageFormat; |
jjg@46 | 40 | import java.util.ArrayList; |
jjg@46 | 41 | import java.util.Arrays; |
jjg@283 | 42 | import java.util.EnumSet; |
jjg@46 | 43 | import java.util.HashMap; |
jjg@46 | 44 | import java.util.Iterator; |
jjg@46 | 45 | import java.util.List; |
jjg@46 | 46 | import java.util.Locale; |
jjg@46 | 47 | import java.util.Map; |
jjg@46 | 48 | import java.util.MissingResourceException; |
jjg@46 | 49 | import java.util.ResourceBundle; |
jjg@46 | 50 | |
jjg@46 | 51 | import javax.tools.Diagnostic; |
jjg@46 | 52 | import javax.tools.DiagnosticListener; |
jjg@46 | 53 | import javax.tools.JavaFileManager; |
jjg@46 | 54 | import javax.tools.JavaFileObject; |
jjg@46 | 55 | import javax.tools.StandardJavaFileManager; |
jjg@46 | 56 | import javax.tools.StandardLocation; |
jjg@46 | 57 | |
jjg@46 | 58 | import com.sun.tools.classfile.*; |
jjg@46 | 59 | |
jjg@46 | 60 | /** |
jjg@46 | 61 | * "Main" class for javap, normally accessed from the command line |
jjg@46 | 62 | * via Main, or from JSR199 via DisassemblerTool. |
jjg@46 | 63 | * |
jjg@46 | 64 | * <p><b>This is NOT part of any API supported by Sun Microsystems. If |
jjg@46 | 65 | * you write code that depends on this, you do so at your own risk. |
jjg@46 | 66 | * This code and its internal interfaces are subject to change or |
jjg@46 | 67 | * deletion without notice.</b> |
jjg@46 | 68 | */ |
jjg@283 | 69 | public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages { |
jjg@46 | 70 | public class BadArgs extends Exception { |
jjg@46 | 71 | static final long serialVersionUID = 8765093759964640721L; |
jjg@46 | 72 | BadArgs(String key, Object... args) { |
jjg@46 | 73 | super(JavapTask.this.getMessage(key, args)); |
jjg@46 | 74 | this.key = key; |
jjg@46 | 75 | this.args = args; |
jjg@46 | 76 | } |
jjg@46 | 77 | |
jjg@46 | 78 | BadArgs showUsage(boolean b) { |
jjg@46 | 79 | showUsage = b; |
jjg@46 | 80 | return this; |
jjg@46 | 81 | } |
jjg@46 | 82 | |
jjg@46 | 83 | final String key; |
jjg@46 | 84 | final Object[] args; |
jjg@46 | 85 | boolean showUsage; |
jjg@46 | 86 | } |
jjg@46 | 87 | |
jjg@46 | 88 | static abstract class Option { |
jjg@46 | 89 | Option(boolean hasArg, String... aliases) { |
jjg@46 | 90 | this.hasArg = hasArg; |
jjg@46 | 91 | this.aliases = aliases; |
jjg@46 | 92 | } |
jjg@46 | 93 | |
jjg@46 | 94 | boolean matches(String opt) { |
jjg@46 | 95 | for (String a: aliases) { |
jjg@46 | 96 | if (a.equals(opt)) |
jjg@46 | 97 | return true; |
jjg@46 | 98 | } |
jjg@46 | 99 | return false; |
jjg@46 | 100 | } |
jjg@46 | 101 | |
jjg@46 | 102 | boolean ignoreRest() { |
jjg@46 | 103 | return false; |
jjg@46 | 104 | } |
jjg@46 | 105 | |
jjg@46 | 106 | abstract void process(JavapTask task, String opt, String arg) throws BadArgs; |
jjg@46 | 107 | |
jjg@46 | 108 | final boolean hasArg; |
jjg@46 | 109 | final String[] aliases; |
jjg@46 | 110 | } |
jjg@46 | 111 | |
jjg@46 | 112 | static Option[] recognizedOptions = { |
jjg@46 | 113 | |
jjg@46 | 114 | new Option(false, "-help", "--help", "-?") { |
jjg@46 | 115 | void process(JavapTask task, String opt, String arg) { |
jjg@46 | 116 | task.options.help = true; |
jjg@46 | 117 | } |
jjg@46 | 118 | }, |
jjg@46 | 119 | |
jjg@46 | 120 | new Option(false, "-version") { |
jjg@46 | 121 | void process(JavapTask task, String opt, String arg) { |
jjg@46 | 122 | task.options.version = true; |
jjg@46 | 123 | } |
jjg@46 | 124 | }, |
jjg@46 | 125 | |
jjg@46 | 126 | new Option(false, "-fullversion") { |
jjg@46 | 127 | void process(JavapTask task, String opt, String arg) { |
jjg@46 | 128 | task.options.fullVersion = true; |
jjg@46 | 129 | } |
jjg@46 | 130 | }, |
jjg@46 | 131 | |
jjg@46 | 132 | new Option(false, "-v", "-verbose", "-all") { |
jjg@46 | 133 | void process(JavapTask task, String opt, String arg) { |
jjg@46 | 134 | task.options.verbose = true; |
jjg@46 | 135 | task.options.showFlags = true; |
jjg@46 | 136 | task.options.showAllAttrs = true; |
jjg@46 | 137 | } |
jjg@46 | 138 | }, |
jjg@46 | 139 | |
jjg@46 | 140 | new Option(false, "-l") { |
jjg@46 | 141 | void process(JavapTask task, String opt, String arg) { |
jjg@46 | 142 | task.options.showLineAndLocalVariableTables = true; |
jjg@46 | 143 | } |
jjg@46 | 144 | }, |
jjg@46 | 145 | |
jjg@46 | 146 | new Option(false, "-public") { |
jjg@46 | 147 | void process(JavapTask task, String opt, String arg) { |
jjg@68 | 148 | task.options.accessOptions.add(opt); |
jjg@46 | 149 | task.options.showAccess = AccessFlags.ACC_PUBLIC; |
jjg@46 | 150 | } |
jjg@46 | 151 | }, |
jjg@46 | 152 | |
jjg@46 | 153 | new Option(false, "-protected") { |
jjg@46 | 154 | void process(JavapTask task, String opt, String arg) { |
jjg@68 | 155 | task.options.accessOptions.add(opt); |
jjg@46 | 156 | task.options.showAccess = AccessFlags.ACC_PROTECTED; |
jjg@46 | 157 | } |
jjg@46 | 158 | }, |
jjg@46 | 159 | |
jjg@46 | 160 | new Option(false, "-package") { |
jjg@46 | 161 | void process(JavapTask task, String opt, String arg) { |
jjg@68 | 162 | task.options.accessOptions.add(opt); |
jjg@46 | 163 | task.options.showAccess = 0; |
jjg@46 | 164 | } |
jjg@46 | 165 | }, |
jjg@46 | 166 | |
jjg@46 | 167 | new Option(false, "-p", "-private") { |
jjg@46 | 168 | void process(JavapTask task, String opt, String arg) { |
jjg@68 | 169 | if (!task.options.accessOptions.contains("-p") && |
jjg@68 | 170 | !task.options.accessOptions.contains("-private")) { |
jjg@68 | 171 | task.options.accessOptions.add(opt); |
jjg@68 | 172 | } |
jjg@46 | 173 | task.options.showAccess = AccessFlags.ACC_PRIVATE; |
jjg@46 | 174 | } |
jjg@46 | 175 | }, |
jjg@46 | 176 | |
jjg@46 | 177 | new Option(false, "-c") { |
jjg@46 | 178 | void process(JavapTask task, String opt, String arg) { |
jjg@46 | 179 | task.options.showDisassembled = true; |
jjg@46 | 180 | } |
jjg@46 | 181 | }, |
jjg@46 | 182 | |
jjg@46 | 183 | new Option(false, "-s") { |
jjg@46 | 184 | void process(JavapTask task, String opt, String arg) { |
jjg@46 | 185 | task.options.showInternalSignatures = true; |
jjg@46 | 186 | } |
jjg@46 | 187 | }, |
jjg@46 | 188 | |
jjg@46 | 189 | // new Option(false, "-all") { |
jjg@46 | 190 | // void process(JavapTask task, String opt, String arg) { |
jjg@46 | 191 | // task.options.showAllAttrs = true; |
jjg@46 | 192 | // } |
jjg@46 | 193 | // }, |
jjg@46 | 194 | |
jjg@46 | 195 | new Option(false, "-h") { |
jjg@46 | 196 | void process(JavapTask task, String opt, String arg) throws BadArgs { |
jjg@46 | 197 | throw task.new BadArgs("err.h.not.supported"); |
jjg@46 | 198 | } |
jjg@46 | 199 | }, |
jjg@46 | 200 | |
jjg@46 | 201 | new Option(false, "-verify", "-verify-verbose") { |
jjg@46 | 202 | void process(JavapTask task, String opt, String arg) throws BadArgs { |
jjg@46 | 203 | throw task.new BadArgs("err.verify.not.supported"); |
jjg@46 | 204 | } |
jjg@46 | 205 | }, |
jjg@46 | 206 | |
jjg@88 | 207 | new Option(false, "-sysinfo") { |
jjg@88 | 208 | void process(JavapTask task, String opt, String arg) { |
jjg@88 | 209 | task.options.sysInfo = true; |
jjg@88 | 210 | } |
jjg@88 | 211 | }, |
jjg@88 | 212 | |
jjg@46 | 213 | new Option(false, "-Xold") { |
jjg@46 | 214 | void process(JavapTask task, String opt, String arg) throws BadArgs { |
jjg@46 | 215 | // -Xold is only supported as first arg when invoked from |
jjg@46 | 216 | // command line; this is handled in Main,main |
jjg@46 | 217 | throw task.new BadArgs("err.Xold.not.supported.here"); |
jjg@46 | 218 | } |
jjg@46 | 219 | }, |
jjg@46 | 220 | |
jjg@46 | 221 | new Option(false, "-Xnew") { |
jjg@46 | 222 | void process(JavapTask task, String opt, String arg) throws BadArgs { |
jjg@46 | 223 | // ignore: this _is_ the new version |
jjg@46 | 224 | } |
jjg@46 | 225 | }, |
jjg@46 | 226 | |
jjg@46 | 227 | new Option(false, "-XDcompat") { |
jjg@46 | 228 | void process(JavapTask task, String opt, String arg) { |
jjg@46 | 229 | task.options.compat = true; |
jjg@46 | 230 | } |
jjg@46 | 231 | }, |
jjg@46 | 232 | |
jjg@46 | 233 | new Option(false, "-XDjsr277") { |
jjg@46 | 234 | void process(JavapTask task, String opt, String arg) { |
jjg@46 | 235 | task.options.jsr277 = true; |
jjg@46 | 236 | } |
jjg@46 | 237 | }, |
jjg@46 | 238 | |
jjg@46 | 239 | new Option(false, "-XDignore.symbol.file") { |
jjg@46 | 240 | void process(JavapTask task, String opt, String arg) { |
jjg@46 | 241 | task.options.ignoreSymbolFile = true; |
jjg@46 | 242 | } |
jjg@87 | 243 | }, |
jjg@87 | 244 | |
jjg@283 | 245 | new Option(false, "-XDdetails") { |
jjg@283 | 246 | void process(JavapTask task, String opt, String arg) { |
jjg@283 | 247 | task.options.details = EnumSet.allOf(InstructionDetailWriter.Kind.class); |
jjg@283 | 248 | } |
jjg@283 | 249 | |
jjg@283 | 250 | }, |
jjg@283 | 251 | |
jjg@283 | 252 | new Option(false, "-XDdetails:") { |
jjg@283 | 253 | @Override |
jjg@283 | 254 | boolean matches(String opt) { |
jjg@283 | 255 | int sep = opt.indexOf(":"); |
jjg@283 | 256 | return sep != -1 && super.matches(opt.substring(0, sep + 1)); |
jjg@283 | 257 | } |
jjg@283 | 258 | |
jjg@283 | 259 | void process(JavapTask task, String opt, String arg) throws BadArgs { |
jjg@283 | 260 | int sep = opt.indexOf(":"); |
jjg@283 | 261 | for (String v: opt.substring(sep + 1).split("[,: ]+")) { |
jjg@283 | 262 | if (!handleArg(task, v)) |
jjg@283 | 263 | throw task.new BadArgs("err.invalid.arg.for.option", v); |
jjg@283 | 264 | } |
jjg@283 | 265 | } |
jjg@283 | 266 | |
jjg@283 | 267 | boolean handleArg(JavapTask task, String arg) { |
jjg@283 | 268 | if (arg.length() == 0) |
jjg@283 | 269 | return true; |
jjg@283 | 270 | |
jjg@283 | 271 | if (arg.equals("all")) { |
jjg@283 | 272 | task.options.details = EnumSet.allOf(InstructionDetailWriter.Kind.class); |
jjg@283 | 273 | return true; |
jjg@283 | 274 | } |
jjg@283 | 275 | |
jjg@283 | 276 | boolean on = true; |
jjg@283 | 277 | if (arg.startsWith("-")) { |
jjg@283 | 278 | on = false; |
jjg@283 | 279 | arg = arg.substring(1); |
jjg@283 | 280 | } |
jjg@283 | 281 | |
jjg@283 | 282 | for (InstructionDetailWriter.Kind k: InstructionDetailWriter.Kind.values()) { |
jjg@283 | 283 | if (arg.equalsIgnoreCase(k.option)) { |
jjg@283 | 284 | if (on) |
jjg@283 | 285 | task.options.details.add(k); |
jjg@283 | 286 | else |
jjg@283 | 287 | task.options.details.remove(k); |
jjg@283 | 288 | return true; |
jjg@283 | 289 | } |
jjg@283 | 290 | } |
jjg@283 | 291 | return false; |
jjg@283 | 292 | } |
jjg@283 | 293 | }, |
jjg@283 | 294 | |
jjg@87 | 295 | new Option(false, "-constants") { |
jjg@87 | 296 | void process(JavapTask task, String opt, String arg) { |
jjg@87 | 297 | task.options.showConstants = true; |
jjg@87 | 298 | } |
jjg@46 | 299 | } |
jjg@46 | 300 | |
jjg@46 | 301 | }; |
jjg@46 | 302 | |
jjg@46 | 303 | JavapTask() { |
jjg@46 | 304 | context = new Context(); |
jjg@283 | 305 | context.put(Messages.class, this); |
jjg@46 | 306 | options = Options.instance(context); |
jjg@46 | 307 | } |
jjg@46 | 308 | |
jjg@46 | 309 | JavapTask(Writer out, |
jjg@46 | 310 | JavaFileManager fileManager, |
jjg@46 | 311 | DiagnosticListener<? super JavaFileObject> diagnosticListener, |
jjg@46 | 312 | Iterable<String> options, |
jjg@46 | 313 | Iterable<String> classes) { |
jjg@46 | 314 | this(); |
jjg@46 | 315 | this.log = getPrintWriterForWriter(out); |
jjg@46 | 316 | this.fileManager = fileManager; |
jjg@46 | 317 | this.diagnosticListener = diagnosticListener; |
jjg@46 | 318 | |
jjg@46 | 319 | try { |
jjg@46 | 320 | handleOptions(options, false); |
jjg@46 | 321 | } catch (BadArgs e) { |
jjg@46 | 322 | throw new IllegalArgumentException(e.getMessage()); |
jjg@46 | 323 | } |
jjg@46 | 324 | |
jjg@46 | 325 | this.classes = new ArrayList<String>(); |
jjg@46 | 326 | for (String classname: classes) { |
jjg@46 | 327 | classname.getClass(); // null-check |
jjg@46 | 328 | this.classes.add(classname); |
jjg@46 | 329 | } |
jjg@46 | 330 | } |
jjg@46 | 331 | |
jjg@46 | 332 | public void setLocale(Locale locale) { |
jjg@46 | 333 | if (locale == null) |
jjg@46 | 334 | locale = Locale.getDefault(); |
jjg@46 | 335 | task_locale = locale; |
jjg@46 | 336 | } |
jjg@46 | 337 | |
jjg@46 | 338 | public void setLog(PrintWriter log) { |
jjg@46 | 339 | this.log = log; |
jjg@46 | 340 | } |
jjg@46 | 341 | |
jjg@46 | 342 | public void setLog(OutputStream s) { |
jjg@46 | 343 | setLog(getPrintWriterForStream(s)); |
jjg@46 | 344 | } |
jjg@46 | 345 | |
jjg@46 | 346 | private static PrintWriter getPrintWriterForStream(OutputStream s) { |
jjg@46 | 347 | return new PrintWriter(s, true); |
jjg@46 | 348 | } |
jjg@46 | 349 | |
jjg@46 | 350 | private static PrintWriter getPrintWriterForWriter(Writer w) { |
jjg@46 | 351 | if (w == null) |
jjg@46 | 352 | return getPrintWriterForStream(null); |
jjg@46 | 353 | else if (w instanceof PrintWriter) |
jjg@46 | 354 | return (PrintWriter) w; |
jjg@46 | 355 | else |
jjg@46 | 356 | return new PrintWriter(w, true); |
jjg@46 | 357 | } |
jjg@46 | 358 | |
jjg@46 | 359 | public void setDiagnosticListener(DiagnosticListener<? super JavaFileObject> dl) { |
jjg@46 | 360 | diagnosticListener = dl; |
jjg@46 | 361 | } |
jjg@46 | 362 | |
jjg@46 | 363 | public void setDiagnosticListener(OutputStream s) { |
jjg@46 | 364 | setDiagnosticListener(getDiagnosticListenerForStream(s)); |
jjg@46 | 365 | } |
jjg@46 | 366 | |
jjg@46 | 367 | private DiagnosticListener<JavaFileObject> getDiagnosticListenerForStream(OutputStream s) { |
jjg@46 | 368 | return getDiagnosticListenerForWriter(getPrintWriterForStream(s)); |
jjg@46 | 369 | } |
jjg@46 | 370 | |
jjg@46 | 371 | private DiagnosticListener<JavaFileObject> getDiagnosticListenerForWriter(Writer w) { |
jjg@46 | 372 | final PrintWriter pw = getPrintWriterForWriter(w); |
jjg@46 | 373 | return new DiagnosticListener<JavaFileObject> () { |
jjg@46 | 374 | public void report(Diagnostic<? extends JavaFileObject> diagnostic) { |
jjg@46 | 375 | if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { |
jjg@66 | 376 | pw.print(getMessage("err.prefix")); |
jjg@46 | 377 | pw.print(" "); |
jjg@46 | 378 | } |
jjg@46 | 379 | pw.println(diagnostic.getMessage(null)); |
jjg@46 | 380 | } |
jjg@46 | 381 | }; |
jjg@46 | 382 | } |
jjg@46 | 383 | |
jjg@64 | 384 | /** Result codes. |
jjg@64 | 385 | */ |
jjg@64 | 386 | static final int |
jjg@64 | 387 | EXIT_OK = 0, // Compilation completed with no errors. |
jjg@64 | 388 | EXIT_ERROR = 1, // Completed but reported errors. |
jjg@64 | 389 | EXIT_CMDERR = 2, // Bad command-line arguments |
jjg@64 | 390 | EXIT_SYSERR = 3, // System error or resource exhaustion. |
jjg@64 | 391 | EXIT_ABNORMAL = 4; // Compiler terminated abnormally |
jjg@64 | 392 | |
jjg@46 | 393 | int run(String[] args) { |
jjg@46 | 394 | try { |
jjg@46 | 395 | handleOptions(args); |
jjg@64 | 396 | |
jjg@64 | 397 | // the following gives consistent behavior with javac |
jjg@64 | 398 | if (classes == null || classes.size() == 0) { |
jjg@64 | 399 | if (options.help || options.version || options.fullVersion) |
jjg@64 | 400 | return EXIT_OK; |
jjg@64 | 401 | else |
jjg@64 | 402 | return EXIT_CMDERR; |
jjg@64 | 403 | } |
jjg@64 | 404 | |
jjg@46 | 405 | boolean ok = run(); |
jjg@64 | 406 | return ok ? EXIT_OK : EXIT_ERROR; |
jjg@46 | 407 | } catch (BadArgs e) { |
jjg@46 | 408 | diagnosticListener.report(createDiagnostic(e.key, e.args)); |
jjg@66 | 409 | if (e.showUsage) { |
jjg@66 | 410 | log.println(getMessage("main.usage.summary", progname)); |
jjg@66 | 411 | } |
jjg@64 | 412 | return EXIT_CMDERR; |
jjg@46 | 413 | } catch (InternalError e) { |
jjg@46 | 414 | Object[] e_args; |
jjg@46 | 415 | if (e.getCause() == null) |
jjg@46 | 416 | e_args = e.args; |
jjg@46 | 417 | else { |
jjg@46 | 418 | e_args = new Object[e.args.length + 1]; |
jjg@46 | 419 | e_args[0] = e.getCause(); |
jjg@46 | 420 | System.arraycopy(e.args, 0, e_args, 1, e.args.length); |
jjg@46 | 421 | } |
jjg@46 | 422 | diagnosticListener.report(createDiagnostic("err.internal.error", e_args)); |
jjg@64 | 423 | return EXIT_ABNORMAL; |
jjg@46 | 424 | } finally { |
jjg@46 | 425 | log.flush(); |
jjg@46 | 426 | } |
jjg@46 | 427 | } |
jjg@46 | 428 | |
jjg@46 | 429 | public void handleOptions(String[] args) throws BadArgs { |
jjg@46 | 430 | handleOptions(Arrays.asList(args), true); |
jjg@46 | 431 | } |
jjg@46 | 432 | |
jjg@46 | 433 | private void handleOptions(Iterable<String> args, boolean allowClasses) throws BadArgs { |
jjg@46 | 434 | if (log == null) { |
jjg@46 | 435 | log = getPrintWriterForStream(System.out); |
jjg@46 | 436 | if (diagnosticListener == null) |
jjg@46 | 437 | diagnosticListener = getDiagnosticListenerForStream(System.err); |
jjg@46 | 438 | } else { |
jjg@46 | 439 | if (diagnosticListener == null) |
jjg@46 | 440 | diagnosticListener = getDiagnosticListenerForWriter(log); |
jjg@46 | 441 | } |
jjg@46 | 442 | |
jjg@46 | 443 | |
jjg@46 | 444 | if (fileManager == null) |
jjg@46 | 445 | fileManager = getDefaultFileManager(diagnosticListener, log); |
jjg@46 | 446 | |
jjg@46 | 447 | Iterator<String> iter = args.iterator(); |
jjg@64 | 448 | boolean noArgs = !iter.hasNext(); |
jjg@46 | 449 | |
jjg@46 | 450 | while (iter.hasNext()) { |
jjg@46 | 451 | String arg = iter.next(); |
jjg@46 | 452 | if (arg.startsWith("-")) |
jjg@46 | 453 | handleOption(arg, iter); |
jjg@46 | 454 | else if (allowClasses) { |
jjg@46 | 455 | if (classes == null) |
jjg@46 | 456 | classes = new ArrayList<String>(); |
jjg@46 | 457 | classes.add(arg); |
jjg@46 | 458 | while (iter.hasNext()) |
jjg@46 | 459 | classes.add(iter.next()); |
jjg@46 | 460 | } else |
jjg@46 | 461 | throw new BadArgs("err.unknown.option", arg).showUsage(true); |
jjg@46 | 462 | } |
jjg@46 | 463 | |
jjg@68 | 464 | if (!options.compat && options.accessOptions.size() > 1) { |
jjg@68 | 465 | StringBuilder sb = new StringBuilder(); |
jjg@68 | 466 | for (String opt: options.accessOptions) { |
jjg@68 | 467 | if (sb.length() > 0) |
jjg@68 | 468 | sb.append(" "); |
jjg@68 | 469 | sb.append(opt); |
jjg@68 | 470 | } |
jjg@68 | 471 | throw new BadArgs("err.incompatible.options", sb); |
jjg@68 | 472 | } |
jjg@68 | 473 | |
jjg@46 | 474 | if (options.ignoreSymbolFile && fileManager instanceof JavapFileManager) |
jjg@46 | 475 | ((JavapFileManager) fileManager).setIgnoreSymbolFile(true); |
jjg@46 | 476 | |
jjg@46 | 477 | if ((classes == null || classes.size() == 0) && |
jjg@64 | 478 | !(noArgs || options.help || options.version || options.fullVersion)) { |
jjg@46 | 479 | throw new BadArgs("err.no.classes.specified"); |
jjg@46 | 480 | } |
jjg@64 | 481 | |
jjg@64 | 482 | if (noArgs || options.help) |
jjg@64 | 483 | showHelp(); |
jjg@64 | 484 | |
jjg@64 | 485 | if (options.version || options.fullVersion) |
jjg@64 | 486 | showVersion(options.fullVersion); |
jjg@46 | 487 | } |
jjg@46 | 488 | |
jjg@46 | 489 | private void handleOption(String name, Iterator<String> rest) throws BadArgs { |
jjg@46 | 490 | for (Option o: recognizedOptions) { |
jjg@46 | 491 | if (o.matches(name)) { |
jjg@46 | 492 | if (o.hasArg) { |
jjg@46 | 493 | if (rest.hasNext()) |
jjg@46 | 494 | o.process(this, name, rest.next()); |
jjg@46 | 495 | else |
jjg@46 | 496 | throw new BadArgs("err.missing.arg", name).showUsage(true); |
jjg@46 | 497 | } else |
jjg@46 | 498 | o.process(this, name, null); |
jjg@46 | 499 | |
jjg@46 | 500 | if (o.ignoreRest()) { |
jjg@46 | 501 | while (rest.hasNext()) |
jjg@46 | 502 | rest.next(); |
jjg@46 | 503 | } |
jjg@46 | 504 | return; |
jjg@46 | 505 | } |
jjg@46 | 506 | } |
jjg@46 | 507 | |
jjg@46 | 508 | if (fileManager.handleOption(name, rest)) |
jjg@46 | 509 | return; |
jjg@46 | 510 | |
jjg@46 | 511 | throw new BadArgs("err.unknown.option", name).showUsage(true); |
jjg@46 | 512 | } |
jjg@46 | 513 | |
jjg@46 | 514 | public Boolean call() { |
jjg@46 | 515 | return run(); |
jjg@46 | 516 | } |
jjg@46 | 517 | |
jjg@46 | 518 | public boolean run() { |
jjg@46 | 519 | if (classes == null || classes.size() == 0) |
jjg@64 | 520 | return false; |
jjg@46 | 521 | |
jjg@46 | 522 | context.put(PrintWriter.class, log); |
jjg@46 | 523 | ClassWriter classWriter = ClassWriter.instance(context); |
jjg@283 | 524 | SourceWriter sourceWriter = SourceWriter.instance(context); |
jjg@283 | 525 | sourceWriter.setFileManager(fileManager); |
jjg@46 | 526 | |
jjg@46 | 527 | boolean ok = true; |
jjg@46 | 528 | |
jjg@46 | 529 | for (String className: classes) { |
jjg@46 | 530 | JavaFileObject fo; |
jjg@46 | 531 | try { |
jjg@46 | 532 | if (className.endsWith(".class")) { |
jjg@46 | 533 | if (fileManager instanceof StandardJavaFileManager) { |
jjg@46 | 534 | StandardJavaFileManager sfm = (StandardJavaFileManager) fileManager; |
jjg@46 | 535 | fo = sfm.getJavaFileObjects(className).iterator().next(); |
jjg@46 | 536 | } else { |
jjg@46 | 537 | diagnosticListener.report(createDiagnostic("err.not.standard.file.manager", className)); |
jjg@46 | 538 | ok = false; |
jjg@46 | 539 | continue; |
jjg@46 | 540 | } |
jjg@46 | 541 | } else { |
jjg@46 | 542 | fo = getClassFileObject(className); |
jjg@46 | 543 | if (fo == null) { |
jjg@46 | 544 | // see if it is an inner class, by replacing dots to $, starting from the right |
jjg@46 | 545 | String cn = className; |
jjg@46 | 546 | int lastDot; |
jjg@46 | 547 | while (fo == null && (lastDot = cn.lastIndexOf(".")) != -1) { |
jjg@46 | 548 | cn = cn.substring(0, lastDot) + "$" + cn.substring(lastDot + 1); |
jjg@46 | 549 | fo = getClassFileObject(cn); |
jjg@46 | 550 | } |
jjg@46 | 551 | } |
jjg@46 | 552 | if (fo == null) { |
jjg@46 | 553 | diagnosticListener.report(createDiagnostic("err.class.not.found", className)); |
jjg@46 | 554 | ok = false; |
jjg@46 | 555 | continue; |
jjg@46 | 556 | } |
jjg@46 | 557 | } |
jjg@46 | 558 | Attribute.Factory attributeFactory = new Attribute.Factory(); |
jjg@46 | 559 | attributeFactory.setCompat(options.compat); |
jjg@46 | 560 | attributeFactory.setJSR277(options.jsr277); |
jjg@88 | 561 | |
jjg@88 | 562 | InputStream in = fo.openInputStream(); |
jjg@88 | 563 | SizeInputStream sizeIn = null; |
jjg@88 | 564 | MessageDigest md = null; |
jjg@88 | 565 | if (options.sysInfo || options.verbose) { |
jjg@88 | 566 | md = MessageDigest.getInstance("MD5"); |
jjg@88 | 567 | in = new DigestInputStream(in, md); |
jjg@88 | 568 | in = sizeIn = new SizeInputStream(in); |
jjg@88 | 569 | } |
jjg@88 | 570 | |
jjg@88 | 571 | ClassFile cf = ClassFile.read(in, attributeFactory); |
jjg@88 | 572 | |
jjg@88 | 573 | if (options.sysInfo || options.verbose) { |
jjg@88 | 574 | classWriter.setFile(fo.toUri()); |
jjg@88 | 575 | classWriter.setLastModified(fo.getLastModified()); |
jjg@88 | 576 | classWriter.setDigest("MD5", md.digest()); |
jjg@88 | 577 | classWriter.setFileSize(sizeIn.size()); |
jjg@88 | 578 | } |
jjg@88 | 579 | |
jjg@46 | 580 | classWriter.write(cf); |
jjg@88 | 581 | |
jjg@46 | 582 | } catch (ConstantPoolException e) { |
jjg@46 | 583 | diagnosticListener.report(createDiagnostic("err.bad.constant.pool", className, e.getLocalizedMessage())); |
jjg@46 | 584 | ok = false; |
jjg@46 | 585 | } catch (EOFException e) { |
jjg@46 | 586 | diagnosticListener.report(createDiagnostic("err.end.of.file", className)); |
jjg@46 | 587 | ok = false; |
jjg@46 | 588 | } catch (FileNotFoundException e) { |
jjg@46 | 589 | diagnosticListener.report(createDiagnostic("err.file.not.found", e.getLocalizedMessage())); |
jjg@46 | 590 | ok = false; |
jjg@46 | 591 | } catch (IOException e) { |
jjg@46 | 592 | //e.printStackTrace(); |
jjg@46 | 593 | Object msg = e.getLocalizedMessage(); |
jjg@46 | 594 | if (msg == null) |
jjg@46 | 595 | msg = e; |
jjg@46 | 596 | diagnosticListener.report(createDiagnostic("err.ioerror", className, msg)); |
jjg@46 | 597 | ok = false; |
jjg@46 | 598 | } catch (Throwable t) { |
jjg@46 | 599 | StringWriter sw = new StringWriter(); |
jjg@46 | 600 | PrintWriter pw = new PrintWriter(sw); |
jjg@46 | 601 | t.printStackTrace(pw); |
jjg@46 | 602 | pw.close(); |
jjg@46 | 603 | diagnosticListener.report(createDiagnostic("err.crash", t.toString(), sw.toString())); |
jjg@52 | 604 | ok = false; |
jjg@46 | 605 | } |
jjg@46 | 606 | } |
jjg@46 | 607 | |
jjg@46 | 608 | return ok; |
jjg@46 | 609 | } |
jjg@46 | 610 | |
jjg@46 | 611 | private JavaFileManager getDefaultFileManager(final DiagnosticListener<? super JavaFileObject> dl, PrintWriter log) { |
jjg@46 | 612 | return JavapFileManager.create(dl, log, options); |
jjg@46 | 613 | } |
jjg@46 | 614 | |
jjg@46 | 615 | private JavaFileObject getClassFileObject(String className) throws IOException { |
jjg@46 | 616 | JavaFileObject fo; |
jjg@46 | 617 | fo = fileManager.getJavaFileForInput(StandardLocation.PLATFORM_CLASS_PATH, className, JavaFileObject.Kind.CLASS); |
jjg@46 | 618 | if (fo == null) |
jjg@46 | 619 | fo = fileManager.getJavaFileForInput(StandardLocation.CLASS_PATH, className, JavaFileObject.Kind.CLASS); |
jjg@46 | 620 | return fo; |
jjg@46 | 621 | } |
jjg@46 | 622 | |
jjg@46 | 623 | private void showHelp() { |
jjg@46 | 624 | log.println(getMessage("main.usage", progname)); |
jjg@46 | 625 | for (Option o: recognizedOptions) { |
jjg@46 | 626 | String name = o.aliases[0].substring(1); // there must always be at least one name |
jjg@46 | 627 | if (name.startsWith("X") || name.equals("fullversion") || name.equals("h") || name.equals("verify")) |
jjg@46 | 628 | continue; |
jjg@46 | 629 | log.println(getMessage("main.opt." + name)); |
jjg@46 | 630 | } |
jjg@46 | 631 | String[] fmOptions = { "-classpath", "-bootclasspath" }; |
jjg@46 | 632 | for (String o: fmOptions) { |
jjg@46 | 633 | if (fileManager.isSupportedOption(o) == -1) |
jjg@46 | 634 | continue; |
jjg@46 | 635 | String name = o.substring(1); |
jjg@46 | 636 | log.println(getMessage("main.opt." + name)); |
jjg@46 | 637 | } |
jjg@46 | 638 | |
jjg@46 | 639 | } |
jjg@46 | 640 | |
jjg@46 | 641 | private void showVersion(boolean full) { |
jjg@46 | 642 | log.println(version(full ? "full" : "release")); |
jjg@46 | 643 | } |
jjg@46 | 644 | |
jjg@46 | 645 | private static final String versionRBName = "com.sun.tools.javap.resources.version"; |
jjg@46 | 646 | private static ResourceBundle versionRB; |
jjg@46 | 647 | |
jjg@46 | 648 | private String version(String key) { |
jjg@46 | 649 | // key=version: mm.nn.oo[-milestone] |
jjg@46 | 650 | // key=full: mm.mm.oo[-milestone]-build |
jjg@46 | 651 | if (versionRB == null) { |
jjg@46 | 652 | try { |
jjg@46 | 653 | versionRB = ResourceBundle.getBundle(versionRBName); |
jjg@46 | 654 | } catch (MissingResourceException e) { |
jjg@46 | 655 | return getMessage("version.resource.missing", System.getProperty("java.version")); |
jjg@46 | 656 | } |
jjg@46 | 657 | } |
jjg@46 | 658 | try { |
jjg@46 | 659 | return versionRB.getString(key); |
jjg@46 | 660 | } |
jjg@46 | 661 | catch (MissingResourceException e) { |
jjg@46 | 662 | return getMessage("version.unknown", System.getProperty("java.version")); |
jjg@46 | 663 | } |
jjg@46 | 664 | } |
jjg@46 | 665 | |
jjg@46 | 666 | private Diagnostic<JavaFileObject> createDiagnostic(final String key, final Object... args) { |
jjg@46 | 667 | return new Diagnostic<JavaFileObject>() { |
jjg@46 | 668 | public Kind getKind() { |
jjg@46 | 669 | return Diagnostic.Kind.ERROR; |
jjg@46 | 670 | } |
jjg@46 | 671 | |
jjg@46 | 672 | public JavaFileObject getSource() { |
jjg@46 | 673 | return null; |
jjg@46 | 674 | } |
jjg@46 | 675 | |
jjg@46 | 676 | public long getPosition() { |
jjg@46 | 677 | return Diagnostic.NOPOS; |
jjg@46 | 678 | } |
jjg@46 | 679 | |
jjg@46 | 680 | public long getStartPosition() { |
jjg@46 | 681 | return Diagnostic.NOPOS; |
jjg@46 | 682 | } |
jjg@46 | 683 | |
jjg@46 | 684 | public long getEndPosition() { |
jjg@46 | 685 | return Diagnostic.NOPOS; |
jjg@46 | 686 | } |
jjg@46 | 687 | |
jjg@46 | 688 | public long getLineNumber() { |
jjg@46 | 689 | return Diagnostic.NOPOS; |
jjg@46 | 690 | } |
jjg@46 | 691 | |
jjg@46 | 692 | public long getColumnNumber() { |
jjg@46 | 693 | return Diagnostic.NOPOS; |
jjg@46 | 694 | } |
jjg@46 | 695 | |
jjg@46 | 696 | public String getCode() { |
jjg@46 | 697 | return key; |
jjg@46 | 698 | } |
jjg@46 | 699 | |
jjg@46 | 700 | public String getMessage(Locale locale) { |
jjg@46 | 701 | return JavapTask.this.getMessage(locale, key, args); |
jjg@46 | 702 | } |
jjg@46 | 703 | |
jjg@46 | 704 | }; |
jjg@46 | 705 | |
jjg@46 | 706 | } |
jjg@46 | 707 | |
jjg@283 | 708 | public String getMessage(String key, Object... args) { |
jjg@46 | 709 | return getMessage(task_locale, key, args); |
jjg@46 | 710 | } |
jjg@46 | 711 | |
jjg@283 | 712 | public String getMessage(Locale locale, String key, Object... args) { |
jjg@46 | 713 | if (bundles == null) { |
jjg@46 | 714 | // could make this a HashMap<Locale,SoftReference<ResourceBundle>> |
jjg@46 | 715 | // and for efficiency, keep a hard reference to the bundle for the task |
jjg@46 | 716 | // locale |
jjg@46 | 717 | bundles = new HashMap<Locale, ResourceBundle>(); |
jjg@46 | 718 | } |
jjg@46 | 719 | |
jjg@46 | 720 | if (locale == null) |
jjg@46 | 721 | locale = Locale.getDefault(); |
jjg@46 | 722 | |
jjg@46 | 723 | ResourceBundle b = bundles.get(locale); |
jjg@46 | 724 | if (b == null) { |
jjg@46 | 725 | try { |
jjg@46 | 726 | b = ResourceBundle.getBundle("com.sun.tools.javap.resources.javap", locale); |
jjg@46 | 727 | bundles.put(locale, b); |
jjg@46 | 728 | } catch (MissingResourceException e) { |
jjg@46 | 729 | throw new InternalError("Cannot find javap resource bundle for locale " + locale); |
jjg@46 | 730 | } |
jjg@46 | 731 | } |
jjg@46 | 732 | |
jjg@46 | 733 | try { |
jjg@46 | 734 | return MessageFormat.format(b.getString(key), args); |
jjg@46 | 735 | } catch (MissingResourceException e) { |
jjg@46 | 736 | throw new InternalError(e, key); |
jjg@46 | 737 | } |
jjg@46 | 738 | } |
jjg@46 | 739 | |
jjg@46 | 740 | Context context; |
jjg@46 | 741 | JavaFileManager fileManager; |
jjg@46 | 742 | PrintWriter log; |
jjg@46 | 743 | DiagnosticListener<? super JavaFileObject> diagnosticListener; |
jjg@46 | 744 | List<String> classes; |
jjg@46 | 745 | Options options; |
jjg@46 | 746 | //ResourceBundle bundle; |
jjg@46 | 747 | Locale task_locale; |
jjg@46 | 748 | Map<Locale, ResourceBundle> bundles; |
jjg@46 | 749 | |
jjg@46 | 750 | private static final String progname = "javap"; |
jjg@88 | 751 | |
jjg@88 | 752 | private static class SizeInputStream extends FilterInputStream { |
jjg@88 | 753 | SizeInputStream(InputStream in) { |
jjg@88 | 754 | super(in); |
jjg@88 | 755 | } |
jjg@88 | 756 | |
jjg@88 | 757 | int size() { |
jjg@88 | 758 | return size; |
jjg@88 | 759 | } |
jjg@88 | 760 | |
jjg@88 | 761 | @Override |
jjg@88 | 762 | public int read(byte[] buf, int offset, int length) throws IOException { |
jjg@88 | 763 | int n = super.read(buf, offset, length); |
jjg@88 | 764 | if (n > 0) |
jjg@88 | 765 | size += n; |
jjg@88 | 766 | return n; |
jjg@88 | 767 | } |
jjg@88 | 768 | |
jjg@88 | 769 | @Override |
jjg@88 | 770 | public int read() throws IOException { |
jjg@88 | 771 | int b = super.read(); |
jjg@88 | 772 | size += 1; |
jjg@88 | 773 | return b; |
jjg@88 | 774 | } |
jjg@88 | 775 | |
jjg@88 | 776 | private int size; |
jjg@88 | 777 | } |
jjg@46 | 778 | } |