gt:             for (JCCompilationUnit unit : roots) {
context: /*
   * Copyright (c) 1999, 2006, Oracle and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Oracle designates this
   * particular file as subject to the "Classpath" exception as provided
   * by Oracle in the LICENSE file that accompanied this code.
   *
   * This code is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   * version 2 for more details (a copy is included in the LICENSE file that
   * accompanied this code).
   *
   * You should have received a copy of the GNU General Public License version
   * 2 along with this work; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   *
   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  
  package com.sun.tools.javac.main;
  
  import java.io.*;
  import java.util.HashSet;
  import java.util.LinkedHashSet;
  import java.util.LinkedHashMap;
  import java.util.Map;
  import java.util.MissingResourceException;
  import java.util.ResourceBundle;
  import java.util.Set;
  import java.util.logging.Handler;
  import java.util.logging.Level;
  import java.util.logging.Logger;
  
  import javax.tools.JavaFileManager;
  import javax.tools.JavaFileObject;
  import javax.tools.DiagnosticListener;
  
  import com.sun.source.util.TaskEvent;
  import com.sun.source.util.TaskListener;
  
  import com.sun.tools.javac.util.*;
  import com.sun.tools.javac.code.*;
  import com.sun.tools.javac.tree.*;
  import com.sun.tools.javac.parser.*;
  import com.sun.tools.javac.comp.*;
  import com.sun.tools.javac.jvm.*;
  
  import com.sun.tools.javac.code.Symbol.*;
  import com.sun.tools.javac.tree.JCTree.*;
  
  import com.sun.tools.javac.processing.*;
  import javax.annotation.processing.Processor;
  
  import static javax.tools.StandardLocation.CLASS_OUTPUT;
  import static com.sun.tools.javac.util.ListBuffer.lb;
  
  // TEMP, until we have a more efficient way to save doc comment info
  import com.sun.tools.javac.parser.DocCommentScanner;
  
  import java.util.HashMap;
  import java.util.Queue;
  import javax.lang.model.SourceVersion;
  
  /** This class could be the main entry point for GJC when GJC is used as a
   *  component in a larger software system. It provides operations to
   *  construct a new compiler, and to run a new compiler on a set of source
   *  files.
   *
   *  <p><b>This is NOT part of any supported API.
   *  If you write code that depends on this, you do so at your own risk.
   *  This code and its internal interfaces are subject to change or
   *  deletion without notice.</b>
   */
  public class JavaCompiler implements ClassReader.SourceCompleter {
      /** The context key for the compiler. */
      protected static final Context.Key<JavaCompiler> compilerKey =
          new Context.Key<JavaCompiler>();
  
      /** Get the JavaCompiler instance for this context. */
      public static JavaCompiler instance(Context context) {
          JavaCompiler instance = context.get(compilerKey);
          if (instance == null)
              instance = new JavaCompiler(context);
          return instance;
      }
  
      /** The current version number as a string.
       */
      public static String version() {
          return version("release");  // mm.nn.oo[-milestone]
      }
  
      /** The current full version number as a string.
       */
      public static String fullVersion() {
          return version("full"); // mm.mm.oo[-milestone]-build
      }
  
      private static final String versionRBName = "com.sun.tools.javac.resources.version";
      private static ResourceBundle versionRB;
  
      private static String version(String key) {
          if (versionRB == null) {
              try {
                  versionRB = ResourceBundle.getBundle(versionRBName);
              } catch (MissingResourceException e) {
                  return Log.getLocalizedString("version.resource.missing", System.getProperty("java.version"));
              }
          }
          try {
              return versionRB.getString(key);
          }
          catch (MissingResourceException e) {
              return Log.getLocalizedString("version.unknown", System.getProperty("java.version"));
          }
      }
  
      /**
       * Control how the compiler's latter phases (attr, flow, desugar, generate)
       * are connected. Each individual file is processed by each phase in turn,
       * but with different compile policies, you can control the order in which
       * each class is processed through its next phase.
       *
       * <p>Generally speaking, the compiler will "fail fast" in the face of
       * errors, although not aggressively so. flow, desugar, etc become no-ops
       * once any errors have occurred. No attempt is currently made to determine
       * if it might be safe to process a class through its next phase because
       * it does not depend on any unrelated errors that might have occurred.
       */
      protected static enum CompilePolicy {
          /**
           * Just attribute the parse trees.
           */
          ATTR_ONLY,
  
          /**
           * Just attribute and do flow analysis on the parse trees.
           * This should catch most user errors.
           */
          CHECK_ONLY,
  
          /**
           * Attribute everything, then do flow analysis for everything,
           * then desugar everything, and only then generate output.
           * This means no output will be generated if there are any
           * errors in any classes.
           */
          SIMPLE,
  
          /**
           * Groups the classes for each source file together, then process
           * each group in a manner equivalent to the {@code SIMPLE} policy.
           * This means no output will be generated if there are any
           * errors in any of the classes in a source file.
           */
          BY_FILE,
  
          /**
           * Completely process each entry on the todo list in turn.
           * -- this is the same for 1.5.
           * Means output might be generated for some classes in a compilation unit
           * and not others.
           */
          BY_TODO;
  
          static CompilePolicy decode(String option) {
              if (option == null)
                  return DEFAULT_COMPILE_POLICY;
              else if (option.equals("attr"))
                  return ATTR_ONLY;
              else if (option.equals("check"))
                  return CHECK_ONLY;
              else if (option.equals("simple"))
                  return SIMPLE;
              else if (option.equals("byfile"))
                  return BY_FILE;
              else if (option.equals("bytodo"))
                  return BY_TODO;
              else
                  return DEFAULT_COMPILE_POLICY;
          }
      }
  
      private static CompilePolicy DEFAULT_COMPILE_POLICY = CompilePolicy.BY_TODO;
  
      protected static enum ImplicitSourcePolicy {
          /** Don't generate or process implicitly read source files. */
          NONE,
          /** Generate classes for implicitly read source files. */
          CLASS,
          /** Like CLASS, but generate warnings if annotation processing occurs */
          UNSET;
  
          static ImplicitSourcePolicy decode(String option) {
              if (option == null)
                  return UNSET;
              else if (option.equals("none"))
                  return NONE;
              else if (option.equals("class"))
                  return CLASS;
              else
                  return UNSET;
          }
      }
  
      /** The log to be used for error reporting.
       */
      public Log log;
  
      /** The tree factory module.
       */
      protected TreeMaker make;
  
      /** The class reader.
       */
      protected ClassReader reader;
  
      /** The class writer.
       */
      protected ClassWriter writer;
  
      /** The module for the symbol table entry phases.
       */
      protected Enter enter;
  
      /** The symbol table.
       */
      protected Symtab syms;
  
      /** The language version.
       */
      protected Source source;
  
      /** The module for code generation.
       */
      protected Gen gen;
  
      /** The name table.
       */
      protected Name.Table names;
  
      /** The attributor.
       */
      protected Attr attr;
  
      /** The attributor.
       */
      protected Check chk;
  
      /** The flow analyzer.
       */
      protected Flow flow;
  
      /** The type eraser.
       */
      protected TransTypes transTypes;
  
      /** The syntactic sugar desweetener.
       */
      protected Lower lower;
  
      /** The annotation annotator.
       */
      protected Annotate annotate;
  
      /** Force a completion failure on this name
       */
      protected final Name completionFailureName;
  
      /** Type utilities.
       */
      protected Types types;
  
      /** Access to file objects.
       */
      protected JavaFileManager fileManager;
  
      /** Factory for parsers.
       */
      protected Parser.Factory parserFactory;
  
      /** Optional listener for progress events
       */
      protected TaskListener taskListener;
  
      /**
       * Annotation processing may require and provide a new instance
       * of the compiler to be used for the analyze and generate phases.
       */
      protected JavaCompiler delegateCompiler;
  
      /**
       * Flag set if any annotation processing occurred.
       **/
      protected boolean annotationProcessingOccurred;
  
      /**
       * Flag set if any implicit source files read.
       **/
      protected boolean implicitSourceFilesRead;
  
      protected Context context;
  
      /** Construct a new compiler using a shared context.
       */
      public JavaCompiler(final Context context) {
          this.context = context;
          context.put(compilerKey, this);
  
          // if fileManager not already set, register the JavacFileManager to be used
          if (context.get(JavaFileManager.class) == null)
              JavacFileManager.preRegister(context);
  
          names = Name.Table.instance(context);
          log = Log.instance(context);
          reader = ClassReader.instance(context);
          make = TreeMaker.instance(context);
          writer = ClassWriter.instance(context);
          enter = Enter.instance(context);
          todo = Todo.instance(context);
  
          fileManager = context.get(JavaFileManager.class);
          parserFactory = Parser.Factory.instance(context);
  
          try {
              // catch completion problems with predefineds
              syms = Symtab.instance(context);
          } catch (CompletionFailure ex) {
              // inlined Check.completionError as it is not initialized yet
              log.error("cant.access", ex.sym, ex.errmsg);
              if (ex instanceof ClassReader.BadClassFile)
                  throw new Abort();
          }
          source = Source.instance(context);
          attr = Attr.instance(context);
          chk = Check.instance(context);
          gen = Gen.instance(context);
          flow = Flow.instance(context);
          transTypes = TransTypes.instance(context);
          lower = Lower.instance(context);
          annotate = Annotate.instance(context);
          types = Types.instance(context);
          taskListener = context.get(TaskListener.class);
  
          reader.sourceCompleter = this;
  
          Options options = Options.instance(context);
  
          verbose       = options.get("-verbose")       != null;
          sourceOutput  = options.get("-printsource")   != null; // used to be -s
          stubOutput    = options.get("-stubs")         != null;
          relax         = options.get("-relax")         != null;
          printFlat     = options.get("-printflat")     != null;
          attrParseOnly = options.get("-attrparseonly") != null;
          encoding      = options.get("-encoding");
          lineDebugInfo = options.get("-g:")            == null ||
                          options.get("-g:lines")       != null;
          genEndPos     = options.get("-Xjcov")         != null ||
                          context.get(DiagnosticListener.class) != null;
          devVerbose    = options.get("dev") != null;
          processPcks   = options.get("process.packages") != null;
          werror        = options.get("-Werror")        != null;
  
          verboseCompilePolicy = options.get("verboseCompilePolicy") != null;
  
          if (attrParseOnly)
              compilePolicy = CompilePolicy.ATTR_ONLY;
          else
              compilePolicy = CompilePolicy.decode(options.get("compilePolicy"));
  
          implicitSourcePolicy = ImplicitSourcePolicy.decode(options.get("-implicit"));
  
          completionFailureName =
              (options.get("failcomplete") != null)
              ? names.fromString(options.get("failcomplete"))
              : null;
  
          shouldStopPolicy =
              (options.get("shouldStopPolicy") != null)
              ? CompileState.valueOf(options.get("shouldStopPolicy"))
              : null;
      }
  
      /* Switches:
       */
  
      /** Verbose output.
       */
      public boolean verbose;
  
      /** Emit plain Java source files rather than class files.
       */
      public boolean sourceOutput;
  
      /** Emit stub source files rather than class files.
       */
      public boolean stubOutput;
  
      /** Generate attributed parse tree only.
       */
      public boolean attrParseOnly;
  
      /** Switch: relax some constraints for producing the jsr14 prototype.
       */
      boolean relax;
  
      /** Debug switch: Emit Java sources after inner class flattening.
       */
      public boolean printFlat;
  
      /** The encoding to be used for source input.
       */
      public String encoding;
  
      /** Generate code with the LineNumberTable attribute for debugging
       */
      public boolean lineDebugInfo;
  
      /** Switch: should we store the ending positions?
       */
      public boolean genEndPos;
  
      /** Switch: should we debug ignored exceptions
       */
      protected boolean devVerbose;
  
      /** Switch: should we (annotation) process packages as well
       */
      protected boolean processPcks;
  
      /** Switch: treat warnings as errors
       */
      protected boolean werror;
  
      /** Switch: is annotation processing requested explitly via
       * CompilationTask.setProcessors?
       */
      protected boolean explicitAnnotationProcessingRequested = false;
  
      /**
       * The policy for the order in which to perform the compilation
       */
      protected CompilePolicy compilePolicy;
  
      /**
       * The policy for what to do with implicitly read source files
       */
      protected ImplicitSourcePolicy implicitSourcePolicy;
  
      /**
       * Report activity related to compilePolicy
       */
      public boolean verboseCompilePolicy;
  
      /**
       * Policy of how far to continue processing. null means until first
       * error.
       */
      public CompileState shouldStopPolicy;
  
      /** A queue of all as yet unattributed classes.
       */
      public Todo todo;
  
      /** Ordered list of compiler phases for each compilation unit. */
      protected enum CompileState {
          PARSE(1),
          ENTER(2),
          PROCESS(3),
          ATTR(4),
          FLOW(5),
          TRANSTYPES(6),
          LOWER(7),
          GENERATE(8);
          CompileState(int value) {
              this.value = value;
          }
          boolean isDone(CompileState other) {
              return value >= other.value;
          }
          private int value;
      };
      /** Partial map to record which compiler phases have been executed
       * for each compilation unit. Used for ATTR and FLOW phases.
       */
      protected class CompileStates extends HashMap<Env<AttrContext>,CompileState> {
          boolean isDone(Env<AttrContext> env, CompileState cs) {
              CompileState ecs = get(env);
              return ecs != null && ecs.isDone(cs);
          }
      }
      private CompileStates compileStates = new CompileStates();
  
      /** The set of currently compiled inputfiles, needed to ensure
       *  we don't accidentally overwrite an input file when -s is set.
       *  initialized by `compile'.
       */
      protected Set<JavaFileObject> inputFiles = new HashSet<JavaFileObject>();
  
      protected boolean shouldStop(CompileState cs) {
          if (shouldStopPolicy == null)
              return (errorCount() > 0);
          else
              return cs.ordinal() > shouldStopPolicy.ordinal();
      }
  
      /** The number of errors reported so far.
       */
      public int errorCount() {
          if (delegateCompiler != null && delegateCompiler != this)
              return delegateCompiler.errorCount();
          else {
              if (werror && log.nerrors == 0 && log.nwarnings > 0) {
                  log.error("warnings.and.werror");
              }
              return log.nerrors;
          }
      }
  
      protected final <T> Queue<T> stopIfError(CompileState cs, Queue<T> queue) {
          return shouldStop(cs) ? ListBuffer.<T>lb() : queue;
      }
  
      protected final <T> List<T> stopIfError(CompileState cs, List<T> list) {
          return shouldStop(cs) ? List.<T>nil() : list;
      }
  
      /** The number of warnings reported so far.
       */
      public int warningCount() {
          if (delegateCompiler != null && delegateCompiler != this)
              return delegateCompiler.warningCount();
          else
              return log.nwarnings;
      }
  
      protected Scanner.Factory getScannerFactory() {
          return Scanner.Factory.instance(context);
      }
  
      /** Try to open input stream with given name.
       *  Report an error if this fails.
       *  @param filename   The file name of the input stream to be opened.
       */
      public CharSequence readSource(JavaFileObject filename) {
          try {
              inputFiles.add(filename);
              return filename.getCharContent(false);
          } catch (IOException e) {
              log.error("error.reading.file", filename, e.getLocalizedMessage());
              return null;
          }
      }
  
      /** Parse contents of input stream.
       *  @param filename     The name of the file from which input stream comes.
       *  @param input        The input stream to be parsed.
       */
      protected JCCompilationUnit parse(JavaFileObject filename, CharSequence content) {
          long msec = now();
          JCCompilationUnit tree = make.TopLevel(List.<JCTree.JCAnnotation>nil(),
                                        null, List.<JCTree>nil());
          if (content != null) {
              if (verbose) {
                  printVerbose("parsing.started", filename);
              }
              if (taskListener != null) {
                  TaskEvent e = new TaskEvent(TaskEvent.Kind.PARSE, filename);
                  taskListener.started(e);
              }
              int initialErrorCount = log.nerrors;
              Scanner scanner = getScannerFactory().newScanner(content);
              Parser parser = parserFactory.newParser(scanner, keepComments(), genEndPos);
              tree = parser.compilationUnit();
              log.unrecoverableError |= (log.nerrors > initialErrorCount);
              if (lineDebugInfo) {
                  tree.lineMap = scanner.getLineMap();
              }
              if (verbose) {
                  printVerbose("parsing.done", Long.toString(elapsed(msec)));
              }
          }
  
          tree.sourcefile = filename;
  
          if (content != null && taskListener != null) {
              TaskEvent e = new TaskEvent(TaskEvent.Kind.PARSE, tree);
              taskListener.finished(e);
          }
  
          return tree;
      }
      // where
          public boolean keepComments = false;
          protected boolean keepComments() {
              return keepComments || sourceOutput || stubOutput;
          }
  
  
      /** Parse contents of file.
       *  @param filename     The name of the file to be parsed.
       */
      @Deprecated
      public JCTree.JCCompilationUnit parse(String filename) throws IOException {
          JavacFileManager fm = (JavacFileManager)fileManager;
          return parse(fm.getJavaFileObjectsFromStrings(List.of(filename)).iterator().next());
      }
  
      /** Parse contents of file.
       *  @param filename     The name of the file to be parsed.
       */
      public JCTree.JCCompilationUnit parse(JavaFileObject filename) {
          JavaFileObject prev = log.useSource(filename);
          try {
              JCTree.JCCompilationUnit t = parse(filename, readSource(filename));
              if (t.endPositions != null)
                  log.setEndPosTable(filename, t.endPositions);
              return t;
          } finally {
              log.useSource(prev);
          }
      }
  
      /** Resolve an identifier.
       * @param name      The identifier to resolve
       */
      public Symbol resolveIdent(String name) {
          if (name.equals(""))
              return syms.errSymbol;
          JavaFileObject prev = log.useSource(null);
          try {
              JCExpression tree = null;
              for (String s : name.split("\\.", -1)) {
                  if (!SourceVersion.isIdentifier(s)) // TODO: check for keywords
                      return syms.errSymbol;
                  tree = (tree == null) ? make.Ident(names.fromString(s))
                                        : make.Select(tree, names.fromString(s));
              }
              JCCompilationUnit toplevel =
                  make.TopLevel(List.<JCTree.JCAnnotation>nil(), null, List.<JCTree>nil());
              toplevel.packge = syms.unnamedPackage;
              return attr.attribIdent(tree, toplevel);
          } finally {
              log.useSource(prev);
          }
      }
  
      /** Emit plain Java source for a class.
       *  @param env    The attribution environment of the outermost class
       *                containing this class.
       *  @param cdef   The class definition to be printed.
       */
      JavaFileObject printSource(Env<AttrContext> env, JCClassDecl cdef) throws IOException {
          JavaFileObject outFile
              = fileManager.getJavaFileForOutput(CLASS_OUTPUT,
                                                 cdef.sym.flatname.toString(),
                                                 JavaFileObject.Kind.SOURCE,
                                                 null);
          if (inputFiles.contains(outFile)) {
              log.error(cdef.pos(), "source.cant.overwrite.input.file", outFile);
              return null;
          } else {
              BufferedWriter out = new BufferedWriter(outFile.openWriter());
              try {
                  new Pretty(out, true).printUnit(env.toplevel, cdef);
                  if (verbose)
                      printVerbose("wrote.file", outFile);
              } finally {
                  out.close();
              }
              return outFile;
          }
      }
  
      /** Generate code and emit a class file for a given class
       *  @param env    The attribution environment of the outermost class
       *                containing this class.
       *  @param cdef   The class definition from which code is generated.
       */
      JavaFileObject genCode(Env<AttrContext> env, JCClassDecl cdef) throws IOException {
          try {
              if (gen.genClass(env, cdef) && (errorCount() == 0))
                  return writer.writeClass(cdef.sym);
          } catch (ClassWriter.PoolOverflow ex) {
              log.error(cdef.pos(), "limit.pool");
          } catch (ClassWriter.StringOverflow ex) {
              log.error(cdef.pos(), "limit.string.overflow",
                        ex.value.substring(0, 20));
          } catch (CompletionFailure ex) {
              chk.completionError(cdef.pos(), ex);
          }
          return null;
      }
  
      /** Complete compiling a source file that has been accessed
       *  by the class file reader.
       *  @param c          The class the source file of which needs to be compiled.
       *  @param filename   The name of the source file.
       *  @param f          An input stream that reads the source file.
       */
      public void complete(ClassSymbol c) throws CompletionFailure {
  //      System.err.println("completing " + c);//DEBUG
          if (completionFailureName == c.fullname) {
              throw new CompletionFailure(c, "user-selected completion failure by class name");
          }
          JCCompilationUnit tree;
          JavaFileObject filename = c.classfile;
          JavaFileObject prev = log.useSource(filename);
  
          try {
              tree = parse(filename, filename.getCharContent(false));
          } catch (IOException e) {
              log.error("error.reading.file", filename, e);
              tree = make.TopLevel(List.<JCTree.JCAnnotation>nil(), null, List.<JCTree>nil());
          } finally {
              log.useSource(prev);
          }
  
          if (taskListener != null) {
              TaskEvent e = new TaskEvent(TaskEvent.Kind.ENTER, tree);
              taskListener.started(e);
          }
  
          enter.complete(List.of(tree), c);
  
          if (taskListener != null) {
              TaskEvent e = new TaskEvent(TaskEvent.Kind.ENTER, tree);
              taskListener.finished(e);
          }
  
          if (enter.getEnv(c) == null) {
              boolean isPkgInfo =
                  tree.sourcefile.isNameCompatible("package-info",
                                                   JavaFileObject.Kind.SOURCE);
              if (isPkgInfo) {
                  if (enter.getEnv(tree.packge) == null) {
                      String msg
                          = log.getLocalizedString("file.does.not.contain.package",
                                                   c.location());
                      throw new ClassReader.BadClassFile(c, filename, msg);
                  }
              } else {
                  throw new
                      ClassReader.BadClassFile(c, filename, log.
                                               getLocalizedString("file.doesnt.contain.class",
                                                                  c.fullname));
              }
          }
  
          implicitSourceFilesRead = true;
      }
  
      /** Track when the JavaCompiler has been used to compile something. */
      private boolean hasBeenUsed = false;
      private long start_msec = 0;
      public long elapsed_msec = 0;
  
      public void compile(List<JavaFileObject> sourceFileObject)
          throws Throwable {
          compile(sourceFileObject, List.<String>nil(), null);
      }
  
      /**
       * Main method: compile a list of files, return all compiled classes
       *
       * @param sourceFileObjects file objects to be compiled
       * @param classnames class names to process for annotations
       * @param processors user provided annotation processors to bypass
       * discovery, {@code null} means that no processors were provided
       */
      public void compile(List<JavaFileObject> sourceFileObjects,
                          List<String> classnames,
                          Iterable<? extends Processor> processors)
          throws IOException // TODO: temp, from JavacProcessingEnvironment
      {
          if (processors != null && processors.iterator().hasNext())
              explicitAnnotationProcessingRequested = true;
          // as a JavaCompiler can only be used once, throw an exception if
          // it has been used before.
          if (hasBeenUsed)
              throw new AssertionError("attempt to reuse JavaCompiler");
          hasBeenUsed = true;
  
          start_msec = now();
          try {
              initProcessAnnotations(processors);
  
              // These method calls must be chained to avoid memory leaks
              delegateCompiler =
                  processAnnotations(
                      enterTrees(stopIfError(CompileState.PARSE, parseFiles(sourceFileObjects))),
                      classnames);
  
              delegateCompiler.compile2();
              delegateCompiler.close();
              elapsed_msec = delegateCompiler.elapsed_msec;
          } catch (Abort ex) {
              if (devVerbose)
                  ex.printStackTrace();
          } finally {
              if (procEnvImpl != null)
                  procEnvImpl.close();
          }
      }
  
      /**
       * The phases following annotation processing: attribution,
       * desugar, and finally code generation.
       */
      private void compile2() {
          try {
              switch (compilePolicy) {
              case ATTR_ONLY:
                  attribute(todo);
                  break;
  
              case CHECK_ONLY:
                  flow(attribute(todo));
                  break;
  
              case SIMPLE:
                  generate(desugar(flow(attribute(todo))));
                  break;
  
              case BY_FILE: {
                      Queue<Queue<Env<AttrContext>>> q = todo.groupByFile();
                      while (!q.isEmpty() && !shouldStop(CompileState.ATTR)) {
                          generate(desugar(flow(attribute(q.remove()))));
                      }
                  }
                  break;
  
              case BY_TODO:
                  while (!todo.isEmpty())
                      generate(desugar(flow(attribute(todo.remove()))));
                  break;
  
              default:
                  assert false: "unknown compile policy";
              }
          } catch (Abort ex) {
              if (devVerbose)
                  ex.printStackTrace();
          }
  
          if (verbose) {
              elapsed_msec = elapsed(start_msec);
              printVerbose("total", Long.toString(elapsed_msec));
          }
  
          reportDeferredDiagnostics();
  
          if (!log.hasDiagnosticListener()) {
              printCount("error", errorCount());
              printCount("warn", warningCount());
          }
      }
  
      private List<JCClassDecl> rootClasses;
  
      /**
       * Parses a list of files.
       */
     public List<JCCompilationUnit> parseFiles(List<JavaFileObject> fileObjects) throws IOException {
         if (shouldStop(CompileState.PARSE))
             return List.nil();
  
          //parse all files
          ListBuffer<JCCompilationUnit> trees = lb();
          for (JavaFileObject fileObject : fileObjects)
              trees.append(parse(fileObject));
          return trees.toList();
      }
  
      /**
       * Enter the symbols found in a list of parse trees.
       * As a side-effect, this puts elements on the "todo" list.
       * Also stores a list of all top level classes in rootClasses.
       */
      public List<JCCompilationUnit> enterTrees(List<JCCompilationUnit> roots) {
          //enter symbols for all files
          if (taskListener != null) {
              for (JCCompilationUnit unit: roots) {
                  TaskEvent e = new TaskEvent(TaskEvent.Kind.ENTER, unit);
                  taskListener.started(e);
              }
          }
  
          enter.main(roots);
  
          if (taskListener != null) {
              for (JCCompilationUnit unit: roots) {
                  TaskEvent e = new TaskEvent(TaskEvent.Kind.ENTER, unit);
                  taskListener.finished(e);
              }
          }
  
          //If generating source, remember the classes declared in
          //the original compilation units listed on the command line.
          if (sourceOutput || stubOutput) {
              ListBuffer<JCClassDecl> cdefs = lb();
  