/*
* Copyright (c) 1999, 2017, 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.Collection;
import java.util.Collections;
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 java.util.function.Function;
import javax.annotation.processing.Processor;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.ElementVisitor;
import javax.tools.DiagnosticListener;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;
import javax.tools.StandardLocation;
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.ClassSymbol;
import com.sun.tools.javac.code.Symbol.CompletionFailure;
import com.sun.tools.javac.code.Symbol.PackageSymbol;
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.platform.PlatformDescription;
import com.sun.tools.javac.processing.*;
import com.sun.tools.javac.tree.*;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCLambda;
import com.sun.tools.javac.tree.JCTree.JCMemberReference;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.util.*;
import com.sun.tools.javac.util.DefinedBy.Api;
import com.sun.tools.javac.util.JCDiagnostic.Factory;
import com.sun.tools.javac.util.Log.DiagnosticHandler;
import com.sun.tools.javac.util.Log.DiscardDiagnosticHandler;
import com.sun.tools.javac.util.Log.WriterKind;
import static com.sun.tools.javac.code.Kinds.Kind.*;
import com.sun.tools.javac.code.Symbol.ModuleSymbol;
import com.sun.tools.javac.resources.CompilerProperties.Errors;
import com.sun.tools.javac.resources.CompilerProperties.Warnings;
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.*;
import static javax.tools.StandardLocation.CLASS_OUTPUT;
import com.sun.tools.javac.tree.JCTree.JCModuleDecl;
This class could be the main entry point for GJC when GJC is used as a
component in a larger software system. It provides operations to
construct a new compiler, and to run a new compiler on a set of source
files.
This is NOT part of any supported API.
If you write code that depends on this, you do so at your own risk.
This code and its internal interfaces are subject to change or
deletion without notice.
/** 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. /** The context key for the compiler. */
public static final Context.Key<JavaCompiler> compilerKey = new Context.Key<>();
Get the JavaCompiler instance for this context. /** 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.
/** 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.
/** The current full version number as a string.
*/
public static String fullVersion() {
return version("full"); // mm.mm.oo[-milestone]-build
}
private static final String versionRBName = "com.sun.tools.javac.resources.version";
private static ResourceBundle versionRB;
private static String version(String key) {
if (versionRB == null) {
try {
versionRB = ResourceBundle.getBundle(versionRBName);
} catch (MissingResourceException e) {
return Log.getLocalizedString("version.not.available");
}
}
try {
return versionRB.getString(key);
}
catch (MissingResourceException e) {
return Log.getLocalizedString("version.not.available");
}
}
Control how the compiler's latter phases (attr, flow, desugar, generate)
are connected. Each individual file is processed by each phase in turn,
but with different compile policies, you can control the order in which
each class is processed through its next phase.
Generally speaking, the compiler will "fail fast" in the face of
errors, although not aggressively so. flow, desugar, etc become no-ops
once any errors have occurred. No attempt is currently made to determine
if it might be safe to process a class through its next phase because
it does not depend on any unrelated errors that might have occurred.
/**
* 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.
/**
* Just attribute the parse trees.
*/
ATTR_ONLY,
Just attribute and do flow analysis on the parse trees.
This should catch most user errors.
/**
* 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.
/**
* 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 SIMPLE
policy. This means no output will be generated if there are any errors in any of the classes in a source file. /**
* 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.
/**
* 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. /** Don't generate or process implicitly read source files. */
NONE,
Generate classes for implicitly read source files. /** Generate classes for implicitly read source files. */
CLASS,
Like CLASS, but generate warnings if annotation processing occurs /** 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.
/** The log to be used for error reporting.
*/
public Log log;
Factory for creating diagnostic objects
/** Factory for creating diagnostic objects
*/
JCDiagnostic.Factory diagFactory;
The tree factory module.
/** The tree factory module.
*/
protected TreeMaker make;
The class finder.
/** The class finder.
*/
protected ClassFinder finder;
The class reader.
/** The class reader.
*/
protected ClassReader reader;
The class writer.
/** The class writer.
*/
protected ClassWriter writer;
The native header writer.
/** The native header writer.
*/
protected JNIWriter jniWriter;
The module for the symbol table entry phases.
/** The module for the symbol table entry phases.
*/
protected Enter enter;
The symbol table.
/** The symbol table.
*/
protected Symtab syms;
The language version.
/** The language version.
*/
protected Source source;
The module for code generation.
/** The module for code generation.
*/
protected Gen gen;
The name table.
/** The name table.
*/
protected Names names;
The attributor.
/** The attributor.
*/
protected Attr attr;
The attributor.
/** The attributor.
*/
protected Check chk;
The flow analyzer.
/** The flow analyzer.
*/
protected Flow flow;
The modules visitor
/** The modules visitor
*/
protected Modules modules;
The module finder
/** The module finder
*/
protected ModuleFinder moduleFinder;
The diagnostics factory
/** The diagnostics factory
*/
protected JCDiagnostic.Factory diags;
The type eraser.
/** The type eraser.
*/
protected TransTypes transTypes;
The syntactic sugar desweetener.
/** The syntactic sugar desweetener.
*/
protected Lower lower;
The annotation annotator.
/** The annotation annotator.
*/
protected Annotate annotate;
Force a completion failure on this name
/** Force a completion failure on this name
*/
protected final Name completionFailureName;
Type utilities.
/** Type utilities.
*/
protected Types types;
Access to file objects.
/** Access to file objects.
*/
protected JavaFileManager fileManager;
Factory for parsers.
/** Factory for parsers.
*/
protected ParserFactory parserFactory;
Broadcasting listener for progress events
/** Broadcasting listener for progress events
*/
protected MultiTaskListener taskListener;
SourceCompleter that delegates to the readSourceFile method of this class.
/**
* SourceCompleter that delegates to the readSourceFile method of this class.
*/
protected final Symbol.Completer sourceCompleter =
sym -> readSourceFile((ClassSymbol) sym);
Command line options.
/**
* Command line options.
*/
protected Options options;
protected Context context;
Flag set if any annotation processing occurred.
/**
* Flag set if any annotation processing occurred.
**/
protected boolean annotationProcessingOccurred;
Flag set if any implicit source files read.
/**
* Flag set if any implicit source files read.
**/
protected boolean implicitSourceFilesRead;
private boolean enterDone;
protected CompileStates compileStates;
Construct a new compiler using a shared context.
/** 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);
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);
modules = Modules.instance(context);
moduleFinder = ModuleFinder.instance(context);
diags = Factory.instance(context);
finder.sourceCompleter = sourceCompleter;
modules.findPackageInFile = this::findPackageInFile;
moduleFinder.moduleNameFromSourceReader = this::readModuleName;
options = Options.instance(context);
verbose = options.isSet(VERBOSE);
sourceOutput = options.isSet(PRINTSOURCE); // used to be -s
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);
verboseCompilePolicy = options.isSet("verboseCompilePolicy");
if (options.isSet("should-stop.at") &&
CompileState.valueOf(options.get("should-stop.at")) == CompileState.ATTR)
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("should-stop.at") // backwards compatible
? CompileState.valueOf(options.get("should-stop.at"))
: options.isSet("should-stop.ifError")
? CompileState.valueOf(options.get("should-stop.ifError"))
: CompileState.INIT;
shouldStopPolicyIfNoError =
options.isSet("should-stop.ifNoError")
? CompileState.valueOf(options.get("should-stop.ifNoError"))
: CompileState.GENERATE;
if (options.isUnset("diags.legacy"))
log.setDiagnosticFormatter(RichDiagnosticFormatter.instance(context));
PlatformDescription platformProvider = context.get(PlatformDescription.class);
if (platformProvider != null)
closeables = closeables.prepend(platformProvider);
silentFail = new Symbol(ABSENT_TYP, 0, names.empty, Type.noType, syms.rootPackage) {
@DefinedBy(Api.LANGUAGE_MODEL)
public <R, P> R accept(ElementVisitor<R, P> v, P p) {
return v.visitUnknown(this, p);
}
@Override
public boolean exists() {
return false;
}
};
}
/* Switches:
*/
Verbose output.
/** Verbose output.
*/
public boolean verbose;
Emit plain Java source files rather than class files.
/** Emit plain Java source files rather than class files.
*/
public boolean sourceOutput;
Generate code with the LineNumberTable attribute for debugging
/** Generate code with the LineNumberTable attribute for debugging
*/
public boolean lineDebugInfo;
Switch: should we store the ending positions?
/** Switch: should we store the ending positions?
*/
public boolean genEndPos;
Switch: should we debug ignored exceptions
/** Switch: should we debug ignored exceptions
*/
protected boolean devVerbose;
Switch: should we (annotation) process packages as well
/** Switch: should we (annotation) process packages as well
*/
protected boolean processPcks;
Switch: treat warnings as errors
/** Switch: treat warnings as errors
*/
protected boolean werror;
Switch: is annotation processing requested explicitly via
CompilationTask.setProcessors?
/** Switch: is annotation processing requested explicitly via
* CompilationTask.setProcessors?
*/
protected boolean explicitAnnotationProcessingRequested = false;
The policy for the order in which to perform the compilation
/**
* 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
/**
* The policy for what to do with implicitly read source files
*/
protected ImplicitSourcePolicy implicitSourcePolicy;
Report activity related to compilePolicy
/**
* 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.
/**
* 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.
/**
* 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.
/** A queue of all as yet unattributed classes.
*/
public Todo todo;
A list of items to be closed when the compilation is complete.
/** 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'.
/** 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<>();
Used by the resolveBinaryNameOrIdent to say that the given type cannot be found, and that
an error has already been produced about that.
/** Used by the resolveBinaryNameOrIdent to say that the given type cannot be found, and that
* an error has already been produced about that.
*/
private final Symbol silentFail;
protected boolean shouldStop(CompileState cs) {
CompileState shouldStopPolicy = (errorCount() > 0 || unrecoverableError())
? shouldStopPolicyIfError
: shouldStopPolicyIfNoError;
return cs.isAfter(shouldStopPolicy);
}
The number of errors reported so far.
/** 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.nil() : list;
}
The number of warnings reported so far.
/** 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.
/** 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.
/** Parse contents of input stream.
* @param filename The name of the file from which input stream comes.
* @param content The characters to be parsed.
*/
protected JCCompilationUnit parse(JavaFileObject filename, CharSequence content) {
long msec = now();
JCCompilationUnit tree = make.TopLevel(List.nil());
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, filename.isNameCompatible("module-info", Kind.SOURCE));
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;
}
Parse contents of file.
@param filename The name of the file to be parsed.
/** 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.
/** 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.
Params: - name – The name to resolve
/** 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) {
ModuleSymbol msym;
String typeName;
int sep = name.indexOf('/');
if (sep == -1) {
msym = modules.getDefaultModule();
typeName = name;
} else if (source.allowModules()) {
Name modName = names.fromString(name.substring(0, sep));
msym = moduleFinder.findModule(modName);
typeName = name.substring(sep + 1);
} else {
log.error(Errors.InvalidModuleSpecifier(name));
return silentFail;
}
return resolveBinaryNameOrIdent(msym, typeName);
}
Resolve an identifier which may be the binary name of a class or
the Java name of a class or package.
Params: - msym – The module in which the search should be performed
- name – The name to resolve
/** Resolve an identifier which may be the binary name of a class or
* the Java name of a class or package.
* @param msym The module in which the search should be performed
* @param name The name to resolve
*/
public Symbol resolveBinaryNameOrIdent(ModuleSymbol msym, String name) {
try {
Name flatname = names.fromString(name.replace("/", "."));
return finder.loadClass(msym, flatname);
} catch (CompletionFailure ignore) {
return resolveIdent(msym, name);
}
}
Resolve an identifier.
Params: - msym – The module in which the search should be performed
- name – The identifier to resolve
/** Resolve an identifier.
* @param msym The module in which the search should be performed
* @param name The identifier to resolve
*/
public Symbol resolveIdent(ModuleSymbol msym, String name) {
if (name.equals(""))
return syms.errSymbol;
JavaFileObject prev = log.useSource(null);
try {
JCExpression tree = null;
for (String s : name.split("\\.", -1)) {
if (!SourceVersion.isIdentifier(s)) // TODO: check for keywords
return syms.errSymbol;
tree = (tree == null) ? make.Ident(names.fromString(s))
: make.Select(tree, names.fromString(s));
}
JCCompilationUnit toplevel =
make.TopLevel(List.nil());
toplevel.modle = msym;
toplevel.packge = msym.unnamedPackage;
return attr.attribIdent(tree, toplevel);
} finally {
log.useSource(prev);
}
}
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.
/** 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;
}
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.
/** 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;
}
}
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.
/** 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
/** 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);
}
// Process module declarations.
// If module resolution fails, ignore trees, and if trying to
// complete a specific symbol, throw CompletionFailure.
// Note that if module resolution failed, we may not even
// have enough modules available to access java.lang, and
// so risk getting FatalError("no.java.lang") from MemberEnter.
if (!modules.enter(List.of(tree), c)) {
throw new CompletionFailure(c, diags.fragment("cant.resolve.modules"));
}
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);
boolean isModuleInfo =
tree.sourcefile.isNameCompatible("module-info",
JavaFileObject.Kind.SOURCE);
if (isModuleInfo) {
if (enter.getEnv(tree.modle) == null) {
JCDiagnostic diag =
diagFactory.fragment("file.does.not.contain.module");
throw new ClassFinder.BadClassFile(c, filename, diag, diagFactory);
}
} else 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. /** 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.nil(), null, List.nil());
}
Main method: compile a list of files, return all compiled classes
Params: - sourceFileObjects – file objects to be compiled
- classnames – class names to process for annotations
- processors – user provided annotation processors to bypass discovery,
null
means that no processors were provided - addModules – additional root modules to be used during
module resolution.
/**
* 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
* @param addModules additional root modules to be used during
* module resolution.
*/
public void compile(Collection<JavaFileObject> sourceFileObjects,
Collection<String> classnames,
Iterable<? extends Processor> processors,
Collection<String> addModules)
{
if (!taskListener.isEmpty()) {
taskListener.started(new TaskEvent(TaskEvent.Kind.COMPILATION));
}
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)
checkReusable();
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.primaryName + "-" + LintCategory.OPTIONS.option, "true");
options.remove(XLINT_CUSTOM.primaryName + LintCategory.OPTIONS.option);
start_msec = now();
try {
initProcessAnnotations(processors, sourceFileObjects, classnames);
for (String className : classnames) {
int sep = className.indexOf('/');
if (sep != -1) {
modules.addExtraAddModules(className.substring(0, sep));
}
}
for (String moduleName : addModules) {
modules.addExtraAddModules(moduleName);
}
// These method calls must be chained to avoid memory leaks
processAnnotations(
enterTrees(
stopIfError(CompileState.PARSE,
initModules(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());
}
if (!taskListener.isEmpty()) {
taskListener.finished(new TaskEvent(TaskEvent.Kind.COMPILATION));
}
close();
if (procEnvImpl != null)
procEnvImpl.close();
}
}
protected void checkReusable() {
throw new AssertionError("attempt to reuse JavaCompiler");
}
The list of classes explicitly supplied on the command line for compilation.
Not always populated.
/**
* The list of classes explicitly supplied on the command line for compilation.
* Not always populated.
*/
private List<JCClassDecl> rootClasses;
Parses a list of files.
/**
* 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.
/**
* 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(initModules(roots));
}
public List<JCCompilationUnit> initModules(List<JCCompilationUnit> roots) {
modules.initModules(roots);
if (roots.isEmpty()) {
enterDone();
}
return 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.
/**
* 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);
enterDone();
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 (sourceOutput) {
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.
/**
* 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.
/**
* 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.
Params: - processors – user provided annotation processors to bypass discovery,
null
means that no processors were provided
/**
* 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,
Collection<? extends JavaFileObject> initialFiles,
Collection<String> initialClassNames) {
// 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("parameters", "parameters");
reader.saveParameterNames = true;
keepComments = true;
genEndPos = true;
if (!taskListener.isEmpty())
taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING));
deferredDiagnosticHandler = new Log.DeferredDiagnosticHandler(log);
procEnvImpl.getFiler().setInitialState(initialFiles, initialClassNames);
} else { // free resources
procEnvImpl.close();
}
}
}
// TODO: called by JavacTaskImpl
public void processAnnotations(List<JCCompilationUnit> roots) {
processAnnotations(roots, List.nil());
}
Process any annotations found in the specified compilation units.
Params: - roots – a list of compilation units
/**
* Process any annotations found in the specified compilation units.
* @param roots a list of compilation units
*/
// 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,
Collection<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 == PCK && !processPcks) ||
sym.kind == ABSENT_TYP) {
if (sym != silentFail)
log.error(Errors.ProcCantFindClass(nameStr));
errors = true;
continue;
}
try {
if (sym.kind == PCK)
sym.complete();
if (sym.exists()) {
if (sym.kind == PCK)
pckSymbols = pckSymbols.prepend((PackageSymbol)sym);
else
classSymbols = classSymbols.prepend((ClassSymbol)sym);
continue;
}
Assert.check(sym.kind == PCK);
log.warning(Warnings.ProcPackageDoesNotExist(nameStr));
pckSymbols = pckSymbols.prepend((PackageSymbol)sym);
} catch (CompletionFailure e) {
log.error(Errors.ProcCantFindClass(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(PROCESSOR_PATH) ||
options.isSet(PROCESSOR_MODULE_PATH) ||
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 attribute classes.
/**
* 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.
* @return a list of environments for attribute 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
/**
* Attribute a parse tree.
* @return 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);
}
finally {
log.useSource(prev);
}
return env;
}
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
/**
* 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.
* @return 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.
/**
* 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.
/**
* 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 (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
/**
* 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.
* @return 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.
/**
* 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 (!modules.multiModuleMode && env.toplevel.modle != modules.getDefaultModule()) {
//can only generate classfiles for a single module:
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.
/**
* 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) || env.tree.hasTag(JCTree.Tag.MODULEDEF)) {
if (!(sourceOutput)) {
if (shouldStop(CompileState.LOWER))
return;
List<JCTree> def = lower.translateTopLevelClass(env, env.tree, localMake);
if (def.head != null) {
Assert.check(def.tail.isEmpty());
results.add(new Pair<>(env, (JCClassDecl)def.head));
}
}
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.
/** 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;
for (Pair<Env<AttrContext>, JCClassDecl> x: queue) {
Env<AttrContext> env = x.fst;
JCClassDecl cdef = x.snd;
if (verboseCompilePolicy) {
printNote("[generate " + (sourceOutput ? " 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 (sourceOutput) {
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");
}
}
public void enterDone() {
enterDone = true;
annotate.enterDone();
}
public boolean isEnterDone() {
return enterDone;
}
private Name readModuleName(JavaFileObject fo) {
return parseAndGetName(fo, t -> {
JCModuleDecl md = t.getModuleDecl();
return md != null ? TreeInfo.fullName(md.getName()) : null;
});
}
private Name findPackageInFile(JavaFileObject fo) {
return parseAndGetName(fo, t -> t.getPackage() != null ?
TreeInfo.fullName(t.getPackage().getPackageName()) : null);
}
private Name parseAndGetName(JavaFileObject fo,
Function<JCTree.JCCompilationUnit, Name> tree2Name) {
DiagnosticHandler dh = new DiscardDiagnosticHandler(log);
try {
JCTree.JCCompilationUnit t = parse(fo, fo.getCharContent(false));
return tree2Name.apply(t);
} catch (IOException e) {
return null;
} finally {
log.popDiagnosticHandler(dh);
}
}
Close the compiler, flushing the logs
/** 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.
/** 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();
}
}