package org.pitest.mutationtest.engine.gregor.mutators.experimental; 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; /** * Mutate switch statements. We get an array of labels to jump to, plus a * default label. We find the first label that differs from the default and use * this in place of the default label. All other labels are replaced by the * default. */ public class SwitchMutator implements MethodMutatorFactory { @Override public MethodVisitor create(final MutationContext context, final MethodInfo methodInfo, final MethodVisitor methodVisitor) { return new SwitchMethodVisitor(context, methodVisitor); } @Override public String getGloballyUniqueId() { return this.getClass().getName(); } @Override public String getName() { return "EXPERIMENTAL_SWITCH_MUTATOR"; } private final class SwitchMethodVisitor extends MethodVisitor { private final MutationContext context; SwitchMethodVisitor(final MutationContext context, final MethodVisitor methodVisitor) { super(Opcodes.ASM5, methodVisitor); this.context = context; } @Override public void visitTableSwitchInsn(final int i, final int i1, final Label defaultLabel, final Label... labels) { final Label newDefault = firstDifferentLabel(labels, defaultLabel); if ((newDefault != null) && shouldMutate()) { final Label[] newLabels = swapLabels(labels, defaultLabel, newDefault); super.visitTableSwitchInsn(i, i1, newDefault, newLabels); } else { super.visitTableSwitchInsn(i, i1, defaultLabel, labels); } } @Override public void visitLookupSwitchInsn(final Label defaultLabel, final int[] ints, final Label[] labels) { final Label newDefault = firstDifferentLabel(labels, defaultLabel); if ((newDefault != null) && shouldMutate()) { final Label[] newLabels = swapLabels(labels, defaultLabel, newDefault); super.visitLookupSwitchInsn(newDefault, ints, newLabels); } else { super.visitLookupSwitchInsn(defaultLabel, ints, labels); } } private Label[] swapLabels(final Label[] labels, final Label defaultLabel, final Label newDefault) { final Label[] swapped = new Label[labels.length]; for (int i = 0; i < labels.length; i++) { final Label candidate = labels[i]; if (candidate == defaultLabel) { swapped[i] = newDefault; } else { swapped[i] = defaultLabel; } } return swapped; } private Label firstDifferentLabel(final Label[] labels, final Label label) { for (final Label candidate : labels) { if (candidate != label) { return candidate; } } return null; } private boolean shouldMutate() { final MutationIdentifier mutationId = this.context.registerMutation( SwitchMutator.this, "Switch mutation"); return this.context.shouldMutate(mutationId); } } }