![JAR search and dependency download from the Maven repository](/logo.png)
org.evosuite.instrumentation.coverage.MutationInstrumentation Maven / Gradle / Ivy
The newest version!
/**
* Copyright (C) 2010-2018 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 .
*/
/**
*
*/
package org.evosuite.instrumentation.coverage;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.evosuite.Properties;
import org.evosuite.classpath.ResourceList;
import org.evosuite.coverage.mutation.Mutation;
import org.evosuite.coverage.mutation.MutationObserver;
import org.evosuite.graphs.GraphPool;
import org.evosuite.graphs.cfg.BytecodeInstruction;
import org.evosuite.graphs.cfg.RawControlFlowGraph;
import org.evosuite.instrumentation.BooleanValueInterpreter;
import org.evosuite.instrumentation.mutation.InsertUnaryOperator;
import org.evosuite.instrumentation.mutation.MutationOperator;
import org.evosuite.instrumentation.mutation.ReplaceArithmeticOperator;
import org.evosuite.instrumentation.mutation.ReplaceBitwiseOperator;
import org.evosuite.instrumentation.mutation.ReplaceComparisonOperator;
import org.evosuite.instrumentation.mutation.ReplaceConstant;
import org.evosuite.instrumentation.mutation.ReplaceVariable;
import org.evosuite.runtime.classhandling.ClassResetter;
import org.evosuite.runtime.instrumentation.AnnotatedLabel;
import org.evosuite.setup.DependencyAnalysis;
import org.evosuite.testcase.execution.ExecutionTracer;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.analysis.Analyzer;
import org.objectweb.asm.tree.analysis.Frame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* MutationInstrumentation class.
*
*
* @author Gordon Fraser
*/
public class MutationInstrumentation implements MethodInstrumentation {
private static Logger logger = LoggerFactory.getLogger(MethodInstrumentation.class);
private final List mutationOperators;
private Frame[] frames = new Frame[0];
/**
*
* Constructor for MutationInstrumentation.
*
*/
public MutationInstrumentation() {
mutationOperators = new ArrayList();
// FIXME: Don't include > < >= <= for boolean comparisons
mutationOperators.add(new ReplaceComparisonOperator());
mutationOperators.add(new ReplaceBitwiseOperator());
mutationOperators.add(new ReplaceArithmeticOperator());
mutationOperators.add(new ReplaceVariable());
mutationOperators.add(new ReplaceConstant());
// mutationOperators.add(new NegateCondition());
// FIXME: Don't apply to boolean values!
mutationOperators.add(new InsertUnaryOperator());
// FIXME: Can't check return types because of side effects
//mutationOperators.add(new DeleteStatement());
//mutationOperators.add(new DeleteField());
// TODO: Replace iinc?
}
private void getFrames(MethodNode mn, String className) {
try {
Analyzer a = new Analyzer(new BooleanValueInterpreter(mn.desc,
(mn.access & Opcodes.ACC_STATIC) == Opcodes.ACC_STATIC));
a.analyze(className, mn);
this.frames = a.getFrames();
} catch (Exception e) {
logger.info("1. Error during analysis: " + e);
//e.printStackTrace();
// TODO: Handle error
}
}
/* (non-Javadoc)
* @see org.evosuite.cfg.instrumentation.MethodInstrumentation#analyze(org.objectweb.asm.tree.MethodNode, java.lang.String, java.lang.String, int)
*/
/** {@inheritDoc} */
@SuppressWarnings("unchecked")
@Override
public void analyze(ClassLoader classLoader, MethodNode mn, String className,
String methodName, int access) {
if (methodName.startsWith(""))
return;
if (methodName.startsWith(ClassResetter.STATIC_RESET))
return;
RawControlFlowGraph graph = GraphPool.getInstance(classLoader).getRawCFG(className,
methodName);
Iterator j = mn.instructions.iterator();
getFrames(mn, className);
boolean constructorInvoked = false;
if (!methodName.startsWith(""))
constructorInvoked = true;
logger.info("Applying mutation operators ");
int frameIndex = 0;
int numMutants = 0;
if (frames.length != mn.instructions.size()) {
logger.error("Number of frames does not match number number of bytecode instructions: "
+ frames.length + "/" + mn.instructions.size());
logger.error("Skipping mutation of method " + className + "." + methodName);
return;
}
//assert (frames.length == mn.instructions.size()) : "Length " + frames.length
// + " vs " + mn.instructions.size();
while (j.hasNext()) {
Frame currentFrame = frames[frameIndex++];
AbstractInsnNode in = j.next();
if (!constructorInvoked) {
if (in.getOpcode() == Opcodes.INVOKESPECIAL) {
if (className.matches(".*\\$\\d+$")) {
// We will not find the superclasses of an anonymous class this way
// so best not mutate the constructor
continue;
}
MethodInsnNode cn = (MethodInsnNode) in;
Set superClasses = new HashSet();
if(DependencyAnalysis.getInheritanceTree() != null && DependencyAnalysis.getInheritanceTree().hasClass(className))
superClasses.addAll(DependencyAnalysis.getInheritanceTree().getSuperclasses(className));
superClasses.add(className);
String classNameWithDots = ResourceList.getClassNameFromResourcePath(cn.owner);
if (superClasses.contains(classNameWithDots)) {
constructorInvoked = true;
}
} else {
continue;
}
}
boolean inInstrumentation = false;
for (BytecodeInstruction v : graph.vertexSet()) {
// If the bytecode is instrumented by EvoSuite, then don't mutate
if(v.isLabel()) {
LabelNode labelNode = (LabelNode)v.getASMNode();
if (labelNode.getLabel() instanceof AnnotatedLabel) {
AnnotatedLabel aLabel = (AnnotatedLabel) labelNode.getLabel();
if (aLabel.isStartTag()) {
inInstrumentation = true;
} else {
inInstrumentation = false;
}
}
}
if(inInstrumentation) {
continue;
}
// If this is in the CFG
if (in.equals(v.getASMNode())) {
logger.info(v.toString());
List mutations = new LinkedList();
// TODO: More than one mutation operator might apply to the same instruction
for (MutationOperator mutationOperator : mutationOperators) {
if (numMutants++ > Properties.MAX_MUTANTS_PER_METHOD) {
logger.info("Reached maximum number of mutants per method");
break;
}
//logger.info("Checking mutation operator on instruction " + v);
if (mutationOperator.isApplicable(v)) {
logger.info("Applying mutation operator "
+ mutationOperator.getClass().getSimpleName());
mutations.addAll(mutationOperator.apply(mn, className,
methodName, v,
currentFrame));
}
}
if (!mutations.isEmpty()) {
logger.info("Adding instrumentation for mutation");
//InsnList instrumentation = getInstrumentation(in, mutations);
addInstrumentation(mn, in, mutations);
}
}
if (numMutants > Properties.MAX_MUTANTS_PER_METHOD) {
break;
}
}
}
j = mn.instructions.iterator();
logger.info("Result of mutation: ");
while (j.hasNext()) {
AbstractInsnNode in = j.next();
logger.info(new BytecodeInstruction(classLoader, className, methodName, 0, 0,
in).toString());
}
logger.info("Done.");
// mn.maxStack += 3;
}
/* (non-Javadoc)
* @see org.evosuite.cfg.instrumentation.MethodInstrumentation#executeOnMainMethod()
*/
/** {@inheritDoc} */
@Override
public boolean executeOnMainMethod() {
// TODO Auto-generated method stub
return false;
}
/* (non-Javadoc)
* @see org.evosuite.cfg.instrumentation.MethodInstrumentation#executeOnExcludedMethods()
*/
/** {@inheritDoc} */
@Override
public boolean executeOnExcludedMethods() {
// TODO Auto-generated method stub
return false;
}
/**
*
* addInstrumentation
*
*
* @param mn
* a {@link org.objectweb.asm.tree.MethodNode} object.
* @param original
* a {@link org.objectweb.asm.tree.AbstractInsnNode} object.
* @param mutations
* a {@link java.util.List} object.
*/
protected void addInstrumentation(MethodNode mn, AbstractInsnNode original,
List mutations) {
InsnList instructions = new InsnList();
// call mutationTouched(mutationObject.getId());
// TODO: All mutations in the id are touched, not just one!
for (Mutation mutation : mutations) {
instructions.add(mutation.getInfectionDistance());
instructions.add(new LdcInsnNode(mutation.getId()));
MethodInsnNode touched = new MethodInsnNode(Opcodes.INVOKESTATIC,
Type.getInternalName(ExecutionTracer.class), "passedMutation",
Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {
Type.DOUBLE_TYPE, Type.INT_TYPE }), false);
instructions.add(touched);
}
LabelNode endLabel = new LabelNode();
for (Mutation mutation : mutations) {
LabelNode nextLabel = new LabelNode();
LdcInsnNode mutationId = new LdcInsnNode(mutation.getId());
instructions.add(mutationId);
FieldInsnNode activeId = new FieldInsnNode(Opcodes.GETSTATIC,
Type.getInternalName(MutationObserver.class), "activeMutation", "I");
instructions.add(activeId);
instructions.add(new JumpInsnNode(Opcodes.IF_ICMPNE, nextLabel));
instructions.add(mutation.getMutation());
instructions.add(new JumpInsnNode(Opcodes.GOTO, endLabel));
instructions.add(nextLabel);
}
mn.instructions.insertBefore(original, instructions);
mn.instructions.insert(original, endLabel);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy