Skip to content

Commit

Permalink
Merge pull request #860 from hcoles/java_11_try_with_resources
Browse files Browse the repository at this point in the history
Java 11 try with resources
  • Loading branch information
hcoles authored Feb 16, 2021
2 parents 6410c68 + 6abe32b commit 6a4f021
Show file tree
Hide file tree
Showing 16 changed files with 349 additions and 251 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ public Collection<MutationDetails> intercept(
private Predicate<MutationDetails> mutatesIteratorLoopPlumbing() {
return a -> {
final int instruction = a.getInstructionIndex();
final MethodTree method = ForEachLoopFilter.this.currentClass.methods().stream()
final MethodTree method = currentClass.methods().stream()
.filter(MethodMatchers.forLocation(a.getId().getLocation()))
.findFirst()
.get();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package org.pitest.mutationtest.build.intercept.javafeatures;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Predicate;

import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.pitest.bytecode.analysis.ClassTree;
import org.pitest.bytecode.analysis.MethodTree;
import org.pitest.functional.FCollection;
Expand All @@ -13,10 +10,129 @@
import org.pitest.mutationtest.build.MutationInterceptor;
import org.pitest.mutationtest.engine.Mutater;
import org.pitest.mutationtest.engine.MutationDetails;
import org.pitest.sequence.Context;
import org.pitest.sequence.Match;
import org.pitest.sequence.QueryParams;
import org.pitest.sequence.SequenceMatcher;
import org.pitest.sequence.SequenceQuery;
import org.pitest.sequence.Slot;

import java.util.Collection;
import java.util.function.Predicate;

import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ASTORE;
import static org.objectweb.asm.Opcodes.ATHROW;
import static org.objectweb.asm.Opcodes.GOTO;
import static org.objectweb.asm.Opcodes.IFNONNULL;
import static org.objectweb.asm.Opcodes.IFNULL;
import static org.objectweb.asm.Opcodes.IF_ACMPEQ;
import static org.pitest.bytecode.analysis.InstructionMatchers.anyInstruction;
import static org.pitest.bytecode.analysis.InstructionMatchers.isA;
import static org.pitest.bytecode.analysis.InstructionMatchers.methodCallNamed;
import static org.pitest.bytecode.analysis.InstructionMatchers.notAnInstruction;
import static org.pitest.bytecode.analysis.InstructionMatchers.opCode;
import static org.pitest.bytecode.analysis.InstructionMatchers.recordTarget;
import static org.pitest.sequence.QueryStart.any;
import static org.pitest.sequence.QueryStart.match;

public class TryWithResourcesFilter implements MutationInterceptor {

private Set<Integer> lines;
private static final boolean DEBUG = false;

private static final Slot<AbstractInsnNode> MUTATED_INSTRUCTION = Slot.create(AbstractInsnNode.class);
private static final Slot<Boolean> FOUND = Slot.create(Boolean.class);

private static SequenceQuery<AbstractInsnNode> javac11() {
return any(AbstractInsnNode.class)
.zeroOrMore(match(anyInstruction()))
.then(aLabel())
.then(anALoad())
.then(closeCall())
.then(aLabel())
.then(aGoto())
.then(aLabel())
.then(anAStore())
.then(anALoad())
.then(anALoad())
.then(addSuppressedCall())
.zeroOrMore(match(anyInstruction()));
}

private static SequenceQuery<AbstractInsnNode> javac8() {
return any(AbstractInsnNode.class)
.zeroOrMore(match(anyInstruction()))
.then(ifNull())
.then(anALoad())
.then(ifNull())
.then(aLabel())
.then(anALoad())
.then(closeCall())
.then(aLabel())
.then(aGoto())
.then(aLabel())
.then(anAStore())
.then(aLabel())
.then(anALoad())
.then(anALoad())
.then(addSuppressedCall())
.then(aLabel())
.then(aGoto())
.then(aLabel())
.then(anALoad())
.then(closeCall())
.zeroOrMore(match(anyInstruction()));
}

private static SequenceQuery<AbstractInsnNode> ecj() {
return any(AbstractInsnNode.class)
.zeroOrMore(match(anyInstruction()))
.then(closeCall())
.then(aLabel())
.then(anALoad())
.then(opCode(ATHROW).and(mutationPoint()))
.then(aLabel())
.then(anAStore())
.then(anALoad())
.then(ifNonNull())
.then(anALoad())
.then(anAStore())
.then(aGoto())
.then(aLabel())
.zeroOrMore(match(anyInstruction()));
}

private static SequenceQuery<AbstractInsnNode> ecjAddSuppressedCheck() {
return any(AbstractInsnNode.class)
.zeroOrMore(match(anyInstruction()))
.then(ifNonNull())
.then(anALoad())
.then(anAStore())
.then(aGoto())
.then(aLabel())
.then(anALoad())
.then(anALoad())
.then(opCode(IF_ACMPEQ).and(mutationPoint()))
.then(anALoad())
.then(anALoad())
.then(addSuppressedCall())
.then(aLabel())
.zeroOrMore(match(anyInstruction()));
}


private static final SequenceMatcher<AbstractInsnNode> TRY_WITH_RESOURCES = match(Match.<AbstractInsnNode>never())
.or(javac11())
.or(javac8())
.or(ecj())
.or(ecjAddSuppressedCheck())
.then(containMutation(FOUND))
.compile(QueryParams.params(AbstractInsnNode.class)
.withIgnores(notAnInstruction())
.withDebug(DEBUG)
);

private ClassTree currentClass;

@Override
public InterceptorType type() {
Expand All @@ -25,29 +141,76 @@ public InterceptorType type() {

@Override
public void begin(ClassTree clazz) {
this.lines = new HashSet<>();
for (final MethodTree each : clazz.methods()) {
checkMehod(each,this.lines);
}
}

private void checkMehod(MethodTree each, Set<Integer> lines) {
each.rawNode().accept(new TryWithResourcesMethodVisitor(lines));
this.currentClass = clazz;
}

@Override
public Collection<MutationDetails> intercept(
Collection<MutationDetails> mutations, Mutater m) {
return FCollection.filter(mutations, Prelude.not(isOnMarkedLine()));
Collection<MutationDetails> mutations, Mutater m) {
return FCollection.filter(mutations, Prelude.not(mutatesTryWithResourcesScaffolding()));
}

private Predicate<MutationDetails> isOnMarkedLine() {
return a -> TryWithResourcesFilter.this.lines.contains(a.getClassLine().getLineNumber());
private Predicate<MutationDetails> mutatesTryWithResourcesScaffolding() {
return a -> {
int instruction = a.getInstructionIndex();
MethodTree method = currentClass.method(a.getId().getLocation())
.orElseThrow(() -> new IllegalStateException("Could not find method for mutant " + a));

// performance hack
if (method.rawNode().tryCatchBlocks.size() <= 1) {
return false;
}

AbstractInsnNode mutatedInstruction = method.instruction(instruction);

Context<AbstractInsnNode> context = Context.start(method.instructions(), DEBUG);
context.store(MUTATED_INSTRUCTION.write(), mutatedInstruction);
return TRY_WITH_RESOURCES.matches(method.instructions(), context);
};
}

@Override
public void end() {
this.currentClass = null;
}

private static Match<AbstractInsnNode> aLabel() {
return isA(LabelNode.class);
}
private static Match<AbstractInsnNode> anALoad() {
return opCode(ALOAD).and(mutationPoint());
}

private static Match<AbstractInsnNode> aGoto() {
return opCode(GOTO).and(mutationPoint());
}

private static Match<AbstractInsnNode> addSuppressedCall() {
return methodCallNamed("addSuppressed").and(mutationPoint());
}

private static Match<AbstractInsnNode> anAStore() {
return opCode(ASTORE).and(mutationPoint());
}

private static Match<AbstractInsnNode> closeCall() {
return methodCallNamed("close").and(mutationPoint());
}

private static Match<AbstractInsnNode> ifNonNull() {
return opCode(IFNONNULL).and(mutationPoint());
}

private static Match<AbstractInsnNode> ifNull() {
return opCode(IFNULL).and(mutationPoint());
}

private static Match<AbstractInsnNode> mutationPoint() {
return recordTarget(MUTATED_INSTRUCTION.read(), FOUND.write());
}

private static Match<AbstractInsnNode> containMutation(final Slot<Boolean> found) {
return (c, t) -> c.retrieve(found.read()).isPresent();
}

}
Loading

0 comments on commit 6a4f021

Please sign in to comment.