duke@1: /* jjg@1210: * Copyright (c) 1999, 2012, 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@1340: /** A queue of all as yet unattributed classes.oLo 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); 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 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: 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: 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: } 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 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); 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: } 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: */ duke@1: protected 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: }