From 3bdf8e934773576d01b37ed8f2f309c41d6ac304 Mon Sep 17 00:00:00 2001 From: acolyer Date: Tue, 4 May 2004 16:02:29 +0000 Subject: [PATCH] AspectJ building from inside Eclipse --- .../core/builder/AjBatchImageBuilder.java | 16 +- .../internal/core/builder/AjBuildManager.java | 3 +- .../core/builder/AjBuildNotifier.java | 65 ++++++ .../builder/AjIncrementalImageBuilder.java | 16 +- .../internal/core/builder/AspectJBuilder.java | 215 ++++++++++++++++-- .../core/builder/EclipseClassPathManager.java | 113 +++++++++ 6 files changed, 394 insertions(+), 34 deletions(-) create mode 100644 org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjBuildNotifier.java create mode 100644 org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/EclipseClassPathManager.java diff --git a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjBatchImageBuilder.java b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjBatchImageBuilder.java index d245af2d9..53442e20a 100644 --- a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjBatchImageBuilder.java +++ b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjBatchImageBuilder.java @@ -10,8 +10,9 @@ *******************************************************************************/ package org.aspectj.ajdt.internal.core.builder; -import org.eclipse.jdt.internal.compiler.Compiler; +import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.core.builder.BatchImageBuilder; +import org.eclipse.jdt.internal.core.builder.SourceFile; /** * @author colyer @@ -25,10 +26,15 @@ public class AjBatchImageBuilder extends BatchImageBuilder { } /* (non-Javadoc) - * @see org.eclipse.jdt.internal.core.builder.AbstractImageBuilder#newCompiler() + * @see org.eclipse.jdt.internal.compiler.ICompilerRequestor#acceptResult(org.eclipse.jdt.internal.compiler.CompilationResult) */ - protected Compiler newCompiler() { - // we must pass in an AjCompilerOptions instance... - return super.newCompiler(); + public void acceptResult(CompilationResult result) { + if ((result.getCompilationUnit() != null) && (result.getCompilationUnit() instanceof SourceFile)) { + super.acceptResult(result); + } else { + // it's a file originating from binary source... + // we need to handle it ourselves + // TODO handle binary source output + } } } diff --git a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjBuildManager.java b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjBuildManager.java index 36a21a7d2..32c39a839 100644 --- a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjBuildManager.java +++ b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjBuildManager.java @@ -872,10 +872,9 @@ public class AjBuildManager implements IOutputClassFileNameProvider,IBinarySourc AjLookupEnvironment le = new AjLookupEnvironment(forCompiler, forCompiler.options, pr, environment); - EclipseFactory factory = new EclipseFactory(le); + EclipseFactory factory = new EclipseFactory(le,this); le.factory = factory; pr.factory = factory; - le.factory.buildManager = this; forCompiler.lookupEnvironment = le; diff --git a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjBuildNotifier.java b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjBuildNotifier.java new file mode 100644 index 000000000..121d68c24 --- /dev/null +++ b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjBuildNotifier.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.aspectj.ajdt.internal.core.builder; + +import org.aspectj.bridge.IProgressListener; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.internal.core.builder.BuildNotifier; + +/** + * @author colyer + * + * Build progress notification inside Eclipse + */ +public class AjBuildNotifier extends BuildNotifier implements IProgressListener { + + /** + * @param monitor + * @param project + */ + public AjBuildNotifier(IProgressMonitor monitor, IProject project) { + super(monitor, project); + } + + /* (non-Javadoc) + * @see org.aspectj.bridge.IProgressListener#setText(java.lang.String) + */ + public void setText(String text) { + subTask(text); + } + + /* (non-Javadoc) + * @see org.aspectj.bridge.IProgressListener#setProgress(double) + */ + public void setProgress(double percentDone) { + updateProgress((float)(percentDone/100.0f)); + } + + /* (non-Javadoc) + * @see org.aspectj.bridge.IProgressListener#setCancelledRequested(boolean) + */ + public void setCancelledRequested(boolean cancelRequested) { + // no-op + } + + /* (non-Javadoc) + * @see org.aspectj.bridge.IProgressListener#isCancelledRequested() + */ + public boolean isCancelledRequested() { + // can't delegate to super methods as they throw exception, which is not what we want inside weaver + boolean cancelRequested = cancelling; + if (monitor != null) { + cancelRequested = cancelRequested || monitor.isCanceled(); + } + return cancelRequested; + } +} diff --git a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjIncrementalImageBuilder.java b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjIncrementalImageBuilder.java index 734dd31ae..d3b781ff6 100644 --- a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjIncrementalImageBuilder.java +++ b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjIncrementalImageBuilder.java @@ -10,8 +10,9 @@ *******************************************************************************/ package org.aspectj.ajdt.internal.core.builder; -import org.eclipse.jdt.internal.compiler.Compiler; +import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.core.builder.IncrementalImageBuilder; +import org.eclipse.jdt.internal.core.builder.SourceFile; /** * @author colyer @@ -25,10 +26,15 @@ public class AjIncrementalImageBuilder extends IncrementalImageBuilder { } /* (non-Javadoc) - * @see org.eclipse.jdt.internal.core.builder.AbstractImageBuilder#newCompiler() + * @see org.eclipse.jdt.internal.compiler.ICompilerRequestor#acceptResult(org.eclipse.jdt.internal.compiler.CompilationResult) */ - protected Compiler newCompiler() { - // we must pass in an AjCompilerOptions instance... - return super.newCompiler(); + public void acceptResult(CompilationResult result) { + if ((result.getCompilationUnit() != null) && (result.getCompilationUnit() instanceof SourceFile)) { + super.acceptResult(result); + } else { + // it's a file originating from binary source... + // we need to handle it ourselves + // TODO handle binary source output + } } } diff --git a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AspectJBuilder.java b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AspectJBuilder.java index 5089d0ac3..da10e2e6d 100644 --- a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AspectJBuilder.java +++ b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AspectJBuilder.java @@ -10,17 +10,46 @@ *******************************************************************************/ package org.aspectj.ajdt.internal.core.builder; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Locale; import java.util.Map; +import java.util.Properties; +import org.aspectj.ajdt.core.AspectJCore; +import org.aspectj.ajdt.internal.compiler.AjCompilerAdapter; +import org.aspectj.ajdt.internal.compiler.IBinarySourceProvider; +import org.aspectj.ajdt.internal.compiler.IIntermediateResultsRequestor; +import org.aspectj.ajdt.internal.compiler.IOutputClassFileNameProvider; +import org.aspectj.ajdt.internal.compiler.InterimCompilationResult; +import org.aspectj.ajdt.internal.compiler.lookup.AjLookupEnvironment; +import org.aspectj.ajdt.internal.compiler.lookup.EclipseFactory; +import org.aspectj.ajdt.internal.compiler.problem.AjProblemReporter; +import org.aspectj.bridge.AbortException; +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.bridge.IMessage.Kind; +import org.aspectj.weaver.Lint; import org.aspectj.weaver.bcel.BcelWeaver; import org.aspectj.weaver.bcel.BcelWorld; +import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.Path; +import org.eclipse.jdt.core.IJavaModelMarker; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.Compiler; +import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies; import org.eclipse.jdt.internal.compiler.ICompilerAdapter; import org.eclipse.jdt.internal.compiler.ICompilerAdapterFactory; +import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; import org.eclipse.jdt.internal.core.builder.BatchImageBuilder; +import org.eclipse.jdt.internal.core.builder.BuildNotifier; import org.eclipse.jdt.internal.core.builder.IncrementalImageBuilder; import org.eclipse.jdt.internal.core.builder.JavaBuilder; @@ -36,8 +65,10 @@ public class AspectJBuilder extends JavaBuilder implements ICompilerAdapterFacto // One builder instance per project (important) private BcelWeaver myWeaver = null; private BcelWorld myBcelWorld = null; + private UnwovenResultCollector unwovenResultCollector = null; + private OutputFileNameProvider fileNameProvider = null; - private boolean isBuildIncremental = false; + private boolean isBatchBuild = false; /* (non-Javadoc) @@ -49,45 +80,185 @@ public class AspectJBuilder extends JavaBuilder implements ICompilerAdapterFacto // causes construction of a new Compiler, so we will be detected as the // adapter. Compiler.setCompilerAdapterFactory(this); - initializeAjBuilder(); return super.build(kind, ignored, monitor); } protected BatchImageBuilder getBatchImageBuilder() { - isBuildIncremental = false; + isBatchBuild = true; return new AjBatchImageBuilder(this); } protected IncrementalImageBuilder getIncrementalImageBuilder() { - isBuildIncremental = true; + isBatchBuild = false; return new AjIncrementalImageBuilder(this); } - private void initializeAjBuilder() { - // get hold of the project we are building (getProject()), and - // create a world, weaver etc. if we do not already have them. - } - /* (non-Javadoc) * @see org.eclipse.jdt.internal.compiler.ICompilerAdapterFactory#getAdapter(org.eclipse.jdt.internal.compiler.Compiler) */ public ICompilerAdapter getAdapter(Compiler forCompiler) { - // TODO Auto-generated method stub - // to create an AjCompilerAdapter we need... - // * a Compiler instance [DONE - passed in] - // * to know whether this is a batch or incremental build [DONE] - // * a world -- local state, create if not available (or batch build) - // * a weaver -- local state, create if not available (or batch build) + AjCompilerOptions options = (AjCompilerOptions) forCompiler.options; + + if (isBatchBuild || myBcelWorld == null || myWeaver == null) { + initWorldAndWeaver(options); + } + // * an eclipse factory -- create from AjLookupEnvironment, need to hide AjBuildManager field - // * optional intermediate results requestor -- not required (or use AjBuildNotifier) - // * optional progress listener -- build() is passed a progress monitor, JavaBuilder makes a BuildNotifier out of this, we - // should replace with AjBuildNotifier subclass - move creation to factory method in JavaBuilder?? - // * an output file name provider - // * the set of binary source entries for this compile -- from analyzing deltas + AjProblemReporter pr = + new AjProblemReporter(DefaultErrorHandlingPolicies.proceedWithAllProblems(), + forCompiler.options, new DefaultProblemFactory(Locale.getDefault())); + forCompiler.problemReporter = pr; + AjLookupEnvironment le = + new AjLookupEnvironment(forCompiler, forCompiler.options, pr,nameEnvironment); + EclipseFactory eFactory = new EclipseFactory(le,myBcelWorld,options.xSerializableAspects); + + AjBuildNotifier ajNotifier = (AjBuildNotifier) notifier; + if (fileNameProvider == null ) fileNameProvider = new OutputFileNameProvider(getProject()); + + // * the set of binary source entries for this compile -- from analyzing deltas, or everything if batch // * the full set of binary source entries for the project -- from IAspectJProject - // * the value of the -XNoWeave option -- from aspectJProject.getOptions() (return a subclass of CompilerOptions). - return null; + // TODO deal with inpath, injars here... + IBinarySourceProvider bsProvider = new NullBinarySourceProvider(); + Map fullBinarySourceEntries = new HashMap(); + + // * the intermediate result set from the last batch compile + if (isBatchBuild) { + unwovenResultCollector = new UnwovenResultCollector(); + } + Collection resultSetForFullWeave = unwovenResultCollector.getIntermediateResults(); + + return new AjCompilerAdapter(forCompiler,isBatchBuild,myBcelWorld, + myWeaver,eFactory,unwovenResultCollector,ajNotifier,fileNameProvider,bsProvider, + fullBinarySourceEntries,resultSetForFullWeave, + options.noWeave); + } + + /* (non-Javadoc) + * @see org.eclipse.jdt.internal.core.builder.JavaBuilder#createBuildNotifier(org.eclipse.core.runtime.IProgressMonitor, org.eclipse.core.resources.IProject) + */ + protected BuildNotifier createBuildNotifier(IProgressMonitor monitor, + IProject currentProject) { + return new AjBuildNotifier(monitor, currentProject); + } + + + private void initWorldAndWeaver(AjCompilerOptions options) { + EclipseClassPathManager cpMgr = new EclipseClassPathManager(nameEnvironment); + myBcelWorld = new BcelWorld(cpMgr,new UnhandledMessageHandler(getProject()),null /*(xrefHandler)*/); + myBcelWorld.setXnoInline(options.xNoInline); + myBcelWorld.setXlazyTjp(options.xLazyThisJoinPoint); + setLintProperties(myBcelWorld,options); + myWeaver = new BcelWeaver(myBcelWorld); + myWeaver.setReweavableMode(options.xReweavable,options.xReweavableCompress); + // TODO deal with injars, inpath, and aspectpath here... + } + + private void setLintProperties(BcelWorld world, AjCompilerOptions options) { + Properties p = new Properties(); + Lint lintSettings = world.getLint(); + Map map = options.getMap(); + p.put(lintSettings.invalidAbsoluteTypeName.getName(),map.get(AjCompilerOptions.OPTION_ReportInvalidAbsoluteTypeName)); + p.put(lintSettings.invalidWildcardTypeName.getName(),map.get(AjCompilerOptions.OPTION_ReportInvalidWildcardTypeName)); + p.put(lintSettings.unresolvableMember.getName(),map.get(AjCompilerOptions.OPTION_ReportUnresolvableMember)); + p.put(lintSettings.typeNotExposedToWeaver.getName(),map.get(AjCompilerOptions.OPTION_ReportTypeNotExposedToWeaver)); + p.put(lintSettings.shadowNotInStructure.getName(),map.get(AjCompilerOptions.OPTION_ReportShadowNotInStructure)); + p.put(lintSettings.unmatchedSuperTypeInCall.getName(),map.get(AjCompilerOptions.OPTION_ReportUnmatchedSuperTypeInCall)); + p.put(lintSettings.canNotImplementLazyTjp.getName(),map.get(AjCompilerOptions.OPTION_ReportCannotImplementLazyTJP)); + p.put(lintSettings.needsSerialVersionUIDField.getName(),map.get(AjCompilerOptions.OPTION_ReportNeedSerialVersionUIDField)); + p.put(lintSettings.serialVersionUIDBroken.getName(),map.get(AjCompilerOptions.OPTION_ReportIncompatibleSerialVersion)); + lintSettings.setFromProperties(p); + } + + private static class UnwovenResultCollector implements IIntermediateResultsRequestor { + + private Collection results = new ArrayList(); + + /* (non-Javadoc) + * @see org.aspectj.ajdt.internal.compiler.IIntermediateResultsRequestor#acceptResult(org.aspectj.ajdt.internal.compiler.InterimCompilationResult) + */ + public void acceptResult(InterimCompilationResult intRes) { + results.add(intRes); + } + + public Collection getIntermediateResults() { + return results; + } + + } + + // this class will only get messages that the weaver adapter couldn't tie into + // an originating resource in the project - make them messages on the project + // itself. + private static class UnhandledMessageHandler implements IMessageHandler { + + private IProject project; + + public UnhandledMessageHandler(IProject p) { + this.project = p; + } + + /* (non-Javadoc) + * @see org.aspectj.bridge.IMessageHandler#handleMessage(org.aspectj.bridge.IMessage) + */ + public boolean handleMessage(IMessage message) throws AbortException { + try { + IMarker marker = project.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER); + marker.setAttribute(IMarker.MESSAGE, message); + marker.setAttribute(IMarker.SEVERITY, message.isError() ? IMarker.SEVERITY_ERROR : IMarker.SEVERITY_WARNING); + } catch (CoreException e) { + AspectJCore.getPlugin().getLog().log(e.getStatus()); + } + return true; + } + + /* (non-Javadoc) + * @see org.aspectj.bridge.IMessageHandler#isIgnoring(org.aspectj.bridge.IMessage.Kind) + */ + public boolean isIgnoring(Kind kind) { + if (kind == IMessage.DEBUG || kind == IMessage.INFO) return true; + return false; + } + } + private static class OutputFileNameProvider implements IOutputClassFileNameProvider { + + private IPath outputLocation; + + public OutputFileNameProvider(IProject p) { + try { + outputLocation = JavaCore.create(p).getOutputLocation(); + } catch (JavaModelException e) { + outputLocation = new Path("."); + AspectJCore.getPlugin().getLog().log(e.getStatus()); + } + } + + /* (non-Javadoc) + * @see org.aspectj.ajdt.internal.compiler.IOutputClassFileNameProvider#getOutputClassFileName(char[], org.eclipse.jdt.internal.compiler.CompilationResult) + */ + public String getOutputClassFileName(char[] eclipseClassFileName, CompilationResult result) { + // In the AJDT implementation, the name provided here will be ignored, we write the results + // out in xxxImageBuilder.acceptResult() instead. + // simply return the default output directory for the project. + String filename = new String(eclipseClassFileName); + IPath out = outputLocation.append(filename); + out.addFileExtension(".class"); + return out.toOSString(); + } + + } + + // default impl class until the implementation is extended to cope with inpath, injars + private static class NullBinarySourceProvider implements IBinarySourceProvider { + + /* (non-Javadoc) + * @see org.aspectj.ajdt.internal.compiler.IBinarySourceProvider#getBinarySourcesForThisWeave() + */ + public Map getBinarySourcesForThisWeave() { + return new HashMap(); + } + + } } diff --git a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/EclipseClassPathManager.java b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/EclipseClassPathManager.java new file mode 100644 index 000000000..38e3a889f --- /dev/null +++ b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/EclipseClassPathManager.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright (c) 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.aspectj.ajdt.internal.core.builder; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.weaver.TypeX; +import org.aspectj.weaver.bcel.ClassPathManager; +import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; +import org.eclipse.jdt.internal.compiler.env.IBinaryType; +import org.eclipse.jdt.internal.compiler.env.INameEnvironment; +import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; + +/** + * @author colyer + * + * Provide a type lookup environment for the weaver, without having to convert + * the various eclipse paths into their external form. + */ +public class EclipseClassPathManager extends ClassPathManager { + + private INameEnvironment nameEnv; + + public EclipseClassPathManager(INameEnvironment env) { + this.nameEnv = env; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.bcel.ClassPathManager#addPath(java.lang.String, org.aspectj.bridge.IMessageHandler) + */ + public void addPath(String name, IMessageHandler handler) { + throw new UnsupportedOperationException("Can't add paths to an *Eclipse*ClassPathManager."); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.bcel.ClassPathManager#find(org.aspectj.weaver.TypeX) + */ + public ClassFile find(TypeX type) { + ClassFile cf = null; + String name = type.getName(); + if (name.endsWith(".class")) { + name = name.substring(0,name.length() - ".class".length()); + } + char[][] cname = CharOperation.splitOn('.',name.toCharArray()); + NameEnvironmentAnswer answer = nameEnv.findType(cname); + if (answer == null || !answer.isBinaryType()) { + return null; + } else { + IBinaryType binType = answer.getBinaryType(); + // XXX - but better than the alternative hacks + if (binType instanceof ClassFileReader) { + ClassFileReader cfr = (ClassFileReader) binType; + cf = new ClassFileReaderBackedClassFile(cfr); + } else { + throw new IllegalArgumentException( + "I'm only geared up to handle ClassFileReaders, and you gave me a " + + binType.getClass().getName()); + } + return cf; + } + } + /* (non-Javadoc) + * @see org.aspectj.weaver.bcel.ClassPathManager#getAllClassFiles() + */ + public List getAllClassFiles() { + throw new UnsupportedOperationException("I don't implement getAllClassFiles()"); + //return Collections.EMPTY_LIST; + } + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + public String toString() { + StringBuffer buf = new StringBuffer("EclipseClassPathManager: "); + buf.append(nameEnv.toString()); + return buf.toString(); + } + + private class ClassFileReaderBackedClassFile extends ClassPathManager.ClassFile { + + private ClassFileReader source; + + public ClassFileReaderBackedClassFile(ClassFileReader cfr) { + source = cfr; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.bcel.ClassPathManager.ClassFile#getInputStream() + */ + public InputStream getInputStream() throws IOException { + return new ByteArrayInputStream(source.getReferenceBytes()); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.bcel.ClassPathManager.ClassFile#getPath() + */ + public String getPath() { + return new String(source.getFileName()); + } + } +} -- 2.39.5