diff --git a/java/compiler/instrumentation-util/src/com/intellij/compiler/instrumentation/InstrumentationClassFinder.java b/java/compiler/instrumentation-util/src/com/intellij/compiler/instrumentation/InstrumentationClassFinder.java index a5d56e4e632a..994036161c0a 100644 --- a/java/compiler/instrumentation-util/src/com/intellij/compiler/instrumentation/InstrumentationClassFinder.java +++ b/java/compiler/instrumentation-util/src/com/intellij/compiler/instrumentation/InstrumentationClassFinder.java @@ -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; } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/ChunkBuildOutputConsumerImpl.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/ChunkBuildOutputConsumerImpl.java index 737ed412ee7c..a171800cf82e 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/ChunkBuildOutputConsumerImpl.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/ChunkBuildOutputConsumerImpl.java @@ -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, BuildOutputConsumerImpl> myTarget2Consumer = new THashMap, BuildOutputConsumerImpl>(); private Map myClasses = new THashMap(); + private Map, Collection> myTargetToClassesMap = new THashMap, Collection>(); public ChunkBuildOutputConsumerImpl(CompileContext context) { myContext = context; } + @Override + public Collection getTargetCompiledClasses(BuildTarget target) { + final Collection classes = myTargetToClassesMap.get(target); + if (classes != null) { + return Collections.unmodifiableCollection(classes); + } + return Collections.emptyList(); + } + @NotNull @Override public Map 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 classes = myTargetToClassesMap.get(target); + if (classes == null) { + classes = new ArrayList(); + myTargetToClassesMap.put(target, classes); + } + classes.add(compiled); } registerOutputFile(target, compiled.getOutputFile(), Collections.singleton(compiled.getSourceFile().getPath())); } @@ -66,5 +83,6 @@ class ChunkBuildOutputConsumerImpl implements ModuleLevelBuilder.OutputConsumer public void clear() { myTarget2Consumer.clear(); myClasses.clear(); + myTargetToClassesMap.clear(); } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleLevelBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleLevelBuilder.java index bbc39426e3d9..33e6c27a84cb 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleLevelBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleLevelBuilder.java @@ -36,6 +36,7 @@ public abstract class ModuleLevelBuilder extends Builder { void registerCompiledClass(BuildTarget target, CompiledClass compiled) throws IOException; + Collection getTargetCompiledClasses(BuildTarget target); @NotNull Map getCompiledClasses(); diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/instrumentation/BaseInstrumentingBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/instrumentation/BaseInstrumentingBuilder.java index a41bba5ecc0e..7f518a5d9c4b 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/instrumentation/BaseInstrumentingBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/instrumentation/BaseInstrumentingBuilder.java @@ -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 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 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 platformCp = paths.getPlatformCompilationClasspath(chunk, false); - - final Collection classpath = new ArrayList(); - 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 platformCp, Collection 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 result = new Ref(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(); - } - } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/instrumentation/ClassProcessingBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/instrumentation/ClassProcessingBuilder.java new file mode 100644 index 000000000000..a25f257acc52 --- /dev/null +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/instrumentation/ClassProcessingBuilder.java @@ -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 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 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 platformCp = paths.getPlatformCompilationClasspath(chunk, false); + + final Collection classpath = new ArrayList(); + 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 platformCp, Collection 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 result = new Ref(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(); + } + +}