-
Notifications
You must be signed in to change notification settings - Fork 354
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Filter junk mutations in compiler generated calls to Objects.requireN…
…onNull for #404
- Loading branch information
Showing
6 changed files
with
179 additions
and
0 deletions.
There are no files selected for viewing
92 changes: 92 additions & 0 deletions
92
.../org/pitest/mutationtest/build/intercept/javafeatures/MethodReferenceNullCheckFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package org.pitest.mutationtest.build.intercept.javafeatures; | ||
|
||
import static org.pitest.bytecode.analysis.InstructionMatchers.anyInstruction; | ||
import static org.pitest.bytecode.analysis.InstructionMatchers.isInstruction; | ||
import static org.pitest.bytecode.analysis.InstructionMatchers.methodCallTo; | ||
import static org.pitest.bytecode.analysis.InstructionMatchers.notAnInstruction; | ||
import static org.pitest.bytecode.analysis.InstructionMatchers.opCode; | ||
|
||
import java.util.Collection; | ||
import java.util.Objects; | ||
import java.util.function.Predicate; | ||
|
||
import org.objectweb.asm.Opcodes; | ||
import org.objectweb.asm.tree.AbstractInsnNode; | ||
import org.pitest.bytecode.analysis.ClassTree; | ||
import org.pitest.bytecode.analysis.MethodMatchers; | ||
import org.pitest.bytecode.analysis.MethodTree; | ||
import org.pitest.classinfo.ClassName; | ||
import org.pitest.functional.FCollection; | ||
import org.pitest.functional.prelude.Prelude; | ||
import org.pitest.mutationtest.build.InterceptorType; | ||
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.QueryParams; | ||
import org.pitest.sequence.QueryStart; | ||
import org.pitest.sequence.SequenceMatcher; | ||
import org.pitest.sequence.Slot; | ||
|
||
/** | ||
* Filters out the calls to Objects.requireNotNull the compiler inserts when using method references. | ||
* | ||
*/ | ||
public class MethodReferenceNullCheckFilter implements MutationInterceptor { | ||
|
||
private static final boolean DEBUG = false; | ||
|
||
private static final Slot<AbstractInsnNode> MUTATED_INSTRUCTION = Slot.create(AbstractInsnNode.class); | ||
|
||
static final SequenceMatcher<AbstractInsnNode> NULL_CHECK = QueryStart | ||
.any(AbstractInsnNode.class) | ||
.then(methodCallTo(ClassName.fromClass(Objects.class), "requireNonNull").and(isInstruction(MUTATED_INSTRUCTION.read()))) | ||
.then(opCode(Opcodes.POP)) | ||
.then(opCode(Opcodes.INVOKEDYNAMIC)) | ||
.zeroOrMore(QueryStart.match(anyInstruction())) | ||
.compile(QueryParams.params(AbstractInsnNode.class) | ||
.withIgnores(notAnInstruction()) | ||
.withDebug(DEBUG) | ||
); | ||
|
||
|
||
private ClassTree currentClass; | ||
|
||
@Override | ||
public InterceptorType type() { | ||
return InterceptorType.FILTER; | ||
} | ||
|
||
@Override | ||
public void begin(ClassTree clazz) { | ||
this.currentClass = clazz; | ||
} | ||
|
||
@Override | ||
public Collection<MutationDetails> intercept( | ||
Collection<MutationDetails> mutations, Mutater m) { | ||
return FCollection.filter(mutations, Prelude.not(isAnImplicitNullCheck())); | ||
} | ||
|
||
private Predicate<MutationDetails> isAnImplicitNullCheck() { | ||
return a -> { | ||
final int instruction = a.getInstructionIndex(); | ||
final MethodTree method = MethodReferenceNullCheckFilter.this.currentClass.methods().stream() | ||
.filter(MethodMatchers.forLocation(a.getId().getLocation())) | ||
.findFirst() | ||
.get(); | ||
|
||
final AbstractInsnNode mutatedInstruction = method.instruction(instruction); | ||
|
||
final Context<AbstractInsnNode> context = Context.start(method.instructions(), DEBUG); | ||
context.store(MUTATED_INSTRUCTION.write(), mutatedInstruction); | ||
return NULL_CHECK.matches(method.instructions(), context); | ||
}; | ||
} | ||
|
||
@Override | ||
public void end() { | ||
this.currentClass = null; | ||
} | ||
|
||
} |
27 changes: 27 additions & 0 deletions
27
...test/mutationtest/build/intercept/javafeatures/MethodReferenceNullCheckFilterFactory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package org.pitest.mutationtest.build.intercept.javafeatures; | ||
|
||
import org.pitest.mutationtest.build.InterceptorParameters; | ||
import org.pitest.mutationtest.build.MutationInterceptor; | ||
import org.pitest.mutationtest.build.MutationInterceptorFactory; | ||
import org.pitest.plugin.Feature; | ||
|
||
public class MethodReferenceNullCheckFilterFactory implements MutationInterceptorFactory { | ||
|
||
@Override | ||
public String description() { | ||
return "Method reference null check filter"; | ||
} | ||
|
||
@Override | ||
public MutationInterceptor createInterceptor(InterceptorParameters params) { | ||
return new MethodReferenceNullCheckFilter(); | ||
} | ||
|
||
@Override | ||
public Feature provides() { | ||
return Feature.named("FMRNULL") | ||
.withOnByDefault(true) | ||
.withDescription("Filters mutations in compiler generated code that inserts Objects.requireNonNull for method references"); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
44 changes: 44 additions & 0 deletions
44
.../pitest/mutationtest/build/intercept/javafeatures/MethodReferenceNullCheckFilterTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package org.pitest.mutationtest.build.intercept.javafeatures; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import java.util.Objects; | ||
|
||
import org.junit.Test; | ||
import org.pitest.mutationtest.build.InterceptorType; | ||
import org.pitest.mutationtest.engine.gregor.config.Mutator; | ||
|
||
public class MethodReferenceNullCheckFilterTest { | ||
|
||
private static final String PATH = "requirenotnull/{0}_{1}"; | ||
|
||
MethodReferenceNullCheckFilter testee = new MethodReferenceNullCheckFilter(); | ||
|
||
FilterTester verifier = new FilterTester(PATH, this.testee, Mutator.all()); | ||
|
||
@Test | ||
public void shouldDeclareTypeAsFilter() { | ||
assertThat(this.testee.type()).isEqualTo(InterceptorType.FILTER); | ||
} | ||
|
||
@Test | ||
public void filtersRequireNotNullChecksForMethodReferences() { | ||
this.verifier.assertFiltersNMutationFromSample(2, "MethodReferenceNullChecks"); | ||
} | ||
|
||
@Test | ||
public void shouldNotFilterDeadCallsToGetClassInNonLambdaMethods() { | ||
this.verifier.assertFiltersNMutationFromClass(0, HasNormalRequireNonNullCheck.class); | ||
} | ||
|
||
} | ||
|
||
|
||
class HasNormalRequireNonNullCheck { | ||
String aField; | ||
public void amethod() { | ||
Objects.requireNonNull(aField); | ||
System.out.println(aField); | ||
} | ||
} | ||
|
Binary file added
BIN
+1.65 KB
...src/test/resources/sampleClasses/requirenotnull/MethodReferenceNullChecks_javac.class.bin
Binary file not shown.