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