From e80f8243c5db93dd9e469aee16400eadcbee5a10 Mon Sep 17 00:00:00 2001 From: henry Date: Mon, 12 Feb 2024 18:55:16 +0000 Subject: [PATCH] filter all returns of unmodifiable wrappers --- .../analysis/InstructionMatchers.java | 7 +++- .../ReturnUnmodifiableCollection.java | 4 +- ...turnUnmodifiableCollectionFactoryTest.java | 38 +++++++++++++++++++ 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/pitest-entry/src/main/java/org/pitest/bytecode/analysis/InstructionMatchers.java b/pitest-entry/src/main/java/org/pitest/bytecode/analysis/InstructionMatchers.java index d8b2a8daa..8061d3b1c 100644 --- a/pitest-entry/src/main/java/org/pitest/bytecode/analysis/InstructionMatchers.java +++ b/pitest-entry/src/main/java/org/pitest/bytecode/analysis/InstructionMatchers.java @@ -188,16 +188,19 @@ public static Match methodDescEquals(final String desc) { } public static Match methodCallTo(final ClassName owner, final String name) { + return methodCallTo(owner, c -> c.equals(name)); + } + + public static Match methodCallTo(final ClassName owner, Predicate name) { return (c, t) -> { if ( t instanceof MethodInsnNode ) { final MethodInsnNode call = (MethodInsnNode) t; - return result( call.name.equals(name) && call.owner.equals(owner.asInternalName()), c); + return result( name.test(call.name) && call.owner.equals(owner.asInternalName()), c); } return result(false, c); }; } - public static Match isInstruction(final SlotRead target) { return (c, t) -> result(c.retrieve(target).get() == t, c); } diff --git a/pitest-entry/src/main/java/org/pitest/mutationtest/build/intercept/defensive/ReturnUnmodifiableCollection.java b/pitest-entry/src/main/java/org/pitest/mutationtest/build/intercept/defensive/ReturnUnmodifiableCollection.java index cb96139b3..efe9b2b5a 100644 --- a/pitest-entry/src/main/java/org/pitest/mutationtest/build/intercept/defensive/ReturnUnmodifiableCollection.java +++ b/pitest-entry/src/main/java/org/pitest/mutationtest/build/intercept/defensive/ReturnUnmodifiableCollection.java @@ -28,13 +28,11 @@ public class ReturnUnmodifiableCollection extends RegionInterceptor { - private static final ClassName COLLECTIONS = ClassName.fromClass(Collections.class); - static final Slot MUTATED_INSTRUCTION = Slot.create(AbstractInsnNode.class); static final SequenceMatcher DEFENSIVE_RETURN = QueryStart .any(AbstractInsnNode.class) - .then(INVOKESTATIC.and(methodCallTo(COLLECTIONS, "unmodifiableSet")).and(store(MUTATED_INSTRUCTION.write()))) + .then(INVOKESTATIC.and(methodCallTo(ClassName.fromClass(Collections.class), n -> n.startsWith("unmodifiable"))).and(store(MUTATED_INSTRUCTION.write()))) .then(ARETURN) .zeroOrMore(QueryStart.match(anyInstruction())) .compile(QueryParams.params(AbstractInsnNode.class) diff --git a/pitest-entry/src/test/java/org/pitest/mutationtest/build/intercept/defensive/ReturnUnmodifiableCollectionFactoryTest.java b/pitest-entry/src/test/java/org/pitest/mutationtest/build/intercept/defensive/ReturnUnmodifiableCollectionFactoryTest.java index 9ed6cced7..b53d1086e 100644 --- a/pitest-entry/src/test/java/org/pitest/mutationtest/build/intercept/defensive/ReturnUnmodifiableCollectionFactoryTest.java +++ b/pitest-entry/src/test/java/org/pitest/mutationtest/build/intercept/defensive/ReturnUnmodifiableCollectionFactoryTest.java @@ -8,8 +8,11 @@ import org.pitest.verifier.interceptors.InterceptorVerifier; import org.pitest.verifier.interceptors.VerifierStart; +import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Set; import static org.pitest.bytecode.analysis.OpcodeMatchers.INVOKESTATIC; @@ -53,6 +56,22 @@ public void filtersMutationsToReturnUnmodifiableSet() { .verify(); } + @Test + public void filtersMutationsToReturnUnmodifiableList() { + v.forClass(HasUnmodifiableListReturn.class) + .forCodeMatching(INVOKESTATIC.asPredicate()) + .allMutantsAreFiltered() + .verify(); + } + + @Test + public void filtersMutationsToReturnUnmodifiableMap() { + v.forClass(HasUnmodifiableMapReturn.class) + .forCodeMatching(INVOKESTATIC.asPredicate()) + .allMutantsAreFiltered() + .verify(); + } + @Test public void doesNotFilterOtherCode() { v.forClass(HasUnmodifiableSetReturn.class) @@ -82,6 +101,25 @@ public Set mutateMe(int i) { } } +class HasUnmodifiableListReturn { + private final List s = new ArrayList<>(); + + public List mutateMe(int i) { + if (i != 1) { + return Collections.unmodifiableList(s); + } + + return s; + } +} + +class HasUnmodifiableMapReturn { + + public Map mutateMe(Map m) { + return Collections.unmodifiableMap(m); + } +} + class HasUnmodifiableSetNonReturn { private final Set s = new HashSet<>(); private Set copy;