package org.pitest.coverage;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.pitest.bytecode.FrameOptions;
import org.pitest.classinfo.ComputeClassWriter;
import org.pitest.classpath.ClassloaderByteArraySource;
import org.pitest.functional.predicate.Predicate;
import sun.pitest.CodeCoverageStore;
public class CoverageTransformer implements ClassFileTransformer {
private final Predicate<String> filter;
private final Map<String, String> computeCache = new ConcurrentHashMap<String, String>();
public CoverageTransformer(final Predicate<String> filter) {
this.filter = filter;
}
@Override
public byte[] transform(final ClassLoader loader, final String className,
final Class<?> classBeingRedefined,
final ProtectionDomain protectionDomain, final byte[] classfileBuffer)
throws IllegalClassFormatException {
final boolean include = shouldInclude(className);
if (include) {
try {
return transformBytes(loader, className, classfileBuffer);
} catch (final RuntimeException t) {
System.err.println("RuntimeException while transforming " + className);
t.printStackTrace();
throw t;
}
} else {
return null;
}
}
private byte[] transformBytes(final ClassLoader loader,
final String className, final byte[] classfileBuffer) {
final ClassReader reader = new ClassReader(classfileBuffer);
final ClassWriter writer = new ComputeClassWriter(
new ClassloaderByteArraySource(loader), this.computeCache,
FrameOptions.pickFlags(classfileBuffer));
final int id = CodeCoverageStore.registerClass(className);
reader.accept(new CoverageClassVisitor(id, writer),
ClassReader.EXPAND_FRAMES);
return writer.toByteArray();
}
private boolean shouldInclude(final String className) {
return this.filter.apply(className);
}
}