class processing builder generalization

This commit is contained in:
Eugene Zhuravlev
2012-12-03 18:06:48 +01:00
parent 7812128a45
commit bbff6c06b8
5 changed files with 172 additions and 124 deletions
@@ -238,7 +238,7 @@ public class InstrumentationClassFinder {
return superClass != null? loadClass(superClass) : null;
}
private PseudoClass[] getInterfaces() throws IOException, ClassNotFoundException {
public PseudoClass[] getInterfaces() throws IOException, ClassNotFoundException {
if (myInterfaces == null) {
return EMPTY_PSEUDOCLASS_ARRAY;
}
@@ -9,6 +9,7 @@ import org.jetbrains.jps.javac.BinaryContent;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
@@ -21,11 +22,21 @@ class ChunkBuildOutputConsumerImpl implements ModuleLevelBuilder.OutputConsumer
private final CompileContext myContext;
private Map<BuildTarget<?>, BuildOutputConsumerImpl> myTarget2Consumer = new THashMap<BuildTarget<?>, BuildOutputConsumerImpl>();
private Map<String, CompiledClass> myClasses = new THashMap<String, CompiledClass>();
private Map<BuildTarget<?>, Collection<CompiledClass>> myTargetToClassesMap = new THashMap<BuildTarget<?>, Collection<CompiledClass>>();
public ChunkBuildOutputConsumerImpl(CompileContext context) {
myContext = context;
}
@Override
public Collection<CompiledClass> getTargetCompiledClasses(BuildTarget<?> target) {
final Collection<CompiledClass> classes = myTargetToClassesMap.get(target);
if (classes != null) {
return Collections.unmodifiableCollection(classes);
}
return Collections.emptyList();
}
@NotNull
@Override
public Map<String, CompiledClass> getCompiledClasses() {
@@ -43,6 +54,12 @@ class ChunkBuildOutputConsumerImpl implements ModuleLevelBuilder.OutputConsumer
public void registerCompiledClass(BuildTarget<?> target, CompiledClass compiled) throws IOException {
if (compiled.getClassName() != null) {
myClasses.put(compiled.getClassName(), compiled);
Collection<CompiledClass> classes = myTargetToClassesMap.get(target);
if (classes == null) {
classes = new ArrayList<CompiledClass>();
myTargetToClassesMap.put(target, classes);
}
classes.add(compiled);
}
registerOutputFile(target, compiled.getOutputFile(), Collections.<String>singleton(compiled.getSourceFile().getPath()));
}
@@ -66,5 +83,6 @@ class ChunkBuildOutputConsumerImpl implements ModuleLevelBuilder.OutputConsumer
public void clear() {
myTarget2Consumer.clear();
myClasses.clear();
myTargetToClassesMap.clear();
}
}
@@ -36,6 +36,7 @@ public abstract class ModuleLevelBuilder extends Builder {
void registerCompiledClass(BuildTarget<?> target, CompiledClass compiled) throws IOException;
Collection<CompiledClass> getTargetCompiledClasses(BuildTarget<?> target);
@NotNull
Map<String, CompiledClass> getCompiledClasses();
@@ -2,111 +2,45 @@ package org.jetbrains.jps.incremental.instrumentation;
import com.intellij.compiler.instrumentation.InstrumentationClassFinder;
import com.intellij.compiler.instrumentation.InstrumenterClassWriter;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.ClassReader;
import org.jetbrains.asm4.ClassVisitor;
import org.jetbrains.asm4.ClassWriter;
import org.jetbrains.asm4.Opcodes;
import org.jetbrains.jps.ModuleChunk;
import org.jetbrains.jps.ProjectPaths;
import org.jetbrains.jps.builders.DirtyFilesHolder;
import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor;
import org.jetbrains.jps.incremental.*;
import org.jetbrains.jps.incremental.messages.ProgressMessage;
import org.jetbrains.jps.incremental.BuilderCategory;
import org.jetbrains.jps.incremental.CompileContext;
import org.jetbrains.jps.incremental.CompiledClass;
import org.jetbrains.jps.javac.BinaryContent;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
/**
* @author Eugene Zhuravlev
* Date: 11/25/12
*/
public abstract class BaseInstrumentingBuilder extends ModuleLevelBuilder{
private static final Key<InstrumentationClassFinder> CLASS_FINDER = Key.create("_cached_instrumentation_class_finder_");
public abstract class BaseInstrumentingBuilder extends ClassProcessingBuilder {
public BaseInstrumentingBuilder() {
super(BuilderCategory.CLASS_INSTRUMENTER);
}
@Override
public void chunkBuildStarted(CompileContext context, ModuleChunk chunk) {
}
@Override
public void chunkBuildFinished(CompileContext context, ModuleChunk chunk) {
final InstrumentationClassFinder finder = CLASS_FINDER.get(context);
CLASS_FINDER.set(context, null);
if (finder != null) {
finder.releaseResources();
}
}
@Override
public final ExitCode build(CompileContext context, ModuleChunk chunk, DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder, OutputConsumer outputConsumer) throws ProjectBuildException, IOException {
protected final ExitCode performBuild(CompileContext context, ModuleChunk chunk, InstrumentationClassFinder finder, OutputConsumer outputConsumer) {
ExitCode exitCode = ExitCode.NOTHING_DONE;
if (outputConsumer.getCompiledClasses().isEmpty() || !isEnabled(context, chunk)) {
return exitCode;
}
InstrumentationClassFinder finder = null;
final String progress = getProgressMessage();
final boolean shouldShowProgress = !StringUtil.isEmptyOrSpaces(progress);
if (shouldShowProgress) {
context.processMessage(new ProgressMessage(progress + " [" + chunk.getName() + "]"));
}
try {
for (CompiledClass compiledClass : outputConsumer.getCompiledClasses().values()) {
final BinaryContent originalContent = compiledClass.getContent();
final ClassReader reader = new ClassReader(originalContent.getBuffer(), originalContent.getOffset(), originalContent.getLength());
final int version = getClassFileVersion(reader);
if (!canInstrument(compiledClass, version)) {
continue;
}
if (finder == null) { // lazy init for this particular builder
finder = CLASS_FINDER.get(context); // try using shared finder
if (finder == null) {
final ProjectPaths paths = context.getProjectPaths();
final Collection<File> platformCp = paths.getPlatformCompilationClasspath(chunk, false);
final Collection<File> classpath = new ArrayList<File>();
classpath.addAll(paths.getCompilationClasspath(chunk, false));
classpath.addAll(ProjectPaths.getSourceRootsWithDependents(chunk).keySet());
finder = createInstrumentationClassFinder(platformCp, classpath, outputConsumer);
CLASS_FINDER.set(context, finder);
}
}
final ClassWriter writer = new InstrumenterClassWriter(getAsmClassWriterFlags(version), finder);
final BinaryContent instrumented = instrument(context, compiledClass, reader, writer, finder);
if (instrumented != null) {
compiledClass.setContent(instrumented);
exitCode = ExitCode.OK;
}
for (CompiledClass compiledClass : outputConsumer.getCompiledClasses().values()) {
final BinaryContent originalContent = compiledClass.getContent();
final ClassReader reader = new ClassReader(originalContent.getBuffer(), originalContent.getOffset(), originalContent.getLength());
final int version = getClassFileVersion(reader);
if (!canInstrument(compiledClass, version)) {
continue;
}
}
finally {
if (shouldShowProgress) {
context.processMessage(new ProgressMessage("")); // cleanup progress
final ClassWriter writer = new InstrumenterClassWriter(getAsmClassWriterFlags(version), finder);
final BinaryContent instrumented = instrument(context, compiledClass, reader, writer, finder);
if (instrumented != null) {
compiledClass.setContent(instrumented);
exitCode = ExitCode.OK;
}
}
return exitCode;
}
protected abstract boolean isEnabled(CompileContext context, ModuleChunk chunk);
protected abstract boolean canInstrument(CompiledClass compiledClass, int classFileVersion);
@Nullable
@@ -116,45 +50,4 @@ public abstract class BaseInstrumentingBuilder extends ModuleLevelBuilder{
ClassWriter writer,
InstrumentationClassFinder finder);
protected abstract String getProgressMessage();
// utility methods
public static InstrumentationClassFinder createInstrumentationClassFinder(Collection<File> platformCp, Collection<File> cp, final OutputConsumer outputConsumer) throws MalformedURLException {
final URL[] platformUrls = new URL[platformCp.size()];
int index = 0;
for (File file : platformCp) {
platformUrls[index++] = file.toURI().toURL();
}
final URL[] urls = new URL[cp.size()];
index = 0;
for (File file : cp) {
urls[index++] = file.toURI().toURL();
}
return new InstrumentationClassFinder(platformUrls, urls) {
protected InputStream lookupClassBeforeClasspath(String internalClassName) {
final BinaryContent content = outputConsumer.lookupClassBytes(internalClassName.replace("/", "."));
if (content != null) {
return new ByteArrayInputStream(content.getBuffer(), content.getOffset(), content.getLength());
}
return null;
}
};
}
public static int getAsmClassWriterFlags(int version) {
return version >= Opcodes.V1_6 && version != Opcodes.V1_1 ? ClassWriter.COMPUTE_FRAMES : ClassWriter.COMPUTE_MAXS;
}
public static int getClassFileVersion(ClassReader reader) {
final Ref<Integer> result = new Ref<Integer>(0);
reader.accept(new ClassVisitor(Opcodes.ASM4) {
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
result.set(version);
}
}, 0);
return result.get();
}
}
@@ -0,0 +1,136 @@
package org.jetbrains.jps.incremental.instrumentation;
import com.intellij.compiler.instrumentation.InstrumentationClassFinder;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.asm4.ClassReader;
import org.jetbrains.asm4.ClassVisitor;
import org.jetbrains.asm4.ClassWriter;
import org.jetbrains.asm4.Opcodes;
import org.jetbrains.jps.ModuleChunk;
import org.jetbrains.jps.ProjectPaths;
import org.jetbrains.jps.builders.DirtyFilesHolder;
import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor;
import org.jetbrains.jps.incremental.*;
import org.jetbrains.jps.incremental.messages.ProgressMessage;
import org.jetbrains.jps.javac.BinaryContent;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
/**
* @author Eugene Zhuravlev
* Date: 11/30/12
*/
public abstract class ClassProcessingBuilder extends ModuleLevelBuilder {
private static final Key<InstrumentationClassFinder> CLASS_FINDER = Key.create("_cached_instrumentation_class_finder_");
public ClassProcessingBuilder(BuilderCategory category) {
super(category);
}
protected abstract boolean isEnabled(CompileContext context, ModuleChunk chunk);
protected abstract String getProgressMessage();
@Override
public void chunkBuildStarted(CompileContext context, ModuleChunk chunk) {
}
@Override
public void chunkBuildFinished(CompileContext context, ModuleChunk chunk) {
final InstrumentationClassFinder finder = CLASS_FINDER.get(context);
CLASS_FINDER.set(context, null);
if (finder != null) {
finder.releaseResources();
}
}
@Override
public final ExitCode build(CompileContext context, ModuleChunk chunk, DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder, OutputConsumer outputConsumer) throws ProjectBuildException, IOException {
if (outputConsumer.getCompiledClasses().isEmpty() || !isEnabled(context, chunk)) {
return ExitCode.NOTHING_DONE;
}
final String progress = getProgressMessage();
final boolean shouldShowProgress = !StringUtil.isEmptyOrSpaces(progress);
if (shouldShowProgress) {
context.processMessage(new ProgressMessage(progress + " [" + chunk.getName() + "]"));
}
ExitCode exitCode = ExitCode.NOTHING_DONE;
try {
InstrumentationClassFinder finder = CLASS_FINDER.get(context); // try using shared finder
if (finder == null) {
final ProjectPaths paths = context.getProjectPaths();
final Collection<File> platformCp = paths.getPlatformCompilationClasspath(chunk, false);
final Collection<File> classpath = new ArrayList<File>();
classpath.addAll(paths.getCompilationClasspath(chunk, false));
classpath.addAll(ProjectPaths.getSourceRootsWithDependents(chunk).keySet());
finder = createInstrumentationClassFinder(platformCp, classpath, outputConsumer);
CLASS_FINDER.set(context, finder);
}
exitCode = performBuild(context, chunk, finder, outputConsumer);
}
finally {
if (shouldShowProgress) {
context.processMessage(new ProgressMessage("")); // cleanup progress
}
}
return exitCode;
}
protected abstract ExitCode performBuild(CompileContext context, ModuleChunk chunk, InstrumentationClassFinder finder, OutputConsumer outputConsumer);
// utility methods
public static InstrumentationClassFinder createInstrumentationClassFinder(Collection<File> platformCp, Collection<File> cp, final OutputConsumer outputConsumer) throws
MalformedURLException {
final URL[] platformUrls = new URL[platformCp.size()];
int index = 0;
for (File file : platformCp) {
platformUrls[index++] = file.toURI().toURL();
}
final URL[] urls = new URL[cp.size()];
index = 0;
for (File file : cp) {
urls[index++] = file.toURI().toURL();
}
return new InstrumentationClassFinder(platformUrls, urls) {
protected InputStream lookupClassBeforeClasspath(String internalClassName) {
final BinaryContent content = outputConsumer.lookupClassBytes(internalClassName.replace("/", "."));
if (content != null) {
return new ByteArrayInputStream(content.getBuffer(), content.getOffset(), content.getLength());
}
return null;
}
};
}
public static int getAsmClassWriterFlags(int version) {
return version >= Opcodes.V1_6 && version != Opcodes.V1_1 ? ClassWriter.COMPUTE_FRAMES : ClassWriter.COMPUTE_MAXS;
}
public static int getClassFileVersion(ClassReader reader) {
final Ref<Integer> result = new Ref<Integer>(0);
reader.accept(new ClassVisitor(Opcodes.ASM4) {
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
result.set(version);
}
}, 0);
return result.get();
}
}