diff --git a/pitest-entry/src/main/java/org/pitest/bytecode/analysis/MethodTree.java b/pitest-entry/src/main/java/org/pitest/bytecode/analysis/MethodTree.java index 9a8b475d3..919b2cb09 100644 --- a/pitest-entry/src/main/java/org/pitest/bytecode/analysis/MethodTree.java +++ b/pitest-entry/src/main/java/org/pitest/bytecode/analysis/MethodTree.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Optional; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AbstractInsnNode; @@ -34,6 +35,7 @@ public Location asLocation() { * @param index index to work backwards from * @return The previous instruction */ + @Deprecated public AbstractInsnNode realInstructionBefore(int index) { AbstractInsnNode candidate = instructions().get(index - 1); if (candidate.getOpcode() == -1) { @@ -41,9 +43,17 @@ public AbstractInsnNode realInstructionBefore(int index) { } return candidate; } - + + public Optional instructionForIndex(int index) { + return Optional.ofNullable(instruction(index)); + } + + @Deprecated public AbstractInsnNode instruction(int index) { - return instructions().get(index); + if (index < 0 || index >= instructions().size() ) { + return null; + } + return instructions().get(index); } public List instructions() { diff --git a/pitest-entry/src/main/java/org/pitest/mutationtest/build/intercept/equivalent/EqualsPerformanceShortcutFilter.java b/pitest-entry/src/main/java/org/pitest/mutationtest/build/intercept/equivalent/EqualsPerformanceShortcutFilter.java index 33d40768b..3afb4d72a 100644 --- a/pitest-entry/src/main/java/org/pitest/mutationtest/build/intercept/equivalent/EqualsPerformanceShortcutFilter.java +++ b/pitest-entry/src/main/java/org/pitest/mutationtest/build/intercept/equivalent/EqualsPerformanceShortcutFilter.java @@ -112,8 +112,10 @@ private boolean shortCutEquals(MethodTree tree, MutationDetails a, Mutater m) { } private boolean mutatesAConditionalJump(MethodTree tree, int index) { - final AbstractInsnNode mutatedInsns = tree.instruction(index); - return InstructionMatchers.aConditionalJump().asPredicate().test(mutatedInsns); + var mutatedInsns = tree.instructionForIndex(index); + return mutatedInsns + .map(n -> InstructionMatchers.aConditionalJump().asPredicate().test(n)) + .orElse(false); } private Predicate inEqualsMethod() { diff --git a/pitest-entry/src/main/java/org/pitest/mutationtest/build/intercept/javafeatures/ForEachLoopFilter.java b/pitest-entry/src/main/java/org/pitest/mutationtest/build/intercept/javafeatures/ForEachLoopFilter.java index d6c4cf3f7..6ed3d9e11 100644 --- a/pitest-entry/src/main/java/org/pitest/mutationtest/build/intercept/javafeatures/ForEachLoopFilter.java +++ b/pitest-entry/src/main/java/org/pitest/mutationtest/build/intercept/javafeatures/ForEachLoopFilter.java @@ -202,11 +202,14 @@ private Predicate mutatesIteratorLoopPlumbing() { return false; } - final AbstractInsnNode mutatedInstruction = method.instruction(instruction); + var mutatedInstruction = method.instructionForIndex(instruction); + if (mutatedInstruction.isEmpty()) { + return false; + } Set toAvoid = cache.computeIfAbsent(method, this::findLoopInstructions); - return toAvoid.contains(mutatedInstruction); + return toAvoid.contains(mutatedInstruction.get()); }; } diff --git a/pitest-entry/src/main/java/org/pitest/mutationtest/build/intercept/javafeatures/InlinedFinallyBlockFilter.java b/pitest-entry/src/main/java/org/pitest/mutationtest/build/intercept/javafeatures/InlinedFinallyBlockFilter.java index e16bde277..4e2084425 100644 --- a/pitest-entry/src/main/java/org/pitest/mutationtest/build/intercept/javafeatures/InlinedFinallyBlockFilter.java +++ b/pitest-entry/src/main/java/org/pitest/mutationtest/build/intercept/javafeatures/InlinedFinallyBlockFilter.java @@ -184,10 +184,13 @@ private boolean isInFinallyBlock(MutationDetails m) { return false; } - AbstractInsnNode mutatedInstruction = method.instruction(m.getInstructionIndex()); + var mutatedInstruction = method.instructionForIndex(m.getInstructionIndex()); + if (mutatedInstruction.isEmpty()) { + return false; + } Context context = Context.start(DEBUG); - context = context.store(MUTATED_INSTRUCTION.write(), mutatedInstruction); + context = context.store(MUTATED_INSTRUCTION.write(), mutatedInstruction.get()); context = context.store(HANDLERS.write(), handlers); return IS_IN_HANDLER.matches(method.instructions(), context); } diff --git a/pitest-entry/src/main/java/org/pitest/mutationtest/build/intercept/timeout/AvoidForLoopCounterFilter.java b/pitest-entry/src/main/java/org/pitest/mutationtest/build/intercept/timeout/AvoidForLoopCounterFilter.java index 858f44869..9e1812473 100644 --- a/pitest-entry/src/main/java/org/pitest/mutationtest/build/intercept/timeout/AvoidForLoopCounterFilter.java +++ b/pitest-entry/src/main/java/org/pitest/mutationtest/build/intercept/timeout/AvoidForLoopCounterFilter.java @@ -170,7 +170,12 @@ private Predicate mutatesAForLoopCounter() { return false; } - final AbstractInsnNode mutatedInstruction = method.instruction(instruction); + var maybeIns = method.instructionForIndex(instruction); + if (maybeIns.isEmpty()) { + return false; + } + + var mutatedInstruction = maybeIns.get(); // performance hack if (!(mutatedInstruction instanceof IincInsnNode)) { diff --git a/pitest-entry/src/main/java/org/pitest/mutationtest/build/intercept/timeout/InfiniteForLoopFilter.java b/pitest-entry/src/main/java/org/pitest/mutationtest/build/intercept/timeout/InfiniteForLoopFilter.java index 6023b007d..7fab08e88 100644 --- a/pitest-entry/src/main/java/org/pitest/mutationtest/build/intercept/timeout/InfiniteForLoopFilter.java +++ b/pitest-entry/src/main/java/org/pitest/mutationtest/build/intercept/timeout/InfiniteForLoopFilter.java @@ -53,8 +53,10 @@ SequenceMatcher infiniteLoopMatcher() { @Override boolean couldCauseInfiniteLoop(MethodTree method, MutationDetails each) { - final AbstractInsnNode instruction = method.instruction(each.getInstructionIndex()); - return instruction.getOpcode() == Opcodes.IINC; + var instruction = method.instructionForIndex(each.getInstructionIndex()); + return instruction + .map(i -> i.getOpcode() == Opcodes.IINC) + .orElse(false); } private static SequenceQuery countingLoopWithoutWriteConditionalAtStart() { diff --git a/pitest-entry/src/main/java/org/pitest/mutationtest/build/intercept/timeout/InfiniteIteratorLoopFilter.java b/pitest-entry/src/main/java/org/pitest/mutationtest/build/intercept/timeout/InfiniteIteratorLoopFilter.java index 4b7ed6ccc..3d682ff45 100644 --- a/pitest-entry/src/main/java/org/pitest/mutationtest/build/intercept/timeout/InfiniteIteratorLoopFilter.java +++ b/pitest-entry/src/main/java/org/pitest/mutationtest/build/intercept/timeout/InfiniteIteratorLoopFilter.java @@ -44,8 +44,10 @@ SequenceMatcher infiniteLoopMatcher() { @Override boolean couldCauseInfiniteLoop(MethodTree method, MutationDetails each) { - final AbstractInsnNode instruction = method.instruction(each.getInstructionIndex()); - return isIteratorNext(instruction); + var instruction = method.instructionForIndex(each.getInstructionIndex()); + return instruction + .map(this::isIteratorNext) + .orElse(false); } private static SequenceQuery doesNotBreakIteratorLoop() { diff --git a/pitest-entry/src/test/java/org/pitest/bytecode/analysis/MethodTreeTest.java b/pitest-entry/src/test/java/org/pitest/bytecode/analysis/MethodTreeTest.java index a7bf186c8..194d795a0 100644 --- a/pitest-entry/src/test/java/org/pitest/bytecode/analysis/MethodTreeTest.java +++ b/pitest-entry/src/test/java/org/pitest/bytecode/analysis/MethodTreeTest.java @@ -9,6 +9,15 @@ public class MethodTreeTest { ClassloaderByteArraySource bytes = ClassloaderByteArraySource.fromContext(); + @Test + public void retrievesInstructionsWithinMethod() { + var clazz = loadClass(MethodTreeTest.class.getName()); + var method = findMethod(clazz, "retrievesInstructionsWithinMethod"); + assertThat(method.instructionForIndex(0)).isNotEmpty(); + assertThat(method.instructionForIndex(-1)).isEmpty(); + assertThat(method.instructionForIndex(Integer.MAX_VALUE)).isEmpty(); + } + @Test public void recognisesAbstractMethods() { var clazz = loadClass(Bar.class.getName());