package org.pitest.coverage.analysis;
import java.util.List;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.AdviceAdapter;
import org.pitest.mutationtest.engine.gregor.analysis.InstructionCounter;
abstract class AbstractCoverageStrategy extends AdviceAdapter {
protected final MethodVisitor methodVisitor;
protected final int classId;
protected final int probeOffset;
protected final List<Block> blocks;
private final InstructionCounter counter;
/**
* label to mark start of try finally block that is added to each method
*/
private final Label before = new Label();
/**
* label to mark handler block of try finally
*/
private final Label handler = new Label();
protected int probeCount = 0;
AbstractCoverageStrategy(List<Block> blocks, InstructionCounter counter,
final int classId, final MethodVisitor writer, final int access,
final String name, final String desc, final int probeOffset) {
super(Opcodes.ASM5, writer, access, name, desc);
this.methodVisitor = writer;
this.classId = classId;
this.counter = counter;
this.blocks = blocks;
this.probeOffset = probeOffset;
}
abstract void prepare();
abstract void generateProbeReportCode();
abstract void insertProbe();
@Override
public void visitCode() {
super.visitCode();
prepare();
this.mv.visitLabel(this.before);
}
@Override
public void visitMaxs(final int maxStack, final int maxLocals) {
this.mv.visitTryCatchBlock(this.before, this.handler, this.handler, null);
this.mv.visitLabel(this.handler);
generateProbeReportCode();
this.mv.visitInsn(ATHROW);
// values actually unimportant as we're using compute max
this.mv.visitMaxs(maxStack, this.nextLocal);
}
@Override
protected void onMethodExit(final int opcode) {
// generated catch block will handle any throws ending block
if (opcode != ATHROW) {
generateProbeReportCode();
}
}
protected void pushConstant(final int value) {
switch (value) {
case 0:
this.mv.visitInsn(ICONST_0);
break;
case 1:
this.mv.visitInsn(ICONST_1);
break;
case 2:
this.mv.visitInsn(ICONST_2);
break;
case 3:
this.mv.visitInsn(ICONST_3);
break;
case 4:
this.mv.visitInsn(ICONST_4);
break;
case 5:
this.mv.visitInsn(ICONST_5);
break;
default:
if (value <= Byte.MAX_VALUE) {
this.mv.visitIntInsn(Opcodes.BIPUSH, value);
} else if (value <= Short.MAX_VALUE) {
this.mv.visitIntInsn(Opcodes.SIPUSH, value);
} else {
this.mv.visitLdcInsn(value);
}
}
}
@Override
public void visitFrame(final int type, final int nLocal,
final Object[] local, final int nStack, final Object[] stack) {
insertProbeIfAppropriate();
super.visitFrame(type, nLocal, local, nStack, stack);
}
@Override
public void visitInsn(final int opcode) {
insertProbeIfAppropriate();
super.visitInsn(opcode);
}
@Override
public void visitIntInsn(final int opcode, final int operand) {
insertProbeIfAppropriate();
super.visitIntInsn(opcode, operand);
}
@Override
public void visitVarInsn(final int opcode, final int var) {
insertProbeIfAppropriate();
super.visitVarInsn(opcode, var);
}
@Override
public void visitTypeInsn(final int opcode, final String type) {
insertProbeIfAppropriate();
super.visitTypeInsn(opcode, type);
}
@Override
public void visitFieldInsn(final int opcode, final String owner,
final String name, final String desc) {
insertProbeIfAppropriate();
super.visitFieldInsn(opcode, owner, name, desc);
}
@Override
public void visitMethodInsn(final int opcode, final String owner,
final String name, final String desc, boolean itf) {
insertProbeIfAppropriate();
super.visitMethodInsn(opcode, owner, name, desc, itf);
}
@Override
public void visitInvokeDynamicInsn(final String name, final String desc,
final Handle bsm, final Object... bsmArgs) {
insertProbeIfAppropriate();
super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
}
@Override
public void visitJumpInsn(final int opcode, final Label label) {
insertProbeIfAppropriate();
super.visitJumpInsn(opcode, label);
}
@Override
public void visitLabel(final Label label) {
super.visitLabel(label);
// note - probe goes after the label
insertProbeIfAppropriate();
}
@Override
public void visitLdcInsn(final Object cst) {
insertProbeIfAppropriate();
super.visitLdcInsn(cst);
}
@Override
public void visitIincInsn(final int var, final int increment) {
insertProbeIfAppropriate();
super.visitIincInsn(var, increment);
}
@Override
public void visitTableSwitchInsn(final int min, final int max,
final Label dflt, final Label... labels) {
insertProbeIfAppropriate();
super.visitTableSwitchInsn(min, max, dflt, labels);
}
@Override
public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
final Label[] labels) {
insertProbeIfAppropriate();
super.visitLookupSwitchInsn(dflt, keys, labels);
}
@Override
public void visitMultiANewArrayInsn(final String desc, final int dims) {
insertProbeIfAppropriate();
super.visitMultiANewArrayInsn(desc, dims);
}
@Override
public void visitLineNumber(final int line, final Label start) {
insertProbeIfAppropriate();
super.visitLineNumber(line, start);
}
private void insertProbeIfAppropriate() {
if (needsProbe(this.counter.currentInstructionCount())) {
insertProbe();
this.probeCount++;
}
}
private boolean needsProbe(int currentInstructionCount) {
for (Block each : this.blocks) {
if (each.firstInstructionIs(currentInstructionCount - 1)) {
return true;
}
}
return false;
}
}