package org.pitest.mutationtest.engine.gregor.mutators; import java.util.ArrayList; import java.util.List; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.pitest.mutationtest.engine.MutationIdentifier; import org.pitest.mutationtest.engine.gregor.MethodInfo; import org.pitest.mutationtest.engine.gregor.MethodMutatorFactory; import org.pitest.mutationtest.engine.gregor.MutationContext; public class RemoveConditionalMutator implements MethodMutatorFactory { // REMOVE_CONDITIONALS_MUTATOR; // EQUAL : Mutate only equality operators // ORDER : Mutate only Ordering operators public enum Choice { EQUAL("equality"), ORDER("comparison"); private String desc; Choice(String desc) { this.desc = desc; } String description() { return this.desc; } } private final Choice kind; private final boolean replaceWith; // Default is true public RemoveConditionalMutator(final Choice c, final boolean rc) { this.kind = c; this.replaceWith = rc; } public static Iterable<MethodMutatorFactory> makeMutators() { final List<MethodMutatorFactory> variations = new ArrayList<MethodMutatorFactory>(); final Choice[] allChoices = { Choice.EQUAL, Choice.ORDER }; final boolean[] arrWith = { true, false }; for (final Choice c : allChoices) { for (final boolean b : arrWith) { variations.add(new RemoveConditionalMutator(c, b)); } } return variations; } @Override public MethodVisitor create(final MutationContext context, final MethodInfo methodInfo, final MethodVisitor methodVisitor) { return new RemoveConditionalMethodVisitor(this, context, methodVisitor, "removed conditional - replaced " + this.kind.description() + " check with " + this.replaceWith); } @Override public String getGloballyUniqueId() { return this.getClass().getName() + "_" + this.kind + "_" + (this.replaceWith ? "IF" : "ELSE"); } @Override public String getName() { return "REMOVE_CONDITIONALS_" + this.kind + "_" + (this.replaceWith ? "IF" : "ELSE") + "_MUTATOR"; } private final class RemoveConditionalMethodVisitor extends MethodVisitor { private final String description; private final MutationContext context; private final MethodMutatorFactory factory; RemoveConditionalMethodVisitor(final MethodMutatorFactory factory, final MutationContext context, final MethodVisitor delegateMethodVisitor, String description) { super(Opcodes.ASM5, delegateMethodVisitor); this.context = context; this.factory = factory; this.description = description; } @Override public void visitJumpInsn(final int opcode, final Label label) { if (canMutate(opcode)) { final MutationIdentifier newId = this.context.registerMutation( this.factory, this.description); if (this.context.shouldMutate(newId)) { emptyStack(opcode); if (!RemoveConditionalMutator.this.replaceWith) { super.visitJumpInsn(Opcodes.GOTO, label); } } else { this.mv.visitJumpInsn(opcode, label); } } else { this.mv.visitJumpInsn(opcode, label); } } private void emptyStack(final int opcode) { switch (opcode) { // EQUAL case Opcodes.IF_ICMPNE: case Opcodes.IF_ICMPEQ: case Opcodes.IF_ACMPEQ: case Opcodes.IF_ACMPNE: // ORDER case Opcodes.IF_ICMPGE: case Opcodes.IF_ICMPGT: case Opcodes.IF_ICMPLE: case Opcodes.IF_ICMPLT: super.visitInsn(Opcodes.POP2); break; default: super.visitInsn(Opcodes.POP); } } private boolean canMutate(final int opcode) { switch (opcode) { case Opcodes.IFLE: case Opcodes.IFGE: case Opcodes.IFGT: case Opcodes.IFLT: case Opcodes.IF_ICMPGE: case Opcodes.IF_ICMPGT: case Opcodes.IF_ICMPLE: case Opcodes.IF_ICMPLT: return (RemoveConditionalMutator.this.kind == Choice.ORDER); case Opcodes.IFEQ: case Opcodes.IFNE: case Opcodes.IFNONNULL: case Opcodes.IFNULL: case Opcodes.IF_ICMPNE: case Opcodes.IF_ICMPEQ: case Opcodes.IF_ACMPEQ: case Opcodes.IF_ACMPNE: return (RemoveConditionalMutator.this.kind == Choice.EQUAL); default: return false; } } } }