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.pitest.mutationtest.engine.gregor.analysis.InstructionCounter; import sun.pitest.CodeCoverageStore; /** * Instruments via a method call at each line. * * This simplistic approach is generally slow, but does not require finally * blocks that are difficult to generate correctly for constructors. * * This simple approach should however provide better performance for single * line methods. */ public class SimpleBlockCoverageVisitor extends MethodVisitor { private final MethodVisitor methodVisitor; private final int classId; private final int probeOffset; private final InstructionCounter counter; private final List<Block> blocks; private int probeCount = 0; public SimpleBlockCoverageVisitor(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); this.counter = counter; this.methodVisitor = writer; this.classId = classId; this.blocks = blocks; this.probeOffset = probeOffset; } @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())) { this.methodVisitor.visitLdcInsn(this.classId); this.methodVisitor.visitLdcInsn(this.probeCount + this.probeOffset); this.methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, CodeCoverageStore.CLASS_NAME, "visitSingleProbe", "(II)V", false); this.probeCount++; } } private boolean needsProbe(int currentInstructionCount) { for (Block each : this.blocks) { if (each.firstInstructionIs(currentInstructionCount - 1)) { return true; } } return false; } }