/*******************************************************************************
* Copyright (c) 2000, 2011 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Stephan Herrmann - contribution for bug 337868 - [compiler][model] incomplete support for package-info.java when using
* SearchableEnvironment
*******************************************************************************/
package org.eclipse.che.ide.ext.java.jdt.internal.compiler;
import org.eclipse.che.ide.ext.java.jdt.core.compiler.CategorizedProblem;
import org.eclipse.che.ide.ext.java.jdt.core.compiler.IProblem;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ast.ImportReference;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.env.AccessRestriction;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.env.IBinaryType;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.env.ICompilationUnit;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.env.INameEnvironment;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.env.ISourceType;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.impl.CompilerStats;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.impl.ITypeRequestor;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.PackageBinding;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.parser.Parser;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.problem.AbortCompilation;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.problem.AbortCompilationUnit;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.problem.DefaultProblem;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.problem.ProblemReporter;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.problem.ProblemSeverities;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.util.Messages;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.util.Util;
public class Compiler implements ITypeRequestor, ProblemSeverities {
public Parser parser;
public ICompilerRequestor requestor;
public CompilerOptions options;
public ProblemReporter problemReporter;
public CompilerStats stats;
public int remainingIterations = 1;
// management of unit to be processed
// public CompilationUnitResult currentCompilationUnitResult;
public CompilationUnitDeclaration[] unitsToProcess;
public int totalUnits; // (totalUnits-1) gives the last unit in unitToProcess
// 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)
// /**
// * 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.che.ide.java.client.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.che.ide.java.client.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 /* 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.che.ide.java.client.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.che.ide.java.client.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) {
// this(environment, policy, options, requestor, problemFactory, null /* progress */);
// }
public Compiler(INameEnvironment environment, IErrorHandlingPolicy policy, CompilerOptions options,
final ICompilerRequestor requestor, IProblemFactory problemFactory) {
this.options = options;
// wrap requestor in DebugRequestor if one is specified
this.requestor = requestor;
this.problemReporter = new ProblemReporter(policy, this.options, problemFactory);
this.lookupEnvironment = new LookupEnvironment(this, this.options, this.problemReporter, environment);
this.stats = new CompilerStats();
initializeParser();
}
/** Add an additional binary type */
@Override
public void accept(IBinaryType binaryType, PackageBinding packageBinding, AccessRestriction accessRestriction) {
this.lookupEnvironment.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 {
// 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);
}
parsedUnit.bits |= ASTNode.IsImplicitUnit;
// initial type binding creation
this.lookupEnvironment.buildTypeBindings(parsedUnit, accessRestriction);
addCompilationUnit(sourceUnit, parsedUnit);
// binding resolution
this.lookupEnvironment.completeTypeBindings(parsedUnit);
} 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.instance.abort_againstSourceModel(
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);
}
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);
}
// public synchronized CompilationUnitDeclaration getUnitToProcess(int next)
// {
// if (next < this.totalUnits)
// {
// CompilationUnitDeclaration unit = this.unitsToProcess[next];
// this.unitsToProcess[next] = null; // release reference to processed unit declaration
// return unit;
// }
// return null;
// }
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.instance.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);
/* 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);
}
} 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);
}
/**
* 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) {
// Switch the current policy and compilation result for this unit to the requested one.
for (int i = 0; i < maxUnits; i++) {
try {
// diet parsing for large collection of units
CompilationUnitDeclaration parsedUnit;
CompilationResult 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);
}
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());
} finally {
sourceUnits[i] = null; // no longer hold onto the unit
}
}
// 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();
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();
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
}
long generateStart = System.currentTimeMillis();
this.stats.analyzeTime += generateStart - analyzeStart;
// if (!this.options.ignoreMethodBodies) unit.generateCode(); // code generation
// 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];
// if ((currentUnit.bits & ASTNode.IsImplicitUnit) == 0)
// {
// currentUnits[index++] = currentUnit;
// }
// }
// if (index != length)
// {
// System.arraycopy(currentUnits, 0, (currentUnits = new CompilationUnitDeclaration[index]), 0, index);
// }
// this.annotationProcessorManager.processAnnotations(currentUnits, binaryTypeBindingsTemp, false);
// ICompilationUnit[] newUnits = this.annotationProcessorManager.getNewUnits();
// newUnitSize = newUnits.length;
// ReferenceBinding[] newClassFiles = this.annotationProcessorManager.getNewClassFiles();
// binaryTypeBindingsTemp = newClassFiles;
// newClassFilesSize = newClassFiles.length;
// if (newUnitSize != 0)
// {
// ICompilationUnit[] newProcessedUnits = org.eclipse.che.ide.java.client.internal.core.util.Util.clone(newUnits); // 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
// }
// 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 = org.eclipse.che.ide.java.client.internal.core.util.Util.clone(newUnits); // 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();
// }
// }
// else
// {
// this.annotationProcessorManager.reset();
// }
// }
public void reset() {
this.lookupEnvironment.reset();
this.parser.scanner.source = null;
this.unitsToProcess = null;
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) {
try {
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;
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();
// finalize problems (suppressWarnings)
unit.finalizeProblems();
}
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 e) {
this.handleInternalException(e, unit, null);
throw e; // rethrow
} catch (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();
}
}
/** 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);
}
}