/*
* Copyright 2011 Henry Coles and Stefan Penndorf
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pitest.mutationtest.engine.gregor.mutators;
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;
import org.pitest.util.PitError;
/**
* The <code>InlineConstantMutator</code> is a mutator that mutates integer
* inline constants (including short, byte, long) by adding 1 and that mutates
* float inline constants (including double) by replacing them with 1.
*
*
* @author Stefan Penndorf <stefan.penndorf@gmail.com>
*/
public class InlineConstantMutator implements MethodMutatorFactory {
private final class InlineConstantVisitor extends MethodVisitor {
private final MutationContext context;
InlineConstantVisitor(final MutationContext context,
final MethodVisitor delegateVisitor) {
super(Opcodes.ASM5, delegateVisitor);
this.context = context;
}
private void mutate(final Double constant) {
// avoid addition to floating points as may yield same value
final Double replacement = (constant == 1D) ? 2D : 1D;
if (shouldMutate(constant, replacement)) {
translateToByteCode(replacement);
} else {
translateToByteCode(constant);
}
}
private void mutate(final Float constant) {
// avoid addition to floating points as may yield same value
final Float replacement = (constant == 1F) ? 2F : 1F;
if (shouldMutate(constant, replacement)) {
translateToByteCode(replacement);
} else {
translateToByteCode(constant);
}
}
private void mutate(final Integer constant) {
final Integer replacement;
switch (constant.intValue()) {
case 1:
replacement = Integer.valueOf(0);
break;
case Byte.MAX_VALUE:
replacement = Integer.valueOf(Byte.MIN_VALUE);
break;
case Short.MAX_VALUE:
replacement = Integer.valueOf(Short.MIN_VALUE);
break;
default:
replacement = Integer.valueOf(constant + 1);
break;
}
if (shouldMutate(constant, replacement)) {
translateToByteCode(replacement);
} else {
translateToByteCode(constant);
}
}
private void mutate(final Long constant) {
final Long replacement = constant + 1L;
if (shouldMutate(constant, replacement)) {
translateToByteCode(replacement);
} else {
translateToByteCode(constant);
}
}
private void mutate(final Number constant) {
if (constant instanceof Integer) {
mutate((Integer) constant);
} else if (constant instanceof Long) {
mutate((Long) constant);
} else if (constant instanceof Float) {
mutate((Float) constant);
} else if (constant instanceof Double) {
mutate((Double) constant);
} else {
throw new PitError("Unsupported subtype of Number found:"
+ constant.getClass());
}
}
private <T extends Number> boolean shouldMutate(final T constant,
final T replacement) {
final MutationIdentifier mutationId = this.context.registerMutation(
InlineConstantMutator.this, "Substituted " + constant + " with "
+ replacement);
return this.context.shouldMutate(mutationId);
}
private void translateToByteCode(final Double constant) {
if (constant == 0D) {
super.visitInsn(Opcodes.DCONST_0);
} else if (constant == 1D) {
super.visitInsn(Opcodes.DCONST_1);
} else {
super.visitLdcInsn(constant);
}
}
private void translateToByteCode(final Float constant) {
if (constant == 0.0F) {
super.visitInsn(Opcodes.FCONST_0);
} else if (constant == 1.0F) {
super.visitInsn(Opcodes.FCONST_1);
} else if (constant == 2.0F) {
super.visitInsn(Opcodes.FCONST_2);
} else {
super.visitLdcInsn(constant);
}
}
private void translateToByteCode(final Integer constant) {
switch (constant) {
case -1:
super.visitInsn(Opcodes.ICONST_M1);
break;
case 0:
super.visitInsn(Opcodes.ICONST_0);
break;
case 1:
super.visitInsn(Opcodes.ICONST_1);
break;
case 2:
super.visitInsn(Opcodes.ICONST_2);
break;
case 3:
super.visitInsn(Opcodes.ICONST_3);
break;
case 4:
super.visitInsn(Opcodes.ICONST_4);
break;
case 5:
super.visitInsn(Opcodes.ICONST_5);
break;
default:
super.visitLdcInsn(constant);
break;
}
}
private void translateToByteCode(final Long constant) {
if (constant == 0L) {
super.visitInsn(Opcodes.LCONST_0);
} else if (constant == 1L) {
super.visitInsn(Opcodes.LCONST_1);
} else {
super.visitLdcInsn(constant);
}
}
/**
* Translates the opcode to a number (inline constant) if possible or
* returns <code>null</code> if the opcode cannot be translated.
*
* @param opcode
* that might represent an inline constant.
* @return the value of the inline constant represented by opcode or
* <code>null</code> if the opcode does not represent a
* number/constant.
*/
private Number translateToNumber(final int opcode) {
switch (opcode) {
case Opcodes.ICONST_M1:
return Integer.valueOf(-1);
case Opcodes.ICONST_0:
return Integer.valueOf(0);
case Opcodes.ICONST_1:
return Integer.valueOf(1);
case Opcodes.ICONST_2:
return Integer.valueOf(2);
case Opcodes.ICONST_3:
return Integer.valueOf(3);
case Opcodes.ICONST_4:
return Integer.valueOf(4);
case Opcodes.ICONST_5:
return Integer.valueOf(5);
case Opcodes.LCONST_0:
return Long.valueOf(0L);
case Opcodes.LCONST_1:
return Long.valueOf(1L);
case Opcodes.FCONST_0:
return Float.valueOf(0F);
case Opcodes.FCONST_1:
return Float.valueOf(1F);
case Opcodes.FCONST_2:
return Float.valueOf(2F);
case Opcodes.DCONST_0:
return Double.valueOf(0D);
case Opcodes.DCONST_1:
return Double.valueOf(1D);
default:
return null;
}
}
/*
* (non-Javadoc)
*
* @see org.objectweb.asm.MethodAdapter#visitInsn(int)
*/
@Override
public void visitInsn(final int opcode) {
final Number inlineConstant = translateToNumber(opcode);
if (inlineConstant == null) {
super.visitInsn(opcode);
return;
}
mutate(inlineConstant);
}
/*
* (non-Javadoc)
*
* @see org.objectweb.asm.MethodAdapter#visitIntInsn(int, int)
*/
@Override
public void visitIntInsn(final int opcode, final int operand) {
if ((opcode == Opcodes.BIPUSH) || (opcode == Opcodes.SIPUSH)) {
mutate(operand);
} else {
super.visitIntInsn(opcode, operand);
}
}
/*
* (non-Javadoc)
*
* @see org.objectweb.asm.MethodAdapter#visitLdcInsn(java.lang.Object)
*/
@Override
public void visitLdcInsn(final Object constant) {
// do not mutate strings or .class here
if (constant instanceof Number) {
mutate((Number) constant);
} else {
super.visitLdcInsn(constant);
}
}
}
@Override
public MethodVisitor create(final MutationContext context,
final MethodInfo methodInfo, final MethodVisitor methodVisitor) {
return new InlineConstantVisitor(context, methodVisitor);
}
@Override
public String getGloballyUniqueId() {
return this.getClass().getName();
}
@Override
public String toString() {
return "INLINE_CONSTANT_MUTATOR";
}
@Override
public String getName() {
return toString();
}
}