Skip to content

Commit

Permalink
check field type when looking for stored delayed execution methods
Browse files Browse the repository at this point in the history
  • Loading branch information
hcoles committed Aug 27, 2024
1 parent 7d8ce22 commit a53e47e
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
import java.util.stream.Stream;

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.OpcodeMatchers.PUTSTATIC;
Expand All @@ -62,13 +61,32 @@ class StaticInitializerInterceptor implements MutationInterceptor {

static final SequenceMatcher<AbstractInsnNode> DELAYED_EXECUTION = QueryStart
.any(AbstractInsnNode.class)
.then(returnsDeferredExecutionCode().or(isA(InvokeDynamicInsnNode.class)).and(store(START.write())))
.then(enumConstructorCallAndStore().or(QueryStart.match(PUTSTATIC)))
// look for calls returning delayed execution types. Unfortunately this is not guarantee that we
// store the result to an appropriate type
.then(returnsDeferredExecutionCode().or(dynamicallyReturnsDeferredExecutionCode()).and(store(START.write())))
// allow for other method calls etc
.zeroOrMore(QueryStart.match(anyInstruction()))
.then(enumConstructorCallAndStore().or(QueryStart.match(delayedExecutionField())))
.zeroOrMore(QueryStart.match(anyInstruction()))
.compile(QueryParams.params(AbstractInsnNode.class)
.withIgnores(notAnInstruction())
);

private static Match<AbstractInsnNode> delayedExecutionField() {
return PUTSTATIC.and(isAUtilFunctionField());
}

private static Match<AbstractInsnNode> isAUtilFunctionField() {
return (c,n) -> {
FieldInsnNode fieldNode = ((FieldInsnNode) n);
return result( fieldNode.desc.startsWith("Ljava/util/function/"), c);
};
}

private static Match<AbstractInsnNode> dynamicallyReturnsDeferredExecutionCode() {
return (c,n) -> result(n.getOpcode() == Opcodes.INVOKEDYNAMIC && returnDelayedExecutionType(((InvokeDynamicInsnNode) n).desc), c);
}

private static Match<AbstractInsnNode> returnsDeferredExecutionCode() {
return (c,n) -> result(n.getOpcode() == Opcodes.INVOKESTATIC && returnDelayedExecutionType(((MethodInsnNode) n).desc), c);
}
Expand Down Expand Up @@ -111,11 +129,11 @@ private void analyseClass(ClassTree tree) {
// We can't see if a method *call* is private from the call site
// so collect a set of private methods within the class first
Set<Location> privateMethods = tree.methods().stream()
.filter(m -> m.isPrivate())
.filter(MethodTree::isPrivate)
.map(MethodTree::asLocation)
.collect(Collectors.toSet());

Set<Call> storedToSupplier = findsCallsStoredToSuppliers(tree);
Set<Call> storedToSupplier = findCallsStoredToDelayedExecutionCode(tree);

// Get map of each private method to the private methods it calls
// Any call to a non private method breaks the chain
Expand All @@ -135,17 +153,12 @@ private void analyseClass(ClassTree tree) {
}
}

private Set<Call> findsCallsStoredToSuppliers(ClassTree tree) {
Set<Call> all = new HashSet<>(directClinitCallsToDelayedExecutionCode(tree));
all.addAll(storedViaEnumConstructor());
return all;
private Set<Call> findCallsStoredToDelayedExecutionCode(ClassTree tree) {
return new HashSet<>(privateAndClinitCallsToDelayedExecutionCode(tree));
}

private Set<Call> storedViaEnumConstructor() {
return Collections.emptySet();
}

private Set<Call> directClinitCallsToDelayedExecutionCode(ClassTree tree) {
private Set<Call> privateAndClinitCallsToDelayedExecutionCode(ClassTree tree) {
return tree.methods().stream()
.filter(m -> m.isPrivate() || m.rawNode().name.equals("<clinit>"))
.flatMap(m -> delayedExecutionCall(m).stream().map(c -> new Call(m.asLocation(), c)))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.example.staticinitializers.delayedexecution;

import java.util.function.Supplier;

public enum EnumFieldMethodRef {
A(EnumFieldMethodRef::canMutate), B(EnumFieldMethodRef::canMutate);

private final Supplier<String> supplier;


EnumFieldMethodRef(Supplier<String> supplier) {
this.supplier = supplier;
}

private static String canMutate() {
return "Do not mutate";
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.example.staticinitializers;
package com.example.staticinitializers.delayedexecution;

import java.util.function.Supplier;

public enum EnumFieldSupplier {
A(canMutate());
A(canMutate()), B(canMutate());

private final Supplier<String> supplier;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.example.staticinitializers.delayedexecution;

import java.util.function.Supplier;

public enum EnumMixedFields {
A(EnumMixedFields::canMutate, doNotMutate()), B(EnumMixedFields::canMutate, doNotMutate());

private final Supplier<String> supplier;
private final String s;

EnumMixedFields(Supplier<String> supplier, String s) {
this.supplier = supplier;
this.s = s;
}

private static String canMutate() {
return "mutate me"; // mutate
}

private static String doNotMutate() {
return "Do not mutate";
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.example.staticinitializers;
package com.example.staticinitializers.delayedexecution;

import java.util.function.Function;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.example.staticinitializers;
package com.example.staticinitializers.delayedexecution;

import java.util.function.Supplier;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
package org.pitest.mutationtest.build.intercept.staticinitializers;

import com.example.staticinitializers.BrokenChain;
import com.example.staticinitializers.EnumFieldSupplier;
import com.example.staticinitializers.delayedexecution.EnumFieldMethodRef;
import com.example.staticinitializers.delayedexecution.EnumFieldSupplier;
import com.example.staticinitializers.EnumWithLambdaInConstructor;
import com.example.staticinitializers.MethodsCallsEachOtherInLoop;
import com.example.staticinitializers.NestedEnumWithLambdaInStaticInitializer;
import com.example.staticinitializers.SecondLevelPrivateMethods;
import com.example.staticinitializers.SingletonWithWorkInInitializer;
import com.example.staticinitializers.StaticFunctionField;
import com.example.staticinitializers.StaticSupplierField;
import com.example.staticinitializers.delayedexecution.EnumMixedFields;
import com.example.staticinitializers.delayedexecution.StaticFunctionField;
import com.example.staticinitializers.delayedexecution.StaticSupplierField;
import com.example.staticinitializers.ThirdLevelPrivateMethods;
import org.junit.Ignore;
import org.junit.Test;
import org.pitest.mutationtest.engine.MutationDetails;
import org.pitest.mutationtest.engine.gregor.mutators.NullMutateEverything;
Expand Down Expand Up @@ -196,6 +197,37 @@ public void doesNotSuppressDownStreamMutationsForEnumFieldSuppliers() {
.verify();
}


@Test
public void doesNotSuppressDownStreamMutationsForMethodRefsStoredToEnumFields() {
v.forClass(EnumFieldMethodRef.class)
.forMethod("canMutate")
.forAnyCode()
.mutantsAreGenerated()
.noMutantsAreFiltered()
.verify();
}

@Test
public void doesNotSuppressDownStreamMutationsForMethodRefsStoredToEnumFieldsWhenOtherFieldsInitialized() {
v.forClass(EnumMixedFields.class)
.forMethod("canMutate")
.forAnyCode()
.mutantsAreGenerated()
.noMutantsAreFiltered()
.verify();
}

@Test
public void suppressesMutationsForStringsStoredToEnumFields() {
v.forClass(EnumMixedFields.class)
.forMethod("doNotMutate")
.forAnyCode()
.mutantsAreGenerated()
.allMutantsAreFiltered()
.verify();
}

private Predicate<MutationDetails> inMethod(String name, String desc) {
return m -> m.getMethod().equals(name) && m.getId().getLocation().getMethodDesc().equals(desc);
}
Expand Down

0 comments on commit a53e47e

Please sign in to comment.