diff -r 000000000000 -r 959103a6100f src/share/classes/com/sun/tools/javac/main/JavaCompiler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/com/sun/tools/javac/main/JavaCompiler.java Wed Apr 27 01:34:52 2016 +0800 @@ -0,0 +1,1764 @@ +/* + * Copyright (c) 1999, 2013, 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.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.MissingResourceException; +import java.util.Queue; +import java.util.ResourceBundle; +import java.util.Set; + +import javax.annotation.processing.Processor; +import javax.lang.model.SourceVersion; +import javax.tools.DiagnosticListener; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.StandardLocation; + +import static javax.tools.StandardLocation.CLASS_OUTPUT; + +import com.sun.source.util.TaskEvent; +import com.sun.tools.javac.api.MultiTaskListener; +import com.sun.tools.javac.code.*; +import com.sun.tools.javac.code.Lint.LintCategory; +import com.sun.tools.javac.code.Symbol.*; +import com.sun.tools.javac.comp.*; +import com.sun.tools.javac.comp.CompileStates.CompileState; +import com.sun.tools.javac.file.JavacFileManager; +import com.sun.tools.javac.jvm.*; +import com.sun.tools.javac.parser.*; +import com.sun.tools.javac.processing.*; +import com.sun.tools.javac.tree.*; +import com.sun.tools.javac.tree.JCTree.*; +import com.sun.tools.javac.util.*; +import com.sun.tools.javac.util.Log.WriterKind; + +import static com.sun.tools.javac.code.TypeTag.CLASS; +import static com.sun.tools.javac.main.Option.*; +import static com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag.*; + + +/** 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. + * + *

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. + */ +public class JavaCompiler { + /** The context key for the compiler. */ + protected static final Context.Key compilerKey = + new Context.Key(); + + /** 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.not.available"); + } + } + try { + return versionRB.getString(key); + } + catch (MissingResourceException e) { + return Log.getLocalizedString("version.not.available"); + } + } + + /** + * 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. + * + *

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 final 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; + + /** Factory for creating diagnostic objects + */ + JCDiagnostic.Factory diagFactory; + + /** The tree factory module. + */ + protected TreeMaker make; + + /** The class reader. + */ + protected ClassReader reader; + + /** The class writer. + */ + protected ClassWriter writer; + + /** The native header writer. + */ + protected JNIWriter jniWriter; + + /** 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 Names 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 ParserFactory parserFactory; + + /** Broadcasting listener for progress events + */ + protected MultiTaskListener 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; + + /** + * SourceCompleter that delegates to the complete-method of this class. + */ + protected final ClassReader.SourceCompleter thisCompleter = + new ClassReader.SourceCompleter() { + @Override + public void complete(ClassSymbol sym) throws CompletionFailure { + JavaCompiler.this.complete(sym); + } + }; + + /** + * Command line options. + */ + protected Options options; + + protected Context context; + + /** + * Flag set if any annotation processing occurred. + **/ + protected boolean annotationProcessingOccurred; + + /** + * Flag set if any implicit source files read. + **/ + protected boolean implicitSourceFilesRead; + + protected CompileStates compileStates; + + /** Construct a new compiler using a shared context. + */ + public JavaCompiler(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 = Names.instance(context); + log = Log.instance(context); + diagFactory = JCDiagnostic.Factory.instance(context); + reader = ClassReader.instance(context); + make = TreeMaker.instance(context); + writer = ClassWriter.instance(context); + jniWriter = JNIWriter.instance(context); + enter = Enter.instance(context); + todo = Todo.instance(context); + + fileManager = context.get(JavaFileManager.class); + parserFactory = ParserFactory.instance(context); + compileStates = CompileStates.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.getDetailValue()); + if (ex instanceof ClassReader.BadClassFile) + throw new Abort(); + } + source = Source.instance(context); + Target target = Target.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 = MultiTaskListener.instance(context); + + reader.sourceCompleter = thisCompleter; + + options = Options.instance(context); + + verbose = options.isSet(VERBOSE); + sourceOutput = options.isSet(PRINTSOURCE); // used to be -s + stubOutput = options.isSet("-stubs"); + relax = options.isSet("-relax"); + printFlat = options.isSet("-printflat"); + attrParseOnly = options.isSet("-attrparseonly"); + encoding = options.get(ENCODING); + lineDebugInfo = options.isUnset(G_CUSTOM) || + options.isSet(G_CUSTOM, "lines"); + genEndPos = options.isSet(XJCOV) || + context.get(DiagnosticListener.class) != null; + devVerbose = options.isSet("dev"); + processPcks = options.isSet("process.packages"); + werror = options.isSet(WERROR); + + if (source.compareTo(Source.DEFAULT) < 0) { + if (options.isUnset(XLINT_CUSTOM, "-" + LintCategory.OPTIONS.option)) { + if (fileManager instanceof BaseFileManager) { + if (((BaseFileManager) fileManager).isDefaultBootClassPath()) + log.warning(LintCategory.OPTIONS, "source.no.bootclasspath", source.name); + } + } + } + + checkForObsoleteOptions(target); + + verboseCompilePolicy = options.isSet("verboseCompilePolicy"); + + if (attrParseOnly) + compilePolicy = CompilePolicy.ATTR_ONLY; + else + compilePolicy = CompilePolicy.decode(options.get("compilePolicy")); + + implicitSourcePolicy = ImplicitSourcePolicy.decode(options.get("-implicit")); + + completionFailureName = + options.isSet("failcomplete") + ? names.fromString(options.get("failcomplete")) + : null; + + shouldStopPolicyIfError = + options.isSet("shouldStopPolicy") // backwards compatible + ? CompileState.valueOf(options.get("shouldStopPolicy")) + : options.isSet("shouldStopPolicyIfError") + ? CompileState.valueOf(options.get("shouldStopPolicyIfError")) + : CompileState.INIT; + shouldStopPolicyIfNoError = + options.isSet("shouldStopPolicyIfNoError") + ? CompileState.valueOf(options.get("shouldStopPolicyIfNoError")) + : CompileState.GENERATE; + + if (options.isUnset("oldDiags")) + log.setDiagnosticFormatter(RichDiagnosticFormatter.instance(context)); + } + + private void checkForObsoleteOptions(Target target) { + // Unless lint checking on options is disabled, check for + // obsolete source and target options. + boolean obsoleteOptionFound = false; + if (options.isUnset(XLINT_CUSTOM, "-" + LintCategory.OPTIONS.option)) { + if (source.compareTo(Source.JDK1_5) <= 0) { + log.warning(LintCategory.OPTIONS, "option.obsolete.source", source.name); + obsoleteOptionFound = true; + } + + if (target.compareTo(Target.JDK1_5) <= 0) { + log.warning(LintCategory.OPTIONS, "option.obsolete.target", target.name); + obsoleteOptionFound = true; + } + + if (obsoleteOptionFound) + log.warning(LintCategory.OPTIONS, "option.obsolete.suppression"); + } + } + + /* 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 explicitly 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 compilation after errors have occurred. + * Set this to minimum CompileState (INIT) to stop as soon as possible + * after errors. + */ + public CompileState shouldStopPolicyIfError; + + /** + * Policy of how far to continue compilation when no errors have occurred. + * Set this to maximum CompileState (GENERATE) to perform full compilation. + * Set this lower to perform partial compilation, such as -proc:only. + */ + public CompileState shouldStopPolicyIfNoError; + + /** A queue of all as yet unattributed classes. + */ + public Todo todo; + + /** A list of items to be closed when the compilation is complete. + */ + public List closeables = List.nil(); + + /** 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 inputFiles = new HashSet(); + + protected boolean shouldStop(CompileState cs) { + CompileState shouldStopPolicy = (errorCount() > 0 || unrecoverableError()) + ? shouldStopPolicyIfError + : shouldStopPolicyIfNoError; + return cs.isAfter(shouldStopPolicy); + } + + /** 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 Queue stopIfError(CompileState cs, Queue queue) { + return shouldStop(cs) ? new ListBuffer() : queue; + } + + protected final List stopIfError(CompileState cs, List list) { + return shouldStop(cs) ? List.nil() : list; + } + + /** The number of warnings reported so far. + */ + public int warningCount() { + if (delegateCompiler != null && delegateCompiler != this) + return delegateCompiler.warningCount(); + else + return log.nwarnings; + } + + /** 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, JavacFileManager.getMessage(e)); + return null; + } + } + + /** Parse contents of input stream. + * @param filename The name of the file from which input stream comes. + * @param content The characters to be parsed. + */ + protected JCCompilationUnit parse(JavaFileObject filename, CharSequence content) { + long msec = now(); + JCCompilationUnit tree = make.TopLevel(List.nil(), + null, List.nil()); + if (content != null) { + if (verbose) { + log.printVerbose("parsing.started", filename); + } + if (!taskListener.isEmpty()) { + TaskEvent e = new TaskEvent(TaskEvent.Kind.PARSE, filename); + taskListener.started(e); + keepComments = true; + genEndPos = true; + } + Parser parser = parserFactory.newParser(content, keepComments(), genEndPos, lineDebugInfo); + tree = parser.parseCompilationUnit(); + if (verbose) { + log.printVerbose("parsing.done", Long.toString(elapsed(msec))); + } + } + + tree.sourcefile = filename; + + if (content != null && !taskListener.isEmpty()) { + 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) { + 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 which may be the binary name of a class or + * the Java name of a class or package. + * @param name The name to resolve + */ + public Symbol resolveBinaryNameOrIdent(String name) { + try { + Name flatname = names.fromString(name.replace("/", ".")); + return reader.loadClass(flatname); + } catch (CompletionFailure ignore) { + return resolveIdent(name); + } + } + + /** 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.nil(), null, List.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 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) + log.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 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. + */ + 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, JavacFileManager.getMessage(e)); + tree = make.TopLevel(List.nil(), null, List.nil()); + } finally { + log.useSource(prev); + } + + if (!taskListener.isEmpty()) { + TaskEvent e = new TaskEvent(TaskEvent.Kind.ENTER, tree); + taskListener.started(e); + } + + enter.complete(List.of(tree), c); + + if (!taskListener.isEmpty()) { + 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) { + JCDiagnostic diag = + diagFactory.fragment("file.does.not.contain.package", + c.location()); + throw reader.new BadClassFile(c, filename, diag); + } + } else { + JCDiagnostic diag = + diagFactory.fragment("file.doesnt.contain.class", + c.getQualifiedName()); + throw reader.new BadClassFile(c, filename, diag); + } + } + + 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 sourceFileObject) + throws Throwable { + compile(sourceFileObject, List.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 sourceFileObjects, + List classnames, + Iterable processors) + { + 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; + + // forcibly set the equivalent of -Xlint:-options, so that no further + // warnings about command line options are generated from this point on + options.put(XLINT_CUSTOM.text + "-" + LintCategory.OPTIONS.option, "true"); + options.remove(XLINT_CUSTOM.text + LintCategory.OPTIONS.option); + + 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(System.err); + } 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>> 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.error("unknown compile policy"); + } + } catch (Abort ex) { + if (devVerbose) + ex.printStackTrace(System.err); + } + + if (verbose) { + elapsed_msec = elapsed(start_msec); + log.printVerbose("total", Long.toString(elapsed_msec)); + } + + reportDeferredDiagnostics(); + + if (!log.hasDiagnosticListener()) { + printCount("error", errorCount()); + printCount("warn", warningCount()); + } + } + + /** + * Set needRootClasses to true, in JavaCompiler subclass constructor + * that want to collect public apis of classes supplied on the command line. + */ + protected boolean needRootClasses = false; + + /** + * The list of classes explicitly supplied on the command line for compilation. + * Not always populated. + */ + private List rootClasses; + + /** + * Parses a list of files. + */ + public List parseFiles(Iterable fileObjects) { + if (shouldStop(CompileState.PARSE)) + return List.nil(); + + //parse all files + ListBuffer trees = new ListBuffer<>(); + Set filesSoFar = new HashSet(); + for (JavaFileObject fileObject : fileObjects) { + if (!filesSoFar.contains(fileObject)) { + filesSoFar.add(fileObject); + trees.append(parse(fileObject)); + } + } + return trees.toList(); + } + + /** + * Enter the symbols found in a list of parse trees if the compilation + * is expected to proceed beyond anno processing into attr. + * As a side-effect, this puts elements on the "todo" list. + * Also stores a list of all top level classes in rootClasses. + */ + public List enterTreesIfNeeded(List roots) { + if (shouldStop(CompileState.ATTR)) + return List.nil(); + return enterTrees(roots); + } + + /** + * 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 enterTrees(List roots) { + //enter symbols for all files + if (!taskListener.isEmpty()) { + for (JCCompilationUnit unit: roots) { + TaskEvent e = new TaskEvent(TaskEvent.Kind.ENTER, unit); + taskListener.started(e); + } + } + + enter.main(roots); + + if (!taskListener.isEmpty()) { + for (JCCompilationUnit unit: roots) { + TaskEvent e = new TaskEvent(TaskEvent.Kind.ENTER, unit); + taskListener.finished(e); + } + } + + // If generating source, or if tracking public apis, + // then remember the classes declared in + // the original compilation units listed on the command line. + if (needRootClasses || sourceOutput || stubOutput) { + ListBuffer cdefs = new ListBuffer<>(); + for (JCCompilationUnit unit : roots) { + for (List defs = unit.defs; + defs.nonEmpty(); + defs = defs.tail) { + if (defs.head instanceof JCClassDecl) + cdefs.append((JCClassDecl)defs.head); + } + } + rootClasses = cdefs.toList(); + } + + // Ensure the input files have been recorded. Although this is normally + // done by readSource, it may not have been done if the trees were read + // in a prior round of annotation processing, and the trees have been + // cleaned and are being reused. + for (JCCompilationUnit unit : roots) { + inputFiles.add(unit.sourcefile); + } + + return roots; + } + + /** + * Set to true to enable skeleton annotation processing code. + * Currently, we assume this variable will be replaced more + * advanced logic to figure out if annotation processing is + * needed. + */ + boolean processAnnotations = false; + + Log.DeferredDiagnosticHandler deferredDiagnosticHandler; + + /** + * Object to handle annotation processing. + */ + private JavacProcessingEnvironment procEnvImpl = null; + + /** + * Check if we should process annotations. + * If so, and if no scanner is yet registered, then set up the DocCommentScanner + * to catch doc comments, and set keepComments so the parser records them in + * the compilation unit. + * + * @param processors user provided annotation processors to bypass + * discovery, {@code null} means that no processors were provided + */ + public void initProcessAnnotations(Iterable processors) { + // Process annotations if processing is not disabled and there + // is at least one Processor available. + if (options.isSet(PROC, "none")) { + processAnnotations = false; + } else if (procEnvImpl == null) { + procEnvImpl = JavacProcessingEnvironment.instance(context); + procEnvImpl.setProcessors(processors); + processAnnotations = procEnvImpl.atLeastOneProcessor(); + + if (processAnnotations) { + options.put("save-parameter-names", "save-parameter-names"); + reader.saveParameterNames = true; + keepComments = true; + genEndPos = true; + if (!taskListener.isEmpty()) + taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING)); + deferredDiagnosticHandler = new Log.DeferredDiagnosticHandler(log); + } else { // free resources + procEnvImpl.close(); + } + } + } + + // TODO: called by JavacTaskImpl + public JavaCompiler processAnnotations(List roots) { + return processAnnotations(roots, List.nil()); + } + + /** + * Process any annotations found in the specified compilation units. + * @param roots a list of compilation units + * @return an instance of the compiler in which to complete the compilation + */ + // Implementation note: when this method is called, log.deferredDiagnostics + // will have been set true by initProcessAnnotations, meaning that any diagnostics + // that are reported will go into the log.deferredDiagnostics queue. + // By the time this method exits, log.deferDiagnostics must be set back to false, + // and all deferredDiagnostics must have been handled: i.e. either reported + // or determined to be transient, and therefore suppressed. + public JavaCompiler processAnnotations(List roots, + List classnames) { + if (shouldStop(CompileState.PROCESS)) { + // Errors were encountered. + // Unless all the errors are resolve errors, the errors were parse errors + // or other errors during enter which cannot be fixed by running + // any annotation processors. + if (unrecoverableError()) { + deferredDiagnosticHandler.reportDeferredDiagnostics(); + log.popDiagnosticHandler(deferredDiagnosticHandler); + return this; + } + } + + // ASSERT: processAnnotations and procEnvImpl should have been set up by + // by initProcessAnnotations + + // NOTE: The !classnames.isEmpty() checks should be refactored to Main. + + if (!processAnnotations) { + // If there are no annotation processors present, and + // annotation processing is to occur with compilation, + // emit a warning. + if (options.isSet(PROC, "only")) { + log.warning("proc.proc-only.requested.no.procs"); + todo.clear(); + } + // If not processing annotations, classnames must be empty + if (!classnames.isEmpty()) { + log.error("proc.no.explicit.annotation.processing.requested", + classnames); + } + Assert.checkNull(deferredDiagnosticHandler); + return this; // continue regular compilation + } + + Assert.checkNonNull(deferredDiagnosticHandler); + + try { + List classSymbols = List.nil(); + List pckSymbols = List.nil(); + if (!classnames.isEmpty()) { + // Check for explicit request for annotation + // processing + if (!explicitAnnotationProcessingRequested()) { + log.error("proc.no.explicit.annotation.processing.requested", + classnames); + deferredDiagnosticHandler.reportDeferredDiagnostics(); + log.popDiagnosticHandler(deferredDiagnosticHandler); + return this; // TODO: Will this halt compilation? + } else { + boolean errors = false; + for (String nameStr : classnames) { + Symbol sym = resolveBinaryNameOrIdent(nameStr); + if (sym == null || + (sym.kind == Kinds.PCK && !processPcks) || + sym.kind == Kinds.ABSENT_TYP) { + log.error("proc.cant.find.class", nameStr); + errors = true; + continue; + } + try { + if (sym.kind == Kinds.PCK) + sym.complete(); + if (sym.exists()) { + if (sym.kind == Kinds.PCK) + pckSymbols = pckSymbols.prepend((PackageSymbol)sym); + else + classSymbols = classSymbols.prepend((ClassSymbol)sym); + continue; + } + Assert.check(sym.kind == Kinds.PCK); + log.warning("proc.package.does.not.exist", nameStr); + pckSymbols = pckSymbols.prepend((PackageSymbol)sym); + } catch (CompletionFailure e) { + log.error("proc.cant.find.class", nameStr); + errors = true; + continue; + } + } + if (errors) { + deferredDiagnosticHandler.reportDeferredDiagnostics(); + log.popDiagnosticHandler(deferredDiagnosticHandler); + return this; + } + } + } + try { + JavaCompiler c = procEnvImpl.doProcessing(context, roots, classSymbols, pckSymbols, + deferredDiagnosticHandler); + if (c != this) + annotationProcessingOccurred = c.annotationProcessingOccurred = true; + // doProcessing will have handled deferred diagnostics + return c; + } finally { + procEnvImpl.close(); + } + } catch (CompletionFailure ex) { + log.error("cant.access", ex.sym, ex.getDetailValue()); + deferredDiagnosticHandler.reportDeferredDiagnostics(); + log.popDiagnosticHandler(deferredDiagnosticHandler); + return this; + } + } + + private boolean unrecoverableError() { + if (deferredDiagnosticHandler != null) { + for (JCDiagnostic d: deferredDiagnosticHandler.getDiagnostics()) { + if (d.getKind() == JCDiagnostic.Kind.ERROR && !d.isFlagSet(RECOVERABLE)) + return true; + } + } + return false; + } + + boolean explicitAnnotationProcessingRequested() { + return + explicitAnnotationProcessingRequested || + explicitAnnotationProcessingRequested(options); + } + + static boolean explicitAnnotationProcessingRequested(Options options) { + return + options.isSet(PROCESSOR) || + options.isSet(PROCESSORPATH) || + options.isSet(PROC, "only") || + options.isSet(XPRINT); + } + + /** + * Attribute a list of parse trees, such as found on the "todo" list. + * Note that attributing classes may cause additional files to be + * parsed and entered via the SourceCompleter. + * Attribution of the entries in the list does not stop if any errors occur. + * @returns a list of environments for attributd classes. + */ + public Queue> attribute(Queue> envs) { + ListBuffer> results = new ListBuffer<>(); + while (!envs.isEmpty()) + results.append(attribute(envs.remove())); + return stopIfError(CompileState.ATTR, results); + } + + /** + * Attribute a parse tree. + * @returns the attributed parse tree + */ + public Env attribute(Env env) { + if (compileStates.isDone(env, CompileState.ATTR)) + return env; + + if (verboseCompilePolicy) + printNote("[attribute " + env.enclClass.sym + "]"); + if (verbose) + log.printVerbose("checking.attribution", env.enclClass.sym); + + if (!taskListener.isEmpty()) { + TaskEvent e = new TaskEvent(TaskEvent.Kind.ANALYZE, env.toplevel, env.enclClass.sym); + taskListener.started(e); + } + + JavaFileObject prev = log.useSource( + env.enclClass.sym.sourcefile != null ? + env.enclClass.sym.sourcefile : + env.toplevel.sourcefile); + try { + attr.attrib(env); + if (errorCount() > 0 && !shouldStop(CompileState.ATTR)) { + //if in fail-over mode, ensure that AST expression nodes + //are correctly initialized (e.g. they have a type/symbol) + attr.postAttr(env.tree); + } + compileStates.put(env, CompileState.ATTR); + if (rootClasses != null && rootClasses.contains(env.enclClass)) { + // This was a class that was explicitly supplied for compilation. + // If we want to capture the public api of this class, + // then now is a good time to do it. + reportPublicApi(env.enclClass.sym); + } + } + finally { + log.useSource(prev); + } + + return env; + } + + /** Report the public api of a class that was supplied explicitly for compilation, + * for example on the command line to javac. + * @param sym The symbol of the class. + */ + public void reportPublicApi(ClassSymbol sym) { + // Override to collect the reported public api. + } + + /** + * Perform dataflow checks on attributed parse trees. + * These include checks for definite assignment and unreachable statements. + * If any errors occur, an empty list will be returned. + * @returns the list of attributed parse trees + */ + public Queue> flow(Queue> envs) { + ListBuffer> results = new ListBuffer<>(); + for (Env env: envs) { + flow(env, results); + } + return stopIfError(CompileState.FLOW, results); + } + + /** + * Perform dataflow checks on an attributed parse tree. + */ + public Queue> flow(Env env) { + ListBuffer> results = new ListBuffer<>(); + flow(env, results); + return stopIfError(CompileState.FLOW, results); + } + + /** + * Perform dataflow checks on an attributed parse tree. + */ + protected void flow(Env env, Queue> results) { + if (compileStates.isDone(env, CompileState.FLOW)) { + results.add(env); + return; + } + + try { + if (shouldStop(CompileState.FLOW)) + return; + + if (relax) { + results.add(env); + return; + } + + if (verboseCompilePolicy) + printNote("[flow " + env.enclClass.sym + "]"); + JavaFileObject prev = log.useSource( + env.enclClass.sym.sourcefile != null ? + env.enclClass.sym.sourcefile : + env.toplevel.sourcefile); + try { + make.at(Position.FIRSTPOS); + TreeMaker localMake = make.forToplevel(env.toplevel); + flow.analyzeTree(env, localMake); + compileStates.put(env, CompileState.FLOW); + + if (shouldStop(CompileState.FLOW)) + return; + + results.add(env); + } + finally { + log.useSource(prev); + } + } + finally { + if (!taskListener.isEmpty()) { + TaskEvent e = new TaskEvent(TaskEvent.Kind.ANALYZE, env.toplevel, env.enclClass.sym); + taskListener.finished(e); + } + } + } + + /** + * Prepare attributed parse trees, in conjunction with their attribution contexts, + * for source or code generation. + * If any errors occur, an empty list will be returned. + * @returns a list containing the classes to be generated + */ + public Queue, JCClassDecl>> desugar(Queue> envs) { + ListBuffer, JCClassDecl>> results = new ListBuffer<>(); + for (Env env: envs) + desugar(env, results); + return stopIfError(CompileState.FLOW, results); + } + + HashMap, Queue, JCClassDecl>>> desugaredEnvs = + new HashMap, Queue, JCClassDecl>>>(); + + /** + * Prepare attributed parse trees, in conjunction with their attribution contexts, + * for source or code generation. If the file was not listed on the command line, + * the current implicitSourcePolicy is taken into account. + * The preparation stops as soon as an error is found. + */ + protected void desugar(final Env env, Queue, JCClassDecl>> results) { + if (shouldStop(CompileState.TRANSTYPES)) + return; + + if (implicitSourcePolicy == ImplicitSourcePolicy.NONE + && !inputFiles.contains(env.toplevel.sourcefile)) { + return; + } + + if (compileStates.isDone(env, CompileState.LOWER)) { + results.addAll(desugaredEnvs.get(env)); + return; + } + + /** + * Ensure that superclasses of C are desugared before C itself. This is + * required for two reasons: (i) as erasure (TransTypes) destroys + * information needed in flow analysis and (ii) as some checks carried + * out during lowering require that all synthetic fields/methods have + * already been added to C and its superclasses. + */ + class ScanNested extends TreeScanner { + Set> dependencies = new LinkedHashSet>(); + protected boolean hasLambdas; + @Override + public void visitClassDef(JCClassDecl node) { + Type st = types.supertype(node.sym.type); + boolean envForSuperTypeFound = false; + while (!envForSuperTypeFound && st.hasTag(CLASS)) { + ClassSymbol c = st.tsym.outermostClass(); + Env stEnv = enter.getEnv(c); + if (stEnv != null && env != stEnv) { + if (dependencies.add(stEnv)) { + boolean prevHasLambdas = hasLambdas; + try { + scan(stEnv.tree); + } finally { + /* + * ignore any updates to hasLambdas made during + * the nested scan, this ensures an initalized + * LambdaToMethod is available only to those + * classes that contain lambdas + */ + hasLambdas = prevHasLambdas; + } + } + envForSuperTypeFound = true; + } + st = types.supertype(st); + } + super.visitClassDef(node); + } + @Override + public void visitLambda(JCLambda tree) { + hasLambdas = true; + super.visitLambda(tree); + } + @Override + public void visitReference(JCMemberReference tree) { + hasLambdas = true; + super.visitReference(tree); + } + } + ScanNested scanner = new ScanNested(); + scanner.scan(env.tree); + for (Env dep: scanner.dependencies) { + if (!compileStates.isDone(dep, CompileState.FLOW)) + desugaredEnvs.put(dep, desugar(flow(attribute(dep)))); + } + + //We need to check for error another time as more classes might + //have been attributed and analyzed at this stage + if (shouldStop(CompileState.TRANSTYPES)) + return; + + if (verboseCompilePolicy) + printNote("[desugar " + env.enclClass.sym + "]"); + + JavaFileObject prev = log.useSource(env.enclClass.sym.sourcefile != null ? + env.enclClass.sym.sourcefile : + env.toplevel.sourcefile); + try { + //save tree prior to rewriting + JCTree untranslated = env.tree; + + make.at(Position.FIRSTPOS); + TreeMaker localMake = make.forToplevel(env.toplevel); + + if (env.tree instanceof JCCompilationUnit) { + if (!(stubOutput || sourceOutput || printFlat)) { + if (shouldStop(CompileState.LOWER)) + return; + List pdef = lower.translateTopLevelClass(env, env.tree, localMake); + if (pdef.head != null) { + Assert.check(pdef.tail.isEmpty()); + results.add(new Pair, JCClassDecl>(env, (JCClassDecl)pdef.head)); + } + } + return; + } + + if (stubOutput) { + //emit stub Java source file, only for compilation + //units enumerated explicitly on the command line + JCClassDecl cdef = (JCClassDecl)env.tree; + if (untranslated instanceof JCClassDecl && + rootClasses.contains((JCClassDecl)untranslated) && + ((cdef.mods.flags & (Flags.PROTECTED|Flags.PUBLIC)) != 0 || + cdef.sym.packge().getQualifiedName() == names.java_lang)) { + results.add(new Pair, JCClassDecl>(env, removeMethodBodies(cdef))); + } + return; + } + + if (shouldStop(CompileState.TRANSTYPES)) + return; + + env.tree = transTypes.translateTopLevelClass(env.tree, localMake); + compileStates.put(env, CompileState.TRANSTYPES); + + if (source.allowLambda() && scanner.hasLambdas) { + if (shouldStop(CompileState.UNLAMBDA)) + return; + + env.tree = LambdaToMethod.instance(context).translateTopLevelClass(env, env.tree, localMake); + compileStates.put(env, CompileState.UNLAMBDA); + } + + if (shouldStop(CompileState.LOWER)) + return; + + if (sourceOutput) { + //emit standard Java source file, only for compilation + //units enumerated explicitly on the command line + JCClassDecl cdef = (JCClassDecl)env.tree; + if (untranslated instanceof JCClassDecl && + rootClasses.contains((JCClassDecl)untranslated)) { + results.add(new Pair, JCClassDecl>(env, cdef)); + } + return; + } + + //translate out inner classes + List cdefs = lower.translateTopLevelClass(env, env.tree, localMake); + compileStates.put(env, CompileState.LOWER); + + if (shouldStop(CompileState.LOWER)) + return; + + //generate code for each class + for (List l = cdefs; l.nonEmpty(); l = l.tail) { + JCClassDecl cdef = (JCClassDecl)l.head; + results.add(new Pair, JCClassDecl>(env, cdef)); + } + } + finally { + log.useSource(prev); + } + + } + + /** Generates the source or class file for a list of classes. + * The decision to generate a source file or a class file is + * based upon the compiler's options. + * Generation stops if an error occurs while writing files. + */ + public void generate(Queue, JCClassDecl>> queue) { + generate(queue, null); + } + + public void generate(Queue, JCClassDecl>> queue, Queue results) { + if (shouldStop(CompileState.GENERATE)) + return; + + boolean usePrintSource = (stubOutput || sourceOutput || printFlat); + + for (Pair, JCClassDecl> x: queue) { + Env env = x.fst; + JCClassDecl cdef = x.snd; + + if (verboseCompilePolicy) { + printNote("[generate " + + (usePrintSource ? " source" : "code") + + " " + cdef.sym + "]"); + } + + if (!taskListener.isEmpty()) { + TaskEvent e = new TaskEvent(TaskEvent.Kind.GENERATE, env.toplevel, cdef.sym); + taskListener.started(e); + } + + JavaFileObject prev = log.useSource(env.enclClass.sym.sourcefile != null ? + env.enclClass.sym.sourcefile : + env.toplevel.sourcefile); + try { + JavaFileObject file; + if (usePrintSource) + file = printSource(env, cdef); + else { + if (fileManager.hasLocation(StandardLocation.NATIVE_HEADER_OUTPUT) + && jniWriter.needsHeader(cdef.sym)) { + jniWriter.write(cdef.sym); + } + file = genCode(env, cdef); + } + if (results != null && file != null) + results.add(file); + } catch (IOException ex) { + log.error(cdef.pos(), "class.cant.write", + cdef.sym, ex.getMessage()); + return; + } finally { + log.useSource(prev); + } + + if (!taskListener.isEmpty()) { + TaskEvent e = new TaskEvent(TaskEvent.Kind.GENERATE, env.toplevel, cdef.sym); + taskListener.finished(e); + } + } + } + + // where + Map>> groupByFile(Queue> envs) { + // use a LinkedHashMap to preserve the order of the original list as much as possible + Map>> map = new LinkedHashMap>>(); + for (Env env: envs) { + Queue> sublist = map.get(env.toplevel); + if (sublist == null) { + sublist = new ListBuffer>(); + map.put(env.toplevel, sublist); + } + sublist.add(env); + } + return map; + } + + JCClassDecl removeMethodBodies(JCClassDecl cdef) { + final boolean isInterface = (cdef.mods.flags & Flags.INTERFACE) != 0; + class MethodBodyRemover extends TreeTranslator { + @Override + public void visitMethodDef(JCMethodDecl tree) { + tree.mods.flags &= ~Flags.SYNCHRONIZED; + for (JCVariableDecl vd : tree.params) + vd.mods.flags &= ~Flags.FINAL; + tree.body = null; + super.visitMethodDef(tree); + } + @Override + public void visitVarDef(JCVariableDecl tree) { + if (tree.init != null && tree.init.type.constValue() == null) + tree.init = null; + super.visitVarDef(tree); + } + @Override + public void visitClassDef(JCClassDecl tree) { + ListBuffer newdefs = new ListBuffer<>(); + for (List it = tree.defs; it.tail != null; it = it.tail) { + JCTree t = it.head; + switch (t.getTag()) { + case CLASSDEF: + if (isInterface || + (((JCClassDecl) t).mods.flags & (Flags.PROTECTED|Flags.PUBLIC)) != 0 || + (((JCClassDecl) t).mods.flags & (Flags.PRIVATE)) == 0 && ((JCClassDecl) t).sym.packge().getQualifiedName() == names.java_lang) + newdefs.append(t); + break; + case METHODDEF: + if (isInterface || + (((JCMethodDecl) t).mods.flags & (Flags.PROTECTED|Flags.PUBLIC)) != 0 || + ((JCMethodDecl) t).sym.name == names.init || + (((JCMethodDecl) t).mods.flags & (Flags.PRIVATE)) == 0 && ((JCMethodDecl) t).sym.packge().getQualifiedName() == names.java_lang) + newdefs.append(t); + break; + case VARDEF: + if (isInterface || (((JCVariableDecl) t).mods.flags & (Flags.PROTECTED|Flags.PUBLIC)) != 0 || + (((JCVariableDecl) t).mods.flags & (Flags.PRIVATE)) == 0 && ((JCVariableDecl) t).sym.packge().getQualifiedName() == names.java_lang) + newdefs.append(t); + break; + default: + break; + } + } + tree.defs = newdefs.toList(); + super.visitClassDef(tree); + } + } + MethodBodyRemover r = new MethodBodyRemover(); + return r.translate(cdef); + } + + public void reportDeferredDiagnostics() { + if (errorCount() == 0 + && annotationProcessingOccurred + && implicitSourceFilesRead + && implicitSourcePolicy == ImplicitSourcePolicy.UNSET) { + if (explicitAnnotationProcessingRequested()) + log.warning("proc.use.implicit"); + else + log.warning("proc.use.proc.or.implicit"); + } + chk.reportDeferredDiagnostics(); + if (log.compressedOutput) { + log.mandatoryNote(null, "compressed.diags"); + } + } + + /** Close the compiler, flushing the logs + */ + public void close() { + close(true); + } + + public void close(boolean disposeNames) { + rootClasses = null; + reader = null; + make = null; + writer = null; + enter = null; + if (todo != null) + todo.clear(); + todo = null; + parserFactory = null; + syms = null; + source = null; + attr = null; + chk = null; + gen = null; + flow = null; + transTypes = null; + lower = null; + annotate = null; + types = null; + + log.flush(); + try { + fileManager.flush(); + } catch (IOException e) { + throw new Abort(e); + } finally { + if (names != null && disposeNames) + names.dispose(); + names = null; + + for (Closeable c: closeables) { + try { + c.close(); + } catch (IOException e) { + // When javac uses JDK 7 as a baseline, this code would be + // better written to set any/all exceptions from all the + // Closeables as suppressed exceptions on the FatalError + // that is thrown. + JCDiagnostic msg = diagFactory.fragment("fatal.err.cant.close"); + throw new FatalError(msg, e); + } + } + closeables = List.nil(); + } + } + + protected void printNote(String lines) { + log.printRawLines(Log.WriterKind.NOTICE, lines); + } + + /** Print numbers of errors and warnings. + */ + public void printCount(String kind, int count) { + if (count != 0) { + String key; + if (count == 1) + key = "count." + kind; + else + key = "count." + kind + ".plural"; + log.printLines(WriterKind.ERROR, key, String.valueOf(count)); + log.flush(Log.WriterKind.ERROR); + } + } + + private static long now() { + return System.currentTimeMillis(); + } + + private static long elapsed(long then) { + return now() - then; + } + + public void initRound(JavaCompiler prev) { + genEndPos = prev.genEndPos; + keepComments = prev.keepComments; + start_msec = prev.start_msec; + hasBeenUsed = true; + closeables = prev.closeables; + prev.closeables = List.nil(); + shouldStopPolicyIfError = prev.shouldStopPolicyIfError; + shouldStopPolicyIfNoError = prev.shouldStopPolicyIfNoError; + } +}