/** * Copyright (C) 2010-2017 Gordon Fraser, Andrea Arcuri and EvoSuite * contributors * * This file is part of EvoSuite. * * EvoSuite is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3.0 of the License, or * (at your option) any later version. * * EvoSuite is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with EvoSuite. If not, see <http://www.gnu.org/licenses/>. */ /** * */ package org.evosuite.instrumentation.mutation; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.evosuite.PackageInfo; import org.evosuite.coverage.mutation.Mutation; import org.evosuite.coverage.mutation.MutationPool; import org.evosuite.graphs.cfg.BytecodeInstruction; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.InsnList; import org.objectweb.asm.tree.InsnNode; import org.objectweb.asm.tree.LdcInsnNode; import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.VarInsnNode; import org.objectweb.asm.tree.analysis.Frame; /** * <p>ReplaceBitwiseOperator class.</p> * * @author Gordon Fraser */ public class ReplaceBitwiseOperator implements MutationOperator { public static final String NAME = "ReplaceBitwiseOperator"; private static Set<Integer> opcodesInt = new HashSet<Integer>(); private static Set<Integer> opcodesIntShift = new HashSet<Integer>(); private static Set<Integer> opcodesLong = new HashSet<Integer>(); private static Set<Integer> opcodesLongShift = new HashSet<Integer>(); private int numVariable = 0; static { opcodesInt.addAll(Arrays.asList(new Integer[] { Opcodes.IAND, Opcodes.IOR, Opcodes.IXOR })); opcodesIntShift.addAll(Arrays.asList(new Integer[] { Opcodes.ISHL, Opcodes.ISHR, Opcodes.IUSHR })); opcodesLong.addAll(Arrays.asList(new Integer[] { Opcodes.LAND, Opcodes.LOR, Opcodes.LXOR })); opcodesLongShift.addAll(Arrays.asList(new Integer[] { Opcodes.LSHL, Opcodes.LSHR, Opcodes.LUSHR })); } /* (non-Javadoc) * @see org.evosuite.cfg.instrumentation.MutationOperator#apply(org.objectweb.asm.tree.MethodNode, java.lang.String, java.lang.String, org.evosuite.cfg.BytecodeInstruction) */ /** {@inheritDoc} */ @Override public List<Mutation> apply(MethodNode mn, String className, String methodName, BytecodeInstruction instruction, Frame frame) { numVariable = ReplaceArithmeticOperator.getNextIndex(mn); // TODO: Check if this operator is applicable at all first // Should we do this via a method defined in the interface? InsnNode node = (InsnNode) instruction.getASMNode(); List<Mutation> mutations = new LinkedList<Mutation>(); Set<Integer> replacement = new HashSet<Integer>(); if (opcodesInt.contains(node.getOpcode())) replacement.addAll(opcodesInt); else if (opcodesIntShift.contains(node.getOpcode())) replacement.addAll(opcodesIntShift); else if (opcodesLong.contains(node.getOpcode())) replacement.addAll(opcodesLong); else if (opcodesLongShift.contains(node.getOpcode())) replacement.addAll(opcodesLongShift); replacement.remove(node.getOpcode()); for (int opcode : replacement) { InsnNode mutation = new InsnNode(opcode); // insert mutation into pool Mutation mutationObject = MutationPool.addMutation(className, methodName, NAME + " " + getOp(node.getOpcode()) + " -> " + getOp(opcode), instruction, mutation, getInfectionDistance(node.getOpcode(), opcode)); mutations.add(mutationObject); } return mutations; } private String getOp(int opcode) { switch (opcode) { case Opcodes.IAND: case Opcodes.LAND: return "&"; case Opcodes.IOR: case Opcodes.LOR: return "|"; case Opcodes.IXOR: case Opcodes.LXOR: return "^"; case Opcodes.ISHR: return ">> I"; case Opcodes.LSHR: return ">> L"; case Opcodes.ISHL: return "<< I"; case Opcodes.LSHL: return "<< L"; case Opcodes.IUSHR: return ">>> I"; case Opcodes.LUSHR: return ">>> L"; } throw new RuntimeException("Unknown opcode: " + opcode); } /** * <p>getInfectionDistance</p> * * @param opcodeOrig a int. * @param opcodeNew a int. * @return a {@link org.objectweb.asm.tree.InsnList} object. */ public InsnList getInfectionDistance(int opcodeOrig, int opcodeNew) { InsnList distance = new InsnList(); if (opcodesInt.contains(opcodeOrig)) { distance.add(new InsnNode(Opcodes.DUP2)); distance.add(new LdcInsnNode(opcodeOrig)); distance.add(new LdcInsnNode(opcodeNew)); distance.add(new MethodInsnNode( Opcodes.INVOKESTATIC, PackageInfo.getNameWithSlash(ReplaceBitwiseOperator.class), "getInfectionDistanceInt", "(IIII)D", false)); } else if (opcodesIntShift.contains(opcodeOrig)) { distance.add(new InsnNode(Opcodes.DUP2)); distance.add(new LdcInsnNode(opcodeOrig)); distance.add(new LdcInsnNode(opcodeNew)); distance.add(new MethodInsnNode( Opcodes.INVOKESTATIC, PackageInfo.getNameWithSlash(ReplaceBitwiseOperator.class), "getInfectionDistanceInt", "(IIII)D", false)); } else if (opcodesLong.contains(opcodeOrig)) { distance.add(new VarInsnNode(Opcodes.LSTORE, numVariable)); distance.add(new InsnNode(Opcodes.DUP2)); distance.add(new VarInsnNode(Opcodes.LLOAD, numVariable)); distance.add(new InsnNode(Opcodes.DUP2_X2)); distance.add(new LdcInsnNode(opcodeOrig)); distance.add(new LdcInsnNode(opcodeNew)); distance.add(new MethodInsnNode( Opcodes.INVOKESTATIC, PackageInfo.getNameWithSlash(ReplaceBitwiseOperator.class), "getInfectionDistanceLong", "(JJII)D", false)); numVariable += 2; } else if (opcodesLongShift.contains(opcodeOrig)) { distance.add(new VarInsnNode(Opcodes.ISTORE, numVariable)); distance.add(new InsnNode(Opcodes.DUP2)); distance.add(new VarInsnNode(Opcodes.ILOAD, numVariable)); distance.add(new InsnNode(Opcodes.DUP_X2)); distance.add(new LdcInsnNode(opcodeOrig)); distance.add(new LdcInsnNode(opcodeNew)); distance.add(new MethodInsnNode( Opcodes.INVOKESTATIC, PackageInfo.getNameWithSlash(ReplaceBitwiseOperator.class), "getInfectionDistanceLong", "(JIII)D", false)); numVariable += 1; } return distance; } /** * <p>getInfectionDistanceInt</p> * * @param x a int. * @param y a int. * @param opcodeOrig a int. * @param opcodeNew a int. * @return a double. */ public static double getInfectionDistanceInt(int x, int y, int opcodeOrig, int opcodeNew) { if (opcodeOrig == Opcodes.ISHR && opcodeNew == Opcodes.IUSHR) { if (x < 0 && y != 0) { int origValue = calculate(x, y, opcodeOrig); int newValue = calculate(x, y, opcodeNew); assert (origValue != newValue); return 0.0; } else // TODO x >= 0? return y != 0 && x > 0 ? x + 1.0 : 1.0; } int origValue = calculate(x, y, opcodeOrig); int newValue = calculate(x, y, opcodeNew); return origValue == newValue ? 1.0 : 0.0; } /** * <p>getInfectionDistanceLong</p> * * @param x a long. * @param y a int. * @param opcodeOrig a int. * @param opcodeNew a int. * @return a double. */ public static double getInfectionDistanceLong(long x, int y, int opcodeOrig, int opcodeNew) { if (opcodeOrig == Opcodes.LSHR && opcodeNew == Opcodes.LUSHR) { if (x < 0 && y != 0) { long origValue = calculate(x, y, opcodeOrig); long newValue = calculate(x, y, opcodeNew); assert (origValue != newValue); return 0.0; } else return y != 0 && x > 0 ? x + 1.0 : 1.0; } long origValue = calculate(x, y, opcodeOrig); long newValue = calculate(x, y, opcodeNew); return origValue == newValue ? 1.0 : 0.0; } /** * <p>getInfectionDistanceLong</p> * * @param x a long. * @param y a long. * @param opcodeOrig a int. * @param opcodeNew a int. * @return a double. */ public static double getInfectionDistanceLong(long x, long y, int opcodeOrig, int opcodeNew) { long origValue = calculate(x, y, opcodeOrig); long newValue = calculate(x, y, opcodeNew); return origValue == newValue ? 1.0 : 0.0; } /** * <p>calculate</p> * * @param x a int. * @param y a int. * @param opcode a int. * @return a int. */ public static int calculate(int x, int y, int opcode) { switch (opcode) { case Opcodes.IAND: return x & y; case Opcodes.IOR: return x | y; case Opcodes.IXOR: return x ^ y; case Opcodes.ISHL: return x << y; case Opcodes.ISHR: return x >> y; case Opcodes.IUSHR: return x >>> y; } throw new RuntimeException("Unknown integer opcode: " + opcode); } /** * <p>calculate</p> * * @param x a long. * @param y a long. * @param opcode a int. * @return a long. */ public static long calculate(long x, long y, int opcode) { switch (opcode) { case Opcodes.LAND: return x & y; case Opcodes.LOR: return x | y; case Opcodes.LXOR: return x ^ y; case Opcodes.LSHL: return x << y; case Opcodes.LSHR: return x >> y; case Opcodes.LUSHR: return x >>> y; } throw new RuntimeException("Unknown long opcode: " + opcode); } /* (non-Javadoc) * @see org.evosuite.cfg.instrumentation.mutation.MutationOperator#isApplicable(org.evosuite.cfg.BytecodeInstruction) */ /** {@inheritDoc} */ @Override public boolean isApplicable(BytecodeInstruction instruction) { AbstractInsnNode node = instruction.getASMNode(); int opcode = node.getOpcode(); if (opcodesInt.contains(opcode)) return true; else if (opcodesIntShift.contains(opcode)) return true; else if (opcodesLong.contains(opcode)) return true; else if (opcodesLongShift.contains(opcode)) return true; return false; } }