org.pitest.bytecode.analysis.InstructionMatchers Maven / Gradle / Ivy
The newest version!
package org.pitest.bytecode.analysis;
import static java.util.function.Predicate.isEqual;
import static org.objectweb.asm.Opcodes.ICONST_0;
import static org.objectweb.asm.Opcodes.ICONST_1;
import static org.objectweb.asm.Opcodes.ICONST_2;
import static org.objectweb.asm.Opcodes.ICONST_3;
import static org.objectweb.asm.Opcodes.ICONST_4;
import static org.objectweb.asm.Opcodes.ICONST_5;
import static org.objectweb.asm.Opcodes.ICONST_M1;
import static org.objectweb.asm.Opcodes.ILOAD;
import static org.objectweb.asm.Opcodes.ISTORE;
import static org.pitest.sequence.Result.result;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.FrameNode;
import org.objectweb.asm.tree.IincInsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.LineNumberNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
import org.pitest.classinfo.ClassName;
import org.pitest.sequence.Match;
import org.pitest.sequence.Slot;
import org.pitest.sequence.SlotRead;
import org.pitest.sequence.SlotWrite;
import java.util.function.Predicate;
public class InstructionMatchers {
public static Match anyInstruction() {
return Match.always();
}
/**
* Matches nodes that do not represent an instruction or label
*/
public static Match notAnInstruction() {
return isA(LineNumberNode.class).or(isA(FrameNode.class));
}
public static Match newCall(ClassName target) {
final String clazz = target.asInternalName();
return (c, t) -> {
if ( t instanceof TypeInsnNode ) {
final TypeInsnNode call = (TypeInsnNode) t;
return result(call.getOpcode() == Opcodes.NEW && call.desc.equals(clazz), c);
}
return result(false, c);
};
}
public static Match ldcString(Predicate match) {
return (c, t) -> {
if ( t instanceof LdcInsnNode) {
final LdcInsnNode ldc = (LdcInsnNode) t;
return result(ldc.cst instanceof String && match.test((String) ldc.cst), c);
}
return result(false, c);
};
}
public static Match opCode(final int opcode) {
return (c, a) -> result(a.getOpcode() == opcode, c);
}
public static Match isA(
final Class cls) {
return (c, a) -> result(a.getClass().isAssignableFrom(cls), c);
}
public static Match incrementsVariable(final SlotRead counterVariable) {
return (context, a) -> result((a instanceof IincInsnNode)
&& context.retrieve(counterVariable).filter(isEqual(((IincInsnNode)a).var)).isPresent(), context);
}
public static Match anIStore(
final SlotWrite counterVariable) {
return opCode(Opcodes.ISTORE).and(aVariableAccess(counterVariable));
}
public static Match anILoad(
final SlotWrite counterVariable) {
return opCode(Opcodes.ILOAD).and(aVariableAccess(counterVariable));
}
public static Match aVariableAccess(
final SlotWrite counterVariable) {
return (c, t) -> {
if (t instanceof VarInsnNode) {
return result(true, c.store(counterVariable, ((VarInsnNode) t).var));
}
return result(false, c);
};
}
public static Match anIStoreTo(
final SlotRead counterVariable) {
return opCode(ISTORE).and(variableMatches(counterVariable));
}
public static Match anILoadOf(
final SlotRead counterVariable) {
return opCode(ILOAD).and(variableMatches(counterVariable));
}
public static Match variableMatches(
final SlotRead counterVariable) {
return (c, t) -> result((t instanceof VarInsnNode)
&& c.retrieve(counterVariable).filter(isEqual(((VarInsnNode) t).var)).isPresent(), c);
}
public static Match anIntegerConstant() {
return opCode(ICONST_M1)
.or(opCode(ICONST_0))
.or(opCode(ICONST_1))
.or(opCode(ICONST_2))
.or(opCode(ICONST_3))
.or(opCode(ICONST_4))
.or(opCode(ICONST_5));
}
public static Match aLabelNode(SlotWrite slot) {
return isA(LabelNode.class).and(writeNodeToSlot(slot, LabelNode.class));
}
public static Match aJump() {
return isA(JumpInsnNode.class);
}
public static Match aConditionalJump() {
return (c, t) -> result((t instanceof JumpInsnNode)
&& (t.getOpcode() != Opcodes.GOTO)
&& (t.getOpcode() != Opcodes.JSR), c);
}
public static Match aConditionalJumpTo(Slot label) {
return jumpsTo(label.read()).and(aConditionalJump());
}
public static Match writeNodeToSlot(final SlotWrite slot, final Class clazz) {
return (c, t) -> {
if (clazz.isAssignableFrom(t.getClass()) ) {
return result(true, c.store(slot, clazz.cast(t)));
}
return result(false, c);
};
}
public static Match methodCallThatReturns(final ClassName type) {
return (c, t) -> {
if ( t instanceof MethodInsnNode ) {
return result(((MethodInsnNode) t).desc.endsWith(type.asInternalName() + ";"), c);
}
return result(false, c);
};
}
public static Match methodCall() {
return isA(MethodInsnNode.class);
}
public static Match methodCallNamed(String name) {
return (c, t) -> {
if ( t instanceof MethodInsnNode ) {
final MethodInsnNode call = (MethodInsnNode) t;
return result(call.name.equals(name), c);
}
return result(false, c);
};
}
public static Match methodDescEquals(final String desc) {
return (c, t) -> {
if ( t instanceof MethodInsnNode ) {
return result(((MethodInsnNode) t).desc.equals(desc), c);
}
return result(false, c);
};
}
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( 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);
}
public static Match getStatic(String owner, String field) {
return (c, t) -> {
if (t instanceof FieldInsnNode) {
FieldInsnNode fieldNode = (FieldInsnNode) t;
return result( t.getOpcode() == Opcodes.GETSTATIC && fieldNode.name.equals(field) && fieldNode.owner.equals(owner), c);
}
return result(false, c);
};
}
/**
* Records if a instruction matches the target, but always returns true
*/
public static Match recordTarget(final SlotRead target, final SlotWrite found) {
return (c, t) -> {
if (c.retrieve(target).get() == t) {
return result(true, c.store(found, true));
}
return result(true, c);
};
}
private static Match storeJumpTarget(
final SlotWrite label) {
return (c, t) -> {
if (t instanceof JumpInsnNode ) {
return result(true, c.store(label, ((JumpInsnNode) t).label));
}
return result(false, c);
};
}
public static Match jumpsTo(
final SlotRead loopStart) {
return (context, a) -> {
if (!(a instanceof JumpInsnNode)) {
return result(false, context);
}
final JumpInsnNode jump = (JumpInsnNode) a;
return result(context.retrieve(loopStart).filter(isEqual(jump.label)).isPresent(), context);
};
}
public static Match jumpsTo(
final SlotWrite label) {
return storeJumpTarget(label);
}
public static Match gotoLabel(
final SlotWrite loopEnd) {
return opCode(Opcodes.GOTO).and(storeJumpTarget(loopEnd));
}
public static Match labelNode(
final SlotRead loopEnd) {
return (c, t) -> {
if (!(t instanceof LabelNode)) {
return result(false, c);
}
final LabelNode l = (LabelNode) t;
return result(c.retrieve(loopEnd).filter(isEqual(l)).isPresent(), c);
};
}
public static Match debug(final String msg) {
return (context, a) -> {
context.debug(msg, a);
return result(true, context);
};
}
}