Skip to content

Commit

Permalink
enable annotation mutations
Browse files Browse the repository at this point in the history
  • Loading branch information
hcoles committed Apr 21, 2023
1 parent adc90d4 commit 812670a
Show file tree
Hide file tree
Showing 13 changed files with 138 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -44,9 +45,13 @@ public Collection<MutationDetails> intercept(
private Predicate<MutationDetails> buildPredicate() {
return a -> {
final int instruction = a.getInstructionIndex();
final MethodTree method = this.currentClass.method(a.getId().getLocation()).get();
final Optional<MethodTree> method = this.currentClass.method(a.getId().getLocation());

List<RegionIndex> regions = cache.computeIfAbsent(method, this::computeRegionIndex);
if (!method.isPresent()) {
return false;
}

List<RegionIndex> regions = cache.computeIfAbsent(method.get(), this::computeRegionIndex);
return regions.stream()
.anyMatch(r -> r.start() <= instruction && r.end() >= instruction);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -189,7 +190,11 @@ public Collection<MutationDetails> intercept(
private Predicate<MutationDetails> mutatesIteratorLoopPlumbing() {
return a -> {
final int instruction = a.getInstructionIndex();
final MethodTree method = currentClass.method(a.getId().getLocation()).get();
final Optional<MethodTree> maybeMethod = currentClass.method(a.getId().getLocation());
if (!maybeMethod.isPresent()) {
return false;
}
MethodTree method = maybeMethod.get();

final AbstractInsnNode mutatedInstruction = method.instruction(instruction);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.logging.Logger;
Expand Down Expand Up @@ -170,7 +171,11 @@ private void checkForInlinedCode(Collection<MutationDetails> mutantsToReturn,


private boolean isInFinallyBlock(MutationDetails m) {
MethodTree method = currentClass.method(m.getId().getLocation()).get();
Optional<MethodTree> maybeMethod = currentClass.method(m.getId().getLocation());
if (!maybeMethod.isPresent()) {
return false;
}
MethodTree method = maybeMethod.get();
List<LabelNode> handlers = method.rawNode().tryCatchBlocks.stream()
.filter(t -> t.type == null)
.map(t -> t.handler)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,12 @@ public Collection<MutationDetails> intercept(
private Predicate<MutationDetails> mutatesAForLoopCounter() {
return a -> {
final int instruction = a.getInstructionIndex();
final MethodTree method = AvoidForLoopCounterFilter.this.currentClass.method(a.getId().getLocation()).get();
Optional<MethodTree> maybeMethod = AvoidForLoopCounterFilter.this.currentClass.method(a.getId().getLocation());
if (!maybeMethod.isPresent()) {
return false;
}
MethodTree method = maybeMethod.get();

final AbstractInsnNode mutatedInstruction = method.instruction(instruction);

// performance hack
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,19 @@ public Collection<MutationDetails> intercept(
private Collection<MutationDetails> findTimeoutMutants(Location location,
Collection<MutationDetails> mutations, Mutater m) {

final MethodTree method = this.currentClass.method(location)
.get();
final Optional<MethodTree> maybeMethod = this.currentClass.method(location);
if (!maybeMethod.isPresent()) {
return Collections.emptyList();
}
MethodTree method = maybeMethod.get();

// give up if our matcher thinks loop is already infinite
if (infiniteLoopMatcher().matches(method.instructions())) {
return Collections.emptyList();
}

final List<MutationDetails> timeouts = new ArrayList<>();
for ( final MutationDetails each : mutations ) {
for (final MutationDetails each : mutations) {
// avoid cost of static analysis by first checking mutant is on
// on instruction that could affect looping
if (couldCauseInfiniteLoop(method, each) && isInfiniteLoop(each,m) ) {
Expand Down
9 changes: 9 additions & 0 deletions pitest/src/main/java/org/pitest/bytecode/ASMVersion.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,13 @@

public class ASMVersion {
public static final int ASM_VERSION = Opcodes.ASM9;

/**
* Provide the asm version via a method call so third party plugins built against pitest
* will receive the current asm version instead of one inlined at build time
* @return asm version
*/
public static int asmVersion() {
return ASM_VERSION;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.pitest.mutationtest.engine.gregor;

public class AnnotationInfo {
private final String descriptor;
private final boolean visible;

public AnnotationInfo(String descriptor, boolean visible) {
this.descriptor = descriptor;
this.visible = visible;
}

public String descriptor() {
return descriptor;
}

public boolean isVisible() {
return visible;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.pitest.mutationtest.engine.gregor;

import org.pitest.mutationtest.engine.MutationIdentifier;

public interface BasicContext {

ClassInfo getClassInfo();

void registerMutation(MutationIdentifier id, String description);

boolean shouldMutate(MutationIdentifier newId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ public String getMethodDescriptor() {
return this.methodDescriptor;
}

public ClassInfo getOwningClass() {
return this.owningClass;
}

public int getAccess() {
return this.access;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
*/
package org.pitest.mutationtest.engine.gregor;

import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.pitest.mutationtest.engine.MutationIdentifier;
Expand All @@ -25,7 +26,7 @@
* mutation points (locations in byte code where mutations can be applied) and
* applying those mutations to the byte code.
*
* Name of this class is misleading as it is capable of mutating both methods
* Name of this class is misleading as it is capable of mutating methods, annotations
* and field, but it is retained for historic reasons.
*
* <p>
Expand All @@ -44,7 +45,12 @@ default MethodVisitor create(MutationContext context,
return null;
}

default FieldVisitor createForField(ClassContext context, FieldInfo fieldInfo, FieldVisitor fieldVisitor) {

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

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import java.util.List;
import java.util.function.Predicate;

import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
Expand All @@ -29,15 +30,18 @@
class MutatingClassVisitor extends ClassVisitor {
private final Predicate<MethodInfo> filter;
private final ClassContext context;
private final List<MethodMutatorFactory> methodMutators;
private final List<MethodMutatorFactory> mutators;

private final NoMethodContext nonMethodContext;

MutatingClassVisitor(final ClassVisitor delegateClassVisitor,
final ClassContext context, final Predicate<MethodInfo> filter,
final List<MethodMutatorFactory> mutators) {
super(ASMVersion.ASM_VERSION, delegateClassVisitor);
this.context = context;
this.filter = filter;
this.methodMutators = mutators;
this.mutators = mutators;
this.nonMethodContext = new NoMethodContext(context);
}

@Override
Expand All @@ -54,12 +58,25 @@ public void visitSource(final String source, final String debug) {
this.context.registerSourceFile(source);
}

@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
AnnotationVisitor next = super.visitAnnotation(descriptor, visible);
AnnotationInfo annotationInfo = new AnnotationInfo(descriptor, visible);
for (final MethodMutatorFactory each : this.mutators) {
AnnotationVisitor fv = each.createForAnnotation(this.nonMethodContext, annotationInfo, next);
if (fv != null) {
next = fv;
}
}
return next;
}

@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
FieldVisitor next = super.visitField(access, name, descriptor, signature, value);
FieldInfo fieldInfo = new FieldInfo(access, name, descriptor, signature, value);
for (final MethodMutatorFactory each : this.methodMutators) {
FieldVisitor fv = each.createForField(this.context, fieldInfo, next);
for (final MethodMutatorFactory each : this.mutators) {
FieldVisitor fv = each.createForField(this.nonMethodContext, fieldInfo, next);
if (fv != null) {
next = fv;
}
Expand Down Expand Up @@ -114,7 +131,7 @@ private MethodVisitor visitMethodForMutation(
final MethodVisitor methodVisitor) {

MethodVisitor next = methodVisitor;
for (final MethodMutatorFactory each : this.methodMutators) {
for (final MethodMutatorFactory each : this.mutators) {
MethodVisitor mv = each.create(methodContext, methodInfo, next);
if (mv != null) {
next = mv;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,10 @@
import org.pitest.mutationtest.engine.MutationIdentifier;
import org.pitest.mutationtest.engine.gregor.blocks.BlockCounter;

public interface MutationContext extends BlockCounter {
public interface MutationContext extends BasicContext, BlockCounter {

void registerCurrentLine(int line);

ClassInfo getClassInfo();

MutationIdentifier registerMutation(MethodMutatorFactory factory,
String description);

void registerMutation(MutationIdentifier id, String description);

boolean shouldMutate(MutationIdentifier newId);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.pitest.mutationtest.engine.gregor;

import org.pitest.mutationtest.engine.MutationDetails;
import org.pitest.mutationtest.engine.MutationIdentifier;

public class NoMethodContext implements BasicContext {

private final ClassContext context;

public NoMethodContext(ClassContext context) {
this.context = context;
}

@Override
public ClassInfo getClassInfo() {
return context.getClassInfo();
}

@Override
public void registerMutation(MutationIdentifier id, String description) {
registerMutation(new MutationDetails(id, context.getFileName(), description,0, 1));
}

@Override
public boolean shouldMutate(MutationIdentifier id) {
return this.context.shouldMutate(id);
}

private void registerMutation(MutationDetails details) {
this.context.addMutation(details);
}
}

0 comments on commit 812670a

Please sign in to comment.