Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure mutators do not modify bytecode during scan stage #1227

Merged
merged 1 commit into from
Jun 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,15 @@ default MethodVisitor create(MutationContext context,
}


default AnnotationVisitor createForAnnotation(NoMethodContext context, AnnotationInfo annotationInfo, AnnotationVisitor next) {
default AnnotationVisitor createForAnnotation(BasicContext context, AnnotationInfo annotationInfo, AnnotationVisitor next) {
return null;
}

default boolean skipAnnotation(NoMethodContext nonMethodContext, AnnotationInfo annotationInfo) {
default boolean skipAnnotation(BasicContext nonMethodContext, AnnotationInfo annotationInfo) {
return false;
}

default FieldVisitor createForField(NoMethodContext context, FieldInfo fieldInfo, FieldVisitor fieldVisitor) {
default FieldVisitor createForField(BasicContext context, FieldInfo fieldInfo, FieldVisitor fieldVisitor) {
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,31 +43,33 @@ private final class InlineConstantVisitor extends MethodVisitor {
this.context = context;
}

private void mutate(final Double constant) {
private boolean 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);
return true;
}

return false;
}

private void mutate(final Float constant) {
private boolean 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);
return true;
}

return false;
}

private void mutate(final Integer constant) {
private boolean mutate(final Integer constant) {
final Integer replacement;

switch (constant.intValue()) {
Expand All @@ -87,33 +89,35 @@ private void mutate(final Integer constant) {

if (shouldMutate(constant, replacement)) {
translateToByteCode(replacement);
} else {
translateToByteCode(constant);
return true;
}

return false;
}

private void mutate(final Long constant) {
private boolean mutate(final Long constant) {

final Long replacement = constant + 1L;

if (shouldMutate(constant, replacement)) {
translateToByteCode(replacement);
} else {
translateToByteCode(constant);
return true;
}

return false;

}

private void mutate(final Number constant) {
private boolean mutate(final Number constant) {

if (constant instanceof Integer) {
mutate((Integer) constant);
return mutate((Integer) constant);
} else if (constant instanceof Long) {
mutate((Long) constant);
return mutate((Long) constant);
} else if (constant instanceof Float) {
mutate((Float) constant);
return mutate((Float) constant);
} else if (constant instanceof Double) {
mutate((Double) constant);
return mutate((Double) constant);
} else {
throw new PitError("Unsupported subtype of Number found:"
+ constant.getClass());
Expand Down Expand Up @@ -251,7 +255,9 @@ public void visitInsn(final int opcode) {
return;
}

mutate(inlineConstant);
if (!mutate(inlineConstant) ) {
super.visitInsn(opcode);
}
}

/*
Expand All @@ -262,10 +268,12 @@ public void visitInsn(final int opcode) {
@Override
public void visitIntInsn(final int opcode, final int operand) {
if ((opcode == Opcodes.BIPUSH) || (opcode == Opcodes.SIPUSH)) {
mutate(operand);
} else {
super.visitIntInsn(opcode, operand);
if (mutate(operand) ) {
return;
}
}

super.visitIntInsn(opcode, operand);
}

/*
Expand All @@ -277,10 +285,12 @@ public void visitIntInsn(final int opcode, final int operand) {
public void visitLdcInsn(final Object constant) {
// do not mutate strings or .class here
if (constant instanceof Number) {
mutate((Number) constant);
} else {
super.visitLdcInsn(constant);
if (mutate((Number) constant)) {
return;
}
}

super.visitLdcInsn(constant);
}

}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package org.pitest.verifier.mutants;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.util.Textifier;
import org.objectweb.asm.util.TraceClassVisitor;
import org.pitest.classinfo.ClassByteArraySource;
import org.pitest.classinfo.ClassName;
import org.pitest.classpath.ClassloaderByteArraySource;
Expand All @@ -8,6 +12,8 @@
import org.pitest.mutationtest.engine.gregor.MethodInfo;
import org.pitest.mutationtest.engine.gregor.MethodMutatorFactory;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
Expand All @@ -21,6 +27,7 @@
import java.util.stream.Collectors;

import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;

public class MutatorVerifierStart {

Expand Down Expand Up @@ -69,6 +76,7 @@ public MutatorVerifierStart notCheckingUnMutatedValues() {
}

public MutatorVerifier forClass(Class<?> clazz) {
assertScanDoesNotAlterClass(clazz);
GregorMutater engine = makeEngine();
return new MutatorVerifier(engine, clazz, mutantFilter, checkUnmutatedValues);
}
Expand All @@ -79,31 +87,37 @@ public MutatorVerifier forClass(String clazz) {
}

public <B> CallableMutantVerifier<B> forCallableClass(Class<? extends Callable<B>> clazz) {
assertScanDoesNotAlterClass(clazz);
GregorMutater engine = makeEngine();
return new CallableMutantVerifier<B>(engine, clazz, mutantFilter, checkUnmutatedValues);
}

public <A,B> MutantVerifier<A,B> forFunctionClass(Class<? extends Function<A,B>> clazz) {
assertScanDoesNotAlterClass(clazz);
GregorMutater engine = makeEngine();
return new MutantVerifier<A,B>(engine, clazz, mutantFilter, checkUnmutatedValues);
}

public <B> IntMutantVerifier<B> forIntFunctionClass(Class<? extends IntFunction<B>> clazz) {
assertScanDoesNotAlterClass(clazz);
GregorMutater engine = makeEngine();
return new IntMutantVerifier<>(engine, clazz, mutantFilter, checkUnmutatedValues);
}

public <B> LongMutantVerifier<B> forLongFunctionClass(Class<? extends LongFunction<B>> clazz) {
assertScanDoesNotAlterClass(clazz);
GregorMutater engine = makeEngine();
return new LongMutantVerifier<>(engine, clazz, mutantFilter, checkUnmutatedValues);
}

public <B> DoubleMutantVerifier<B> forDoubleFunctionClass(Class<? extends DoubleFunction<B>> clazz) {
assertScanDoesNotAlterClass(clazz);
GregorMutater engine = makeEngine();
return new DoubleMutantVerifier<>(engine, clazz, mutantFilter, checkUnmutatedValues);
}

public <A,B,C> BiFunctionMutantVerifier<A,B,C> forBiFunctionClass(Class<? extends BiFunction<A,B,C>> clazz) {
assertScanDoesNotAlterClass(clazz);
GregorMutater engine = makeEngine();
return new BiFunctionMutantVerifier<>(engine, clazz, mutantFilter, checkUnmutatedValues);
}
Expand All @@ -118,4 +132,35 @@ private GregorMutater makeEngine() {
return new GregorMutater(source, filter, mutators);
}

public void assertScanDoesNotAlterClass(Class<?> clazz) {
ClassByteArraySource source = ClassloaderByteArraySource.fromContext();
byte[] bytes = source.getBytes(clazz.getName()).get();

ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
final ClassReader reader = new ClassReader(bytes);
final ScanningClassVisitor nv = new ScanningClassVisitor(writer, mmfs);

reader.accept(nv, ClassReader.EXPAND_FRAMES);

assertThat(asString(writer.toByteArray()))
.isEqualTo(asString(plainTransform(bytes)))
.describedAs("Mutator modified class when mutation was not active");

}

private byte[] plainTransform(byte[] bytes) {
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
final ClassReader reader = new ClassReader(bytes);
reader.accept(writer, ClassReader.EXPAND_FRAMES);
return writer.toByteArray();
}

private String asString(byte[] bytes) {
ClassReader reader = new ClassReader(bytes);
StringWriter writer = new StringWriter();
reader.accept(new TraceClassVisitor(null, new Textifier(), new PrintWriter(
writer)), ClassReader.EXPAND_FRAMES);
return writer.toString();
}

}
Loading