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

Tue, 16 Sep 2008 18:35:18 -0700

author
jjg
date
Tue, 16 Sep 2008 18:35:18 -0700
changeset 113
eff38cc97183
parent 90
6be961ee2290
child 283
cd0630109de5
permissions
-rw-r--r--

6574134: Allow for alternative implementation of Name Table with garbage collection of name bytes
Reviewed-by: darcy, mcimadamore

     1 /*
     2  * Copyright 2007-2008 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-1301 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.FilterInputStream;
    31 import java.io.InputStream;
    32 import java.io.IOException;
    33 import java.io.OutputStream;
    34 import java.io.PrintWriter;
    35 import java.io.StringWriter;
    36 import java.io.Writer;
    37 import java.security.DigestInputStream;
    38 import java.security.MessageDigest;
    39 import java.text.MessageFormat;
    40 import java.util.ArrayList;
    41 import java.util.Arrays;
    42 import java.util.HashMap;
    43 import java.util.Iterator;
    44 import java.util.List;
    45 import java.util.Locale;
    46 import java.util.Map;
    47 import java.util.MissingResourceException;
    48 import java.util.ResourceBundle;
    50 import javax.tools.Diagnostic;
    51 import javax.tools.DiagnosticListener;
    52 import javax.tools.JavaFileManager;
    53 import javax.tools.JavaFileObject;
    54 import javax.tools.StandardJavaFileManager;
    55 import javax.tools.StandardLocation;
    57 import com.sun.tools.classfile.*;
    59 /**
    60  *  "Main" class for javap, normally accessed from the command line
    61  *  via Main, or from JSR199 via DisassemblerTool.
    62  *
    63  *  <p><b>This is NOT part of any API supported by Sun Microsystems.  If
    64  *  you write code that depends on this, you do so at your own risk.
    65  *  This code and its internal interfaces are subject to change or
    66  *  deletion without notice.</b>
    67  */
    68 public class JavapTask implements DisassemblerTool.DisassemblerTask {
    69     public class BadArgs extends Exception {
    70         static final long serialVersionUID = 8765093759964640721L;
    71         BadArgs(String key, Object... args) {
    72             super(JavapTask.this.getMessage(key, args));
    73             this.key = key;
    74             this.args = args;
    75         }
    77         BadArgs showUsage(boolean b) {
    78             showUsage = b;
    79             return this;
    80         }
    82         final String key;
    83         final Object[] args;
    84         boolean showUsage;
    85     }
    87     static abstract class Option {
    88         Option(boolean hasArg, String... aliases) {
    89             this.hasArg = hasArg;
    90             this.aliases = aliases;
    91         }
    93         boolean matches(String opt) {
    94             for (String a: aliases) {
    95                 if (a.equals(opt))
    96                     return true;
    97             }
    98             return false;
    99         }
   101         boolean ignoreRest() {
   102             return false;
   103         }
   105         abstract void process(JavapTask task, String opt, String arg) throws BadArgs;
   107         final boolean hasArg;
   108         final String[] aliases;
   109     }
   111     static Option[] recognizedOptions = {
   113         new Option(false, "-help", "--help", "-?") {
   114             void process(JavapTask task, String opt, String arg) {
   115                 task.options.help = true;
   116             }
   117         },
   119         new Option(false, "-version") {
   120             void process(JavapTask task, String opt, String arg) {
   121                 task.options.version = true;
   122             }
   123         },
   125         new Option(false, "-fullversion") {
   126             void process(JavapTask task, String opt, String arg) {
   127                 task.options.fullVersion = true;
   128             }
   129         },
   131         new Option(false, "-v", "-verbose", "-all") {
   132             void process(JavapTask task, String opt, String arg) {
   133                 task.options.verbose = true;
   134                 task.options.showFlags = true;
   135                 task.options.showAllAttrs = true;
   136             }
   137         },
   139         new Option(false, "-l") {
   140             void process(JavapTask task, String opt, String arg) {
   141                 task.options.showLineAndLocalVariableTables = true;
   142             }
   143         },
   145         new Option(false, "-public") {
   146             void process(JavapTask task, String opt, String arg) {
   147                 task.options.accessOptions.add(opt);
   148                 task.options.showAccess = AccessFlags.ACC_PUBLIC;
   149             }
   150         },
   152         new Option(false, "-protected") {
   153             void process(JavapTask task, String opt, String arg) {
   154                 task.options.accessOptions.add(opt);
   155                 task.options.showAccess = AccessFlags.ACC_PROTECTED;
   156             }
   157         },
   159         new Option(false, "-package") {
   160             void process(JavapTask task, String opt, String arg) {
   161                 task.options.accessOptions.add(opt);
   162                 task.options.showAccess = 0;
   163             }
   164         },
   166         new Option(false, "-p", "-private") {
   167             void process(JavapTask task, String opt, String arg) {
   168                 if (!task.options.accessOptions.contains("-p") &&
   169                         !task.options.accessOptions.contains("-private")) {
   170                     task.options.accessOptions.add(opt);
   171                 }
   172                 task.options.showAccess = AccessFlags.ACC_PRIVATE;
   173             }
   174         },
   176         new Option(false, "-c") {
   177             void process(JavapTask task, String opt, String arg) {
   178                 task.options.showDisassembled = true;
   179             }
   180         },
   182         new Option(false, "-s") {
   183             void process(JavapTask task, String opt, String arg) {
   184                 task.options.showInternalSignatures = true;
   185             }
   186         },
   188 //        new Option(false, "-all") {
   189 //            void process(JavapTask task, String opt, String arg) {
   190 //                task.options.showAllAttrs = true;
   191 //            }
   192 //        },
   194         new Option(false, "-h") {
   195             void process(JavapTask task, String opt, String arg) throws BadArgs {
   196                 throw task.new BadArgs("err.h.not.supported");
   197             }
   198         },
   200         new Option(false, "-verify", "-verify-verbose") {
   201             void process(JavapTask task, String opt, String arg) throws BadArgs {
   202                 throw task.new BadArgs("err.verify.not.supported");
   203             }
   204         },
   206         new Option(false, "-sysinfo") {
   207             void process(JavapTask task, String opt, String arg) {
   208                 task.options.sysInfo = true;
   209             }
   210         },
   212         new Option(false, "-Xold") {
   213             void process(JavapTask task, String opt, String arg) throws BadArgs {
   214                 // -Xold is only supported as first arg when invoked from
   215                 // command line; this is handled in Main,main
   216                 throw task.new BadArgs("err.Xold.not.supported.here");
   217             }
   218         },
   220         new Option(false, "-Xnew") {
   221             void process(JavapTask task, String opt, String arg) throws BadArgs {
   222                 // ignore: this _is_ the new version
   223             }
   224         },
   226         new Option(false, "-XDcompat") {
   227             void process(JavapTask task, String opt, String arg) {
   228                 task.options.compat = true;
   229             }
   230         },
   232         new Option(false, "-XDjsr277") {
   233             void process(JavapTask task, String opt, String arg) {
   234                 task.options.jsr277 = true;
   235             }
   236         },
   238         new Option(false, "-XDignore.symbol.file") {
   239             void process(JavapTask task, String opt, String arg) {
   240                 task.options.ignoreSymbolFile = true;
   241             }
   242         },
   244         new Option(false, "-constants") {
   245             void process(JavapTask task, String opt, String arg) {
   246                 task.options.showConstants = true;
   247             }
   248         }
   250     };
   252     JavapTask() {
   253         context = new Context();
   254         options = Options.instance(context);
   255     }
   257     JavapTask(Writer out,
   258             JavaFileManager fileManager,
   259             DiagnosticListener<? super JavaFileObject> diagnosticListener,
   260             Iterable<String> options,
   261             Iterable<String> classes) {
   262         this();
   263         this.log = getPrintWriterForWriter(out);
   264         this.fileManager = fileManager;
   265         this.diagnosticListener = diagnosticListener;
   267         try {
   268             handleOptions(options, false);
   269         } catch (BadArgs e) {
   270             throw new IllegalArgumentException(e.getMessage());
   271         }
   273         this.classes = new ArrayList<String>();
   274         for (String classname: classes) {
   275             classname.getClass(); // null-check
   276             this.classes.add(classname);
   277         }
   278     }
   280     public void setLocale(Locale locale) {
   281         if (locale == null)
   282             locale = Locale.getDefault();
   283         task_locale = locale;
   284     }
   286     public void setLog(PrintWriter log) {
   287         this.log = log;
   288     }
   290     public void setLog(OutputStream s) {
   291         setLog(getPrintWriterForStream(s));
   292     }
   294     private static PrintWriter getPrintWriterForStream(OutputStream s) {
   295         return new PrintWriter(s, true);
   296     }
   298     private static PrintWriter getPrintWriterForWriter(Writer w) {
   299         if (w == null)
   300             return getPrintWriterForStream(null);
   301         else if (w instanceof PrintWriter)
   302             return (PrintWriter) w;
   303         else
   304             return new PrintWriter(w, true);
   305     }
   307     public void setDiagnosticListener(DiagnosticListener<? super JavaFileObject> dl) {
   308         diagnosticListener = dl;
   309     }
   311     public void setDiagnosticListener(OutputStream s) {
   312         setDiagnosticListener(getDiagnosticListenerForStream(s));
   313     }
   315     private DiagnosticListener<JavaFileObject> getDiagnosticListenerForStream(OutputStream s) {
   316         return getDiagnosticListenerForWriter(getPrintWriterForStream(s));
   317     }
   319     private DiagnosticListener<JavaFileObject> getDiagnosticListenerForWriter(Writer w) {
   320         final PrintWriter pw = getPrintWriterForWriter(w);
   321         return new DiagnosticListener<JavaFileObject> () {
   322             public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
   323                 if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
   324                         pw.print(getMessage("err.prefix"));
   325                     pw.print(" ");
   326                 }
   327                 pw.println(diagnostic.getMessage(null));
   328             }
   329         };
   330     }
   332     /** Result codes.
   333      */
   334     static final int
   335         EXIT_OK = 0,        // Compilation completed with no errors.
   336         EXIT_ERROR = 1,     // Completed but reported errors.
   337         EXIT_CMDERR = 2,    // Bad command-line arguments
   338         EXIT_SYSERR = 3,    // System error or resource exhaustion.
   339         EXIT_ABNORMAL = 4;  // Compiler terminated abnormally
   341     int run(String[] args) {
   342         try {
   343             handleOptions(args);
   345             // the following gives consistent behavior with javac
   346             if (classes == null || classes.size() == 0) {
   347                 if (options.help || options.version || options.fullVersion)
   348                     return EXIT_OK;
   349                 else
   350                     return EXIT_CMDERR;
   351             }
   353             boolean ok = run();
   354             return ok ? EXIT_OK : EXIT_ERROR;
   355         } catch (BadArgs e) {
   356             diagnosticListener.report(createDiagnostic(e.key, e.args));
   357             if (e.showUsage) {
   358                 log.println(getMessage("main.usage.summary", progname));
   359             }
   360             return EXIT_CMDERR;
   361         } catch (InternalError e) {
   362             Object[] e_args;
   363             if (e.getCause() == null)
   364                 e_args = e.args;
   365             else {
   366                 e_args = new Object[e.args.length + 1];
   367                 e_args[0] = e.getCause();
   368                 System.arraycopy(e.args, 0, e_args, 1, e.args.length);
   369             }
   370             diagnosticListener.report(createDiagnostic("err.internal.error", e_args));
   371             return EXIT_ABNORMAL;
   372         } finally {
   373             log.flush();
   374         }
   375     }
   377     public void handleOptions(String[] args) throws BadArgs {
   378         handleOptions(Arrays.asList(args), true);
   379     }
   381     private void handleOptions(Iterable<String> args, boolean allowClasses) throws BadArgs {
   382         if (log == null) {
   383             log = getPrintWriterForStream(System.out);
   384             if (diagnosticListener == null)
   385               diagnosticListener = getDiagnosticListenerForStream(System.err);
   386         } else {
   387             if (diagnosticListener == null)
   388               diagnosticListener = getDiagnosticListenerForWriter(log);
   389         }
   392         if (fileManager == null)
   393             fileManager = getDefaultFileManager(diagnosticListener, log);
   395         Iterator<String> iter = args.iterator();
   396         boolean noArgs = !iter.hasNext();
   398         while (iter.hasNext()) {
   399             String arg = iter.next();
   400             if (arg.startsWith("-"))
   401                 handleOption(arg, iter);
   402             else if (allowClasses) {
   403                 if (classes == null)
   404                     classes = new ArrayList<String>();
   405                 classes.add(arg);
   406                 while (iter.hasNext())
   407                     classes.add(iter.next());
   408             } else
   409                 throw new BadArgs("err.unknown.option", arg).showUsage(true);
   410         }
   412         if (!options.compat && options.accessOptions.size() > 1) {
   413             StringBuilder sb = new StringBuilder();
   414             for (String opt: options.accessOptions) {
   415                 if (sb.length() > 0)
   416                     sb.append(" ");
   417                 sb.append(opt);
   418             }
   419             throw new BadArgs("err.incompatible.options", sb);
   420         }
   422         if (options.ignoreSymbolFile && fileManager instanceof JavapFileManager)
   423             ((JavapFileManager) fileManager).setIgnoreSymbolFile(true);
   425         if ((classes == null || classes.size() == 0) &&
   426                 !(noArgs || options.help || options.version || options.fullVersion)) {
   427             throw new BadArgs("err.no.classes.specified");
   428         }
   430         if (noArgs || options.help)
   431             showHelp();
   433         if (options.version || options.fullVersion)
   434             showVersion(options.fullVersion);
   435     }
   437     private void handleOption(String name, Iterator<String> rest) throws BadArgs {
   438         for (Option o: recognizedOptions) {
   439             if (o.matches(name)) {
   440                 if (o.hasArg) {
   441                     if (rest.hasNext())
   442                         o.process(this, name, rest.next());
   443                     else
   444                         throw new BadArgs("err.missing.arg", name).showUsage(true);
   445                 } else
   446                     o.process(this, name, null);
   448                 if (o.ignoreRest()) {
   449                     while (rest.hasNext())
   450                         rest.next();
   451                 }
   452                 return;
   453             }
   454         }
   456         if (fileManager.handleOption(name, rest))
   457             return;
   459         throw new BadArgs("err.unknown.option", name).showUsage(true);
   460     }
   462     public Boolean call() {
   463         return run();
   464     }
   466     public boolean run() {
   467         if (classes == null || classes.size() == 0)
   468             return false;
   470         context.put(PrintWriter.class, log);
   471         ClassWriter classWriter = ClassWriter.instance(context);
   473         boolean ok = true;
   475         for (String className: classes) {
   476             JavaFileObject fo;
   477             try {
   478                 if (className.endsWith(".class")) {
   479                     if (fileManager instanceof StandardJavaFileManager) {
   480                         StandardJavaFileManager sfm = (StandardJavaFileManager) fileManager;
   481                         fo = sfm.getJavaFileObjects(className).iterator().next();
   482                     } else {
   483                        diagnosticListener.report(createDiagnostic("err.not.standard.file.manager", className));
   484                        ok = false;
   485                        continue;
   486                     }
   487                 } else {
   488                     fo = getClassFileObject(className);
   489                     if (fo == null) {
   490                         // see if it is an inner class, by replacing dots to $, starting from the right
   491                         String cn = className;
   492                         int lastDot;
   493                         while (fo == null && (lastDot = cn.lastIndexOf(".")) != -1) {
   494                             cn = cn.substring(0, lastDot) + "$" + cn.substring(lastDot + 1);
   495                             fo = getClassFileObject(cn);
   496                         }
   497                     }
   498                     if (fo == null) {
   499                        diagnosticListener.report(createDiagnostic("err.class.not.found", className));
   500                        ok = false;
   501                        continue;
   502                     }
   503                 }
   504                 Attribute.Factory attributeFactory = new Attribute.Factory();
   505                 attributeFactory.setCompat(options.compat);
   506                 attributeFactory.setJSR277(options.jsr277);
   508                 InputStream in = fo.openInputStream();
   509                 SizeInputStream sizeIn = null;
   510                 MessageDigest md  = null;
   511                 if (options.sysInfo || options.verbose) {
   512                     md = MessageDigest.getInstance("MD5");
   513                     in = new DigestInputStream(in, md);
   514                     in = sizeIn = new SizeInputStream(in);
   515                 }
   517                 ClassFile cf = ClassFile.read(in, attributeFactory);
   519                 if (options.sysInfo || options.verbose) {
   520                     classWriter.setFile(fo.toUri());
   521                     classWriter.setLastModified(fo.getLastModified());
   522                     classWriter.setDigest("MD5", md.digest());
   523                     classWriter.setFileSize(sizeIn.size());
   524                 }
   526                 classWriter.write(cf);
   528             } catch (ConstantPoolException e) {
   529                 diagnosticListener.report(createDiagnostic("err.bad.constant.pool", className, e.getLocalizedMessage()));
   530                 ok = false;
   531             } catch (EOFException e) {
   532                 diagnosticListener.report(createDiagnostic("err.end.of.file", className));
   533                 ok = false;
   534             } catch (FileNotFoundException e) {
   535                 diagnosticListener.report(createDiagnostic("err.file.not.found", e.getLocalizedMessage()));
   536                 ok = false;
   537             } catch (IOException e) {
   538                 //e.printStackTrace();
   539                 Object msg = e.getLocalizedMessage();
   540                 if (msg == null)
   541                     msg = e;
   542                 diagnosticListener.report(createDiagnostic("err.ioerror", className, msg));
   543                 ok = false;
   544             } catch (Throwable t) {
   545                 StringWriter sw = new StringWriter();
   546                 PrintWriter pw = new PrintWriter(sw);
   547                 t.printStackTrace(pw);
   548                 pw.close();
   549                 diagnosticListener.report(createDiagnostic("err.crash", t.toString(), sw.toString()));
   550                 ok = false;
   551             }
   552         }
   554         return ok;
   555     }
   557     private JavaFileManager getDefaultFileManager(final DiagnosticListener<? super JavaFileObject> dl, PrintWriter log) {
   558         return JavapFileManager.create(dl, log, options);
   559     }
   561     private JavaFileObject getClassFileObject(String className) throws IOException {
   562         JavaFileObject fo;
   563         fo = fileManager.getJavaFileForInput(StandardLocation.PLATFORM_CLASS_PATH, className, JavaFileObject.Kind.CLASS);
   564         if (fo == null)
   565             fo = fileManager.getJavaFileForInput(StandardLocation.CLASS_PATH, className, JavaFileObject.Kind.CLASS);
   566         return fo;
   567     }
   569     private void showHelp() {
   570         log.println(getMessage("main.usage", progname));
   571         for (Option o: recognizedOptions) {
   572             String name = o.aliases[0].substring(1); // there must always be at least one name
   573             if (name.startsWith("X") || name.equals("fullversion") || name.equals("h") || name.equals("verify"))
   574                 continue;
   575             log.println(getMessage("main.opt." + name));
   576         }
   577         String[] fmOptions = { "-classpath", "-bootclasspath" };
   578         for (String o: fmOptions) {
   579             if (fileManager.isSupportedOption(o) == -1)
   580                 continue;
   581             String name = o.substring(1);
   582             log.println(getMessage("main.opt." + name));
   583         }
   585     }
   587     private void showVersion(boolean full) {
   588         log.println(version(full ? "full" : "release"));
   589     }
   591     private static final String versionRBName = "com.sun.tools.javap.resources.version";
   592     private static ResourceBundle versionRB;
   594     private String version(String key) {
   595         // key=version:  mm.nn.oo[-milestone]
   596         // key=full:     mm.mm.oo[-milestone]-build
   597         if (versionRB == null) {
   598             try {
   599                 versionRB = ResourceBundle.getBundle(versionRBName);
   600             } catch (MissingResourceException e) {
   601                 return getMessage("version.resource.missing", System.getProperty("java.version"));
   602             }
   603         }
   604         try {
   605             return versionRB.getString(key);
   606         }
   607         catch (MissingResourceException e) {
   608             return getMessage("version.unknown", System.getProperty("java.version"));
   609         }
   610     }
   612     private Diagnostic<JavaFileObject> createDiagnostic(final String key, final Object... args) {
   613         return new Diagnostic<JavaFileObject>() {
   614             public Kind getKind() {
   615                 return Diagnostic.Kind.ERROR;
   616             }
   618             public JavaFileObject getSource() {
   619                 return null;
   620             }
   622             public long getPosition() {
   623                 return Diagnostic.NOPOS;
   624             }
   626             public long getStartPosition() {
   627                 return Diagnostic.NOPOS;
   628             }
   630             public long getEndPosition() {
   631                 return Diagnostic.NOPOS;
   632             }
   634             public long getLineNumber() {
   635                 return Diagnostic.NOPOS;
   636             }
   638             public long getColumnNumber() {
   639                 return Diagnostic.NOPOS;
   640             }
   642             public String getCode() {
   643                 return key;
   644             }
   646             public String getMessage(Locale locale) {
   647                 return JavapTask.this.getMessage(locale, key, args);
   648             }
   650         };
   652     }
   654     private String getMessage(String key, Object... args) {
   655         return getMessage(task_locale, key, args);
   656     }
   658     private String getMessage(Locale locale, String key, Object... args) {
   659         if (bundles == null) {
   660             // could make this a HashMap<Locale,SoftReference<ResourceBundle>>
   661             // and for efficiency, keep a hard reference to the bundle for the task
   662             // locale
   663             bundles = new HashMap<Locale, ResourceBundle>();
   664         }
   666         if (locale == null)
   667             locale = Locale.getDefault();
   669         ResourceBundle b = bundles.get(locale);
   670         if (b == null) {
   671             try {
   672                 b = ResourceBundle.getBundle("com.sun.tools.javap.resources.javap", locale);
   673                 bundles.put(locale, b);
   674             } catch (MissingResourceException e) {
   675                 throw new InternalError("Cannot find javap resource bundle for locale " + locale);
   676             }
   677         }
   679         try {
   680             return MessageFormat.format(b.getString(key), args);
   681         } catch (MissingResourceException e) {
   682             throw new InternalError(e, key);
   683         }
   684     }
   686     Context context;
   687     JavaFileManager fileManager;
   688     PrintWriter log;
   689     DiagnosticListener<? super JavaFileObject> diagnosticListener;
   690     List<String> classes;
   691     Options options;
   692     //ResourceBundle bundle;
   693     Locale task_locale;
   694     Map<Locale, ResourceBundle> bundles;
   696     private static final String progname = "javap";
   698     private static class SizeInputStream extends FilterInputStream {
   699         SizeInputStream(InputStream in) {
   700             super(in);
   701         }
   703         int size() {
   704             return size;
   705         }
   707         @Override
   708         public int read(byte[] buf, int offset, int length) throws IOException {
   709             int n = super.read(buf, offset, length);
   710             if (n > 0)
   711                 size += n;
   712             return n;
   713         }
   715         @Override
   716         public int read() throws IOException {
   717             int b = super.read();
   718             size += 1;
   719             return b;
   720         }
   722         private int size;
   723     }
   724 }

mercurial