001    /* Copyright (c) 2005-2008, Torbjorn Ekman
002     *                    2013, Jesper Öqvist <jesper.oqvist@cs.lth.se>
003     * All rights reserved.
004     *
005     * Redistribution and use in source and binary forms, with or without
006     * modification, are permitted provided that the following conditions are met:
007     *
008     * 1. Redistributions of source code must retain the above copyright notice,
009     * this list of conditions and the following disclaimer.
010     *
011     * 2. Redistributions in binary form must reproduce the above copyright notice,
012     * this list of conditions and the following disclaimer in the documentation
013     * and/or other materials provided with the distribution.
014     *
015     * 3. Neither the name of the copyright holder nor the names of its
016     * contributors may be used to endorse or promote products derived from this
017     * software without specific prior written permission.
018     *
019     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
020     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
021     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
022     * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
023     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
024     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
025     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
026     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
027     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
028     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
029     * POSSIBILITY OF SUCH DAMAGE.
030     */
031    
032    import org.jastadd.util.*;
033    
034    import java.util.Collections;
035    
036    aspect FrontendMain {
037      public long Program.javaParseTime;
038      public long Program.bytecodeParseTime;
039      public long Program.codeGenTime;
040      public long Program.errorCheckTime;
041      public int Program.numJavaFiles;
042      public int Program.numClassFiles;
043    
044      /**
045       * Reset the profile statistics.
046       */
047      public void Program.resetStatistics() {
048        javaParseTime = 0;
049        bytecodeParseTime = 0;
050        codeGenTime = 0;
051        errorCheckTime = 0;
052        numJavaFiles = 0;
053        numClassFiles = 0;
054      }
055    
056      public void Program.printStatistics(PrintStream out) {
057        out.println("javaParseTime: " + javaParseTime);
058        out.println("numJavaFiles: " + numJavaFiles);
059        out.println("bytecodeParseTime: " + javaParseTime);
060        out.println("numClassFiles: " + numClassFiles);
061        out.println("errorCheckTime: " + errorCheckTime);
062        out.println("codeGenTime: " + codeGenTime);
063      }
064    
065      /**
066       * Abstract Java compiler frontend.
067       */
068      abstract public class Frontend {
069        protected Program program;
070    
071        /**
072         * Compile success
073         */
074        public static final int EXIT_SUCCESS = 0;
075    
076        /**
077         * Lexical/semantic error.
078         */
079        public static final int EXIT_ERROR = 1;
080    
081        /**
082         * Command-line configuration error.
083         */
084        public static final int EXIT_CONFIG_ERROR = 2;
085    
086        /**
087         * The compiler terminated by system error.
088         */
089        public static final int EXIT_SYSTEM_ERROR = 3;
090    
091        /**
092         * The compiler terminated abnormally.
093         */
094        public static final int EXIT_UNHANDLED_ERROR = 4;
095    
096        private final String name;
097        private final String version;
098    
099        /**
100         * Initialize the program object.
101         */
102        protected Frontend() {
103          this("Unknown", "0");
104        }
105    
106        /**
107         * Initialize the program object and set compiler name and version.
108         * @param name compiler name
109         * @param version compiler version
110         */
111        protected Frontend(String name, String version) {
112          this.name = name;
113          this.version = version;
114          program = new Program();
115          program.state().reset();
116        }
117    
118        /**
119         * Process all compilation units listed in the command-line arguments, and
120         * all compilation units referenced from those.
121         *
122         * @return 0 on success, 1 on error, 2 on configuration error, 3 on system
123         * error, 4 on unhandled error
124         */
125        public int run(String[] args, BytecodeReader reader, JavaParser parser) {
126    
127          program.resetStatistics();
128          program.initBytecodeReader(reader);
129          program.initJavaParser(parser);
130    
131          initOptions();
132          int argResult = processArgs(args);
133          if (argResult != 0) {
134            return argResult;
135          }
136    
137          Collection<String> files = program.options().files();
138    
139          if (program.options().hasOption("-version")) {
140            printVersion();
141            return EXIT_SUCCESS;
142          }
143    
144          if (program.options().hasOption("-help") || files.isEmpty()) {
145            printUsage();
146            return EXIT_SUCCESS;
147          }
148    
149          Collection<CompilationUnit> work = new LinkedList<CompilationUnit>();
150    
151          try {
152            for (String file: files) {
153              program.addSourceFile(file);
154            }
155    
156            int compileResult = EXIT_SUCCESS;
157    
158            // process source compilation units
159            Iterator<CompilationUnit> iter = program.compilationUnitIterator();
160            while (iter.hasNext()) {
161              CompilationUnit unit = iter.next();
162              work.add(unit);
163              int result = processCompilationUnit(unit);
164              switch (result) {
165                case EXIT_SUCCESS:
166                  break;
167                case EXIT_UNHANDLED_ERROR:
168                  return result;
169                default:
170                  compileResult = result;
171              }
172            }
173    
174            // Process library compilation units.
175            RobustMap<String, CompilationUnit> valueMap = (RobustMap<String, CompilationUnit>)
176                program.getLibCompilationUnitValueMap();
177            if (valueMap != null) {
178              iter = valueMap.robustValueIterator();
179              while (iter.hasNext()) {
180                CompilationUnit unit = iter.next();
181                work.add(unit);
182                int result = processCompilationUnit(unit);
183                switch (result) {
184                  case EXIT_SUCCESS:
185                    break;
186                  case EXIT_UNHANDLED_ERROR:
187                    return result;
188                  default:
189                    compileResult = result;
190                }
191              }
192            }
193    
194            if (compileResult != EXIT_SUCCESS) {
195              return compileResult;
196            }
197    
198            for (CompilationUnit unit: work) {
199              if (unit != null && unit.fromSource()) {
200                long start = System.nanoTime();
201                processNoErrors(unit);
202                program.codeGenTime += System.nanoTime() - start;
203              }
204            }
205    
206          } catch (AbstractClassfileParser.ClassfileFormatError e) {
207            System.err.println(e.getMessage());
208            return EXIT_UNHANDLED_ERROR;
209          } catch (Throwable t) {
210            System.err.println("Fatal exception:");
211            t.printStackTrace(System.err);
212            return EXIT_UNHANDLED_ERROR;
213          } finally {
214            if (program.options().hasOption("-profile")) {
215              program.printStatistics(System.out);
216            }
217          }
218          return EXIT_SUCCESS;
219        }
220    
221        private Collection<Problem> EMPTY_PROBLEM_LIST = Collections.emptyList();
222    
223        /**
224         * Processes from-source compilation units by error-checking them.
225         * This method only report semantic errors and warnings.
226         *
227         * @return zero on success, non-zero on error
228         */
229        protected int processCompilationUnit(CompilationUnit unit) throws Error {
230          if (unit != null && unit.fromSource()) {
231            try {
232              Collection<Problem> errors = unit.parseErrors();
233              Collection<Problem> warnings = EMPTY_PROBLEM_LIST;
234              // Compute static semantic errors when there are no parse errors
235              // or the recover from parse errors option is specified.
236              if (errors.isEmpty() || program.options().hasOption("-recover")) {
237                long start = System.nanoTime();
238                unit.collectErrors();
239                errors = unit.errors();
240                warnings = unit.warnings();
241                program.errorCheckTime += System.nanoTime() - start;
242              }
243              if (!errors.isEmpty()) {
244                processErrors(errors, unit);
245                return EXIT_ERROR;
246              } else {
247                if (!warnings.isEmpty() && !program.options().hasOption("-nowarn")) {
248                  processWarnings(warnings, unit);
249                }
250              }
251            } catch (Error e) {
252              System.err.println("Encountered error while processing " + unit.pathName());
253              throw e;
254            }
255          }
256          return EXIT_SUCCESS;
257        }
258    
259        /**
260         * Initialize the command-line options.
261         * Override this method to add your own command-line options.
262         */
263        protected void initOptions() {
264          Options options = program.options();
265          options.initOptions();
266          options.addKeyOption("-version");
267          options.addKeyOption("-print");
268          options.addKeyOption("-g");
269          options.addKeyOption("-g:none");
270          options.addKeyOption("-g:lines,vars,source");
271          options.addKeyOption("-nowarn");
272          options.addKeyOption("-verbose");
273          options.addKeyOption("-deprecation");
274          options.addKeyValueOption("-classpath");
275          options.addKeyValueOption("-cp");
276          options.addKeyValueOption("-sourcepath");
277          options.addKeyValueOption("-bootclasspath");
278          options.addKeyValueOption("-extdirs");
279          options.addKeyValueOption("-d");
280          options.addKeyValueOption("-encoding");
281          options.addKeyValueOption("-source");
282          options.addKeyValueOption("-target");
283          options.addKeyOption("-help");
284          options.addKeyOption("-O");
285          options.addKeyOption("-J-Xmx128M");
286          options.addKeyOption("-recover");
287          options.addKeyOption("-XprettyPrint");
288          options.addKeyOption("-XdumpTree");
289    
290          // non-javac options
291          options.addKeyOption("-profile"); // output profiling information
292          options.addKeyOption("-debug"); // extra debug checks and information
293        }
294    
295        /**
296         * Configure the compiler with command-line arguments.
297         * @return 0 if there were no configuration errors
298         */
299        protected int processArgs(String[] args) {
300          program.options().addOptions(args);
301          boolean error = false;
302          Collection<String> files = program.options().files();
303          for (String file: files) {
304            if (!new File(file).isFile()) {
305              System.err.println("Error: neither a valid option nor a filename: " + file);
306              error = true;
307            }
308          }
309          return error ? EXIT_CONFIG_ERROR : EXIT_SUCCESS;
310        }
311    
312        /**
313         * Print errors for a compilation unit.
314         *
315         * @param errors collection of compile problems
316         * @param unit affected compilation unit
317         */
318        protected void processErrors(Collection<Problem> errors, CompilationUnit unit) {
319          System.err.println("Errors:");
320          for (Iterator iter2 = errors.iterator(); iter2.hasNext(); ) {
321            System.err.println(iter2.next());
322          }
323        }
324    
325        /**
326         * Print the warnings for a compilation unit.
327         *
328         * @param warnings collection of warnings
329         * @param unit affected compilation unit
330         */
331        protected void processWarnings(Collection<Problem> warnings, CompilationUnit unit) {
332          System.err.println("Warnings:");
333          for (Problem warning : warnings) {
334            System.err.println(warning);
335          }
336        }
337    
338        /**
339         * Called for each from-source compilation unit with no errors.
340         */
341        protected void processNoErrors(CompilationUnit unit) {
342        }
343    
344        /**
345         * Echo the command-line usage help to sysout.
346         */
347        protected void printUsage() {
348          System.out.println(name() + " " + version());
349          System.out.println("\n"
350              + "Usage: java " + name() + " <options> <source files>\n"
351              + "  -verbose                  Output messages about what the compiler is doing\n"
352              + "  -classpath <path>         Specify where to find user class files\n"
353              + "  -sourcepath <path>        Specify where to find input source files\n"
354              + "  -bootclasspath <path>     Override location of bootstrap class files\n"
355              + "  -extdirs <dirs>           Override location of installed extensions\n"
356              + "  -d <directory>            Specify where to place generated class files\n"
357              + "  -nowarn                   Disable warning messages\n"
358              + "  -help                     Print a synopsis of standard options\n"
359              + "  -version                  Print version information");
360        }
361    
362        /**
363         * Echo the version to sysout.
364         */
365        protected void printVersion() {
366          System.out.println(name() + " " + version());
367        }
368    
369        /**
370         * @return the name of the compiler
371         */
372        protected String name() {
373          return name;
374        }
375    
376        /**
377         * @return the version of the compiler
378         */
379        protected String version() {
380          return version;
381        }
382      }
383    }