/*******************************************************************************
* Copyright (c) 2004 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
*******************************************************************************/
package org.aspectj.ajdt.internal.compiler;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.aspectj.ajdt.internal.compiler.ast.AddAtAspectJAnnotationsVisitor;
import org.aspectj.ajdt.internal.compiler.ast.ValidateAtAspectJAnnotationsVisitor;
import org.aspectj.ajdt.internal.compiler.lookup.EclipseFactory;
import org.aspectj.ajdt.internal.core.builder.AjState;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.IMessageHandler;
import org.aspectj.bridge.IProgressListener;
import org.aspectj.bridge.context.CompilationAndWeavingContext;
import org.aspectj.bridge.context.ContextToken;
import org.aspectj.org.eclipse.jdt.internal.compiler.CompilationResult;
import org.aspectj.org.eclipse.jdt.internal.compiler.Compiler;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.aspectj.org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
import org.aspectj.org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
import org.aspectj.weaver.bcel.BcelWeaver;
import org.aspectj.weaver.bcel.BcelWorld;
/**
* @author colyer
*
* Adapts standard JDT Compiler to add in AspectJ specific behaviours.
*/
public class AjCompilerAdapter extends AbstractCompilerAdapter {
private Compiler compiler;
private BcelWeaver weaver;
private EclipseFactory eWorld;
private boolean isBatchCompile;
private boolean reportedErrors;
private boolean isXTerminateAfterCompilation;
private boolean proceedOnError;
private boolean inJava5Mode;
private boolean reflectable;
private boolean noAtAspectJAnnotationProcessing;
private IIntermediateResultsRequestor intermediateResultsRequestor;
private IProgressListener progressListener;
private IOutputClassFileNameProvider outputFileNameProvider;
private IBinarySourceProvider binarySourceProvider;
private WeaverMessageHandler weaverMessageHandler;
private Map /* fileName |-> List<UnwovenClassFile> */binarySourceSetForFullWeave = new HashMap();
private ContextToken processingToken = null;
private ContextToken resolvingToken = null;
private ContextToken analysingToken = null;
private ContextToken generatingToken = null;
private AjState incrementalCompilationState;
List /* InterimResult */resultsPendingWeave = new ArrayList();
/**
* Create an adapter, and tell it everything it needs to now to drive the AspectJ parts of a compile cycle.
*
* @param compiler the JDT compiler that produces class files from source
* @param isBatchCompile true if this is a full build (non-incremental)
* @param world the bcelWorld used for type resolution during weaving
* @param weaver the weaver
* @param intRequestor recipient of interim compilation results from compiler (pre-weave)
* @param outputFileNameProvider implementor of a strategy providing output file names for results
* @param binarySourceEntries binary source that we didn't compile, but that we need to weave
* @param resultSetForFullWeave if we are doing an incremental build, and the weaver determines that we need to weave the world,
* this is the set of intermediate results that will be passed to the weaver.
*/
public AjCompilerAdapter(Compiler compiler, boolean isBatchCompile, BcelWorld world, BcelWeaver weaver,
EclipseFactory eFactory, IIntermediateResultsRequestor intRequestor, IProgressListener progressListener,
IOutputClassFileNameProvider outputFileNameProvider, IBinarySourceProvider binarySourceProvider,
Map fullBinarySourceEntries, /* fileName |-> List<UnwovenClassFile> */
boolean isXterminateAfterCompilation, boolean proceedOnError, boolean noAtAspectJProcessing, boolean reflectable,
AjState incrementalCompilationState) {
this.compiler = compiler;
this.isBatchCompile = isBatchCompile;
this.weaver = weaver;
this.intermediateResultsRequestor = intRequestor;
this.progressListener = progressListener;
this.outputFileNameProvider = outputFileNameProvider;
this.binarySourceProvider = binarySourceProvider;
this.isXTerminateAfterCompilation = isXterminateAfterCompilation;
this.proceedOnError = proceedOnError;
this.binarySourceSetForFullWeave = fullBinarySourceEntries;
this.eWorld = eFactory;
this.reflectable = reflectable;
this.inJava5Mode = false;
this.noAtAspectJAnnotationProcessing = noAtAspectJProcessing;
this.incrementalCompilationState = incrementalCompilationState;
if (compiler.options.complianceLevel >= ClassFileConstants.JDK1_5) {
inJava5Mode = true;
}
IMessageHandler msgHandler = world.getMessageHandler();
// Do we need to reset the message handler or create a new one? (This saves a ton of memory lost on incremental compiles...)
if (msgHandler instanceof WeaverMessageHandler) {
((WeaverMessageHandler) msgHandler).resetCompiler(compiler);
weaverMessageHandler = (WeaverMessageHandler) msgHandler;
} else {
weaverMessageHandler = new WeaverMessageHandler(msgHandler, compiler);
world.setMessageHandler(weaverMessageHandler);
}
}
// the compilation lifecycle methods below are called in order as compilation progresses...
public void beforeCompiling(ICompilationUnit[] sourceUnits) {
resultsPendingWeave = new ArrayList();
reportedErrors = false;
}
public void beforeProcessing(CompilationUnitDeclaration unit) {
eWorld.showMessage(IMessage.INFO, "compiling " + new String(unit.getFileName()), null, null);
processingToken = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.PROCESSING_COMPILATION_UNIT, unit
.getFileName());
if (inJava5Mode && !noAtAspectJAnnotationProcessing) {
ContextToken tok = CompilationAndWeavingContext.enteringPhase(
CompilationAndWeavingContext.ADDING_AT_ASPECTJ_ANNOTATIONS, unit.getFileName());
AddAtAspectJAnnotationsVisitor atAspectJVisitor = new AddAtAspectJAnnotationsVisitor(unit, reflectable);
unit.traverse(atAspectJVisitor, unit.scope);
CompilationAndWeavingContext.leavingPhase(tok);
}
}
public void beforeResolving(CompilationUnitDeclaration unit) {
resolvingToken = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.RESOLVING_COMPILATION_UNIT, unit
.getFileName());
}
public void afterResolving(CompilationUnitDeclaration unit) {
if (resolvingToken != null) {
CompilationAndWeavingContext.leavingPhase(resolvingToken);
}
}
public void beforeAnalysing(CompilationUnitDeclaration unit) {
analysingToken = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.ANALYSING_COMPILATION_UNIT, unit
.getFileName());
if (inJava5Mode && !noAtAspectJAnnotationProcessing) {
ValidateAtAspectJAnnotationsVisitor atAspectJVisitor = new ValidateAtAspectJAnnotationsVisitor(unit);
unit.traverse(atAspectJVisitor, unit.scope);
}
}
public void afterAnalysing(CompilationUnitDeclaration unit) {
if (analysingToken != null) {
CompilationAndWeavingContext.leavingPhase(analysingToken);
}
}
public void beforeGenerating(CompilationUnitDeclaration unit) {
generatingToken = CompilationAndWeavingContext.enteringPhase(
CompilationAndWeavingContext.GENERATING_UNWOVEN_CODE_FOR_COMPILATION_UNIT, unit.getFileName());
}
public void afterGenerating(CompilationUnitDeclaration unit) {
if (generatingToken != null) {
CompilationAndWeavingContext.leavingPhase(generatingToken);
}
}
public void afterCompiling(CompilationUnitDeclaration[] units) {
this.eWorld.cleanup();
try {
// not great ... but one more check before we continue, see pr132314
if (!reportedErrors && units != null) {
for (int i = 0; i < units.length; i++) {
if (units[i] != null && units[i].compilationResult != null && units[i].compilationResult.hasErrors()) {
reportedErrors = true;
break;
}
}
}
if (isXTerminateAfterCompilation || (reportedErrors && !proceedOnError)) {
// no point weaving... just tell the requestor we're done
notifyRequestor();
} else {
weave(); // notification happens as weave progresses...
// weaver.getWorld().flush(); // pr152257
}
} catch (IOException ex) {
AbortCompilation ac = new AbortCompilation(null, ex);
throw ac;
} catch (RuntimeException rEx) {
if (rEx instanceof AbortCompilation) {
throw rEx; // Don't wrap AbortCompilation exceptions!
}
// This will be unwrapped in Compiler.handleInternalException() and the nested
// RuntimeException thrown back to the original caller - which is AspectJ
// which will then then log it as a compiler problem.
throw new AbortCompilation(true, rEx);
}
}
public void afterProcessing(CompilationUnitDeclaration unit, int unitIndex) {
CompilationAndWeavingContext.leavingPhase(processingToken);
eWorld.finishedCompilationUnit(unit);
InterimCompilationResult intRes = new InterimCompilationResult(unit.compilationResult, outputFileNameProvider);
if (unit.compilationResult.hasErrors()) {
reportedErrors = true;
}
if (intermediateResultsRequestor != null) {
intermediateResultsRequestor.acceptResult(intRes);
}
if (isXTerminateAfterCompilation) {
acceptResult(unit.compilationResult);
} else {
resultsPendingWeave.add(intRes);
}
}
// public void beforeResolving(CompilationUnitDeclaration unit, ICompilationUnit sourceUnit, boolean verifyMethods, boolean
// analyzeCode, boolean generateCode) {
// resultsPendingWeave = new ArrayList();
// reportedErrors = false;
// }
//
// public void afterResolving(CompilationUnitDeclaration unit, ICompilationUnit sourceUnit, boolean verifyMethods, boolean
// analyzeCode, boolean generateCode) {
// InterimCompilationResult intRes = new InterimCompilationResult(unit.compilationResult,outputFileNameProvider);
// if (unit.compilationResult.hasErrors()) reportedErrors = true;
// if (isXNoWeave || !generateCode) {
// acceptResult(unit.compilationResult);
// } else if (generateCode){
// resultsPendingWeave.add(intRes);
// try {
// weave();
// } catch (IOException ex) {
// AbortCompilation ac = new AbortCompilation(null,ex);
// throw ac;
// }
// }
// }
// helper methods...
// ==================================================================================
/*
* Called from the weaverAdapter once it has finished weaving the class files associated with a given compilation result.
*/
public void acceptResult(CompilationResult result) {
compiler.requestor.acceptResult(result.tagAsAccepted());
if (compiler.unitsToProcess != null) {
for (int i = 0; i < compiler.unitsToProcess.length; i++) {
if (compiler.unitsToProcess[i] != null) {
if (compiler.unitsToProcess[i].compilationResult == result) {
compiler.unitsToProcess[i] = null;
}
}
}
}
}
private List getBinarySourcesFrom(Map binarySourceEntries) {
// Map is fileName |-> List<UnwovenClassFile>
List ret = new ArrayList();
for (Iterator binIter = binarySourceEntries.keySet().iterator(); binIter.hasNext();) {
String sourceFileName = (String) binIter.next();
List unwovenClassFiles = (List) binarySourceEntries.get(sourceFileName);
// XXX - see bugs 57432,58679 - final parameter on next call should be "compiler.options.maxProblemsPerUnit"
CompilationResult result = new CompilationResult(sourceFileName.toCharArray(), 0, 0, Integer.MAX_VALUE);
result.noSourceAvailable();
InterimCompilationResult binarySource = new InterimCompilationResult(result, unwovenClassFiles);
ret.add(binarySource);
}
return ret;
}
private void notifyRequestor() {
for (Iterator iter = resultsPendingWeave.iterator(); iter.hasNext();) {
InterimCompilationResult iresult = (InterimCompilationResult) iter.next();
compiler.requestor.acceptResult(iresult.result().tagAsAccepted());
}
}
private void weave() throws IOException {
// ensure weaver state is set up correctly
for (Iterator iter = resultsPendingWeave.iterator(); iter.hasNext();) {
InterimCompilationResult iresult = (InterimCompilationResult) iter.next();
for (int i = 0; i < iresult.unwovenClassFiles().length; i++) {
weaver.addClassFile(iresult.unwovenClassFiles()[i], false);
}
}
weaver.setIsBatchWeave(isBatchCompile);
weaver.prepareForWeave();
if (weaver.needToReweaveWorld()) {
if (!isBatchCompile) {
// force full recompilation from source
this.incrementalCompilationState.forceBatchBuildNextTimeAround();
return;
// addAllKnownClassesToWeaveList(); // if it's batch, they're already on the list...
}
resultsPendingWeave.addAll(getBinarySourcesFrom(binarySourceSetForFullWeave));
} else {
Map binarySourcesToAdd = binarySourceProvider.getBinarySourcesForThisWeave();
resultsPendingWeave.addAll(getBinarySourcesFrom(binarySourcesToAdd));
}
// if (isBatchCompile) {
// resultsPendingWeave.addAll(getBinarySourcesFrom(binarySourceSetForFullWeave));
// // passed into the compiler, the set of classes in injars and inpath...
// } else if (weaver.needToReweaveWorld()) {
// addAllKnownClassesToWeaveList();
// resultsPendingWeave.addAll(getBinarySourcesFrom(binarySourceSetForFullWeave));
// }
try {
weaver.weave(new WeaverAdapter(this, weaverMessageHandler, progressListener));
} finally {
// ???: is this the right point for this? After weaving has finished clear the caches.
weaverMessageHandler.setCurrentResult(null);
weaver.allWeavingComplete();
weaver.tidyUp();
IMessageHandler imh = weaver.getWorld().getMessageHandler();
if (imh instanceof WeaverMessageHandler) {
((WeaverMessageHandler) imh).resetCompiler(null);
}
}
}
public void afterDietParsing(CompilationUnitDeclaration[] units) {
// to be filled in...
}
public List getResultsPendingWeave() {
return resultsPendingWeave;
}
}