blob: ffe3dc69ad155eaeb9327a98f8ee2a8ffc98e57f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2017 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Fraunhofer FIRST - extended API and implementation
* Technical University Berlin - extended API and implementation
* Stephan Herrmann - contributions for
* bug 337868 - [compiler][model] incomplete support for package-info.java when using SearchableEnvironment
* bug 186342 - [compiler][null] Using annotations for null checking
*******************************************************************************/
package org.eclipse.jdt.internal.compiler;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.CompilationProgress;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.internal.compiler.env.*;
import org.eclipse.jdt.internal.compiler.impl.*;
import org.eclipse.jdt.internal.compiler.ast.*;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.*;
import org.eclipse.jdt.internal.compiler.parser.*;
import org.eclipse.jdt.internal.compiler.problem.*;
import org.eclipse.jdt.internal.compiler.util.Messages;
import org.eclipse.jdt.internal.compiler.util.Util;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.Config;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.Dependencies;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.ITranslationStates;
/**
* OTDT changes:
* ( MANY ;-).
*
* DEPENDENCIES:
* What: extracted most of the control to Dependencies.
* How: use Dependencies.{setup,release} and Dependencies.ensureState(unit, state).
* Where:beginToCompiler, accept(....)
* Why: we need to control compilation steps on a much more fine-grained level than the plain JDT.
*
* What: allow invocation of getMethodBodies from Dependencies.
*
* ROLE FILES:
*
* What: When requesting the team type from a role file's team package request source types only.
* How: Uses so far unused flag ClasspathDirectory.mode
*
* OTHER TOOLS:
* What: give a chance to use a JavaDocParser
* Why: support for documentation generator
*
*/
@SuppressWarnings("rawtypes")
public class Compiler implements ITypeRequestor, ProblemSeverities {
public Parser parser;
public ICompilerRequestor requestor;
public CompilerOptions options;
public ProblemReporter problemReporter;
protected PrintWriter out; // output for messages that are not sent to problemReporter
public CompilerStats stats;
public CompilationProgress progress;
public int remainingIterations = 1;
//{ObjectTeams: mark kind of compiler:
public boolean isBatchCompiler = false;
public boolean sortCompilationUnits = true; // only JDT-tests set to false to maintain original error output
// SH}
// management of unit to be processed
//public CompilationUnitResult currentCompilationUnitResult;
public CompilationUnitDeclaration[] unitsToProcess;
public int totalUnits; // (totalUnits-1) gives the last unit in unitToProcess
private Map<String, APTProblem[]> aptProblems;
// name lookup
public LookupEnvironment lookupEnvironment;
// ONCE STABILIZED, THESE SHOULD RETURN TO A FINAL FIELD
public static boolean DEBUG = false;
public int parseThreshold = -1;
public AbstractAnnotationProcessorManager annotationProcessorManager;
public int annotationProcessorStartIndex = 0;
public ReferenceBinding[] referenceBindings;
public boolean useSingleThread = true; // by default the compiler will not use worker threads to read/process/write
// number of initial units parsed at once (-1: none)
/*
* Static requestor reserved to listening compilation results in debug mode,
* so as for example to monitor compiler activity independantly from a particular
* builder implementation. It is reset at the end of compilation, and should not
* persist any information after having been reset.
*/
public static IDebugRequestor DebugRequestor = null;
/**
* Answer a new compiler using the given name environment and compiler options.
* The environment and options will be in effect for the lifetime of the compiler.
* When the compiler is run, compilation results are sent to the given requestor.
*
* @param environment org.eclipse.jdt.internal.compiler.api.env.INameEnvironment
* Environment used by the compiler in order to resolve type and package
* names. The name environment implements the actual connection of the compiler
* to the outside world (e.g. in batch mode the name environment is performing
* pure file accesses, reuse previous build state or connection to repositories).
* Note: the name environment is responsible for implementing the actual classpath
* rules.
*
* @param policy org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy
* Configurable part for problem handling, allowing the compiler client to
* specify the rules for handling problems (stop on first error or accumulate
* them all) and at the same time perform some actions such as opening a dialog
* in UI when compiling interactively.
* @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies
*
* @param settings java.util.Map
* The settings that control the compiler behavior.
*
* @param requestor org.eclipse.jdt.internal.compiler.api.ICompilerRequestor
* Component which will receive and persist all compilation results and is intended
* to consume them as they are produced. Typically, in a batch compiler, it is
* responsible for writing out the actual .class files to the file system.
* @see org.eclipse.jdt.internal.compiler.CompilationResult
*
* @param problemFactory org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory
* Factory used inside the compiler to create problem descriptors. It allows the
* compiler client to supply its own representation of compilation problems in
* order to avoid object conversions. Note that the factory is not supposed
* to accumulate the created problems, the compiler will gather them all and hand
* them back as part of the compilation unit result.
*
* @deprecated this constructor is kept to preserve 3.1 and 3.2M4 compatibility
*/
public Compiler(
INameEnvironment environment,
IErrorHandlingPolicy policy,
Map<String, String> settings,
final ICompilerRequestor requestor,
IProblemFactory problemFactory) {
this(environment, policy, new CompilerOptions(settings), requestor, problemFactory, null /* printwriter */, null /* progress */);
}
/**
* Answer a new compiler using the given name environment and compiler options.
* The environment and options will be in effect for the lifetime of the compiler.
* When the compiler is run, compilation results are sent to the given requestor.
*
* @param environment org.eclipse.jdt.internal.compiler.api.env.INameEnvironment
* Environment used by the compiler in order to resolve type and package
* names. The name environment implements the actual connection of the compiler
* to the outside world (e.g. in batch mode the name environment is performing
* pure file accesses, reuse previous build state or connection to repositories).
* Note: the name environment is responsible for implementing the actual classpath
* rules.
*
* @param policy org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy
* Configurable part for problem handling, allowing the compiler client to
* specify the rules for handling problems (stop on first error or accumulate
* them all) and at the same time perform some actions such as opening a dialog
* in UI when compiling interactively.
* @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies
*
* @param settings java.util.Map
* The settings that control the compiler behavior.
*
* @param requestor org.eclipse.jdt.internal.compiler.api.ICompilerRequestor
* Component which will receive and persist all compilation results and is intended
* to consume them as they are produced. Typically, in a batch compiler, it is
* responsible for writing out the actual .class files to the file system.
* @see org.eclipse.jdt.internal.compiler.CompilationResult
*
* @param problemFactory org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory
* Factory used inside the compiler to create problem descriptors. It allows the
* compiler client to supply its own representation of compilation problems in
* order to avoid object conversions. Note that the factory is not supposed
* to accumulate the created problems, the compiler will gather them all and hand
* them back as part of the compilation unit result.
*
* @param parseLiteralExpressionsAsConstants <code>boolean</code>
* This parameter is used to optimize the literals or leave them as they are in the source.
* If you put true, "Hello" + " world" will be converted to "Hello world".
*
* @deprecated this constructor is kept to preserve 3.1 and 3.2M4 compatibility
*/
public Compiler(
INameEnvironment environment,
IErrorHandlingPolicy policy,
Map settings,
final ICompilerRequestor requestor,
IProblemFactory problemFactory,
boolean parseLiteralExpressionsAsConstants) {
this(environment, policy, new CompilerOptions(settings, parseLiteralExpressionsAsConstants), requestor, problemFactory, null /* printwriter */, null /* progress */);
}
/**
* Answer a new compiler using the given name environment and compiler options.
* The environment and options will be in effect for the lifetime of the compiler.
* When the compiler is run, compilation results are sent to the given requestor.
*
* @param environment org.eclipse.jdt.internal.compiler.api.env.INameEnvironment
* Environment used by the compiler in order to resolve type and package
* names. The name environment implements the actual connection of the compiler
* to the outside world (e.g. in batch mode the name environment is performing
* pure file accesses, reuse previous build state or connection to repositories).
* Note: the name environment is responsible for implementing the actual classpath
* rules.
*
* @param policy org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy
* Configurable part for problem handling, allowing the compiler client to
* specify the rules for handling problems (stop on first error or accumulate
* them all) and at the same time perform some actions such as opening a dialog
* in UI when compiling interactively.
* @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies
*
* @param options org.eclipse.jdt.internal.compiler.impl.CompilerOptions
* The options that control the compiler behavior.
*
* @param requestor org.eclipse.jdt.internal.compiler.api.ICompilerRequestor
* Component which will receive and persist all compilation results and is intended
* to consume them as they are produced. Typically, in a batch compiler, it is
* responsible for writing out the actual .class files to the file system.
* @see org.eclipse.jdt.internal.compiler.CompilationResult
*
* @param problemFactory org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory
* Factory used inside the compiler to create problem descriptors. It allows the
* compiler client to supply its own representation of compilation problems in
* order to avoid object conversions. Note that the factory is not supposed
* to accumulate the created problems, the compiler will gather them all and hand
* them back as part of the compilation unit result.
*/
public Compiler(
INameEnvironment environment,
IErrorHandlingPolicy policy,
CompilerOptions options,
final ICompilerRequestor requestor,
IProblemFactory problemFactory) {
this(environment, policy, options, requestor, problemFactory, null /* printwriter */, null /* progress */);
}
/**
* Answer a new compiler using the given name environment and compiler options.
* The environment and options will be in effect for the lifetime of the compiler.
* When the compiler is run, compilation results are sent to the given requestor.
*
* @param environment org.eclipse.jdt.internal.compiler.api.env.INameEnvironment
* Environment used by the compiler in order to resolve type and package
* names. The name environment implements the actual connection of the compiler
* to the outside world (e.g. in batch mode the name environment is performing
* pure file accesses, reuse previous build state or connection to repositories).
* Note: the name environment is responsible for implementing the actual classpath
* rules.
*
* @param policy org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy
* Configurable part for problem handling, allowing the compiler client to
* specify the rules for handling problems (stop on first error or accumulate
* them all) and at the same time perform some actions such as opening a dialog
* in UI when compiling interactively.
* @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies
*
* @param options org.eclipse.jdt.internal.compiler.impl.CompilerOptions
* The options that control the compiler behavior.
*
* @param requestor org.eclipse.jdt.internal.compiler.api.ICompilerRequestor
* Component which will receive and persist all compilation results and is intended
* to consume them as they are produced. Typically, in a batch compiler, it is
* responsible for writing out the actual .class files to the file system.
* @see org.eclipse.jdt.internal.compiler.CompilationResult
*
* @param problemFactory org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory
* Factory used inside the compiler to create problem descriptors. It allows the
* compiler client to supply its own representation of compilation problems in
* order to avoid object conversions. Note that the factory is not supposed
* to accumulate the created problems, the compiler will gather them all and hand
* them back as part of the compilation unit result.
* @deprecated
*/
public Compiler(
INameEnvironment environment,
IErrorHandlingPolicy policy,
CompilerOptions options,
final ICompilerRequestor requestor,
IProblemFactory problemFactory,
PrintWriter out) {
this(environment, policy, options, requestor, problemFactory, out, null /* progress */);
}
public Compiler(
INameEnvironment environment,
IErrorHandlingPolicy policy,
CompilerOptions options,
final ICompilerRequestor requestor,
IProblemFactory problemFactory,
PrintWriter out,
CompilationProgress progress) {
this.options = options;
this.progress = progress;
// wrap requestor in DebugRequestor if one is specified
if(DebugRequestor == null) {
this.requestor = requestor;
} else {
this.requestor = new ICompilerRequestor(){
@Override
public void acceptResult(CompilationResult result){
if (DebugRequestor.isActive()){
DebugRequestor.acceptDebugResult(result);
}
requestor.acceptResult(result);
}
};
}
this.problemReporter = new ProblemReporter(policy, this.options, problemFactory);
//{ObjectTeams: swapped order to allow overriding default problemReporter:
/* orig:
this.lookupEnvironment = new LookupEnvironment(this, this.options, this.problemReporter, environment);
:giro */
this.out = out == null ? new PrintWriter(System.out, true) : out;
this.stats = new CompilerStats();
initializeParser();
this.lookupEnvironment = new LookupEnvironment(this, this.options, this.problemReporter, environment);
// SH}
}
/**
* Add an additional binary type
*/
@Override
public void accept(IBinaryType binaryType, PackageBinding packageBinding, AccessRestriction accessRestriction) {
if (this.options.verbose) {
this.out.println(
Messages.bind(Messages.compilation_loadBinary, new String(binaryType.getName())));
// new Exception("TRACE BINARY").printStackTrace(System.out);
// System.out.println();
}
LookupEnvironment env = packageBinding.environment;
env.createBinaryTypeFrom(binaryType, packageBinding, accessRestriction);
}
/**
* Add an additional compilation unit into the loop
* -> build compilation unit declarations, their bindings and record their results.
*/
@Override
public void accept(ICompilationUnit sourceUnit, AccessRestriction accessRestriction) {
// Switch the current policy and compilation result for this unit to the requested one.
CompilationResult unitResult =
new CompilationResult(sourceUnit, this.totalUnits, this.totalUnits, this.options.maxProblemsPerUnit);
unitResult.checkSecondaryTypes = true;
try {
if (this.options.verbose) {
String count = String.valueOf(this.totalUnits + 1);
this.out.println(
Messages.bind(Messages.compilation_request,
new String[] {
count,
count,
new String(sourceUnit.getFileName())
}));
}
// diet parsing for large collection of unit
CompilationUnitDeclaration parsedUnit;
if (this.totalUnits < this.parseThreshold) {
parsedUnit = this.parser.parse(sourceUnit, unitResult);
} else {
parsedUnit = this.parser.dietParse(sourceUnit, unitResult);
}
//{ObjectTeams: unchanged but redirected to Dependencies: (Dependencies.setup must be called before).
// initial type binding creation
this.lookupEnvironment.buildTypeBindings(parsedUnit, accessRestriction);
addCompilationUnit(sourceUnit, parsedUnit);
// binding resolution
this.lookupEnvironment.completeTypeBindings(parsedUnit);
// SH}
} catch (AbortCompilationUnit e) {
// at this point, currentCompilationUnitResult may not be sourceUnit, but some other
// one requested further along to resolve sourceUnit.
if (unitResult.compilationUnit == sourceUnit) { // only report once
this.requestor.acceptResult(unitResult.tagAsAccepted());
} else {
throw e; // want to abort enclosing request to compile
}
}
}
/**
* Add additional source types
*/
@Override
public void accept(ISourceType[] sourceTypes, PackageBinding packageBinding, AccessRestriction accessRestriction) {
this.problemReporter.abortDueToInternalError(
Messages.bind(Messages.abort_againstSourceModel, new String[] { String.valueOf(sourceTypes[0].getName()), String.valueOf(sourceTypes[0].getFileName()) }));
}
protected synchronized void addCompilationUnit(
ICompilationUnit sourceUnit,
CompilationUnitDeclaration parsedUnit) {
if (this.unitsToProcess == null)
return; // not collecting units
// append the unit to the list of ones to process later on
int size = this.unitsToProcess.length;
if (this.totalUnits == size)
// when growing reposition units starting at position 0
System.arraycopy(
this.unitsToProcess,
0,
(this.unitsToProcess = new CompilationUnitDeclaration[size * 2]),
0,
this.totalUnits);
//{ObjectTeams: record 'place' for external invocation of getMethodBodies:
parsedUnit.place = this.totalUnits;
if (parsedUnit.scope != null)
parsedUnit.scope.parser = this.parser;
// SH}
this.unitsToProcess[this.totalUnits++] = parsedUnit;
}
/**
* Add the initial set of compilation units into the loop
* -> build compilation unit declarations, their bindings and record their results.
*/
protected void beginToCompile(ICompilationUnit[] sourceUnits) {
int maxUnits = sourceUnits.length;
this.totalUnits = 0;
this.unitsToProcess = new CompilationUnitDeclaration[maxUnits];
internalBeginToCompile(sourceUnits, maxUnits);
}
/**
* Checks whether the compilation has been canceled and reports the given progress to the compiler progress.
*/
protected void reportProgress(String taskDecription) {
if (this.progress != null) {
if (this.progress.isCanceled()) {
// Only AbortCompilation can stop the compiler cleanly.
// We check cancellation again following the call to compile.
throw new AbortCompilation(true, null);
}
this.progress.setTaskName(taskDecription);
}
}
/**
* Checks whether the compilation has been canceled and reports the given work increment to the compiler progress.
*/
protected void reportWorked(int workIncrement, int currentUnitIndex) {
if (this.progress != null) {
if (this.progress.isCanceled()) {
// Only AbortCompilation can stop the compiler cleanly.
// We check cancellation again following the call to compile.
throw new AbortCompilation(true, null);
}
this.progress.worked(workIncrement, (this.totalUnits* this.remainingIterations) - currentUnitIndex - 1);
}
}
public void compile(ICompilationUnit[] sourceUnits) {
compile(sourceUnits, false);
}
/**
* General API
* -> compile each of supplied files
* -> recompile any required types for which we have an incomplete principle structure
*/
private void compile(ICompilationUnit[] sourceUnits, boolean lastRound) {
this.stats.startTime = System.currentTimeMillis();
//{ObjectTeams:
/* orig:
try {
:giro */
try (Config config = Dependencies.setup(this, this.parser, this.lookupEnvironment, true, true, false))
{
//carp}
// build and record parsed units
reportProgress(Messages.compilation_beginningToCompile);
//{ObjectTeams: OT/J code always needs CU sorting:
/* orig:
if (this.options.complianceLevel >= ClassFileConstants.JDK9) {
:giro */
if (this.options.complianceLevel >= ClassFileConstants.JDK9 || !this.options.isPureJava) {
// SH}
// in Java 9 the compiler must never ask the oracle for a module that is contained in the input units:
sortModuleDeclarationsFirst(sourceUnits);
}
if (this.annotationProcessorManager == null) {
beginToCompile(sourceUnits);
} else {
ICompilationUnit[] originalUnits = sourceUnits.clone(); // remember source units in case a source type collision occurs
try {
beginToCompile(sourceUnits);
if (!lastRound) {
processAnnotations();
}
if (!this.options.generateClassFiles) {
// -proc:only was set on the command line
return;
}
} catch (SourceTypeCollisionException e) {
backupAptProblems();
reset();
// a generated type was referenced before it was created
// the compiler either created a MissingType or found a BinaryType for it
// so add the processor's generated files & start over,
// but remember to only pass the generated files to the annotation processor
int originalLength = originalUnits.length;
int newProcessedLength = e.newAnnotationProcessorUnits.length;
ICompilationUnit[] combinedUnits = new ICompilationUnit[originalLength + newProcessedLength];
System.arraycopy(originalUnits, 0, combinedUnits, 0, originalLength);
System.arraycopy(e.newAnnotationProcessorUnits, 0, combinedUnits, originalLength, newProcessedLength);
this.annotationProcessorStartIndex = originalLength;
compile(combinedUnits, e.isLastRound);
return;
}
}
// Restore the problems before the results are processed and cleaned up.
restoreAptProblems();
processCompiledUnits(0, lastRound);
} catch (AbortCompilation e) {
this.handleInternalException(e, null);
}
if (this.options.verbose) {
if (this.totalUnits > 1) {
this.out.println(
Messages.bind(Messages.compilation_units, String.valueOf(this.totalUnits)));
} else {
this.out.println(
Messages.bind(Messages.compilation_unit, String.valueOf(this.totalUnits)));
}
}
}
private void sortModuleDeclarationsFirst(ICompilationUnit[] sourceUnits) {
Arrays.sort(sourceUnits, (u1, u2) -> {
char[] fn1 = u1.getFileName();
char[] fn2 = u2.getFileName();
boolean isMod1 = CharOperation.endsWith(fn1, TypeConstants.MODULE_INFO_FILE_NAME) || CharOperation.endsWith(fn1, TypeConstants.MODULE_INFO_CLASS_NAME);
boolean isMod2 = CharOperation.endsWith(fn2, TypeConstants.MODULE_INFO_FILE_NAME) || CharOperation.endsWith(fn2, TypeConstants.MODULE_INFO_CLASS_NAME);
if (isMod1 == isMod2)
//{ObjectTeams: ensure teams (short file name) come before their role files (longer file name):
{
if (this.sortCompilationUnits)
return Integer.compare(fn1.length, fn2.length);
// orig:
return 0;
// :giro
}
// SH}
return isMod1 ? -1 : 1;
});
}
class APTProblem {
CategorizedProblem problem;
ReferenceContext context;
APTProblem(CategorizedProblem problem, ReferenceContext context) {
this.problem = problem;
this.context = context;
}
}
protected void backupAptProblems() {
if (this.unitsToProcess == null) return;
for (int i = 0; i < this.totalUnits; i++) {
CompilationUnitDeclaration unitDecl = this.unitsToProcess[i];
CompilationResult result = unitDecl.compilationResult;
if (result != null && result.hasErrors()) {
CategorizedProblem[] errors = result.getErrors();
for (CategorizedProblem problem : errors) {
if (problem.getCategoryID() == CategorizedProblem.CAT_UNSPECIFIED) {
if (this.aptProblems == null) {
this.aptProblems = new HashMap<>();
}
APTProblem[] problems = this.aptProblems.get(new String(unitDecl.getFileName()));
if (problems == null) {
this.aptProblems.put(
new String(unitDecl.getFileName()),
new APTProblem[] { new APTProblem(problem, result.getContext(problem)) });
} else {
APTProblem[] temp = new APTProblem[problems.length + 1];
System.arraycopy(problems, 0, temp, 0, problems.length);
temp[problems.length] = new APTProblem(problem, result.getContext(problem));
this.aptProblems.put(new String(unitDecl.getFileName()), temp);
}
}
}
}
}
}
protected void restoreAptProblems() {
if (this.unitsToProcess != null && this.aptProblems!= null) {
for (int i = 0; i < this.totalUnits; i++) {
CompilationUnitDeclaration unitDecl = this.unitsToProcess[i];
APTProblem[] problems = this.aptProblems.get(new String(unitDecl.getFileName()));
if (problems != null) {
for (APTProblem problem : problems) {
unitDecl.compilationResult.record(problem.problem, problem.context);
}
}
}
}
this.aptProblems = null; // No need for this.
}
protected void processCompiledUnits(int startingIndex, boolean lastRound) throws java.lang.Error {
CompilationUnitDeclaration unit = null;
ProcessTaskManager processingTask = null;
try {
if (this.useSingleThread) {
// process all units (some more could be injected in the loop by the lookup environment)
for (int i = startingIndex; i < this.totalUnits; i++) {
unit = this.unitsToProcess[i];
if (unit.compilationResult != null && unit.compilationResult.hasBeenAccepted)
continue;
reportProgress(Messages.bind(Messages.compilation_processing, new String(unit.getFileName())));
try {
if (this.options.verbose)
this.out.println(
Messages.bind(Messages.compilation_process,
new String[] {
String.valueOf(i + 1),
String.valueOf(this.totalUnits),
new String(this.unitsToProcess[i].getFileName())
}));
process(unit, i);
} finally {
// cleanup compilation unit result, but only if not annotation processed.
if (this.annotationProcessorManager == null || shouldCleanup(i))
unit.cleanUp();
}
if (this.annotationProcessorManager == null) {
this.unitsToProcess[i] = null; // release reference to processed unit declaration
}
reportWorked(1, i);
this.stats.lineCount += unit.compilationResult.lineSeparatorPositions.length;
long acceptStart = System.currentTimeMillis();
this.requestor.acceptResult(unit.compilationResult.tagAsAccepted());
this.stats.generateTime += System.currentTimeMillis() - acceptStart; // record accept time as part of generation
if (this.options.verbose)
this.out.println(
Messages.bind(Messages.compilation_done,
new String[] {
String.valueOf(i + 1),
String.valueOf(this.totalUnits),
new String(unit.getFileName())
}));
}
} else {
processingTask = new ProcessTaskManager(this, startingIndex);
int acceptedCount = 0;
// process all units (some more could be injected in the loop by the lookup environment)
// the processTask can continue to process units until its fixed sized cache is full then it must wait
// for this this thread to accept the units as they appear (it only waits if no units are available)
while (true) {
try {
unit = processingTask.removeNextUnit(); // waits if no units are in the processed queue
} catch (Error | RuntimeException e) {
unit = processingTask.unitToProcess;
throw e;
}
if (unit == null) break;
reportWorked(1, acceptedCount++);
this.stats.lineCount += unit.compilationResult.lineSeparatorPositions.length;
this.requestor.acceptResult(unit.compilationResult.tagAsAccepted());
if (this.options.verbose)
this.out.println(
Messages.bind(Messages.compilation_done,
new String[] {
String.valueOf(acceptedCount),
String.valueOf(this.totalUnits),
new String(unit.getFileName())
}));
}
}
if (!lastRound) {
if (this.annotationProcessorManager != null && this.totalUnits > this.annotationProcessorStartIndex) {
int backup = this.annotationProcessorStartIndex;
int prevUnits = this.totalUnits;
processAnnotations();
// Clean up the units that were left out previously for annotation processing.
for (int i = backup; i < prevUnits; i++) {
this.unitsToProcess[i].cleanUp();
}
processCompiledUnits(backup, lastRound);
}
}
} catch (AbortCompilation e) {
this.handleInternalException(e, unit);
} catch (Error | RuntimeException e) {
this.handleInternalException(e, unit, null);
throw e; // rethrow
} finally {
if (processingTask != null) {
processingTask.shutdown();
processingTask = null;
}
reset();
this.annotationProcessorStartIndex = 0;
this.stats.endTime = System.currentTimeMillis();
}
}
public synchronized CompilationUnitDeclaration getUnitToProcess(int next) {
if (next < this.totalUnits) {
CompilationUnitDeclaration unit = this.unitsToProcess[next];
if (this.annotationProcessorManager == null || next < this.annotationProcessorStartIndex) {
this.unitsToProcess[next] = null; // release reference to processed unit declaration
}
return unit;
}
return null;
}
/*
* Returns whether the compilation unit at the given index should be
* cleaned up after processing. This basically means whether or not
* the unit is still required for annotation processing.
*/
public boolean shouldCleanup(int index) {
return index < this.annotationProcessorStartIndex;
}
public void setBinaryTypes(ReferenceBinding[] binaryTypes) {
this.referenceBindings = binaryTypes;
}
/*
* Compiler crash recovery in case of unexpected runtime exceptions
*/
protected void handleInternalException(
Throwable internalException,
CompilationUnitDeclaration unit,
CompilationResult result) {
if (result == null && unit != null) {
result = unit.compilationResult; // current unit being processed ?
}
// Lookup environment may be in middle of connecting types
if (result == null && this.lookupEnvironment.unitBeingCompleted != null) {
result = this.lookupEnvironment.unitBeingCompleted.compilationResult;
}
if (result == null) {
synchronized (this) {
if (this.unitsToProcess != null && this.totalUnits > 0)
result = this.unitsToProcess[this.totalUnits - 1].compilationResult;
}
}
// last unit in beginToCompile ?
boolean needToPrint = true;
if (result != null) {
/* create and record a compilation problem */
// only keep leading portion of the trace
String[] pbArguments = new String[] {
Messages.bind(Messages.compilation_internalError, Util.getExceptionSummary(internalException)),
};
result
.record(
this.problemReporter
.createProblem(
result.getFileName(),
IProblem.Unclassified,
pbArguments,
pbArguments,
Error, // severity
0, // source start
0, // source end
0, // line number
0),// column number
unit, true);
/* hand back the compilation result */
if (!result.hasBeenAccepted) {
this.requestor.acceptResult(result.tagAsAccepted());
needToPrint = false;
}
}
if (needToPrint) {
/* dump a stack trace to the console */
internalException.printStackTrace();
}
}
/*
* Compiler recovery in case of internal AbortCompilation event
*/
protected void handleInternalException(
AbortCompilation abortException,
CompilationUnitDeclaration unit) {
/* special treatment for SilentAbort: silently cancelling the compilation process */
if (abortException.isSilent) {
if (abortException.silentException == null) {
return;
}
throw abortException.silentException;
}
/* uncomment following line to see where the abort came from */
// abortException.printStackTrace();
// Exception may tell which compilation result it is related, and which problem caused it
CompilationResult result = abortException.compilationResult;
if (result == null && unit != null) {
result = unit.compilationResult; // current unit being processed ?
}
// Lookup environment may be in middle of connecting types
if (result == null && this.lookupEnvironment.unitBeingCompleted != null) {
result = this.lookupEnvironment.unitBeingCompleted.compilationResult;
}
if (result == null) {
synchronized (this) {
if (this.unitsToProcess != null && this.totalUnits > 0)
result = this.unitsToProcess[this.totalUnits - 1].compilationResult;
}
}
// last unit in beginToCompile ?
if (result != null && !result.hasBeenAccepted) {
/* distant problem which could not be reported back there? */
if (abortException.problem != null) {
recordDistantProblem: {
CategorizedProblem distantProblem = abortException.problem;
CategorizedProblem[] knownProblems = result.problems;
for (int i = 0; i < result.problemCount; i++) {
if (knownProblems[i] == distantProblem) { // already recorded
break recordDistantProblem;
}
}
if (distantProblem instanceof DefaultProblem) { // fixup filename TODO (philippe) should improve API to make this official
((DefaultProblem) distantProblem).setOriginatingFileName(result.getFileName());
}
result.record(distantProblem, unit, true);
}
} else {
/* distant internal exception which could not be reported back there */
if (abortException.exception != null) {
this.handleInternalException(abortException.exception, null, result);
return;
}
}
/* hand back the compilation result */
if (!result.hasBeenAccepted) {
this.requestor.acceptResult(result.tagAsAccepted());
}
} else {
abortException.printStackTrace();
}
}
public void initializeParser() {
this.parser = new Parser(this.problemReporter, this.options.parseLiteralExpressionsAsConstants);
}
private void abortIfPreviewNotAllowed(ICompilationUnit[] sourceUnits, int maxUnits) {
if (!this.options.enablePreviewFeatures)
return;
try {
if (this.options.sourceLevel != ClassFileConstants.getLatestJDKLevel()) {
this.problemReporter.abortDueToPreviewEnablingNotAllowed(CompilerOptions.versionFromJdkLevel(this.options.sourceLevel), CompilerOptions.getLatestVersion());
}
} catch (AbortCompilation a) {
// best effort to find a way for reporting this problem: report on the first source
if (a.compilationResult == null) {
a.compilationResult = new CompilationResult(sourceUnits[0], 0, maxUnits, this.options.maxProblemsPerUnit);
}
throw a;
}
}
/**
* Add the initial set of compilation units into the loop
* -> build compilation unit declarations, their bindings and record their results.
*/
protected void internalBeginToCompile(ICompilationUnit[] sourceUnits, int maxUnits) {
abortIfPreviewNotAllowed(sourceUnits,maxUnits);
if (!this.useSingleThread && maxUnits >= ReadManager.THRESHOLD)
this.parser.readManager = new ReadManager(sourceUnits, maxUnits);
// Switch the current policy and compilation result for this unit to the requested one.
for (int i = 0; i < maxUnits; i++) {
CompilationResult unitResult = null;
try {
if (this.options.verbose) {
this.out.println(
Messages.bind(Messages.compilation_request,
new String[] {
String.valueOf(i + 1),
String.valueOf(maxUnits),
new String(sourceUnits[i].getFileName())
}));
}
// diet parsing for large collection of units
CompilationUnitDeclaration parsedUnit;
unitResult = new CompilationResult(sourceUnits[i], i, maxUnits, this.options.maxProblemsPerUnit);
long parseStart = System.currentTimeMillis();
if (this.totalUnits < this.parseThreshold) {
parsedUnit = this.parser.parse(sourceUnits[i], unitResult);
} else {
parsedUnit = this.parser.dietParse(sourceUnits[i], unitResult);
}
//{ObjectTeams: new backpointer needed as "client" for Config:
parsedUnit.compiler = this;
// SH}
long resolveStart = System.currentTimeMillis();
this.stats.parseTime += resolveStart - parseStart;
// initial type binding creation
this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/);
this.stats.resolveTime += System.currentTimeMillis() - resolveStart;
addCompilationUnit(sourceUnits[i], parsedUnit);
ImportReference currentPackage = parsedUnit.currentPackage;
if (currentPackage != null) {
unitResult.recordPackageName(currentPackage.tokens);
}
//} catch (AbortCompilationUnit e) {
// requestor.acceptResult(unitResult.tagAsAccepted());
} catch (AbortCompilation a) {
// best effort to find a way for reporting this problem:
if (a.compilationResult == null)
a.compilationResult = unitResult;
throw a;
} finally {
sourceUnits[i] = null; // no longer hold onto the unit
}
}
if (this.parser.readManager != null) {
this.parser.readManager.shutdown();
this.parser.readManager = null;
}
// binding resolution
this.lookupEnvironment.completeTypeBindings();
}
/**
* Process a compilation unit already parsed and build.
*/
public void process(CompilationUnitDeclaration unit, int i) {
this.lookupEnvironment.unitBeingCompleted = unit;
long parseStart = System.currentTimeMillis();
//{ObjectTeams: check the state of role(s) and team(s) contained in the CU and
// perform necessary translations using Dependencies.
Dependencies.ensureState(unit, ITranslationStates.STATE_METHODS_PARSED);
/* orig:
this.parser.getMethodBodies(unit);
*/
long resolveStart = System.currentTimeMillis();
this.stats.parseTime += resolveStart - parseStart;
/*
// fault in fields & methods
if (unit.scope != null)
unit.scope.faultInTypes();
// verify inherited methods
if (unit.scope != null)
unit.scope.verifyMethods(this.lookupEnvironment.methodVerifier());
// type checking
unit.resolve();
*/
// Note(SH): we do mimic a translation in separate phases in order to capture times:
/*OT:*/ Dependencies.ensureState(unit, ITranslationStates.STATE_RESOLVED);
long analyzeStart = System.currentTimeMillis();
this.stats.resolveTime += analyzeStart - resolveStart;
/*
//No need of analysis or generation of code if statements are not required
if (!this.options.ignoreMethodBodies) unit.analyseCode(); // flow analysis
*/
/*OT:*/ Dependencies.ensureState(unit, ITranslationStates.STATE_BYTE_CODE_GENERATED-1);
long generateStart = System.currentTimeMillis();
this.stats.analyzeTime += generateStart - analyzeStart;
/*
if (!this.options.ignoreMethodBodies) unit.generateCode(); // code generation
:giro */
Dependencies.ensureState(unit, ITranslationStates.STATE_BYTE_CODE_GENERATED);
// SH}
// reference info
if (this.options.produceReferenceInfo && unit.scope != null)
unit.scope.storeDependencyInfo();
// finalize problems (suppressWarnings)
unit.finalizeProblems();
this.stats.generateTime += System.currentTimeMillis() - generateStart;
// refresh the total number of units known at this stage
unit.compilationResult.totalUnitsKnown = this.totalUnits;
this.lookupEnvironment.unitBeingCompleted = null;
}
protected void processAnnotations() {
int newUnitSize = 0;
int newClassFilesSize = 0;
int bottom = this.annotationProcessorStartIndex;
int top = this.totalUnits;
ReferenceBinding[] binaryTypeBindingsTemp = this.referenceBindings;
if (top == 0 && binaryTypeBindingsTemp == null) return;
this.referenceBindings = null;
do {
// extract units to process
int length = top - bottom;
CompilationUnitDeclaration[] currentUnits = new CompilationUnitDeclaration[length];
int index = 0;
for (int i = bottom; i < top; i++) {
CompilationUnitDeclaration currentUnit = this.unitsToProcess[i];
currentUnits[index++] = currentUnit;
}
if (index != length) {
System.arraycopy(currentUnits, 0, (currentUnits = new CompilationUnitDeclaration[index]), 0, index);
}
this.annotationProcessorManager.processAnnotations(currentUnits, binaryTypeBindingsTemp, false);
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=407841
// It is possible that during the #processAnnotations() call, some units in the next batch would have been
// brought forward and compiled already. If there are any such, process them for annotations then and there.
// This would avoid the complications of marking some units as compiled but not-annotation-processed-yet.
if (top < this.totalUnits) {
length = this.totalUnits - top; // NOTE: Reuse the same variable, but make sure it's not used after this point
CompilationUnitDeclaration[] addedUnits = new CompilationUnitDeclaration[length];
System.arraycopy(this.unitsToProcess, top, addedUnits, 0, length);
this.annotationProcessorManager.processAnnotations(addedUnits, binaryTypeBindingsTemp, false);
}
this.annotationProcessorStartIndex = top;
ICompilationUnit[] newUnits = this.annotationProcessorManager.getNewUnits();
newUnitSize = newUnits.length;
ReferenceBinding[] newClassFiles = this.annotationProcessorManager.getNewClassFiles();
binaryTypeBindingsTemp = newClassFiles;
newClassFilesSize = newClassFiles.length;
if (newUnitSize != 0) {
ICompilationUnit[] newProcessedUnits = newUnits.clone(); // remember new units in case a source type collision occurs
try {
this.lookupEnvironment.isProcessingAnnotations = true;
internalBeginToCompile(newUnits, newUnitSize);
} catch (SourceTypeCollisionException e) {
e.newAnnotationProcessorUnits = newProcessedUnits;
throw e;
} finally {
this.lookupEnvironment.isProcessingAnnotations = false;
this.annotationProcessorManager.reset();
}
bottom = top;
top = this.totalUnits; // last unit added
this.annotationProcessorStartIndex = top;
} else {
bottom = top;
this.annotationProcessorManager.reset();
}
} while (newUnitSize != 0 || newClassFilesSize != 0);
this.annotationProcessorManager.processAnnotations(null, null, true);
// process potential units added in the final round see 329156
ICompilationUnit[] newUnits = this.annotationProcessorManager.getNewUnits();
newUnitSize = newUnits.length;
if (newUnitSize != 0) {
ICompilationUnit[] newProcessedUnits = newUnits.clone(); // remember new units in case a source type collision occurs
try {
this.lookupEnvironment.isProcessingAnnotations = true;
internalBeginToCompile(newUnits, newUnitSize);
} catch (SourceTypeCollisionException e) {
e.isLastRound = true;
e.newAnnotationProcessorUnits = newProcessedUnits;
throw e;
} finally {
this.lookupEnvironment.isProcessingAnnotations = false;
this.annotationProcessorManager.reset();
}
} else {
this.annotationProcessorManager.reset();
}
// Units added in final round don't get annotation processed
this.annotationProcessorStartIndex = this.totalUnits;
}
public void reset() {
this.lookupEnvironment.reset();
this.parser.scanner.source = null;
this.unitsToProcess = null;
if (DebugRequestor != null) DebugRequestor.reset();
this.problemReporter.reset();
}
/**
* Internal API used to resolve a given compilation unit. Can run a subset of the compilation process
*/
public CompilationUnitDeclaration resolve(
CompilationUnitDeclaration unit,
ICompilationUnit sourceUnit,
boolean verifyMethods,
boolean analyzeCode,
boolean generateCode) {
//{ObjectTeams:
/* orig:
try {
:giro */
try (Config config = setupDependencies(verifyMethods, analyzeCode, generateCode))
{
//SH}
if (unit == null) {
// build and record parsed units
this.parseThreshold = 0; // will request a full parse
beginToCompile(new ICompilationUnit[] { sourceUnit });
// find the right unit from what was injected via accept(ICompilationUnit,..):
for (int i=0; i<this.totalUnits; i++) {
if ( this.unitsToProcess[i] != null
&& this.unitsToProcess[i].compilationResult.compilationUnit == sourceUnit)
{
unit = this.unitsToProcess[i];
break;
}
}
if (unit == null)
unit = this.unitsToProcess[0]; // fall back to old behavior
} else {
// initial type binding creation
this.lookupEnvironment.buildTypeBindings(unit, null /*no access restriction*/);
// binding resolution
this.lookupEnvironment.completeTypeBindings();
}
this.lookupEnvironment.unitBeingCompleted = unit;
//{ObjectTeams: check the state of role(s) and team(s) contained in the CU and
// perform necessary translations
/* original version:
this.parser.getMethodBodies(unit);
if (unit.scope != null) {
// fault in fields & methods
unit.scope.faultInTypes();
if (unit.scope != null && verifyMethods) {
// http://dev.eclipse.org/bugs/show_bug.cgi?id=23117
// verify inherited methods
unit.scope.verifyMethods(this.lookupEnvironment.methodVerifier());
}
// type checking
unit.resolve();
// flow analysis
if (analyzeCode) unit.analyseCode();
// code generation
if (generateCode) unit.generateCode();
*/
// now completely under our control:
if (unit.scope != null) {
Dependencies.ensureState(unit, ITranslationStates.STATE_BYTE_CODE_GENERATED);
// orig:
// finalize problems (suppressWarnings)
unit.finalizeProblems();
}
// :giro
else {
// moved here from above: do at least this:
this.parser.getMethodBodies(unit);
}
// SH}
if (this.unitsToProcess != null) this.unitsToProcess[0] = null; // release reference to processed unit declaration
this.requestor.acceptResult(unit.compilationResult.tagAsAccepted());
return unit;
} catch (AbortCompilation e) {
this.handleInternalException(e, unit);
return unit == null ? this.unitsToProcess[0] : unit;
} catch (Error | RuntimeException e) {
this.handleInternalException(e, unit, null);
throw e; // rethrow
} finally {
// leave this.lookupEnvironment.unitBeingCompleted set to the unit, until another unit is resolved
// other calls to dom can cause classpath errors to be detected, resulting in AbortCompilation exceptions
// No reset is performed there anymore since,
// within the CodeAssist (or related tools),
// the compiler may be called *after* a call
// to this resolve(...) method. And such a call
// needs to have a compiler with a non-empty
// environment.
// this.reset();
}
}
//{ObjectTeams: hook for subclasses needing more setting up:
protected Config setupDependencies(boolean verifyMethods, boolean analyzeCode, boolean generateCode) {
return Dependencies.setup(this, this.parser, this.lookupEnvironment,
verifyMethods,
analyzeCode && !this.options.ignoreMethodBodies,
generateCode && !this.options.ignoreMethodBodies,
true, true, false);
}
// SH}
/**
* Internal API used to resolve a given compilation unit. Can run a subset of the compilation process
*/
public CompilationUnitDeclaration resolve(
ICompilationUnit sourceUnit,
boolean verifyMethods,
boolean analyzeCode,
boolean generateCode) {
return resolve(
null,
sourceUnit,
verifyMethods,
analyzeCode,
generateCode);
}
//{ObjectTeams: new function in ITypeRequester for use by Config:
@Override
public Parser getPlainParser() {
return null;
}
// SH}
}