src/share/classes/com/sun/tools/javap/JavapTask.java

Tue, 03 Jun 2008 13:26:47 -0700

author
jjg
date
Tue, 03 Jun 2008 13:26:47 -0700
changeset 46
7708bd6d800d
child 52
3cb4fb6e0720
permissions
-rw-r--r--

4075303: Use javap to enquire aboput a specific inner class
4348375: Javap is not internationalized
4459541: "javap -l" shows line numbers as signed short; they should be unsigned
4501660: change diagnostic of -help as 'print this help message and exit'
4776241: unused source file in javap...
4870651: javap should recognize generics, varargs, enum
4876942: javap invoked without args does not print help screen
4880663: javap could output whitespace between class name and opening brace
4975569: javap doesn't print new flag bits
6271787: javap dumps LocalVariableTypeTable attribute in hex, needs to print a table
6305779: javap: support annotations
6439940: Clean up javap implementation
6469569: wrong check of searchpath in JavapEnvironment
6474890: javap does not open .zip files in -classpath
6587786: Javap throws error : "ERROR:Could not find <classname>" for JRE classes
6622215: javap ignores certain relevant access flags
6622216: javap names some attributes incorrectly
6622232: javap gets whitespace confused
6622260: javap prints negative bytes incorrectly in hex
Reviewed-by: ksrini

     1 /*
     2  * Copyright 2007 Sun Microsystems, Inc.  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.  Sun designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Sun 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-15301 USA.
    20  *
    21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
    23  * have any questions.
    24  */
    26 package com.sun.tools.javap;
    28 import java.io.EOFException;
    29 import java.io.FileNotFoundException;
    30 import java.io.IOException;
    31 import java.io.OutputStream;
    32 import java.io.PrintWriter;
    33 import java.io.StringWriter;
    34 import java.io.Writer;
    35 import java.text.MessageFormat;
    36 import java.util.ArrayList;
    37 import java.util.Arrays;
    38 import java.util.HashMap;
    39 import java.util.Iterator;
    40 import java.util.List;
    41 import java.util.Locale;
    42 import java.util.Map;
    43 import java.util.MissingResourceException;
    44 import java.util.ResourceBundle;
    46 import javax.tools.Diagnostic;
    47 import javax.tools.DiagnosticListener;
    48 import javax.tools.JavaFileManager;
    49 import javax.tools.JavaFileObject;
    50 import javax.tools.StandardJavaFileManager;
    51 import javax.tools.StandardLocation;
    53 import com.sun.tools.classfile.*;
    55 /**
    56  *  "Main" class for javap, normally accessed from the command line
    57  *  via Main, or from JSR199 via DisassemblerTool.
    58  *
    59  *  <p><b>This is NOT part of any API supported by Sun Microsystems.  If
    60  *  you write code that depends on this, you do so at your own risk.
    61  *  This code and its internal interfaces are subject to change or
    62  *  deletion without notice.</b>
    63  */
    64 public class JavapTask implements DisassemblerTool.DisassemblerTask {
    65     public class BadArgs extends Exception {
    66         static final long serialVersionUID = 8765093759964640721L;
    67         BadArgs(String key, Object... args) {
    68             super(JavapTask.this.getMessage(key, args));
    69             this.key = key;
    70             this.args = args;
    71         }
    73         BadArgs showUsage(boolean b) {
    74             showUsage = b;
    75             return this;
    76         }
    78         final String key;
    79         final Object[] args;
    80         boolean showUsage;
    81     }
    83     static abstract class Option {
    84         Option(boolean hasArg, String... aliases) {
    85             this.hasArg = hasArg;
    86             this.aliases = aliases;
    87         }
    89         boolean matches(String opt) {
    90             for (String a: aliases) {
    91                 if (a.equals(opt))
    92                     return true;
    93             }
    94             return false;
    95         }
    97         boolean ignoreRest() {
    98             return false;
    99         }
   101         abstract void process(JavapTask task, String opt, String arg) throws BadArgs;
   103         final boolean hasArg;
   104         final String[] aliases;
   105     }
   107     static Option[] recognizedOptions = {
   109         new Option(false, "-help", "--help", "-?") {
   110             void process(JavapTask task, String opt, String arg) {
   111                 task.options.help = true;
   112             }
   113         },
   115         new Option(false, "-version") {
   116             void process(JavapTask task, String opt, String arg) {
   117                 task.options.version = true;
   118             }
   119         },
   121         new Option(false, "-fullversion") {
   122             void process(JavapTask task, String opt, String arg) {
   123                 task.options.fullVersion = true;
   124             }
   125         },
   127         new Option(false, "-v", "-verbose", "-all") {
   128             void process(JavapTask task, String opt, String arg) {
   129                 task.options.verbose = true;
   130                 task.options.showFlags = true;
   131                 task.options.showAllAttrs = true;
   132             }
   133         },
   135         new Option(false, "-l") {
   136             void process(JavapTask task, String opt, String arg) {
   137                 task.options.showLineAndLocalVariableTables = true;
   138             }
   139         },
   141         new Option(false, "-public") {
   142             void process(JavapTask task, String opt, String arg) {
   143                 task.options.showAccess = AccessFlags.ACC_PUBLIC;
   144             }
   145         },
   147         new Option(false, "-protected") {
   148             void process(JavapTask task, String opt, String arg) {
   149                 task.options.showAccess = AccessFlags.ACC_PROTECTED;
   150             }
   151         },
   153         new Option(false, "-package") {
   154             void process(JavapTask task, String opt, String arg) {
   155                 task.options.showAccess = 0;
   156             }
   157         },
   159         new Option(false, "-p", "-private") {
   160             void process(JavapTask task, String opt, String arg) {
   161                 task.options.showAccess = AccessFlags.ACC_PRIVATE;
   162             }
   163         },
   165         new Option(false, "-c") {
   166             void process(JavapTask task, String opt, String arg) {
   167                 task.options.showDisassembled = true;
   168             }
   169         },
   171         new Option(false, "-s") {
   172             void process(JavapTask task, String opt, String arg) {
   173                 task.options.showInternalSignatures = true;
   174             }
   175         },
   177 //        new Option(false, "-all") {
   178 //            void process(JavapTask task, String opt, String arg) {
   179 //                task.options.showAllAttrs = true;
   180 //            }
   181 //        },
   183         new Option(false, "-h") {
   184             void process(JavapTask task, String opt, String arg) throws BadArgs {
   185                 throw task.new BadArgs("err.h.not.supported");
   186             }
   187         },
   189         new Option(false, "-verify", "-verify-verbose") {
   190             void process(JavapTask task, String opt, String arg) throws BadArgs {
   191                 throw task.new BadArgs("err.verify.not.supported");
   192             }
   193         },
   195         new Option(false, "-Xold") {
   196             void process(JavapTask task, String opt, String arg) throws BadArgs {
   197                 // -Xold is only supported as first arg when invoked from
   198                 // command line; this is handled in Main,main
   199                 throw task.new BadArgs("err.Xold.not.supported.here");
   200             }
   201         },
   203         new Option(false, "-Xnew") {
   204             void process(JavapTask task, String opt, String arg) throws BadArgs {
   205                 // ignore: this _is_ the new version
   206             }
   207         },
   209         new Option(false, "-XDcompat") {
   210             void process(JavapTask task, String opt, String arg) {
   211                 task.options.compat = true;
   212             }
   213         },
   215         new Option(false, "-XDjsr277") {
   216             void process(JavapTask task, String opt, String arg) {
   217                 task.options.jsr277 = true;
   218             }
   219         },
   221         new Option(false, "-XDignore.symbol.file") {
   222             void process(JavapTask task, String opt, String arg) {
   223                 task.options.ignoreSymbolFile = true;
   224             }
   225         }
   227     };
   229     JavapTask() {
   230         context = new Context();
   231         options = Options.instance(context);
   232     }
   234     JavapTask(Writer out,
   235             JavaFileManager fileManager,
   236             DiagnosticListener<? super JavaFileObject> diagnosticListener,
   237             Iterable<String> options,
   238             Iterable<String> classes) {
   239         this();
   240         this.log = getPrintWriterForWriter(out);
   241         this.fileManager = fileManager;
   242         this.diagnosticListener = diagnosticListener;
   244         try {
   245             handleOptions(options, false);
   246         } catch (BadArgs e) {
   247             throw new IllegalArgumentException(e.getMessage());
   248         }
   250         this.classes = new ArrayList<String>();
   251         for (String classname: classes) {
   252             classname.getClass(); // null-check
   253             this.classes.add(classname);
   254         }
   255     }
   257     public void setLocale(Locale locale) {
   258         if (locale == null)
   259             locale = Locale.getDefault();
   260         task_locale = locale;
   261     }
   263     public void setLog(PrintWriter log) {
   264         this.log = log;
   265     }
   267     public void setLog(OutputStream s) {
   268         setLog(getPrintWriterForStream(s));
   269     }
   271     private static PrintWriter getPrintWriterForStream(OutputStream s) {
   272         return new PrintWriter(s, true);
   273     }
   275     private static PrintWriter getPrintWriterForWriter(Writer w) {
   276         if (w == null)
   277             return getPrintWriterForStream(null);
   278         else if (w instanceof PrintWriter)
   279             return (PrintWriter) w;
   280         else
   281             return new PrintWriter(w, true);
   282     }
   284     public void setDiagnosticListener(DiagnosticListener<? super JavaFileObject> dl) {
   285         diagnosticListener = dl;
   286     }
   288     public void setDiagnosticListener(OutputStream s) {
   289         setDiagnosticListener(getDiagnosticListenerForStream(s));
   290     }
   292     private DiagnosticListener<JavaFileObject> getDiagnosticListenerForStream(OutputStream s) {
   293         return getDiagnosticListenerForWriter(getPrintWriterForStream(s));
   294     }
   296     private DiagnosticListener<JavaFileObject> getDiagnosticListenerForWriter(Writer w) {
   297         final PrintWriter pw = getPrintWriterForWriter(w);
   298         return new DiagnosticListener<JavaFileObject> () {
   299             public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
   300                 if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
   301                     pw.print(getMessage("err.prefix"));
   302                     pw.print(" ");
   303                 }
   304                 pw.println(diagnostic.getMessage(null));
   305             }
   306         };
   307     }
   309     int run(String[] args) {
   310         try {
   311             handleOptions(args);
   312             boolean ok = run();
   313             return ok ? 0 : 1;
   314         } catch (BadArgs e) {
   315             diagnosticListener.report(createDiagnostic(e.key, e.args));
   316             return 1;
   317         } catch (InternalError e) {
   318             Object[] e_args;
   319             if (e.getCause() == null)
   320                 e_args = e.args;
   321             else {
   322                 e_args = new Object[e.args.length + 1];
   323                 e_args[0] = e.getCause();
   324                 System.arraycopy(e.args, 0, e_args, 1, e.args.length);
   325             }
   326             diagnosticListener.report(createDiagnostic("err.internal.error", e_args));
   327             return 1;
   328         } finally {
   329             log.flush();
   330         }
   331     }
   333     public void handleOptions(String[] args) throws BadArgs {
   334         handleOptions(Arrays.asList(args), true);
   335     }
   337     private void handleOptions(Iterable<String> args, boolean allowClasses) throws BadArgs {
   338         if (log == null) {
   339             log = getPrintWriterForStream(System.out);
   340             if (diagnosticListener == null)
   341               diagnosticListener = getDiagnosticListenerForStream(System.err);
   342         } else {
   343             if (diagnosticListener == null)
   344               diagnosticListener = getDiagnosticListenerForWriter(log);
   345         }
   348         if (fileManager == null)
   349             fileManager = getDefaultFileManager(diagnosticListener, log);
   351         Iterator<String> iter = args.iterator();
   352         if (!iter.hasNext())
   353             options.help = true;
   355         while (iter.hasNext()) {
   356             String arg = iter.next();
   357             if (arg.startsWith("-"))
   358                 handleOption(arg, iter);
   359             else if (allowClasses) {
   360                 if (classes == null)
   361                     classes = new ArrayList<String>();
   362                 classes.add(arg);
   363                 while (iter.hasNext())
   364                     classes.add(iter.next());
   365             } else
   366                 throw new BadArgs("err.unknown.option", arg).showUsage(true);
   367         }
   369         if (options.ignoreSymbolFile && fileManager instanceof JavapFileManager)
   370             ((JavapFileManager) fileManager).setIgnoreSymbolFile(true);
   372         if ((classes == null || classes.size() == 0) &&
   373                 !(options.help || options.version || options.fullVersion)) {
   374             throw new BadArgs("err.no.classes.specified");
   375         }
   376     }
   378     private void handleOption(String name, Iterator<String> rest) throws BadArgs {
   379         for (Option o: recognizedOptions) {
   380             if (o.matches(name)) {
   381                 if (o.hasArg) {
   382                     if (rest.hasNext())
   383                         o.process(this, name, rest.next());
   384                     else
   385                         throw new BadArgs("err.missing.arg", name).showUsage(true);
   386                 } else
   387                     o.process(this, name, null);
   389                 if (o.ignoreRest()) {
   390                     while (rest.hasNext())
   391                         rest.next();
   392                 }
   393                 return;
   394             }
   395         }
   397         if (fileManager.handleOption(name, rest))
   398             return;
   400         throw new BadArgs("err.unknown.option", name).showUsage(true);
   401     }
   403     public Boolean call() {
   404         return run();
   405     }
   407     public boolean run() {
   408         if (options.help)
   409             showHelp();
   411         if (options.version || options.fullVersion)
   412             showVersion(options.fullVersion);
   414         if (classes == null || classes.size() == 0)
   415             return true;
   417         context.put(PrintWriter.class, log);
   418         ClassWriter classWriter = ClassWriter.instance(context);
   420         boolean ok = true;
   422         for (String className: classes) {
   423             JavaFileObject fo;
   424             try {
   425                 if (className.endsWith(".class")) {
   426                     if (fileManager instanceof StandardJavaFileManager) {
   427                         StandardJavaFileManager sfm = (StandardJavaFileManager) fileManager;
   428                         fo = sfm.getJavaFileObjects(className).iterator().next();
   429                     } else {
   430                        diagnosticListener.report(createDiagnostic("err.not.standard.file.manager", className));
   431                        ok = false;
   432                        continue;
   433                     }
   434                 } else {
   435                     fo = getClassFileObject(className);
   436                     if (fo == null) {
   437                         // see if it is an inner class, by replacing dots to $, starting from the right
   438                         String cn = className;
   439                         int lastDot;
   440                         while (fo == null && (lastDot = cn.lastIndexOf(".")) != -1) {
   441                             cn = cn.substring(0, lastDot) + "$" + cn.substring(lastDot + 1);
   442                             fo = getClassFileObject(cn);
   443                         }
   444                     }
   445                     if (fo == null) {
   446                        diagnosticListener.report(createDiagnostic("err.class.not.found", className));
   447                        ok = false;
   448                        continue;
   449                     }
   450                 }
   451                 Attribute.Factory attributeFactory = new Attribute.Factory();
   452                 attributeFactory.setCompat(options.compat);
   453                 attributeFactory.setJSR277(options.jsr277);
   454                 ClassFile cf = ClassFile.read(fo.openInputStream(), attributeFactory);
   455                 classWriter.write(cf);
   456             } catch (ConstantPoolException e) {
   457                 diagnosticListener.report(createDiagnostic("err.bad.constant.pool", className, e.getLocalizedMessage()));
   458                 ok = false;
   459             } catch (EOFException e) {
   460                 diagnosticListener.report(createDiagnostic("err.end.of.file", className));
   461                 ok = false;
   462             } catch (FileNotFoundException e) {
   463                 diagnosticListener.report(createDiagnostic("err.file.not.found", e.getLocalizedMessage()));
   464                 ok = false;
   465             } catch (IOException e) {
   466                 //e.printStackTrace();
   467                 Object msg = e.getLocalizedMessage();
   468                 if (msg == null)
   469                     msg = e;
   470                 diagnosticListener.report(createDiagnostic("err.ioerror", className, msg));
   471                 ok = false;
   472             } catch (Throwable t) {
   473                 StringWriter sw = new StringWriter();
   474                 PrintWriter pw = new PrintWriter(sw);
   475                 t.printStackTrace(pw);
   476                 pw.close();
   477                 diagnosticListener.report(createDiagnostic("err.crash", t.toString(), sw.toString()));
   478             }
   479         }
   481         return ok;
   482     }
   484     private JavaFileManager getDefaultFileManager(final DiagnosticListener<? super JavaFileObject> dl, PrintWriter log) {
   485         return JavapFileManager.create(dl, log, options);
   486     }
   488     private JavaFileObject getClassFileObject(String className) throws IOException {
   489         JavaFileObject fo;
   490         fo = fileManager.getJavaFileForInput(StandardLocation.PLATFORM_CLASS_PATH, className, JavaFileObject.Kind.CLASS);
   491         if (fo == null)
   492             fo = fileManager.getJavaFileForInput(StandardLocation.CLASS_PATH, className, JavaFileObject.Kind.CLASS);
   493         return fo;
   494     }
   496     private void showHelp() {
   497         log.println(getMessage("main.usage", progname));
   498         for (Option o: recognizedOptions) {
   499             String name = o.aliases[0].substring(1); // there must always be at least one name
   500             if (name.startsWith("X") || name.equals("fullversion") || name.equals("h") || name.equals("verify"))
   501                 continue;
   502             log.println(getMessage("main.opt." + name));
   503         }
   504         String[] fmOptions = { "-classpath", "-bootclasspath" };
   505         for (String o: fmOptions) {
   506             if (fileManager.isSupportedOption(o) == -1)
   507                 continue;
   508             String name = o.substring(1);
   509             log.println(getMessage("main.opt." + name));
   510         }
   512     }
   514     private void showVersion(boolean full) {
   515         log.println(version(full ? "full" : "release"));
   516     }
   518     private static final String versionRBName = "com.sun.tools.javap.resources.version";
   519     private static ResourceBundle versionRB;
   521     private String version(String key) {
   522         // key=version:  mm.nn.oo[-milestone]
   523         // key=full:     mm.mm.oo[-milestone]-build
   524         if (versionRB == null) {
   525             try {
   526                 versionRB = ResourceBundle.getBundle(versionRBName);
   527             } catch (MissingResourceException e) {
   528                 return getMessage("version.resource.missing", System.getProperty("java.version"));
   529             }
   530         }
   531         try {
   532             return versionRB.getString(key);
   533         }
   534         catch (MissingResourceException e) {
   535             return getMessage("version.unknown", System.getProperty("java.version"));
   536         }
   537     }
   539     private Diagnostic<JavaFileObject> createDiagnostic(final String key, final Object... args) {
   540         return new Diagnostic<JavaFileObject>() {
   541             public Kind getKind() {
   542                 return Diagnostic.Kind.ERROR;
   543             }
   545             public JavaFileObject getSource() {
   546                 return null;
   547             }
   549             public long getPosition() {
   550                 return Diagnostic.NOPOS;
   551             }
   553             public long getStartPosition() {
   554                 return Diagnostic.NOPOS;
   555             }
   557             public long getEndPosition() {
   558                 return Diagnostic.NOPOS;
   559             }
   561             public long getLineNumber() {
   562                 return Diagnostic.NOPOS;
   563             }
   565             public long getColumnNumber() {
   566                 return Diagnostic.NOPOS;
   567             }
   569             public String getCode() {
   570                 return key;
   571             }
   573             public String getMessage(Locale locale) {
   574                 return JavapTask.this.getMessage(locale, key, args);
   575             }
   577         };
   579     }
   581     private String getMessage(String key, Object... args) {
   582         return getMessage(task_locale, key, args);
   583     }
   585     private String getMessage(Locale locale, String key, Object... args) {
   586         if (bundles == null) {
   587             // could make this a HashMap<Locale,SoftReference<ResourceBundle>>
   588             // and for efficiency, keep a hard reference to the bundle for the task
   589             // locale
   590             bundles = new HashMap<Locale, ResourceBundle>();
   591         }
   593         if (locale == null)
   594             locale = Locale.getDefault();
   596         ResourceBundle b = bundles.get(locale);
   597         if (b == null) {
   598             try {
   599                 b = ResourceBundle.getBundle("com.sun.tools.javap.resources.javap", locale);
   600                 bundles.put(locale, b);
   601             } catch (MissingResourceException e) {
   602                 throw new InternalError("Cannot find javap resource bundle for locale " + locale);
   603             }
   604         }
   606         try {
   607             return MessageFormat.format(b.getString(key), args);
   608         } catch (MissingResourceException e) {
   609             throw new InternalError(e, key);
   610         }
   611     }
   613     Context context;
   614     JavaFileManager fileManager;
   615     PrintWriter log;
   616     DiagnosticListener<? super JavaFileObject> diagnosticListener;
   617     List<String> classes;
   618     Options options;
   619     //ResourceBundle bundle;
   620     Locale task_locale;
   621     Map<Locale, ResourceBundle> bundles;
   623     private static final String progname = "javap";
   624 }

mercurial