duke@1: /*
jjg@1521: * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
duke@1: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
duke@1: *
duke@1: * This code is free software; you can redistribute it and/or modify it
duke@1: * under the terms of the GNU General Public License version 2 only, as
ohair@554: * published by the Free Software Foundation. Oracle designates this
duke@1: * particular file as subject to the "Classpath" exception as provided
ohair@554: * by Oracle in the LICENSE file that accompanied this code.
duke@1: *
duke@1: * This code is distributed in the hope that it will be useful, but WITHOUT
duke@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
duke@1: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
duke@1: * version 2 for more details (a copy is included in the LICENSE file that
duke@1: * accompanied this code).
duke@1: *
duke@1: * You should have received a copy of the GNU General Public License version
duke@1: * 2 along with this work; if not, write to the Free Software Foundation,
duke@1: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
duke@1: *
ohair@554: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
ohair@554: * or visit www.oracle.com if you need additional information or have any
ohair@554: * questions.
duke@1: */
duke@1:
duke@1: package com.sun.tools.javac.main;
duke@1:
duke@1: import java.io.*;
jjg@700: import java.util.HashMap;
duke@1: import java.util.HashSet;
jjg@1210: import java.util.LinkedHashMap;
mcimadamore@95: import java.util.LinkedHashSet;
duke@1: import java.util.Map;
duke@1: import java.util.MissingResourceException;
jjg@700: import java.util.Queue;
duke@1: import java.util.ResourceBundle;
duke@1: import java.util.Set;
duke@1: import java.util.logging.Handler;
duke@1: import java.util.logging.Level;
duke@1: import java.util.logging.Logger;
duke@1:
jjg@700: import javax.annotation.processing.Processor;
jjg@700: import javax.lang.model.SourceVersion;
jjg@1210: import javax.tools.DiagnosticListener;
duke@1: import javax.tools.JavaFileManager;
duke@1: import javax.tools.JavaFileObject;
jjg@1230: import javax.tools.StandardLocation;
jjg@1230:
jjg@1210: import static javax.tools.StandardLocation.CLASS_OUTPUT;
duke@1:
duke@1: import com.sun.source.util.TaskEvent;
jjg@1210: import com.sun.tools.javac.api.MultiTaskListener;
duke@1: import com.sun.tools.javac.code.*;
jjg@757: import com.sun.tools.javac.code.Lint.LintCategory;
jjg@700: import com.sun.tools.javac.code.Symbol.*;
jjg@1136: import com.sun.tools.javac.comp.*;
jjg@1136: import com.sun.tools.javac.file.JavacFileManager;
jjg@1136: import com.sun.tools.javac.jvm.*;
jjg@1136: import com.sun.tools.javac.parser.*;
jjg@1136: import com.sun.tools.javac.processing.*;
duke@1: import com.sun.tools.javac.tree.*;
jjg@700: import com.sun.tools.javac.tree.JCTree.*;
jjg@1136: import com.sun.tools.javac.util.*;
jjg@1136: import com.sun.tools.javac.util.Log.WriterKind;
jjg@1230:
jjg@1374: import static com.sun.tools.javac.code.TypeTag.CLASS;
jjg@1157: import static com.sun.tools.javac.main.Option.*;
jjg@664: import static com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag.*;
duke@1: import static com.sun.tools.javac.util.ListBuffer.lb;
duke@1:
duke@1:
duke@1: /** This class could be the main entry point for GJC when GJC is used as a
duke@1: * component in a larger software system. It provides operations to
duke@1: * construct a new compiler, and to run a new compiler on a set of source
duke@1: * files.
duke@1: *
jjg@581: *
This is NOT part of any supported API.
jjg@581: * If you write code that depends on this, you do so at your own risk.
duke@1: * This code and its internal interfaces are subject to change or
duke@1: * deletion without notice.
duke@1: */
duke@1: public class JavaCompiler implements ClassReader.SourceCompleter {
duke@1: /** The context key for the compiler. */
duke@1: protected static final Context.Key compilerKey =
duke@1: new Context.Key();
duke@1:
duke@1: /** Get the JavaCompiler instance for this context. */
duke@1: public static JavaCompiler instance(Context context) {
duke@1: JavaCompiler instance = context.get(compilerKey);
duke@1: if (instance == null)
duke@1: instance = new JavaCompiler(context);
duke@1: return instance;
duke@1: }
duke@1:
duke@1: /** The current version number as a string.
duke@1: */
duke@1: public static String version() {
duke@1: return version("release"); // mm.nn.oo[-milestone]
duke@1: }
duke@1:
duke@1: /** The current full version number as a string.
duke@1: */
duke@1: public static String fullVersion() {
duke@1: return version("full"); // mm.mm.oo[-milestone]-build
duke@1: }
duke@1:
duke@1: private static final String versionRBName = "com.sun.tools.javac.resources.version";
duke@1: private static ResourceBundle versionRB;
duke@1:
duke@1: private static String version(String key) {
duke@1: if (versionRB == null) {
duke@1: try {
duke@1: versionRB = ResourceBundle.getBundle(versionRBName);
duke@1: } catch (MissingResourceException e) {
jjg@597: return Log.getLocalizedString("version.not.available");
duke@1: }
duke@1: }
duke@1: try {
duke@1: return versionRB.getString(key);
duke@1: }
duke@1: catch (MissingResourceException e) {
jjg@597: return Log.getLocalizedString("version.not.available");
duke@1: }
duke@1: }
duke@1:
jjg@119: /**
jjg@119: * Control how the compiler's latter phases (attr, flow, desugar, generate)
jjg@119: * are connected. Each individual file is processed by each phase in turn,
jjg@119: * but with different compile policies, you can control the order in which
jjg@119: * each class is processed through its next phase.
jjg@119: *
jjg@119: * Generally speaking, the compiler will "fail fast" in the face of
jjg@119: * errors, although not aggressively so. flow, desugar, etc become no-ops
jjg@119: * once any errors have occurred. No attempt is currently made to determine
jjg@119: * if it might be safe to process a class through its next phase because
jjg@119: * it does not depend on any unrelated errors that might have occurred.
jjg@119: */
jjg@119: protected static enum CompilePolicy {
jjg@119: /**
jjg@119: * Just attribute the parse trees.
duke@1: */
duke@1: ATTR_ONLY,
duke@1:
jjg@119: /**
duke@1: * Just attribute and do flow analysis on the parse trees.
duke@1: * This should catch most user errors.
duke@1: */
duke@1: CHECK_ONLY,
duke@1:
jjg@119: /**
duke@1: * Attribute everything, then do flow analysis for everything,
duke@1: * then desugar everything, and only then generate output.
jjg@119: * This means no output will be generated if there are any
jjg@119: * errors in any classes.
duke@1: */
duke@1: SIMPLE,
duke@1:
jjg@119: /**
jjg@119: * Groups the classes for each source file together, then process
jjg@119: * each group in a manner equivalent to the {@code SIMPLE} policy.
jjg@119: * This means no output will be generated if there are any
jjg@119: * errors in any of the classes in a source file.
duke@1: */
duke@1: BY_FILE,
duke@1:
jjg@119: /**
duke@1: * Completely process each entry on the todo list in turn.
duke@1: * -- this is the same for 1.5.
duke@1: * Means output might be generated for some classes in a compilation unit
duke@1: * and not others.
duke@1: */
duke@1: BY_TODO;
duke@1:
duke@1: static CompilePolicy decode(String option) {
duke@1: if (option == null)
duke@1: return DEFAULT_COMPILE_POLICY;
duke@1: else if (option.equals("attr"))
duke@1: return ATTR_ONLY;
duke@1: else if (option.equals("check"))
duke@1: return CHECK_ONLY;
duke@1: else if (option.equals("simple"))
duke@1: return SIMPLE;
duke@1: else if (option.equals("byfile"))
duke@1: return BY_FILE;
duke@1: else if (option.equals("bytodo"))
duke@1: return BY_TODO;
duke@1: else
duke@1: return DEFAULT_COMPILE_POLICY;
duke@1: }
duke@1: }
duke@1:
vromero@1442: private static final CompilePolicy DEFAULT_COMPILE_POLICY = CompilePolicy.BY_TODO;
duke@1:
jjg@119: protected static enum ImplicitSourcePolicy {
duke@1: /** Don't generate or process implicitly read source files. */
duke@1: NONE,
duke@1: /** Generate classes for implicitly read source files. */
duke@1: CLASS,
duke@1: /** Like CLASS, but generate warnings if annotation processing occurs */
duke@1: UNSET;
duke@1:
duke@1: static ImplicitSourcePolicy decode(String option) {
duke@1: if (option == null)
duke@1: return UNSET;
duke@1: else if (option.equals("none"))
duke@1: return NONE;
duke@1: else if (option.equals("class"))
duke@1: return CLASS;
duke@1: else
duke@1: return UNSET;
duke@1: }
duke@1: }
duke@1:
duke@1: /** The log to be used for error reporting.
duke@1: */
duke@1: public Log log;
duke@1:
jjg@12: /** Factory for creating diagnostic objects
jjg@12: */
jjg@12: JCDiagnostic.Factory diagFactory;
jjg@12:
duke@1: /** The tree factory module.
duke@1: */
duke@1: protected TreeMaker make;
duke@1:
duke@1: /** The class reader.
duke@1: */
duke@1: protected ClassReader reader;
duke@1:
duke@1: /** The class writer.
duke@1: */
duke@1: protected ClassWriter writer;
duke@1:
jjg@1230: /** The native header writer.
jjg@1230: */
jjg@1230: protected JNIWriter jniWriter;
jjg@1230:
duke@1: /** The module for the symbol table entry phases.
duke@1: */
duke@1: protected Enter enter;
duke@1:
duke@1: /** The symbol table.
duke@1: */
duke@1: protected Symtab syms;
duke@1:
duke@1: /** The language version.
duke@1: */
duke@1: protected Source source;
duke@1:
duke@1: /** The module for code generation.
duke@1: */
duke@1: protected Gen gen;
duke@1:
duke@1: /** The name table.
duke@1: */
jjg@113: protected Names names;
duke@1:
duke@1: /** The attributor.
duke@1: */
duke@1: protected Attr attr;
duke@1:
duke@1: /** The attributor.
duke@1: */
duke@1: protected Check chk;
duke@1:
duke@1: /** The flow analyzer.
duke@1: */
duke@1: protected Flow flow;
duke@1:
duke@1: /** The type eraser.
duke@1: */
jjg@119: protected TransTypes transTypes;
duke@1:
rfield@1380: /** The lambda translator.
rfield@1380: */
rfield@1380: protected LambdaToMethod lambdaToMethod;
rfield@1380:
duke@1: /** The syntactic sugar desweetener.
duke@1: */
jjg@119: protected Lower lower;
duke@1:
duke@1: /** The annotation annotator.
duke@1: */
duke@1: protected Annotate annotate;
duke@1:
duke@1: /** Force a completion failure on this name
duke@1: */
duke@1: protected final Name completionFailureName;
duke@1:
duke@1: /** Type utilities.
duke@1: */
duke@1: protected Types types;
duke@1:
duke@1: /** Access to file objects.
duke@1: */
duke@1: protected JavaFileManager fileManager;
duke@1:
duke@1: /** Factory for parsers.
duke@1: */
jjg@111: protected ParserFactory parserFactory;
duke@1:
jjg@1210: /** Broadcasting listener for progress events
duke@1: */
jjg@1210: protected MultiTaskListener taskListener;
duke@1:
duke@1: /**
duke@1: * Annotation processing may require and provide a new instance
duke@1: * of the compiler to be used for the analyze and generate phases.
duke@1: */
duke@1: protected JavaCompiler delegateCompiler;
duke@1:
duke@1: /**
jjg@897: * Command line options.
jjg@897: */
jjg@897: protected Options options;
jjg@897:
jjg@897: protected Context context;
jjg@897:
jjg@897: /**
duke@1: * Flag set if any annotation processing occurred.
duke@1: **/
duke@1: protected boolean annotationProcessingOccurred;
duke@1:
duke@1: /**
duke@1: * Flag set if any implicit source files read.
duke@1: **/
duke@1: protected boolean implicitSourceFilesRead;
duke@1:
duke@1: /** Construct a new compiler using a shared context.
duke@1: */
jjg@893: public JavaCompiler(Context context) {
duke@1: this.context = context;
duke@1: context.put(compilerKey, this);
duke@1:
duke@1: // if fileManager not already set, register the JavacFileManager to be used
duke@1: if (context.get(JavaFileManager.class) == null)
duke@1: JavacFileManager.preRegister(context);
duke@1:
jjg@113: names = Names.instance(context);
duke@1: log = Log.instance(context);
jjg@12: diagFactory = JCDiagnostic.Factory.instance(context);
duke@1: reader = ClassReader.instance(context);
duke@1: make = TreeMaker.instance(context);
duke@1: writer = ClassWriter.instance(context);
jjg@1230: jniWriter = JNIWriter.instance(context);
duke@1: enter = Enter.instance(context);
duke@1: todo = Todo.instance(context);
duke@1:
duke@1: fileManager = context.get(JavaFileManager.class);
jjg@111: parserFactory = ParserFactory.instance(context);
duke@1:
duke@1: try {
duke@1: // catch completion problems with predefineds
duke@1: syms = Symtab.instance(context);
duke@1: } catch (CompletionFailure ex) {
duke@1: // inlined Check.completionError as it is not initialized yet
jjg@12: log.error("cant.access", ex.sym, ex.getDetailValue());
duke@1: if (ex instanceof ClassReader.BadClassFile)
duke@1: throw new Abort();
duke@1: }
duke@1: source = Source.instance(context);
duke@1: attr = Attr.instance(context);
duke@1: chk = Check.instance(context);
duke@1: gen = Gen.instance(context);
duke@1: flow = Flow.instance(context);
duke@1: transTypes = TransTypes.instance(context);
duke@1: lower = Lower.instance(context);
duke@1: annotate = Annotate.instance(context);
duke@1: types = Types.instance(context);
jjg@1210: taskListener = MultiTaskListener.instance(context);
duke@1:
duke@1: reader.sourceCompleter = this;
duke@1:
jjg@897: options = Options.instance(context);
duke@1:
rfield@1380: lambdaToMethod = LambdaToMethod.instance(context);
rfield@1380:
jjg@700: verbose = options.isSet(VERBOSE);
jjg@700: sourceOutput = options.isSet(PRINTSOURCE); // used to be -s
jjg@700: stubOutput = options.isSet("-stubs");
jjg@700: relax = options.isSet("-relax");
jjg@700: printFlat = options.isSet("-printflat");
jjg@700: attrParseOnly = options.isSet("-attrparseonly");
jjg@700: encoding = options.get(ENCODING);
jjg@700: lineDebugInfo = options.isUnset(G_CUSTOM) ||
jjg@700: options.isSet(G_CUSTOM, "lines");
jjg@700: genEndPos = options.isSet(XJCOV) ||
duke@1: context.get(DiagnosticListener.class) != null;
jjg@700: devVerbose = options.isSet("dev");
jjg@700: processPcks = options.isSet("process.packages");
jjg@700: werror = options.isSet(WERROR);
duke@1:
jjg@757: if (source.compareTo(Source.DEFAULT) < 0) {
jjg@757: if (options.isUnset(XLINT_CUSTOM, "-" + LintCategory.OPTIONS.option)) {
jjg@757: if (fileManager instanceof BaseFileManager) {
jjg@757: if (((BaseFileManager) fileManager).isDefaultBootClassPath())
jjg@757: log.warning(LintCategory.OPTIONS, "source.no.bootclasspath", source.name);
jjg@757: }
jjg@757: }
jjg@757: }
jjg@757:
jjg@700: verboseCompilePolicy = options.isSet("verboseCompilePolicy");
duke@1:
duke@1: if (attrParseOnly)
duke@1: compilePolicy = CompilePolicy.ATTR_ONLY;
duke@1: else
duke@1: compilePolicy = CompilePolicy.decode(options.get("compilePolicy"));
duke@1:
duke@1: implicitSourcePolicy = ImplicitSourcePolicy.decode(options.get("-implicit"));
duke@1:
duke@1: completionFailureName =
jjg@700: options.isSet("failcomplete")
duke@1: ? names.fromString(options.get("failcomplete"))
duke@1: : null;
jjg@257:
jjg@1340: shouldStopPolicyIfError =
jjg@1340: options.isSet("shouldStopPolicy") // backwards compatible
jjg@257: ? CompileState.valueOf(options.get("shouldStopPolicy"))
jjg@1340: : options.isSet("shouldStopPolicyIfError")
jjg@1340: ? CompileState.valueOf(options.get("shouldStopPolicyIfError"))
jjg@1340: : CompileState.INIT;
jjg@1340: shouldStopPolicyIfNoError =
jjg@1340: options.isSet("shouldStopPolicyIfNoError")
jjg@1340: ? CompileState.valueOf(options.get("shouldStopPolicyIfNoError"))
jjg@1340: : CompileState.GENERATE;
jjg@1340:
jjg@700: if (options.isUnset("oldDiags"))
mcimadamore@288: log.setDiagnosticFormatter(RichDiagnosticFormatter.instance(context));
duke@1: }
duke@1:
duke@1: /* Switches:
duke@1: */
duke@1:
duke@1: /** Verbose output.
duke@1: */
duke@1: public boolean verbose;
duke@1:
duke@1: /** Emit plain Java source files rather than class files.
duke@1: */
duke@1: public boolean sourceOutput;
duke@1:
duke@1: /** Emit stub source files rather than class files.
duke@1: */
duke@1: public boolean stubOutput;
duke@1:
duke@1: /** Generate attributed parse tree only.
duke@1: */
duke@1: public boolean attrParseOnly;
duke@1:
duke@1: /** Switch: relax some constraints for producing the jsr14 prototype.
duke@1: */
duke@1: boolean relax;
duke@1:
duke@1: /** Debug switch: Emit Java sources after inner class flattening.
duke@1: */
duke@1: public boolean printFlat;
duke@1:
duke@1: /** The encoding to be used for source input.
duke@1: */
duke@1: public String encoding;
duke@1:
duke@1: /** Generate code with the LineNumberTable attribute for debugging
duke@1: */
duke@1: public boolean lineDebugInfo;
duke@1:
duke@1: /** Switch: should we store the ending positions?
duke@1: */
duke@1: public boolean genEndPos;
duke@1:
duke@1: /** Switch: should we debug ignored exceptions
duke@1: */
duke@1: protected boolean devVerbose;
duke@1:
duke@1: /** Switch: should we (annotation) process packages as well
duke@1: */
duke@1: protected boolean processPcks;
duke@1:
jjg@215: /** Switch: treat warnings as errors
jjg@215: */
jjg@215: protected boolean werror;
jjg@215:
duke@1: /** Switch: is annotation processing requested explitly via
duke@1: * CompilationTask.setProcessors?
duke@1: */
duke@1: protected boolean explicitAnnotationProcessingRequested = false;
duke@1:
duke@1: /**
duke@1: * The policy for the order in which to perform the compilation
duke@1: */
duke@1: protected CompilePolicy compilePolicy;
duke@1:
duke@1: /**
duke@1: * The policy for what to do with implicitly read source files
duke@1: */
duke@1: protected ImplicitSourcePolicy implicitSourcePolicy;
duke@1:
duke@1: /**
duke@1: * Report activity related to compilePolicy
duke@1: */
duke@1: public boolean verboseCompilePolicy;
duke@1:
jjg@257: /**
jjg@1340: * Policy of how far to continue compilation after errors have occurred.
jjg@1340: * Set this to minimum CompileState (INIT) to stop as soon as possible
jjg@1340: * after errors.
jjg@257: */
jjg@1340: public CompileState shouldStopPolicyIfError;
jjg@257:
jjg@1340: /**
jjg@1340: * Policy of how far to continue compilation when no errors have occurred.
jjg@1340: * Set this to maximum CompileState (GENERATE) to perform full compilation.
jjg@1340: * Set this lower to perform partial compilation, such as -proc:only.
jjg@1340: */
jjg@1340: public CompileState shouldStopPolicyIfNoError;
jjg@1340:
jjg@1521: /** A queue of all as yet unattributed classes.
duke@1: */
duke@1: public Todo todo;
duke@1:
jjg@1096: /** A list of items to be closed when the compilation is complete.
jjg@1096: */
jjg@1096: public List closeables = List.nil();
jjg@1096:
jjg@257: /** Ordered list of compiler phases for each compilation unit. */
jjg@310: public enum CompileState {
jjg@1340: INIT(0),
jjg@257: PARSE(1),
jjg@257: ENTER(2),
jjg@257: PROCESS(3),
jjg@257: ATTR(4),
jjg@257: FLOW(5),
jjg@257: TRANSTYPES(6),
rfield@1380: UNLAMBDA(7),
rfield@1380: LOWER(8),
rfield@1380: GENERATE(9);
rfield@1380:
jjg@77: CompileState(int value) {
jjg@77: this.value = value;
jjg@77: }
jjg@1340: boolean isAfter(CompileState other) {
jjg@1340: return value > other.value;
jjg@1340: }
jjg@1340: public static CompileState max(CompileState a, CompileState b) {
jjg@1340: return a.value > b.value ? a : b;
jjg@77: }
vromero@1442: private final int value;
jjg@77: };
jjg@257: /** Partial map to record which compiler phases have been executed
jjg@257: * for each compilation unit. Used for ATTR and FLOW phases.
jjg@257: */
jjg@77: protected class CompileStates extends HashMap,CompileState> {
jjg@198: private static final long serialVersionUID = 1812267524140424433L;
jjg@77: boolean isDone(Env env, CompileState cs) {
jjg@77: CompileState ecs = get(env);
jjg@1340: return (ecs != null) && !cs.isAfter(ecs);
jjg@77: }
jjg@77: }
jjg@77: private CompileStates compileStates = new CompileStates();
duke@1:
duke@1: /** The set of currently compiled inputfiles, needed to ensure
duke@1: * we don't accidentally overwrite an input file when -s is set.
duke@1: * initialized by `compile'.
duke@1: */
duke@1: protected Set inputFiles = new HashSet();
duke@1:
jjg@257: protected boolean shouldStop(CompileState cs) {
jjg@1340: CompileState shouldStopPolicy = (errorCount() > 0 || unrecoverableError())
jjg@1340: ? shouldStopPolicyIfError
jjg@1340: : shouldStopPolicyIfNoError;
jjg@1340: return cs.isAfter(shouldStopPolicy);
jjg@257: }
jjg@257:
duke@1: /** The number of errors reported so far.
duke@1: */
duke@1: public int errorCount() {
duke@1: if (delegateCompiler != null && delegateCompiler != this)
duke@1: return delegateCompiler.errorCount();
jjg@215: else {
jjg@215: if (werror && log.nerrors == 0 && log.nwarnings > 0) {
jjg@215: log.error("warnings.and.werror");
jjg@215: }
jjg@215: }
jjg@642: return log.nerrors;
duke@1: }
duke@1:
jjg@257: protected final Queue stopIfError(CompileState cs, Queue queue) {
jjg@257: return shouldStop(cs) ? ListBuffer.lb() : queue;
duke@1: }
duke@1:
jjg@257: protected final List stopIfError(CompileState cs, List list) {
jjg@257: return shouldStop(cs) ? List.nil() : list;
duke@1: }
duke@1:
duke@1: /** The number of warnings reported so far.
duke@1: */
duke@1: public int warningCount() {
duke@1: if (delegateCompiler != null && delegateCompiler != this)
duke@1: return delegateCompiler.warningCount();
duke@1: else
duke@1: return log.nwarnings;
duke@1: }
duke@1:
duke@1: /** Try to open input stream with given name.
duke@1: * Report an error if this fails.
duke@1: * @param filename The file name of the input stream to be opened.
duke@1: */
duke@1: public CharSequence readSource(JavaFileObject filename) {
duke@1: try {
duke@1: inputFiles.add(filename);
duke@1: return filename.getCharContent(false);
duke@1: } catch (IOException e) {
jjg@510: log.error("error.reading.file", filename, JavacFileManager.getMessage(e));
duke@1: return null;
duke@1: }
duke@1: }
duke@1:
duke@1: /** Parse contents of input stream.
duke@1: * @param filename The name of the file from which input stream comes.
jjg@1358: * @param content The characters to be parsed.
duke@1: */
duke@1: protected JCCompilationUnit parse(JavaFileObject filename, CharSequence content) {
duke@1: long msec = now();
duke@1: JCCompilationUnit tree = make.TopLevel(List.nil(),
duke@1: null, List.nil());
duke@1: if (content != null) {
duke@1: if (verbose) {
jjg@909: log.printVerbose("parsing.started", filename);
duke@1: }
jjg@1210: if (!taskListener.isEmpty()) {
duke@1: TaskEvent e = new TaskEvent(TaskEvent.Kind.PARSE, filename);
duke@1: taskListener.started(e);
jjg@1539: keepComments = true;
jjg@1539: genEndPos = true;
duke@1: }
jjg@111: Parser parser = parserFactory.newParser(content, keepComments(), genEndPos, lineDebugInfo);
jjg@111: tree = parser.parseCompilationUnit();
duke@1: if (verbose) {
jjg@909: log.printVerbose("parsing.done", Long.toString(elapsed(msec)));
duke@1: }
duke@1: }
duke@1:
duke@1: tree.sourcefile = filename;
duke@1:
jjg@1210: if (content != null && !taskListener.isEmpty()) {
duke@1: TaskEvent e = new TaskEvent(TaskEvent.Kind.PARSE, tree);
duke@1: taskListener.finished(e);
duke@1: }
duke@1:
duke@1: return tree;
duke@1: }
duke@1: // where
duke@1: public boolean keepComments = false;
duke@1: protected boolean keepComments() {
duke@1: return keepComments || sourceOutput || stubOutput;
duke@1: }
duke@1:
duke@1:
duke@1: /** Parse contents of file.
duke@1: * @param filename The name of the file to be parsed.
duke@1: */
duke@1: @Deprecated
jjg@663: public JCTree.JCCompilationUnit parse(String filename) {
duke@1: JavacFileManager fm = (JavacFileManager)fileManager;
duke@1: return parse(fm.getJavaFileObjectsFromStrings(List.of(filename)).iterator().next());
duke@1: }
duke@1:
duke@1: /** Parse contents of file.
duke@1: * @param filename The name of the file to be parsed.
duke@1: */
duke@1: public JCTree.JCCompilationUnit parse(JavaFileObject filename) {
duke@1: JavaFileObject prev = log.useSource(filename);
duke@1: try {
duke@1: JCTree.JCCompilationUnit t = parse(filename, readSource(filename));
duke@1: if (t.endPositions != null)
duke@1: log.setEndPosTable(filename, t.endPositions);
duke@1: return t;
duke@1: } finally {
duke@1: log.useSource(prev);
duke@1: }
duke@1: }
duke@1:
jjg@937: /** Resolve an identifier which may be the binary name of a class or
jjg@937: * the Java name of a class or package.
jjg@937: * @param name The name to resolve
jjg@937: */
jjg@937: public Symbol resolveBinaryNameOrIdent(String name) {
jjg@937: try {
jjg@937: Name flatname = names.fromString(name.replace("/", "."));
jjg@937: return reader.loadClass(flatname);
jjg@937: } catch (CompletionFailure ignore) {
jjg@937: return resolveIdent(name);
jjg@937: }
jjg@937: }
jjg@937:
duke@1: /** Resolve an identifier.
duke@1: * @param name The identifier to resolve
duke@1: */
duke@1: public Symbol resolveIdent(String name) {
duke@1: if (name.equals(""))
duke@1: return syms.errSymbol;
duke@1: JavaFileObject prev = log.useSource(null);
duke@1: try {
duke@1: JCExpression tree = null;
duke@1: for (String s : name.split("\\.", -1)) {
duke@1: if (!SourceVersion.isIdentifier(s)) // TODO: check for keywords
duke@1: return syms.errSymbol;
duke@1: tree = (tree == null) ? make.Ident(names.fromString(s))
duke@1: : make.Select(tree, names.fromString(s));
duke@1: }
duke@1: JCCompilationUnit toplevel =
duke@1: make.TopLevel(List.nil(), null, List.nil());
duke@1: toplevel.packge = syms.unnamedPackage;
duke@1: return attr.attribIdent(tree, toplevel);
duke@1: } finally {
duke@1: log.useSource(prev);
duke@1: }
duke@1: }
duke@1:
duke@1: /** Emit plain Java source for a class.
duke@1: * @param env The attribution environment of the outermost class
duke@1: * containing this class.
duke@1: * @param cdef The class definition to be printed.
duke@1: */
duke@1: JavaFileObject printSource(Env env, JCClassDecl cdef) throws IOException {
duke@1: JavaFileObject outFile
duke@1: = fileManager.getJavaFileForOutput(CLASS_OUTPUT,
duke@1: cdef.sym.flatname.toString(),
duke@1: JavaFileObject.Kind.SOURCE,
duke@1: null);
duke@1: if (inputFiles.contains(outFile)) {
duke@1: log.error(cdef.pos(), "source.cant.overwrite.input.file", outFile);
duke@1: return null;
duke@1: } else {
duke@1: BufferedWriter out = new BufferedWriter(outFile.openWriter());
duke@1: try {
duke@1: new Pretty(out, true).printUnit(env.toplevel, cdef);
duke@1: if (verbose)
jjg@909: log.printVerbose("wrote.file", outFile);
duke@1: } finally {
duke@1: out.close();
duke@1: }
duke@1: return outFile;
duke@1: }
duke@1: }
duke@1:
duke@1: /** Generate code and emit a class file for a given class
duke@1: * @param env The attribution environment of the outermost class
duke@1: * containing this class.
duke@1: * @param cdef The class definition from which code is generated.
duke@1: */
duke@1: JavaFileObject genCode(Env env, JCClassDecl cdef) throws IOException {
duke@1: try {
jjg@257: if (gen.genClass(env, cdef) && (errorCount() == 0))
duke@1: return writer.writeClass(cdef.sym);
duke@1: } catch (ClassWriter.PoolOverflow ex) {
duke@1: log.error(cdef.pos(), "limit.pool");
duke@1: } catch (ClassWriter.StringOverflow ex) {
duke@1: log.error(cdef.pos(), "limit.string.overflow",
duke@1: ex.value.substring(0, 20));
duke@1: } catch (CompletionFailure ex) {
duke@1: chk.completionError(cdef.pos(), ex);
duke@1: }
duke@1: return null;
duke@1: }
duke@1:
duke@1: /** Complete compiling a source file that has been accessed
duke@1: * by the class file reader.
duke@1: * @param c The class the source file of which needs to be compiled.
duke@1: */
duke@1: public void complete(ClassSymbol c) throws CompletionFailure {
duke@1: // System.err.println("completing " + c);//DEBUG
duke@1: if (completionFailureName == c.fullname) {
duke@1: throw new CompletionFailure(c, "user-selected completion failure by class name");
duke@1: }
duke@1: JCCompilationUnit tree;
duke@1: JavaFileObject filename = c.classfile;
duke@1: JavaFileObject prev = log.useSource(filename);
duke@1:
duke@1: try {
duke@1: tree = parse(filename, filename.getCharContent(false));
duke@1: } catch (IOException e) {
jjg@510: log.error("error.reading.file", filename, JavacFileManager.getMessage(e));
duke@1: tree = make.TopLevel(List.nil(), null, List.nil());
duke@1: } finally {
duke@1: log.useSource(prev);
duke@1: }
duke@1:
jjg@1210: if (!taskListener.isEmpty()) {
duke@1: TaskEvent e = new TaskEvent(TaskEvent.Kind.ENTER, tree);
duke@1: taskListener.started(e);
duke@1: }
duke@1:
duke@1: enter.complete(List.of(tree), c);
duke@1:
jjg@1210: if (!taskListener.isEmpty()) {
duke@1: TaskEvent e = new TaskEvent(TaskEvent.Kind.ENTER, tree);
duke@1: taskListener.finished(e);
duke@1: }
duke@1:
duke@1: if (enter.getEnv(c) == null) {
duke@1: boolean isPkgInfo =
duke@1: tree.sourcefile.isNameCompatible("package-info",
duke@1: JavaFileObject.Kind.SOURCE);
duke@1: if (isPkgInfo) {
duke@1: if (enter.getEnv(tree.packge) == null) {
jjg@12: JCDiagnostic diag =
jjg@12: diagFactory.fragment("file.does.not.contain.package",
duke@1: c.location());
jjg@12: throw reader.new BadClassFile(c, filename, diag);
duke@1: }
duke@1: } else {
jjg@12: JCDiagnostic diag =
jjg@12: diagFactory.fragment("file.doesnt.contain.class",
jjg@12: c.getQualifiedName());
jjg@12: throw reader.new BadClassFile(c, filename, diag);
duke@1: }
duke@1: }
duke@1:
duke@1: implicitSourceFilesRead = true;
duke@1: }
duke@1:
duke@1: /** Track when the JavaCompiler has been used to compile something. */
duke@1: private boolean hasBeenUsed = false;
duke@1: private long start_msec = 0;
duke@1: public long elapsed_msec = 0;
duke@1:
duke@1: public void compile(List sourceFileObject)
duke@1: throws Throwable {
duke@1: compile(sourceFileObject, List.nil(), null);
duke@1: }
duke@1:
duke@1: /**
duke@1: * Main method: compile a list of files, return all compiled classes
duke@1: *
duke@1: * @param sourceFileObjects file objects to be compiled
duke@1: * @param classnames class names to process for annotations
duke@1: * @param processors user provided annotation processors to bypass
duke@1: * discovery, {@code null} means that no processors were provided
duke@1: */
duke@1: public void compile(List sourceFileObjects,
duke@1: List classnames,
duke@1: Iterable extends Processor> processors)
duke@1: {
duke@1: if (processors != null && processors.iterator().hasNext())
duke@1: explicitAnnotationProcessingRequested = true;
duke@1: // as a JavaCompiler can only be used once, throw an exception if
duke@1: // it has been used before.
duke@1: if (hasBeenUsed)
duke@1: throw new AssertionError("attempt to reuse JavaCompiler");
duke@1: hasBeenUsed = true;
duke@1:
jjg@897: // forcibly set the equivalent of -Xlint:-options, so that no further
jjg@897: // warnings about command line options are generated from this point on
jjg@1157: options.put(XLINT_CUSTOM.text + "-" + LintCategory.OPTIONS.option, "true");
jjg@1157: options.remove(XLINT_CUSTOM.text + LintCategory.OPTIONS.option);
jjg@897:
duke@1: start_msec = now();
jjg@757:
duke@1: try {
duke@1: initProcessAnnotations(processors);
duke@1:
duke@1: // These method calls must be chained to avoid memory leaks
jjg@257: delegateCompiler =
jjg@257: processAnnotations(
jjg@257: enterTrees(stopIfError(CompileState.PARSE, parseFiles(sourceFileObjects))),
jjg@257: classnames);
duke@1:
duke@1: delegateCompiler.compile2();
duke@1: delegateCompiler.close();
duke@1: elapsed_msec = delegateCompiler.elapsed_msec;
duke@1: } catch (Abort ex) {
duke@1: if (devVerbose)
jjg@757: ex.printStackTrace(System.err);
jjg@372: } finally {
jjg@372: if (procEnvImpl != null)
jjg@372: procEnvImpl.close();
duke@1: }
duke@1: }
duke@1:
duke@1: /**
duke@1: * The phases following annotation processing: attribution,
duke@1: * desugar, and finally code generation.
duke@1: */
duke@1: private void compile2() {
duke@1: try {
duke@1: switch (compilePolicy) {
duke@1: case ATTR_ONLY:
duke@1: attribute(todo);
duke@1: break;
duke@1:
duke@1: case CHECK_ONLY:
duke@1: flow(attribute(todo));
duke@1: break;
duke@1:
duke@1: case SIMPLE:
duke@1: generate(desugar(flow(attribute(todo))));
duke@1: break;
duke@1:
jjg@119: case BY_FILE: {
jjg@119: Queue>> q = todo.groupByFile();
jjg@257: while (!q.isEmpty() && !shouldStop(CompileState.ATTR)) {
jjg@119: generate(desugar(flow(attribute(q.remove()))));
jjg@119: }
jjg@119: }
duke@1: break;
duke@1:
duke@1: case BY_TODO:
jjg@119: while (!todo.isEmpty())
jjg@119: generate(desugar(flow(attribute(todo.remove()))));
duke@1: break;
duke@1:
duke@1: default:
jjg@816: Assert.error("unknown compile policy");
duke@1: }
duke@1: } catch (Abort ex) {
duke@1: if (devVerbose)
jjg@757: ex.printStackTrace(System.err);
duke@1: }
duke@1:
duke@1: if (verbose) {
jjg@70: elapsed_msec = elapsed(start_msec);
jjg@909: log.printVerbose("total", Long.toString(elapsed_msec));
duke@1: }
duke@1:
duke@1: reportDeferredDiagnostics();
duke@1:
duke@1: if (!log.hasDiagnosticListener()) {
duke@1: printCount("error", errorCount());
duke@1: printCount("warn", warningCount());
duke@1: }
duke@1: }
duke@1:
ohrstrom@1460: /**
ohrstrom@1460: * Set needRootClasses to true, in JavaCompiler subclass constructor
ohrstrom@1460: * that want to collect public apis of classes supplied on the command line.
ohrstrom@1460: */
ohrstrom@1460: protected boolean needRootClasses = false;
ohrstrom@1460:
ohrstrom@1460: /**
ohrstrom@1460: * The list of classes explicitly supplied on the command line for compilation.
ohrstrom@1460: * Not always populated.
ohrstrom@1460: */
duke@1: private List rootClasses;
duke@1:
duke@1: /**
duke@1: * Parses a list of files.
duke@1: */
jjg@663: public List parseFiles(Iterable fileObjects) {
jjg@257: if (shouldStop(CompileState.PARSE))
duke@1: return List.nil();
duke@1:
duke@1: //parse all files
duke@1: ListBuffer trees = lb();
sundar@678: Set filesSoFar = new HashSet();
sundar@678: for (JavaFileObject fileObject : fileObjects) {
sundar@678: if (!filesSoFar.contains(fileObject)) {
sundar@678: filesSoFar.add(fileObject);
sundar@678: trees.append(parse(fileObject));
sundar@678: }
sundar@678: }
duke@1: return trees.toList();
duke@1: }
duke@1:
duke@1: /**
jjg@1340: * Enter the symbols found in a list of parse trees if the compilation
jjg@1340: * is expected to proceed beyond anno processing into attr.
jjg@1340: * As a side-effect, this puts elements on the "todo" list.
jjg@1340: * Also stores a list of all top level classes in rootClasses.
jjg@1340: */
jjg@1340: public List enterTreesIfNeeded(List roots) {
jjg@1340: if (shouldStop(CompileState.ATTR))
jjg@1340: return List.nil();
jjg@1340: return enterTrees(roots);
jjg@1340: }
jjg@1340:
jjg@1340: /**
duke@1: * Enter the symbols found in a list of parse trees.
duke@1: * As a side-effect, this puts elements on the "todo" list.
duke@1: * Also stores a list of all top level classes in rootClasses.
duke@1: */
duke@1: public List enterTrees(List roots) {
duke@1: //enter symbols for all files
jjg@1210: if (!taskListener.isEmpty()) {
duke@1: for (JCCompilationUnit unit: roots) {
duke@1: TaskEvent e = new TaskEvent(TaskEvent.Kind.ENTER, unit);
duke@1: taskListener.started(e);
duke@1: }
duke@1: }
duke@1:
duke@1: enter.main(roots);
duke@1:
jjg@1210: if (!taskListener.isEmpty()) {
duke@1: for (JCCompilationUnit unit: roots) {
duke@1: TaskEvent e = new TaskEvent(TaskEvent.Kind.ENTER, unit);
duke@1: taskListener.finished(e);
duke@1: }
duke@1: }
duke@1:
ohrstrom@1460: // If generating source, or if tracking public apis,
ohrstrom@1460: // then remember the classes declared in
ohrstrom@1460: // the original compilation units listed on the command line.
ohrstrom@1460: if (needRootClasses || sourceOutput || stubOutput) {
duke@1: ListBuffer cdefs = lb();
duke@1: for (JCCompilationUnit unit : roots) {
duke@1: for (List defs = unit.defs;
duke@1: defs.nonEmpty();
duke@1: defs = defs.tail) {
duke@1: if (defs.head instanceof JCClassDecl)
duke@1: cdefs.append((JCClassDecl)defs.head);
duke@1: }
duke@1: }
duke@1: rootClasses = cdefs.toList();
duke@1: }
jjg@654:
jjg@654: // Ensure the input files have been recorded. Although this is normally
jjg@654: // done by readSource, it may not have been done if the trees were read
jjg@654: // in a prior round of annotation processing, and the trees have been
jjg@654: // cleaned and are being reused.
jjg@654: for (JCCompilationUnit unit : roots) {
jjg@654: inputFiles.add(unit.sourcefile);
jjg@654: }
jjg@654:
duke@1: return roots;
duke@1: }
duke@1:
duke@1: /**
duke@1: * Set to true to enable skeleton annotation processing code.
duke@1: * Currently, we assume this variable will be replaced more
duke@1: * advanced logic to figure out if annotation processing is
duke@1: * needed.
duke@1: */
duke@1: boolean processAnnotations = false;
duke@1:
jjg@1406: Log.DeferredDiagnosticHandler deferredDiagnosticHandler;
jjg@1406:
duke@1: /**
duke@1: * Object to handle annotation processing.
duke@1: */
jjg@372: private JavacProcessingEnvironment procEnvImpl = null;
duke@1:
duke@1: /**
duke@1: * Check if we should process annotations.
duke@1: * If so, and if no scanner is yet registered, then set up the DocCommentScanner
duke@1: * to catch doc comments, and set keepComments so the parser records them in
duke@1: * the compilation unit.
duke@1: *
duke@1: * @param processors user provided annotation processors to bypass
duke@1: * discovery, {@code null} means that no processors were provided
duke@1: */
jjg@663: public void initProcessAnnotations(Iterable extends Processor> processors) {
duke@1: // Process annotations if processing is not disabled and there
duke@1: // is at least one Processor available.
jjg@700: if (options.isSet(PROC, "none")) {
duke@1: processAnnotations = false;
duke@1: } else if (procEnvImpl == null) {
jjg@1416: procEnvImpl = JavacProcessingEnvironment.instance(context);
jjg@1416: procEnvImpl.setProcessors(processors);
duke@1: processAnnotations = procEnvImpl.atLeastOneProcessor();
duke@1:
duke@1: if (processAnnotations) {
duke@1: options.put("save-parameter-names", "save-parameter-names");
duke@1: reader.saveParameterNames = true;
duke@1: keepComments = true;
jjg@695: genEndPos = true;
jjg@1210: if (!taskListener.isEmpty())
duke@1: taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING));
jjg@1406: deferredDiagnosticHandler = new Log.DeferredDiagnosticHandler(log);
duke@1: } else { // free resources
duke@1: procEnvImpl.close();
duke@1: }
duke@1: }
duke@1: }
duke@1:
duke@1: // TODO: called by JavacTaskImpl
jjg@663: public JavaCompiler processAnnotations(List roots) {
duke@1: return processAnnotations(roots, List.nil());
duke@1: }
duke@1:
duke@1: /**
jjg@1210: * Process any annotations found in the specified compilation units.
duke@1: * @param roots a list of compilation units
duke@1: * @return an instance of the compiler in which to complete the compilation
duke@1: */
jjg@664: // Implementation note: when this method is called, log.deferredDiagnostics
jjg@664: // will have been set true by initProcessAnnotations, meaning that any diagnostics
jjg@664: // that are reported will go into the log.deferredDiagnostics queue.
jjg@664: // By the time this method exits, log.deferDiagnostics must be set back to false,
jjg@664: // and all deferredDiagnostics must have been handled: i.e. either reported
jjg@664: // or determined to be transient, and therefore suppressed.
duke@1: public JavaCompiler processAnnotations(List roots,
jjg@663: List classnames) {
jjg@257: if (shouldStop(CompileState.PROCESS)) {
jjg@642: // Errors were encountered.
jjg@664: // Unless all the errors are resolve errors, the errors were parse errors
jjg@642: // or other errors during enter which cannot be fixed by running
jjg@642: // any annotation processors.
jjg@664: if (unrecoverableError()) {
jjg@1406: deferredDiagnosticHandler.reportDeferredDiagnostics();
jjg@1406: log.popDiagnosticHandler(deferredDiagnosticHandler);
duke@1: return this;
jjg@664: }
duke@1: }
duke@1:
duke@1: // ASSERT: processAnnotations and procEnvImpl should have been set up by
duke@1: // by initProcessAnnotations
duke@1:
duke@1: // NOTE: The !classnames.isEmpty() checks should be refactored to Main.
duke@1:
duke@1: if (!processAnnotations) {
duke@1: // If there are no annotation processors present, and
duke@1: // annotation processing is to occur with compilation,
duke@1: // emit a warning.
jjg@700: if (options.isSet(PROC, "only")) {
duke@1: log.warning("proc.proc-only.requested.no.procs");
duke@1: todo.clear();
duke@1: }
duke@1: // If not processing annotations, classnames must be empty
duke@1: if (!classnames.isEmpty()) {
duke@1: log.error("proc.no.explicit.annotation.processing.requested",
duke@1: classnames);
duke@1: }
jjg@1406: Assert.checkNull(deferredDiagnosticHandler);
duke@1: return this; // continue regular compilation
duke@1: }
duke@1:
jjg@1406: Assert.checkNonNull(deferredDiagnosticHandler);
jjg@1406:
duke@1: try {
duke@1: List classSymbols = List.nil();
duke@1: List pckSymbols = List.nil();
duke@1: if (!classnames.isEmpty()) {
duke@1: // Check for explicit request for annotation
duke@1: // processing
duke@1: if (!explicitAnnotationProcessingRequested()) {
duke@1: log.error("proc.no.explicit.annotation.processing.requested",
duke@1: classnames);
jjg@1406: deferredDiagnosticHandler.reportDeferredDiagnostics();
jjg@1406: log.popDiagnosticHandler(deferredDiagnosticHandler);
duke@1: return this; // TODO: Will this halt compilation?
duke@1: } else {
duke@1: boolean errors = false;
duke@1: for (String nameStr : classnames) {
jjg@937: Symbol sym = resolveBinaryNameOrIdent(nameStr);
jjh@1197: if (sym == null ||
jjh@1197: (sym.kind == Kinds.PCK && !processPcks) ||
jjh@1197: sym.kind == Kinds.ABSENT_TYP) {
duke@1: log.error("proc.cant.find.class", nameStr);
duke@1: errors = true;
duke@1: continue;
duke@1: }
duke@1: try {
duke@1: if (sym.kind == Kinds.PCK)
duke@1: sym.complete();
duke@1: if (sym.exists()) {
duke@1: if (sym.kind == Kinds.PCK)
duke@1: pckSymbols = pckSymbols.prepend((PackageSymbol)sym);
duke@1: else
duke@1: classSymbols = classSymbols.prepend((ClassSymbol)sym);
duke@1: continue;
duke@1: }
jjg@816: Assert.check(sym.kind == Kinds.PCK);
duke@1: log.warning("proc.package.does.not.exist", nameStr);
duke@1: pckSymbols = pckSymbols.prepend((PackageSymbol)sym);
duke@1: } catch (CompletionFailure e) {
duke@1: log.error("proc.cant.find.class", nameStr);
duke@1: errors = true;
duke@1: continue;
duke@1: }
duke@1: }
jjg@664: if (errors) {
jjg@1406: deferredDiagnosticHandler.reportDeferredDiagnostics();
jjg@1406: log.popDiagnosticHandler(deferredDiagnosticHandler);
duke@1: return this;
jjg@664: }
duke@1: }
duke@1: }
jjg@372: try {
jjg@1406: JavaCompiler c = procEnvImpl.doProcessing(context, roots, classSymbols, pckSymbols,
jjg@1406: deferredDiagnosticHandler);
jjg@372: if (c != this)
jjg@372: annotationProcessingOccurred = c.annotationProcessingOccurred = true;
jjg@664: // doProcessing will have handled deferred diagnostics
jjg@372: return c;
jjg@372: } finally {
jjg@372: procEnvImpl.close();
jjg@372: }
duke@1: } catch (CompletionFailure ex) {
jjg@12: log.error("cant.access", ex.sym, ex.getDetailValue());
jjg@1406: deferredDiagnosticHandler.reportDeferredDiagnostics();
jjg@1406: log.popDiagnosticHandler(deferredDiagnosticHandler);
duke@1: return this;
jjg@664: }
jjg@664: }
duke@1:
jjg@664: private boolean unrecoverableError() {
jjg@1406: if (deferredDiagnosticHandler != null) {
jjg@1406: for (JCDiagnostic d: deferredDiagnosticHandler.getDiagnostics()) {
jjg@1406: if (d.getKind() == JCDiagnostic.Kind.ERROR && !d.isFlagSet(RECOVERABLE))
jjg@1406: return true;
jjg@1406: }
duke@1: }
jjg@664: return false;
duke@1: }
duke@1:
duke@1: boolean explicitAnnotationProcessingRequested() {
duke@1: return
duke@1: explicitAnnotationProcessingRequested ||
jjg@902: explicitAnnotationProcessingRequested(options);
jjg@902: }
jjg@902:
jjg@902: static boolean explicitAnnotationProcessingRequested(Options options) {
jjg@902: return
jjg@700: options.isSet(PROCESSOR) ||
jjg@700: options.isSet(PROCESSORPATH) ||
jjg@700: options.isSet(PROC, "only") ||
jjg@700: options.isSet(XPRINT);
duke@1: }
duke@1:
duke@1: /**
duke@1: * Attribute a list of parse trees, such as found on the "todo" list.
duke@1: * Note that attributing classes may cause additional files to be
duke@1: * parsed and entered via the SourceCompleter.
duke@1: * Attribution of the entries in the list does not stop if any errors occur.
duke@1: * @returns a list of environments for attributd classes.
duke@1: */
jjg@70: public Queue> attribute(Queue> envs) {
duke@1: ListBuffer> results = lb();
jjg@70: while (!envs.isEmpty())
jjg@70: results.append(attribute(envs.remove()));
jjg@257: return stopIfError(CompileState.ATTR, results);
duke@1: }
duke@1:
duke@1: /**
duke@1: * Attribute a parse tree.
duke@1: * @returns the attributed parse tree
duke@1: */
duke@1: public Env attribute(Env env) {
jjg@77: if (compileStates.isDone(env, CompileState.ATTR))
jjg@77: return env;
jjg@77:
duke@1: if (verboseCompilePolicy)
jjg@604: printNote("[attribute " + env.enclClass.sym + "]");
duke@1: if (verbose)
jjg@909: log.printVerbose("checking.attribution", env.enclClass.sym);
duke@1:
jjg@1210: if (!taskListener.isEmpty()) {
duke@1: TaskEvent e = new TaskEvent(TaskEvent.Kind.ANALYZE, env.toplevel, env.enclClass.sym);
duke@1: taskListener.started(e);
duke@1: }
duke@1:
duke@1: JavaFileObject prev = log.useSource(
duke@1: env.enclClass.sym.sourcefile != null ?
duke@1: env.enclClass.sym.sourcefile :
duke@1: env.toplevel.sourcefile);
duke@1: try {
jjg@931: attr.attrib(env);
mcimadamore@676: if (errorCount() > 0 && !shouldStop(CompileState.ATTR)) {
mcimadamore@676: //if in fail-over mode, ensure that AST expression nodes
mcimadamore@676: //are correctly initialized (e.g. they have a type/symbol)
mcimadamore@1348: attr.postAttr(env.tree);
mcimadamore@676: }
jjg@77: compileStates.put(env, CompileState.ATTR);
ohrstrom@1460: if (rootClasses != null && rootClasses.contains(env.enclClass)) {
ohrstrom@1460: // This was a class that was explicitly supplied for compilation.
ohrstrom@1460: // If we want to capture the public api of this class,
ohrstrom@1460: // then now is a good time to do it.
ohrstrom@1460: reportPublicApi(env.enclClass.sym);
ohrstrom@1460: }
duke@1: }
duke@1: finally {
duke@1: log.useSource(prev);
duke@1: }
duke@1:
duke@1: return env;
duke@1: }
duke@1:
ohrstrom@1460: /** Report the public api of a class that was supplied explicitly for compilation,
ohrstrom@1460: * for example on the command line to javac.
ohrstrom@1460: * @param sym The symbol of the class.
ohrstrom@1460: */
ohrstrom@1460: public void reportPublicApi(ClassSymbol sym) {
ohrstrom@1460: // Override to collect the reported public api.
ohrstrom@1460: }
ohrstrom@1460:
duke@1: /**
duke@1: * Perform dataflow checks on attributed parse trees.
duke@1: * These include checks for definite assignment and unreachable statements.
duke@1: * If any errors occur, an empty list will be returned.
duke@1: * @returns the list of attributed parse trees
duke@1: */
jjg@70: public Queue> flow(Queue> envs) {
duke@1: ListBuffer> results = lb();
jjg@70: for (Env env: envs) {
jjg@70: flow(env, results);
duke@1: }
jjg@257: return stopIfError(CompileState.FLOW, results);
duke@1: }
duke@1:
duke@1: /**
duke@1: * Perform dataflow checks on an attributed parse tree.
duke@1: */
jjg@70: public Queue> flow(Env env) {
duke@1: ListBuffer> results = lb();
duke@1: flow(env, results);
jjg@257: return stopIfError(CompileState.FLOW, results);
duke@1: }
duke@1:
duke@1: /**
duke@1: * Perform dataflow checks on an attributed parse tree.
duke@1: */
jjg@119: protected void flow(Env env, Queue> results) {
duke@1: try {
jjg@257: if (shouldStop(CompileState.FLOW))
duke@1: return;
duke@1:
jjg@77: if (relax || compileStates.isDone(env, CompileState.FLOW)) {
jjg@119: results.add(env);
duke@1: return;
duke@1: }
duke@1:
duke@1: if (verboseCompilePolicy)
jjg@257: printNote("[flow " + env.enclClass.sym + "]");
duke@1: JavaFileObject prev = log.useSource(
duke@1: env.enclClass.sym.sourcefile != null ?
duke@1: env.enclClass.sym.sourcefile :
duke@1: env.toplevel.sourcefile);
duke@1: try {
duke@1: make.at(Position.FIRSTPOS);
duke@1: TreeMaker localMake = make.forToplevel(env.toplevel);
mcimadamore@617: flow.analyzeTree(env, localMake);
jjg@77: compileStates.put(env, CompileState.FLOW);
duke@1:
jjg@257: if (shouldStop(CompileState.FLOW))
duke@1: return;
duke@1:
jjg@119: results.add(env);
duke@1: }
duke@1: finally {
duke@1: log.useSource(prev);
duke@1: }
duke@1: }
duke@1: finally {
jjg@1210: if (!taskListener.isEmpty()) {
duke@1: TaskEvent e = new TaskEvent(TaskEvent.Kind.ANALYZE, env.toplevel, env.enclClass.sym);
duke@1: taskListener.finished(e);
duke@1: }
duke@1: }
duke@1: }
duke@1:
duke@1: /**
duke@1: * Prepare attributed parse trees, in conjunction with their attribution contexts,
duke@1: * for source or code generation.
duke@1: * If any errors occur, an empty list will be returned.
duke@1: * @returns a list containing the classes to be generated
duke@1: */
jjg@70: public Queue, JCClassDecl>> desugar(Queue> envs) {
duke@1: ListBuffer, JCClassDecl>> results = lb();
jjg@70: for (Env env: envs)
jjg@70: desugar(env, results);
jjg@257: return stopIfError(CompileState.FLOW, results);
duke@1: }
duke@1:
mcimadamore@359: HashMap, Queue, JCClassDecl>>> desugaredEnvs =
mcimadamore@359: new HashMap, Queue, JCClassDecl>>>();
mcimadamore@359:
duke@1: /**
duke@1: * Prepare attributed parse trees, in conjunction with their attribution contexts,
duke@1: * for source or code generation. If the file was not listed on the command line,
duke@1: * the current implicitSourcePolicy is taken into account.
duke@1: * The preparation stops as soon as an error is found.
duke@1: */
jjg@77: protected void desugar(final Env env, Queue, JCClassDecl>> results) {
jjg@257: if (shouldStop(CompileState.TRANSTYPES))
duke@1: return;
duke@1:
duke@1: if (implicitSourcePolicy == ImplicitSourcePolicy.NONE
duke@1: && !inputFiles.contains(env.toplevel.sourcefile)) {
duke@1: return;
duke@1: }
duke@1:
mcimadamore@359: if (compileStates.isDone(env, CompileState.LOWER)) {
mcimadamore@359: results.addAll(desugaredEnvs.get(env));
mcimadamore@359: return;
mcimadamore@359: }
mcimadamore@359:
jjg@77: /**
mcimadamore@359: * Ensure that superclasses of C are desugared before C itself. This is
mcimadamore@359: * required for two reasons: (i) as erasure (TransTypes) destroys
mcimadamore@359: * information needed in flow analysis and (ii) as some checks carried
mcimadamore@359: * out during lowering require that all synthetic fields/methods have
mcimadamore@359: * already been added to C and its superclasses.
jjg@77: */
jjg@77: class ScanNested extends TreeScanner {
mcimadamore@95: Set> dependencies = new LinkedHashSet>();
jjg@257: @Override
jjg@77: public void visitClassDef(JCClassDecl node) {
jjg@77: Type st = types.supertype(node.sym.type);
jjg@1374: if (st.hasTag(CLASS)) {
jjg@77: ClassSymbol c = st.tsym.outermostClass();
jjg@77: Env stEnv = enter.getEnv(c);
mcimadamore@95: if (stEnv != null && env != stEnv) {
mcimadamore@95: if (dependencies.add(stEnv))
mcimadamore@95: scan(stEnv.tree);
mcimadamore@95: }
jjg@77: }
jjg@77: super.visitClassDef(node);
jjg@77: }
duke@1: }
jjg@77: ScanNested scanner = new ScanNested();
jjg@77: scanner.scan(env.tree);
jjg@77: for (Env dep: scanner.dependencies) {
mcimadamore@359: if (!compileStates.isDone(dep, CompileState.FLOW))
mcimadamore@359: desugaredEnvs.put(dep, desugar(flow(attribute(dep))));
jjg@77: }
duke@1:
mcimadamore@95: //We need to check for error another time as more classes might
mcimadamore@95: //have been attributed and analyzed at this stage
jjg@257: if (shouldStop(CompileState.TRANSTYPES))
mcimadamore@95: return;
mcimadamore@95:
duke@1: if (verboseCompilePolicy)
jjg@257: printNote("[desugar " + env.enclClass.sym + "]");
duke@1:
duke@1: JavaFileObject prev = log.useSource(env.enclClass.sym.sourcefile != null ?
duke@1: env.enclClass.sym.sourcefile :
duke@1: env.toplevel.sourcefile);
duke@1: try {
duke@1: //save tree prior to rewriting
duke@1: JCTree untranslated = env.tree;
duke@1:
duke@1: make.at(Position.FIRSTPOS);
duke@1: TreeMaker localMake = make.forToplevel(env.toplevel);
duke@1:
duke@1: if (env.tree instanceof JCCompilationUnit) {
duke@1: if (!(stubOutput || sourceOutput || printFlat)) {
jjg@257: if (shouldStop(CompileState.LOWER))
jjg@257: return;
duke@1: List pdef = lower.translateTopLevelClass(env, env.tree, localMake);
duke@1: if (pdef.head != null) {
jjg@816: Assert.check(pdef.tail.isEmpty());
jjg@70: results.add(new Pair, JCClassDecl>(env, (JCClassDecl)pdef.head));
duke@1: }
duke@1: }
duke@1: return;
duke@1: }
duke@1:
duke@1: if (stubOutput) {
duke@1: //emit stub Java source file, only for compilation
duke@1: //units enumerated explicitly on the command line
duke@1: JCClassDecl cdef = (JCClassDecl)env.tree;
duke@1: if (untranslated instanceof JCClassDecl &&
duke@1: rootClasses.contains((JCClassDecl)untranslated) &&
duke@1: ((cdef.mods.flags & (Flags.PROTECTED|Flags.PUBLIC)) != 0 ||
duke@1: cdef.sym.packge().getQualifiedName() == names.java_lang)) {
jjg@70: results.add(new Pair, JCClassDecl>(env, removeMethodBodies(cdef)));
duke@1: }
duke@1: return;
duke@1: }
duke@1:
jjg@257: if (shouldStop(CompileState.TRANSTYPES))
jjg@257: return;
jjg@257:
duke@1: env.tree = transTypes.translateTopLevelClass(env.tree, localMake);
mcimadamore@359: compileStates.put(env, CompileState.TRANSTYPES);
duke@1:
rfield@1380: if (shouldStop(CompileState.UNLAMBDA))
rfield@1380: return;
rfield@1380:
rfield@1380: env.tree = lambdaToMethod.translateTopLevelClass(env, env.tree, localMake);
rfield@1380: compileStates.put(env, CompileState.UNLAMBDA);
rfield@1380:
jjg@257: if (shouldStop(CompileState.LOWER))
duke@1: return;
duke@1:
duke@1: if (sourceOutput) {
duke@1: //emit standard Java source file, only for compilation
duke@1: //units enumerated explicitly on the command line
duke@1: JCClassDecl cdef = (JCClassDecl)env.tree;
duke@1: if (untranslated instanceof JCClassDecl &&
duke@1: rootClasses.contains((JCClassDecl)untranslated)) {
jjg@70: results.add(new Pair, JCClassDecl>(env, cdef));
duke@1: }
duke@1: return;
duke@1: }
duke@1:
duke@1: //translate out inner classes
duke@1: List cdefs = lower.translateTopLevelClass(env, env.tree, localMake);
mcimadamore@359: compileStates.put(env, CompileState.LOWER);
duke@1:
jjg@257: if (shouldStop(CompileState.LOWER))
duke@1: return;
duke@1:
duke@1: //generate code for each class
duke@1: for (List l = cdefs; l.nonEmpty(); l = l.tail) {
duke@1: JCClassDecl cdef = (JCClassDecl)l.head;
jjg@70: results.add(new Pair, JCClassDecl>(env, cdef));
duke@1: }
duke@1: }
duke@1: finally {
duke@1: log.useSource(prev);
duke@1: }
duke@1:
duke@1: }
duke@1:
duke@1: /** Generates the source or class file for a list of classes.
duke@1: * The decision to generate a source file or a class file is
duke@1: * based upon the compiler's options.
duke@1: * Generation stops if an error occurs while writing files.
duke@1: */
jjg@70: public void generate(Queue, JCClassDecl>> queue) {
jjg@70: generate(queue, null);
duke@1: }
duke@1:
jjg@119: public void generate(Queue, JCClassDecl>> queue, Queue results) {
jjg@257: if (shouldStop(CompileState.GENERATE))
jjg@257: return;
jjg@257:
duke@1: boolean usePrintSource = (stubOutput || sourceOutput || printFlat);
duke@1:
jjg@70: for (Pair, JCClassDecl> x: queue) {
duke@1: Env env = x.fst;
duke@1: JCClassDecl cdef = x.snd;
duke@1:
duke@1: if (verboseCompilePolicy) {
jjg@257: printNote("[generate "
duke@1: + (usePrintSource ? " source" : "code")
jjg@119: + " " + cdef.sym + "]");
duke@1: }
duke@1:
jjg@1210: if (!taskListener.isEmpty()) {
duke@1: TaskEvent e = new TaskEvent(TaskEvent.Kind.GENERATE, env.toplevel, cdef.sym);
duke@1: taskListener.started(e);
duke@1: }
duke@1:
duke@1: JavaFileObject prev = log.useSource(env.enclClass.sym.sourcefile != null ?
duke@1: env.enclClass.sym.sourcefile :
duke@1: env.toplevel.sourcefile);
duke@1: try {
duke@1: JavaFileObject file;
duke@1: if (usePrintSource)
duke@1: file = printSource(env, cdef);
jjg@1230: else {
jjg@1230: if (fileManager.hasLocation(StandardLocation.NATIVE_HEADER_OUTPUT)
jjg@1230: && jniWriter.needsHeader(cdef.sym)) {
jjg@1230: jniWriter.write(cdef.sym);
jjg@1230: }
duke@1: file = genCode(env, cdef);
jjg@1230: }
duke@1: if (results != null && file != null)
jjg@119: results.add(file);
duke@1: } catch (IOException ex) {
duke@1: log.error(cdef.pos(), "class.cant.write",
duke@1: cdef.sym, ex.getMessage());
duke@1: return;
duke@1: } finally {
duke@1: log.useSource(prev);
duke@1: }
duke@1:
jjg@1210: if (!taskListener.isEmpty()) {
duke@1: TaskEvent e = new TaskEvent(TaskEvent.Kind.GENERATE, env.toplevel, cdef.sym);
duke@1: taskListener.finished(e);
duke@1: }
duke@1: }
duke@1: }
duke@1:
duke@1: // where
jjg@70: Map>> groupByFile(Queue> envs) {
duke@1: // use a LinkedHashMap to preserve the order of the original list as much as possible
jjg@70: Map>> map = new LinkedHashMap>>();
jjg@70: for (Env env: envs) {
jjg@70: Queue> sublist = map.get(env.toplevel);
jjg@70: if (sublist == null) {
jjg@70: sublist = new ListBuffer>();
jjg@70: map.put(env.toplevel, sublist);
duke@1: }
jjg@70: sublist.add(env);
duke@1: }
duke@1: return map;
duke@1: }
duke@1:
duke@1: JCClassDecl removeMethodBodies(JCClassDecl cdef) {
duke@1: final boolean isInterface = (cdef.mods.flags & Flags.INTERFACE) != 0;
duke@1: class MethodBodyRemover extends TreeTranslator {
jjg@257: @Override
duke@1: public void visitMethodDef(JCMethodDecl tree) {
duke@1: tree.mods.flags &= ~Flags.SYNCHRONIZED;
duke@1: for (JCVariableDecl vd : tree.params)
duke@1: vd.mods.flags &= ~Flags.FINAL;
duke@1: tree.body = null;
duke@1: super.visitMethodDef(tree);
duke@1: }
jjg@257: @Override
duke@1: public void visitVarDef(JCVariableDecl tree) {
duke@1: if (tree.init != null && tree.init.type.constValue() == null)
duke@1: tree.init = null;
duke@1: super.visitVarDef(tree);
duke@1: }
jjg@257: @Override
duke@1: public void visitClassDef(JCClassDecl tree) {
duke@1: ListBuffer newdefs = lb();
duke@1: for (List it = tree.defs; it.tail != null; it = it.tail) {
duke@1: JCTree t = it.head;
duke@1: switch (t.getTag()) {
jjg@1127: case CLASSDEF:
duke@1: if (isInterface ||
duke@1: (((JCClassDecl) t).mods.flags & (Flags.PROTECTED|Flags.PUBLIC)) != 0 ||
duke@1: (((JCClassDecl) t).mods.flags & (Flags.PRIVATE)) == 0 && ((JCClassDecl) t).sym.packge().getQualifiedName() == names.java_lang)
duke@1: newdefs.append(t);
duke@1: break;
jjg@1127: case METHODDEF:
duke@1: if (isInterface ||
duke@1: (((JCMethodDecl) t).mods.flags & (Flags.PROTECTED|Flags.PUBLIC)) != 0 ||
duke@1: ((JCMethodDecl) t).sym.name == names.init ||
duke@1: (((JCMethodDecl) t).mods.flags & (Flags.PRIVATE)) == 0 && ((JCMethodDecl) t).sym.packge().getQualifiedName() == names.java_lang)
duke@1: newdefs.append(t);
duke@1: break;
jjg@1127: case VARDEF:
duke@1: if (isInterface || (((JCVariableDecl) t).mods.flags & (Flags.PROTECTED|Flags.PUBLIC)) != 0 ||
duke@1: (((JCVariableDecl) t).mods.flags & (Flags.PRIVATE)) == 0 && ((JCVariableDecl) t).sym.packge().getQualifiedName() == names.java_lang)
duke@1: newdefs.append(t);
duke@1: break;
duke@1: default:
duke@1: break;
duke@1: }
duke@1: }
duke@1: tree.defs = newdefs.toList();
duke@1: super.visitClassDef(tree);
duke@1: }
duke@1: }
duke@1: MethodBodyRemover r = new MethodBodyRemover();
duke@1: return r.translate(cdef);
duke@1: }
duke@1:
duke@1: public void reportDeferredDiagnostics() {
jjg@903: if (errorCount() == 0
jjg@903: && annotationProcessingOccurred
duke@1: && implicitSourceFilesRead
duke@1: && implicitSourcePolicy == ImplicitSourcePolicy.UNSET) {
duke@1: if (explicitAnnotationProcessingRequested())
duke@1: log.warning("proc.use.implicit");
duke@1: else
duke@1: log.warning("proc.use.proc.or.implicit");
duke@1: }
duke@1: chk.reportDeferredDiagnostics();
duke@1: }
duke@1:
duke@1: /** Close the compiler, flushing the logs
duke@1: */
duke@1: public void close() {
duke@1: close(true);
duke@1: }
duke@1:
jjg@113: public void close(boolean disposeNames) {
duke@1: rootClasses = null;
duke@1: reader = null;
duke@1: make = null;
duke@1: writer = null;
duke@1: enter = null;
duke@1: if (todo != null)
duke@1: todo.clear();
duke@1: todo = null;
duke@1: parserFactory = null;
duke@1: syms = null;
duke@1: source = null;
duke@1: attr = null;
duke@1: chk = null;
duke@1: gen = null;
duke@1: flow = null;
duke@1: transTypes = null;
duke@1: lower = null;
duke@1: annotate = null;
duke@1: types = null;
duke@1:
duke@1: log.flush();
duke@1: try {
duke@1: fileManager.flush();
duke@1: } catch (IOException e) {
duke@1: throw new Abort(e);
duke@1: } finally {
duke@1: if (names != null && disposeNames)
duke@1: names.dispose();
duke@1: names = null;
jjg@1096:
jjg@1096: for (Closeable c: closeables) {
jjg@1096: try {
jjg@1096: c.close();
jjg@1096: } catch (IOException e) {
jjg@1096: // When javac uses JDK 7 as a baseline, this code would be
jjg@1096: // better written to set any/all exceptions from all the
jjg@1096: // Closeables as suppressed exceptions on the FatalError
jjg@1096: // that is thrown.
jjg@1096: JCDiagnostic msg = diagFactory.fragment("fatal.err.cant.close");
jjg@1096: throw new FatalError(msg, e);
jjg@1096: }
jjg@1096: }
duke@1: }
duke@1: }
duke@1:
jjg@257: protected void printNote(String lines) {
jjg@1136: log.printRawLines(Log.WriterKind.NOTICE, lines);
jjg@257: }
jjg@257:
duke@1: /** Print numbers of errors and warnings.
duke@1: */
jjg@1455: public void printCount(String kind, int count) {
duke@1: if (count != 0) {
jjg@604: String key;
duke@1: if (count == 1)
jjg@604: key = "count." + kind;
duke@1: else
jjg@604: key = "count." + kind + ".plural";
jjg@1136: log.printLines(WriterKind.ERROR, key, String.valueOf(count));
jjg@1135: log.flush(Log.WriterKind.ERROR);
duke@1: }
duke@1: }
duke@1:
duke@1: private static long now() {
duke@1: return System.currentTimeMillis();
duke@1: }
duke@1:
duke@1: private static long elapsed(long then) {
duke@1: return now() - then;
duke@1: }
duke@1:
duke@1: public void initRound(JavaCompiler prev) {
jjg@695: genEndPos = prev.genEndPos;
duke@1: keepComments = prev.keepComments;
duke@1: start_msec = prev.start_msec;
duke@1: hasBeenUsed = true;
jjg@1096: closeables = prev.closeables;
jjg@1096: prev.closeables = List.nil();
jjg@1340: shouldStopPolicyIfError = prev.shouldStopPolicyIfError;
jjg@1340: shouldStopPolicyIfNoError = prev.shouldStopPolicyIfNoError;
duke@1: }
duke@1:
duke@1: public static void enableLogging() {
duke@1: Logger logger = Logger.getLogger(com.sun.tools.javac.Main.class.getPackage().getName());
duke@1: logger.setLevel(Level.ALL);
duke@1: for (Handler h : logger.getParent().getHandlers()) {
duke@1: h.setLevel(Level.ALL);
duke@1: }
duke@1:
duke@1: }
duke@1: }