duke@1: /*
xdono@54: * Copyright 1999-2008 Sun Microsystems, Inc. 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
duke@1: * published by the Free Software Foundation. Sun designates this
duke@1: * particular file as subject to the "Classpath" exception as provided
duke@1: * by Sun 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: *
duke@1: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
duke@1: * CA 95054 USA or visit www.sun.com if you need additional information or
duke@1: * have any questions.
duke@1: */
duke@1:
duke@1: package com.sun.tools.javac.main;
duke@1:
duke@1: import java.io.*;
duke@1: import java.util.HashSet;
mcimadamore@95: import java.util.LinkedHashSet;
duke@1: import java.util.LinkedHashMap;
duke@1: import java.util.Map;
duke@1: import java.util.MissingResourceException;
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:
duke@1: import javax.tools.JavaFileManager;
duke@1: import javax.tools.JavaFileObject;
duke@1: import javax.tools.DiagnosticListener;
duke@1:
jjg@50: import com.sun.tools.javac.file.JavacFileManager;
duke@1: import com.sun.source.util.TaskEvent;
duke@1: import com.sun.source.util.TaskListener;
duke@1:
duke@1: import com.sun.tools.javac.util.*;
duke@1: import com.sun.tools.javac.code.*;
duke@1: import com.sun.tools.javac.tree.*;
duke@1: import com.sun.tools.javac.parser.*;
duke@1: import com.sun.tools.javac.comp.*;
duke@1: import com.sun.tools.javac.jvm.*;
duke@1:
duke@1: import com.sun.tools.javac.code.Symbol.*;
duke@1: import com.sun.tools.javac.tree.JCTree.*;
duke@1:
duke@1: import com.sun.tools.javac.processing.*;
duke@1: import javax.annotation.processing.Processor;
duke@1:
duke@1: import static javax.tools.StandardLocation.CLASS_OUTPUT;
duke@1: import static com.sun.tools.javac.util.ListBuffer.lb;
duke@1:
duke@1: // TEMP, until we have a more efficient way to save doc comment info
duke@1: import com.sun.tools.javac.parser.DocCommentScanner;
duke@1:
jjg@77: import java.util.HashMap;
jjg@70: import java.util.Queue;
duke@1: import javax.lang.model.SourceVersion;
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: *
duke@1: *
This is NOT part of any API supported by Sun Microsystems. If
duke@1: * 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) {
duke@1: return Log.getLocalizedString("version.resource.missing", System.getProperty("java.version"));
duke@1: }
duke@1: }
duke@1: try {
duke@1: return versionRB.getString(key);
duke@1: }
duke@1: catch (MissingResourceException e) {
duke@1: return Log.getLocalizedString("version.unknown", System.getProperty("java.version"));
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:
duke@1: private static 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:
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:
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:
duke@1: /** Optional listener for progress events
duke@1: */
duke@1: protected TaskListener 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: /**
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: protected Context context;
duke@1:
duke@1: /** Construct a new compiler using a shared context.
duke@1: */
duke@1: public JavaCompiler(final 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);
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);
duke@1: taskListener = context.get(TaskListener.class);
duke@1:
duke@1: reader.sourceCompleter = this;
duke@1:
duke@1: Options options = Options.instance(context);
duke@1:
duke@1: verbose = options.get("-verbose") != null;
duke@1: sourceOutput = options.get("-printsource") != null; // used to be -s
duke@1: stubOutput = options.get("-stubs") != null;
duke@1: relax = options.get("-relax") != null;
duke@1: printFlat = options.get("-printflat") != null;
duke@1: attrParseOnly = options.get("-attrparseonly") != null;
duke@1: encoding = options.get("-encoding");
duke@1: lineDebugInfo = options.get("-g:") == null ||
duke@1: options.get("-g:lines") != null;
duke@1: genEndPos = options.get("-Xjcov") != null ||
duke@1: context.get(DiagnosticListener.class) != null;
duke@1: devVerbose = options.get("dev") != null;
duke@1: processPcks = options.get("process.packages") != null;
duke@1:
duke@1: verboseCompilePolicy = options.get("verboseCompilePolicy") != null;
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 =
duke@1: (options.get("failcomplete") != null)
duke@1: ? names.fromString(options.get("failcomplete"))
duke@1: : null;
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:
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:
duke@1: /** A queue of all as yet unattributed classes.
duke@1: */
duke@1: public Todo todo;
duke@1:
jjg@77: protected enum CompileState {
jjg@77: TODO(0),
jjg@77: ATTR(1),
jjg@77: FLOW(2);
jjg@77: CompileState(int value) {
jjg@77: this.value = value;
jjg@77: }
jjg@77: boolean isDone(CompileState other) {
jjg@77: return value >= other.value;
jjg@77: }
jjg@77: private int value;
jjg@77: };
jjg@77: protected class CompileStates extends HashMap,CompileState> {
jjg@77: boolean isDone(Env env, CompileState cs) {
jjg@77: CompileState ecs = get(env);
jjg@77: return ecs != null && ecs.isDone(cs);
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:
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();
duke@1: else
duke@1: return log.nerrors;
duke@1: }
duke@1:
jjg@70: protected final Queue stopIfError(Queue queue) {
duke@1: if (errorCount() == 0)
jjg@70: return queue;
duke@1: else
jjg@70: return ListBuffer.lb();
duke@1: }
duke@1:
duke@1: protected final List stopIfError(List list) {
duke@1: if (errorCount() == 0)
duke@1: return list;
duke@1: else
duke@1: return List.nil();
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: /** Whether or not any parse errors have occurred.
duke@1: */
duke@1: public boolean parseErrors() {
duke@1: return parseErrors;
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) {
duke@1: log.error("error.reading.file", filename, e.getLocalizedMessage());
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.
duke@1: * @param input The input stream 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) {
duke@1: printVerbose("parsing.started", filename);
duke@1: }
duke@1: if (taskListener != null) {
duke@1: TaskEvent e = new TaskEvent(TaskEvent.Kind.PARSE, filename);
duke@1: taskListener.started(e);
duke@1: }
duke@1: int initialErrorCount = log.nerrors;
jjg@111: Parser parser = parserFactory.newParser(content, keepComments(), genEndPos, lineDebugInfo);
jjg@111: tree = parser.parseCompilationUnit();
duke@1: parseErrors |= (log.nerrors > initialErrorCount);
duke@1: if (verbose) {
duke@1: printVerbose("parsing.done", Long.toString(elapsed(msec)));
duke@1: }
duke@1: }
duke@1:
duke@1: tree.sourcefile = filename;
duke@1:
duke@1: if (content != null && taskListener != null) {
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
duke@1: public JCTree.JCCompilationUnit parse(String filename) throws IOException {
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:
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)
duke@1: 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 {
duke@1: if (gen.genClass(env, cdef))
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: * @param filename The name of the source file.
duke@1: * @param f An input stream that reads the source file.
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) {
duke@1: log.error("error.reading.file", filename, e);
duke@1: tree = make.TopLevel(List.nil(), null, List.nil());
duke@1: } finally {
duke@1: log.useSource(prev);
duke@1: }
duke@1:
duke@1: if (taskListener != null) {
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:
duke@1: if (taskListener != null) {
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: /** Track whether any errors occurred while parsing source text. */
duke@1: private boolean parseErrors = false;
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: throws IOException // TODO: temp, from JavacProcessingEnvironment
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:
duke@1: start_msec = now();
duke@1: try {
duke@1: initProcessAnnotations(processors);
duke@1:
duke@1: // These method calls must be chained to avoid memory leaks
duke@1: delegateCompiler = processAnnotations(enterTrees(stopIfError(parseFiles(sourceFileObjects))),
duke@1: 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)
duke@1: ex.printStackTrace();
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@119: while (!q.isEmpty() && errorCount() == 0) {
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:
duke@1: assert false: "unknown compile policy";
duke@1: }
duke@1: } catch (Abort ex) {
duke@1: if (devVerbose)
duke@1: ex.printStackTrace();
duke@1: }
duke@1:
duke@1: if (verbose) {
jjg@70: elapsed_msec = elapsed(start_msec);
duke@1: 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:
duke@1: private List rootClasses;
duke@1:
duke@1: /**
duke@1: * Parses a list of files.
duke@1: */
duke@1: public List parseFiles(List fileObjects) throws IOException {
duke@1: if (errorCount() > 0)
duke@1: return List.nil();
duke@1:
duke@1: //parse all files
duke@1: ListBuffer trees = lb();
duke@1: for (JavaFileObject fileObject : fileObjects)
duke@1: trees.append(parse(fileObject));
duke@1: return trees.toList();
duke@1: }
duke@1:
duke@1: /**
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
duke@1: if (taskListener != null) {
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:
duke@1: if (taskListener != null) {
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:
duke@1: //If generating source, remember the classes declared in
duke@1: //the original compilation units listed on the command line.
duke@1: if (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: }
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:
duke@1: /**
duke@1: * Object to handle annotation processing.
duke@1: */
duke@1: 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: */
duke@1: 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.
duke@1: Options options = Options.instance(context);
duke@1: if (options.get("-proc:none") != null) {
duke@1: processAnnotations = false;
duke@1: } else if (procEnvImpl == null) {
duke@1: procEnvImpl = new JavacProcessingEnvironment(context, processors);
duke@1: processAnnotations = procEnvImpl.atLeastOneProcessor();
duke@1:
duke@1: if (processAnnotations) {
duke@1: if (context.get(Scanner.Factory.scannerFactoryKey) == null)
duke@1: DocCommentScanner.Factory.preRegister(context);
duke@1: options.put("save-parameter-names", "save-parameter-names");
duke@1: reader.saveParameterNames = true;
duke@1: keepComments = true;
duke@1: if (taskListener != null)
duke@1: taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING));
duke@1:
duke@1:
duke@1: } else { // free resources
duke@1: procEnvImpl.close();
duke@1: }
duke@1: }
duke@1: }
duke@1:
duke@1: // TODO: called by JavacTaskImpl
duke@1: public JavaCompiler processAnnotations(List roots) throws IOException {
duke@1: return processAnnotations(roots, List.nil());
duke@1: }
duke@1:
duke@1: /**
duke@1: * Process any anotations found in the specifed 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: */
duke@1: public JavaCompiler processAnnotations(List roots,
duke@1: List classnames)
duke@1: throws IOException { // TODO: see TEMP note in JavacProcessingEnvironment
duke@1: if (errorCount() != 0) {
duke@1: // Errors were encountered. If todo is empty, then the
duke@1: // encountered errors were parse errors. Otherwise, the
duke@1: // errors were found during the enter phase which should
duke@1: // be ignored when processing annotations.
duke@1:
duke@1: if (todo.isEmpty())
duke@1: return this;
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.
duke@1: Options options = Options.instance(context);
duke@1: if (options.get("-proc:only") != null) {
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: }
duke@1: return this; // continue regular compilation
duke@1: }
duke@1:
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);
duke@1: return this; // TODO: Will this halt compilation?
duke@1: } else {
duke@1: boolean errors = false;
duke@1: for (String nameStr : classnames) {
duke@1: Symbol sym = resolveIdent(nameStr);
duke@1: if (sym == null || (sym.kind == Kinds.PCK && !processPcks)) {
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: Name name = names.fromString(nameStr);
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: }
duke@1: assert 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: }
duke@1: if (errors)
duke@1: return this;
duke@1: }
duke@1: }
duke@1: JavaCompiler c = procEnvImpl.doProcessing(context, roots, classSymbols, pckSymbols);
duke@1: if (c != this)
duke@1: annotationProcessingOccurred = c.annotationProcessingOccurred = true;
duke@1: return c;
duke@1: } catch (CompletionFailure ex) {
jjg@12: log.error("cant.access", ex.sym, ex.getDetailValue());
duke@1: return this;
duke@1:
duke@1: }
duke@1: }
duke@1:
duke@1: boolean explicitAnnotationProcessingRequested() {
duke@1: Options options = Options.instance(context);
duke@1: return
duke@1: explicitAnnotationProcessingRequested ||
duke@1: options.get("-processor") != null ||
duke@1: options.get("-processorpath") != null ||
duke@1: options.get("-proc:only") != null ||
duke@1: options.get("-Xprint") != null;
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@70: return 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)
duke@1: log.printLines(log.noticeWriter, "[attribute " + env.enclClass.sym + "]");
duke@1: if (verbose)
duke@1: printVerbose("checking.attribution", env.enclClass.sym);
duke@1:
duke@1: if (taskListener != null) {
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 {
duke@1: attr.attribClass(env.tree.pos(), env.enclClass.sym);
jjg@77: compileStates.put(env, CompileState.ATTR);
duke@1: }
duke@1: finally {
duke@1: log.useSource(prev);
duke@1: }
duke@1:
duke@1: return env;
duke@1: }
duke@1:
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: }
duke@1: return stopIfError(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);
duke@1: return stopIfError(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 {
duke@1: if (errorCount() > 0)
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)
duke@1: log.printLines(log.noticeWriter, "[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);
duke@1: flow.analyzeTree(env.tree, localMake);
jjg@77: compileStates.put(env, CompileState.FLOW);
duke@1:
duke@1: if (errorCount() > 0)
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 {
duke@1: if (taskListener != null) {
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);
duke@1: return stopIfError(results);
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. 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) {
duke@1: if (errorCount() > 0)
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:
jjg@77: /**
jjg@77: * As erasure (TransTypes) destroys information needed in flow analysis,
jjg@77: * including information in supertypes, we need to ensure that supertypes
jjg@77: * are processed through attribute and flow before subtypes are translated.
jjg@77: */
jjg@77: class ScanNested extends TreeScanner {
mcimadamore@95: Set> dependencies = new LinkedHashSet>();
jjg@77: public void visitClassDef(JCClassDecl node) {
jjg@77: Type st = types.supertype(node.sym.type);
jjg@77: if (st.tag == TypeTags.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) {
jjg@77: if (!compileStates.isDone(dep, CompileState.FLOW))
jjg@77: 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
mcimadamore@95: if (errorCount() > 0)
mcimadamore@95: return;
mcimadamore@95:
duke@1: if (verboseCompilePolicy)
duke@1: log.printLines(log.noticeWriter, "[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)) {
duke@1: List pdef = lower.translateTopLevelClass(env, env.tree, localMake);
duke@1: if (pdef.head != null) {
duke@1: assert 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:
duke@1: env.tree = transTypes.translateTopLevelClass(env.tree, localMake);
duke@1:
duke@1: if (errorCount() != 0)
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);
duke@1:
duke@1: if (errorCount() != 0)
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) {
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) {
duke@1: log.printLines(log.noticeWriter, "[generate "
duke@1: + (usePrintSource ? " source" : "code")
jjg@119: + " " + cdef.sym + "]");
duke@1: }
duke@1:
duke@1: if (taskListener != null) {
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);
duke@1: else
duke@1: file = genCode(env, cdef);
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:
duke@1: if (taskListener != null) {
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 {
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: }
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: }
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()) {
duke@1: case JCTree.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;
duke@1: case JCTree.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;
duke@1: case JCTree.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() {
duke@1: if (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;
duke@1: }
duke@1: }
duke@1:
duke@1: /** Output for "-verbose" option.
duke@1: * @param key The key to look up the correct internationalized string.
duke@1: * @param arg An argument for substitution into the output string.
duke@1: */
duke@1: protected void printVerbose(String key, Object arg) {
duke@1: Log.printLines(log.noticeWriter, log.getLocalizedString("verbose." + key, arg));
duke@1: }
duke@1:
duke@1: /** Print numbers of errors and warnings.
duke@1: */
duke@1: protected void printCount(String kind, int count) {
duke@1: if (count != 0) {
duke@1: String text;
duke@1: if (count == 1)
duke@1: text = log.getLocalizedString("count." + kind, String.valueOf(count));
duke@1: else
duke@1: text = log.getLocalizedString("count." + kind + ".plural", String.valueOf(count));
duke@1: Log.printLines(log.errWriter, text);
duke@1: log.errWriter.flush();
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) {
duke@1: keepComments = prev.keepComments;
duke@1: start_msec = prev.start_msec;
duke@1: hasBeenUsed = true;
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: }