blob: bc353aa4b1447703a6cc103a734dcf24c2393113 [file] [log] [blame]
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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.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.org.objectweb.asm.ClassReader;
import org.jetbrains.org.objectweb.asm.ClassVisitor;
import org.jetbrains.org.objectweb.asm.ClassWriter;
import org.jetbrains.org.objectweb.asm.Opcodes;
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;
import java.util.Collections;
import java.util.List;
/**
* @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 chunkBuildFinished(CompileContext context, ModuleChunk chunk) {
final InstrumentationClassFinder finder = CLASS_FINDER.get(context);
if (finder != null) {
CLASS_FINDER.set(context, 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.getPresentableShortName() + "]"));
}
ExitCode exitCode = ExitCode.NOTHING_DONE;
try {
InstrumentationClassFinder finder = CLASS_FINDER.get(context); // try using shared finder
if (finder == null) {
final Collection<File> platformCp = ProjectPaths.getPlatformCompilationClasspath(chunk, false);
final Collection<File> classpath = new ArrayList<File>();
classpath.addAll(ProjectPaths.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;
}
@Override
public List<String> getCompilableFileExtensions() {
return Collections.emptyList();
}
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.ASM5) {
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
result.set(version);
}
}, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
return result.get();
}
}