src/share/classes/com/sun/tools/javac/main/Main.java

Mon, 19 Oct 2009 13:38:09 -0700

author
jjg
date
Mon, 19 Oct 2009 13:38:09 -0700
changeset 428
2485f5641ed0
parent 267
e2722bd43f3a
child 535
96072ad00783
permissions
-rw-r--r--

6889255: javac MethodSymbol throws NPE if ClassReader does not read parameter names correctly
Reviewed-by: darcy

     1 /*
     2  * Copyright 1999-2009 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.javac.main;
    28 import java.io.File;
    29 import java.io.IOException;
    30 import java.io.PrintWriter;
    31 import java.util.MissingResourceException;
    33 import com.sun.tools.javac.code.Source;
    34 import com.sun.tools.javac.file.CacheFSInfo;
    35 import com.sun.tools.javac.file.JavacFileManager;
    36 import com.sun.tools.javac.jvm.Target;
    37 import com.sun.tools.javac.main.JavacOption.Option;
    38 import com.sun.tools.javac.main.RecognizedOptions.OptionHelper;
    39 import com.sun.tools.javac.util.*;
    40 import com.sun.tools.javac.processing.AnnotationProcessingError;
    41 import javax.tools.JavaFileManager;
    42 import javax.tools.JavaFileObject;
    43 import javax.annotation.processing.Processor;
    45 /** This class provides a commandline interface to the GJC compiler.
    46  *
    47  *  <p><b>This is NOT part of any API supported by Sun Microsystems.  If
    48  *  you write code that depends on this, you do so at your own risk.
    49  *  This code and its internal interfaces are subject to change or
    50  *  deletion without notice.</b>
    51  */
    52 public class Main {
    54     /** The name of the compiler, for use in diagnostics.
    55      */
    56     String ownName;
    58     /** The writer to use for diagnostic output.
    59      */
    60     PrintWriter out;
    62     /**
    63      * If true, any command line arg errors will cause an exception.
    64      */
    65     boolean fatalErrors;
    67     /** Result codes.
    68      */
    69     static final int
    70         EXIT_OK = 0,        // Compilation completed with no errors.
    71         EXIT_ERROR = 1,     // Completed but reported errors.
    72         EXIT_CMDERR = 2,    // Bad command-line arguments
    73         EXIT_SYSERR = 3,    // System error or resource exhaustion.
    74         EXIT_ABNORMAL = 4;  // Compiler terminated abnormally
    76     private Option[] recognizedOptions = RecognizedOptions.getJavaCompilerOptions(new OptionHelper() {
    78         public void setOut(PrintWriter out) {
    79             Main.this.out = out;
    80         }
    82         public void error(String key, Object... args) {
    83             Main.this.error(key, args);
    84         }
    86         public void printVersion() {
    87             Log.printLines(out, getLocalizedString("version", ownName,  JavaCompiler.version()));
    88         }
    90         public void printFullVersion() {
    91             Log.printLines(out, getLocalizedString("fullVersion", ownName,  JavaCompiler.fullVersion()));
    92         }
    94         public void printHelp() {
    95             help();
    96         }
    98         public void printXhelp() {
    99             xhelp();
   100         }
   102         public void addFile(File f) {
   103             if (!filenames.contains(f))
   104                 filenames.append(f);
   105         }
   107         public void addClassName(String s) {
   108             classnames.append(s);
   109         }
   111     });
   113     /**
   114      * Construct a compiler instance.
   115      */
   116     public Main(String name) {
   117         this(name, new PrintWriter(System.err, true));
   118     }
   120     /**
   121      * Construct a compiler instance.
   122      */
   123     public Main(String name, PrintWriter out) {
   124         this.ownName = name;
   125         this.out = out;
   126     }
   127     /** A table of all options that's passed to the JavaCompiler constructor.  */
   128     private Options options = null;
   130     /** The list of source files to process
   131      */
   132     public ListBuffer<File> filenames = null; // XXX sb protected
   134     /** List of class files names passed on the command line
   135      */
   136     public ListBuffer<String> classnames = null; // XXX sb protected
   138     /** Print a string that explains usage.
   139      */
   140     void help() {
   141         Log.printLines(out, getLocalizedString("msg.usage.header", ownName));
   142         for (int i=0; i<recognizedOptions.length; i++) {
   143             recognizedOptions[i].help(out);
   144         }
   145         out.println();
   146     }
   148     /** Print a string that explains usage for X options.
   149      */
   150     void xhelp() {
   151         for (int i=0; i<recognizedOptions.length; i++) {
   152             recognizedOptions[i].xhelp(out);
   153         }
   154         out.println();
   155         Log.printLines(out, getLocalizedString("msg.usage.nonstandard.footer"));
   156     }
   158     /** Report a usage error.
   159      */
   160     void error(String key, Object... args) {
   161         if (fatalErrors) {
   162             String msg = getLocalizedString(key, args);
   163             throw new PropagatedException(new IllegalStateException(msg));
   164         }
   165         warning(key, args);
   166         Log.printLines(out, getLocalizedString("msg.usage", ownName));
   167     }
   169     /** Report a warning.
   170      */
   171     void warning(String key, Object... args) {
   172         Log.printLines(out, ownName + ": "
   173                        + getLocalizedString(key, args));
   174     }
   176     public Option getOption(String flag) {
   177         for (Option option : recognizedOptions) {
   178             if (option.matches(flag))
   179                 return option;
   180         }
   181         return null;
   182     }
   184     public void setOptions(Options options) {
   185         if (options == null)
   186             throw new NullPointerException();
   187         this.options = options;
   188     }
   190     public void setFatalErrors(boolean fatalErrors) {
   191         this.fatalErrors = fatalErrors;
   192     }
   194     /** Process command line arguments: store all command line options
   195      *  in `options' table and return all source filenames.
   196      *  @param flags    The array of command line arguments.
   197      */
   198     public List<File> processArgs(String[] flags) { // XXX sb protected
   199         int ac = 0;
   200         while (ac < flags.length) {
   201             String flag = flags[ac];
   202             ac++;
   204             Option option = null;
   206             if (flag.length() > 0) {
   207                 // quick hack to speed up file processing:
   208                 // if the option does not begin with '-', there is no need to check
   209                 // most of the compiler options.
   210                 int firstOptionToCheck = flag.charAt(0) == '-' ? 0 : recognizedOptions.length-1;
   211                 for (int j=firstOptionToCheck; j<recognizedOptions.length; j++) {
   212                     if (recognizedOptions[j].matches(flag)) {
   213                         option = recognizedOptions[j];
   214                         break;
   215                     }
   216                 }
   217             }
   219             if (option == null) {
   220                 error("err.invalid.flag", flag);
   221                 return null;
   222             }
   224             if (option.hasArg()) {
   225                 if (ac == flags.length) {
   226                     error("err.req.arg", flag);
   227                     return null;
   228                 }
   229                 String operand = flags[ac];
   230                 ac++;
   231                 if (option.process(options, flag, operand))
   232                     return null;
   233             } else {
   234                 if (option.process(options, flag))
   235                     return null;
   236             }
   237         }
   239         if (!checkDirectory("-d"))
   240             return null;
   241         if (!checkDirectory("-s"))
   242             return null;
   244         String sourceString = options.get("-source");
   245         Source source = (sourceString != null)
   246             ? Source.lookup(sourceString)
   247             : Source.DEFAULT;
   248         String targetString = options.get("-target");
   249         Target target = (targetString != null)
   250             ? Target.lookup(targetString)
   251             : Target.DEFAULT;
   252         // We don't check source/target consistency for CLDC, as J2ME
   253         // profiles are not aligned with J2SE targets; moreover, a
   254         // single CLDC target may have many profiles.  In addition,
   255         // this is needed for the continued functioning of the JSR14
   256         // prototype.
   257         if (Character.isDigit(target.name.charAt(0))) {
   258             if (target.compareTo(source.requiredTarget()) < 0) {
   259                 if (targetString != null) {
   260                     if (sourceString == null) {
   261                         warning("warn.target.default.source.conflict",
   262                                 targetString,
   263                                 source.requiredTarget().name);
   264                     } else {
   265                         warning("warn.source.target.conflict",
   266                                 sourceString,
   267                                 source.requiredTarget().name);
   268                     }
   269                     return null;
   270                 } else {
   271                     target = source.requiredTarget();
   272                     options.put("-target", target.name);
   273                 }
   274             } else {
   275                 if (targetString == null && !source.allowGenerics()) {
   276                     target = Target.JDK1_4;
   277                     options.put("-target", target.name);
   278                 }
   279             }
   280         }
   281         if (target.hasInvokedynamic()) {
   282             options.put("invokedynamic",  "invokedynamic");
   283         }
   284         return filenames.toList();
   285     }
   286     // where
   287         private boolean checkDirectory(String optName) {
   288             String value = options.get(optName);
   289             if (value == null)
   290                 return true;
   291             File file = new File(value);
   292             if (!file.exists()) {
   293                 error("err.dir.not.found", value);
   294                 return false;
   295             }
   296             if (!file.isDirectory()) {
   297                 error("err.file.not.directory", value);
   298                 return false;
   299             }
   300             return true;
   301         }
   303     /** Programmatic interface for main function.
   304      * @param args    The command line parameters.
   305      */
   306     public int compile(String[] args) {
   307         Context context = new Context();
   308         JavacFileManager.preRegister(context); // can't create it until Log has been set up
   309         int result = compile(args, context);
   310         if (fileManager instanceof JavacFileManager) {
   311             // A fresh context was created above, so jfm must be a JavacFileManager
   312             ((JavacFileManager)fileManager).close();
   313         }
   314         return result;
   315     }
   317     public int compile(String[] args, Context context) {
   318         return compile(args, context, List.<JavaFileObject>nil(), null);
   319     }
   321     /** Programmatic interface for main function.
   322      * @param args    The command line parameters.
   323      */
   324     public int compile(String[] args,
   325                        Context context,
   326                        List<JavaFileObject> fileObjects,
   327                        Iterable<? extends Processor> processors)
   328     {
   329         if (options == null)
   330             options = Options.instance(context); // creates a new one
   332         filenames = new ListBuffer<File>();
   333         classnames = new ListBuffer<String>();
   334         JavaCompiler comp = null;
   335         /*
   336          * TODO: Logic below about what is an acceptable command line
   337          * should be updated to take annotation processing semantics
   338          * into account.
   339          */
   340         try {
   341             if (args.length == 0 && fileObjects.isEmpty()) {
   342                 help();
   343                 return EXIT_CMDERR;
   344             }
   346             List<File> files;
   347             try {
   348                 files = processArgs(CommandLine.parse(args));
   349                 if (files == null) {
   350                     // null signals an error in options, abort
   351                     return EXIT_CMDERR;
   352                 } else if (files.isEmpty() && fileObjects.isEmpty() && classnames.isEmpty()) {
   353                     // it is allowed to compile nothing if just asking for help or version info
   354                     if (options.get("-help") != null
   355                         || options.get("-X") != null
   356                         || options.get("-version") != null
   357                         || options.get("-fullversion") != null)
   358                         return EXIT_OK;
   359                     error("err.no.source.files");
   360                     return EXIT_CMDERR;
   361                 }
   362             } catch (java.io.FileNotFoundException e) {
   363                 Log.printLines(out, ownName + ": " +
   364                                getLocalizedString("err.file.not.found",
   365                                                   e.getMessage()));
   366                 return EXIT_SYSERR;
   367             }
   369             boolean forceStdOut = options.get("stdout") != null;
   370             if (forceStdOut) {
   371                 out.flush();
   372                 out = new PrintWriter(System.out, true);
   373             }
   375             context.put(Log.outKey, out);
   377             // allow System property in following line as a Mustang legacy
   378             boolean batchMode = (options.get("nonBatchMode") == null
   379                         && System.getProperty("nonBatchMode") == null);
   380             if (batchMode)
   381                 CacheFSInfo.preRegister(context);
   383             fileManager = context.get(JavaFileManager.class);
   385             comp = JavaCompiler.instance(context);
   386             if (comp == null) return EXIT_SYSERR;
   388             Log log = Log.instance(context);
   390             if (!files.isEmpty()) {
   391                 // add filenames to fileObjects
   392                 comp = JavaCompiler.instance(context);
   393                 List<JavaFileObject> otherFiles = List.nil();
   394                 JavacFileManager dfm = (JavacFileManager)fileManager;
   395                 for (JavaFileObject fo : dfm.getJavaFileObjectsFromFiles(files))
   396                     otherFiles = otherFiles.prepend(fo);
   397                 for (JavaFileObject fo : otherFiles)
   398                     fileObjects = fileObjects.prepend(fo);
   399             }
   400             comp.compile(fileObjects,
   401                          classnames.toList(),
   402                          processors);
   404             if (log.expectDiagKeys != null) {
   405                 if (log.expectDiagKeys.size() == 0) {
   406                     Log.printLines(log.noticeWriter, "all expected diagnostics found");
   407                     return EXIT_OK;
   408                 } else {
   409                     Log.printLines(log.noticeWriter, "expected diagnostic keys not found: " + log.expectDiagKeys);
   410                     return EXIT_ERROR;
   411                 }
   412             }
   414             if (comp.errorCount() != 0)
   415                 return EXIT_ERROR;
   416         } catch (IOException ex) {
   417             ioMessage(ex);
   418             return EXIT_SYSERR;
   419         } catch (OutOfMemoryError ex) {
   420             resourceMessage(ex);
   421             return EXIT_SYSERR;
   422         } catch (StackOverflowError ex) {
   423             resourceMessage(ex);
   424             return EXIT_SYSERR;
   425         } catch (FatalError ex) {
   426             feMessage(ex);
   427             return EXIT_SYSERR;
   428         } catch(AnnotationProcessingError ex) {
   429             apMessage(ex);
   430             return EXIT_SYSERR;
   431         } catch (ClientCodeException ex) {
   432             // as specified by javax.tools.JavaCompiler#getTask
   433             // and javax.tools.JavaCompiler.CompilationTask#call
   434             throw new RuntimeException(ex.getCause());
   435         } catch (PropagatedException ex) {
   436             throw ex.getCause();
   437         } catch (Throwable ex) {
   438             // Nasty.  If we've already reported an error, compensate
   439             // for buggy compiler error recovery by swallowing thrown
   440             // exceptions.
   441             if (comp == null || comp.errorCount() == 0 ||
   442                 options == null || options.get("dev") != null)
   443                 bugMessage(ex);
   444             return EXIT_ABNORMAL;
   445         } finally {
   446             if (comp != null) comp.close();
   447             filenames = null;
   448             options = null;
   449         }
   450         return EXIT_OK;
   451     }
   453     /** Print a message reporting an internal error.
   454      */
   455     void bugMessage(Throwable ex) {
   456         Log.printLines(out, getLocalizedString("msg.bug",
   457                                                JavaCompiler.version()));
   458         ex.printStackTrace(out);
   459     }
   461     /** Print a message reporting an fatal error.
   462      */
   463     void feMessage(Throwable ex) {
   464         Log.printLines(out, ex.getMessage());
   465     }
   467     /** Print a message reporting an input/output error.
   468      */
   469     void ioMessage(Throwable ex) {
   470         Log.printLines(out, getLocalizedString("msg.io"));
   471         ex.printStackTrace(out);
   472     }
   474     /** Print a message reporting an out-of-resources error.
   475      */
   476     void resourceMessage(Throwable ex) {
   477         Log.printLines(out, getLocalizedString("msg.resource"));
   478 //      System.out.println("(name buffer len = " + Name.names.length + " " + Name.nc);//DEBUG
   479         ex.printStackTrace(out);
   480     }
   482     /** Print a message reporting an uncaught exception from an
   483      * annotation processor.
   484      */
   485     void apMessage(AnnotationProcessingError ex) {
   486         Log.printLines(out,
   487                        getLocalizedString("msg.proc.annotation.uncaught.exception"));
   488         ex.getCause().printStackTrace();
   489     }
   491     private JavaFileManager fileManager;
   493     /* ************************************************************************
   494      * Internationalization
   495      *************************************************************************/
   497     /** Find a localized string in the resource bundle.
   498      *  @param key     The key for the localized string.
   499      */
   500     public static String getLocalizedString(String key, Object... args) { // FIXME sb private
   501         try {
   502             if (messages == null)
   503                 messages = new JavacMessages(javacBundleName);
   504             return messages.getLocalizedString("javac." + key, args);
   505         }
   506         catch (MissingResourceException e) {
   507             throw new Error("Fatal Error: Resource for javac is missing", e);
   508         }
   509     }
   511     public static void useRawMessages(boolean enable) {
   512         if (enable) {
   513             messages = new JavacMessages(javacBundleName) {
   514                     public String getLocalizedString(String key, Object... args) {
   515                         return key;
   516                     }
   517                 };
   518         } else {
   519             messages = new JavacMessages(javacBundleName);
   520         }
   521     }
   523     private static final String javacBundleName =
   524         "com.sun.tools.javac.resources.javac";
   526     private static JavacMessages messages;
   527 }

mercurial