org.evosuite.graphs.cfg.BytecodeInstructionPool 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.graphs.cfg;
import java.util.*;
import org.evosuite.coverage.branch.BranchPool;
import org.evosuite.runtime.instrumentation.AnnotatedLabel;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.VarInsnNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* BytecodeInstructionPool class.
*
*
* @author Andre Mis
*/
public class BytecodeInstructionPool {
private static Logger logger = LoggerFactory.getLogger(BytecodeInstructionPool.class);
private static Map instanceMap = new LinkedHashMap<>();
private final ClassLoader classLoader;
private BytecodeInstructionPool(ClassLoader classLoader) {
this.classLoader = classLoader;
}
public static BytecodeInstructionPool getInstance(ClassLoader classLoader) {
if (!instanceMap.containsKey(classLoader)) {
instanceMap.put(classLoader, new BytecodeInstructionPool(classLoader));
}
return instanceMap.get(classLoader);
}
// maps className -> method inside that class -> list of
// BytecodeInstructions
private final Map>> instructionMap = new LinkedHashMap<>();
private final List knownMethodNodes = new ArrayList<>();
// fill the pool
/**
* Called by each CFGGenerator for it's corresponding method.
*
* The MethodNode contains all instructions within a method. A call to
* registerMethodNode() fills the instructionMap of the
* BytecodeInstructionPool with the instructions in that method and returns
* a List containing the BytecodeInstructions within that method.
*
* While registering all instructions the lineNumber of each
* BytecodeInstruction is set.
*
* @param node
* a {@link org.objectweb.asm.tree.MethodNode} object.
* @param className
* a {@link java.lang.String} object.
* @param methodName
* a {@link java.lang.String} object.
* @return a {@link java.util.List} object.
*/
public List registerMethodNode(MethodNode node,
String className, String methodName) {
registerMethodNode(node);
int lastLineNumber = -1;
int bytecodeOffset = 0;
for (int instructionId = 0; instructionId < node.instructions.size(); instructionId++) {
AbstractInsnNode instructionNode = node.instructions.get(instructionId);
BytecodeInstruction instruction = BytecodeInstructionFactory.createBytecodeInstruction(classLoader,
className,
methodName,
instructionId,
bytecodeOffset,
instructionNode);
if (instruction.isLineNumber())
lastLineNumber = instruction.getLineNumber();
else if (lastLineNumber != -1)
instruction.setLineNumber(lastLineNumber);
bytecodeOffset += getBytecodeIncrement(instructionNode);
if (!instruction.isLabel() && !instruction.isLineNumber()
&& !instruction.isFrame()) {
bytecodeOffset++;
}
registerInstruction(instruction);
}
List r = getInstructionsIn(className, methodName);
if (r == null || r.size() == 0)
throw new IllegalStateException(
"expect instruction pool to return non-null non-empty list of instructions for a previously registered method "
+ methodName);
return r;
}
/**
* Determine how many bytes the current instruction occupies together with
* its operands
*
* @return
*/
private int getBytecodeIncrement(AbstractInsnNode instructionNode) {
int opcode = instructionNode.getOpcode();
switch (opcode) {
case Opcodes.ALOAD: // index
case Opcodes.ASTORE: // index
case Opcodes.DLOAD:
case Opcodes.DSTORE:
case Opcodes.FLOAD:
case Opcodes.FSTORE:
case Opcodes.ILOAD:
case Opcodes.ISTORE:
case Opcodes.LLOAD:
case Opcodes.LSTORE:
VarInsnNode varNode = (VarInsnNode) instructionNode;
if (varNode.var > 3)
return 1;
else
return 0;
case Opcodes.BIPUSH: // byte
case Opcodes.NEWARRAY:
case Opcodes.RET:
return 1;
case Opcodes.LDC:
LdcInsnNode ldcNode = (LdcInsnNode)instructionNode;
if(ldcNode.cst instanceof Double || ldcNode.cst instanceof Long)
return 2; // LDC2_W
else
return 1;
case 19: //LDC_W
case 20: //LDC2_W
return 2;
case Opcodes.ANEWARRAY: // indexbyte1, indexbyte2
case Opcodes.CHECKCAST: // indexbyte1, indexbyte2
case Opcodes.GETFIELD:
case Opcodes.GETSTATIC:
case Opcodes.GOTO:
case Opcodes.IF_ACMPEQ:
case Opcodes.IF_ACMPNE:
case Opcodes.IF_ICMPEQ:
case Opcodes.IF_ICMPNE:
case Opcodes.IF_ICMPGE:
case Opcodes.IF_ICMPGT:
case Opcodes.IF_ICMPLE:
case Opcodes.IF_ICMPLT:
case Opcodes.IFLE:
case Opcodes.IFLT:
case Opcodes.IFGE:
case Opcodes.IFGT:
case Opcodes.IFNE:
case Opcodes.IFEQ:
case Opcodes.IFNONNULL:
case Opcodes.IFNULL:
case Opcodes.IINC:
case Opcodes.INSTANCEOF:
case Opcodes.INVOKESPECIAL:
case Opcodes.INVOKESTATIC:
case Opcodes.INVOKEVIRTUAL:
case Opcodes.JSR:
case Opcodes.NEW:
case Opcodes.PUTFIELD:
case Opcodes.PUTSTATIC:
case Opcodes.SIPUSH:
// case Opcodes.LDC_W
// case Opcodes.LDC2_W
return 2;
case Opcodes.MULTIANEWARRAY:
return 3;
case Opcodes.INVOKEDYNAMIC:
case Opcodes.INVOKEINTERFACE:
return 4;
case Opcodes.LOOKUPSWITCH:
case Opcodes.TABLESWITCH:
// TODO: Could be more
return 4;
// case Opcodes.GOTO_W
// case Opcodes.JSR_W
}
return 0;
}
private void registerMethodNode(MethodNode node) {
for (MethodNode mn : knownMethodNodes)
if (mn == node)
logger.debug("CFGGenerator.analyze() apparently got called for the same MethodNode twice");
knownMethodNodes.add(node);
}
/**
*
* registerInstruction
*
*
* @param instruction
* a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object.
*/
public void registerInstruction(BytecodeInstruction instruction) {
String className = instruction.getClassName();
String methodName = instruction.getMethodName();
if (!instructionMap.containsKey(className))
instructionMap.put(className,
new LinkedHashMap<>());
if (!instructionMap.get(className).containsKey(methodName))
instructionMap.get(className).put(methodName,
new ArrayList<>());
instructionMap.get(className).get(methodName).add(instruction);
logger.debug("Registering instruction "+instruction);
List instructions = instructionMap.get(className).get(methodName);
if(instructions.size() > 1) {
BytecodeInstruction previous = instructions.get(instructions.size() - 2);
if(previous.isLabel()) {
LabelNode ln = (LabelNode)previous.asmNode;
if (ln.getLabel() instanceof AnnotatedLabel) {
AnnotatedLabel aLabel = (AnnotatedLabel) ln.getLabel();
if(aLabel.isStartTag()) {
if(aLabel.shouldIgnore()) {
logger.debug("Ignoring artificial branch: "+instruction);
return;
}
}
}
}
}
if (instruction.isActualBranch()) {
BranchPool.getInstance(classLoader).registerAsBranch(instruction);
}
}
// retrieve data from the pool
/**
*
* getInstruction
*
*
* @param className
* a {@link java.lang.String} object.
* @param methodName
* a {@link java.lang.String} object.
* @param instructionId
* a int.
* @param asmNode
* a {@link org.objectweb.asm.tree.AbstractInsnNode} object.
* @return a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object.
*/
public BytecodeInstruction getInstruction(String className, String methodName,
int instructionId, AbstractInsnNode asmNode) {
BytecodeInstruction r = getInstruction(className, methodName, instructionId);
if (r != null)
assert (r.sanityCheckAbstractInsnNode(asmNode));
return r;
}
/**
*
* getInstruction
*
*
* @param className
* a {@link java.lang.String} object.
* @param methodName
* a {@link java.lang.String} object.
* @param instructionId
* a int.
* @return a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object.
*/
public BytecodeInstruction getInstruction(String className, String methodName,
int instructionId) {
if (instructionMap.get(className) == null) {
logger.debug("unknown class: " + className);
logger.debug(instructionMap.keySet().toString());
return null;
}
if (instructionMap.get(className).get(methodName) == null) {
logger.debug("unknown method: " + methodName);
logger.debug(instructionMap.get(className).keySet().toString());
return null;
}
for (BytecodeInstruction instruction : instructionMap.get(className).get(methodName)) {
if (instruction.getInstructionId() == instructionId)
return instruction;
}
logger.debug("unknown instruction " + instructionId + ", have "
+ instructionMap.get(className).get(methodName).size());
for (int i = 0; i < instructionMap.get(className).get(methodName).size(); i++) {
logger.info(instructionMap.get(className).get(methodName).get(i).toString());
}
return null;
}
/**
*
* getInstruction
*
*
* @param className
* a {@link java.lang.String} object.
* @param methodName
* a {@link java.lang.String} object.
* @param node
* a {@link org.objectweb.asm.tree.AbstractInsnNode} object.
* @return a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object.
*/
public BytecodeInstruction getInstruction(String className, String methodName,
AbstractInsnNode node) {
if (instructionMap.get(className) == null) {
logger.debug("unknown class: " + className);
logger.debug(instructionMap.keySet().toString());
return null;
}
if (instructionMap.get(className).get(methodName) == null) {
logger.debug("unknown method: " + methodName);
logger.debug(instructionMap.get(className).keySet().toString());
return null;
}
for (BytecodeInstruction instruction : instructionMap.get(className).get(methodName)) {
if (instruction.asmNode == node)
return instruction;
}
logger.debug("unknown instruction: " + node + ", have "
+ instructionMap.get(className).get(methodName).size()
+ " instructions for this method");
logger.debug(instructionMap.get(className).get(methodName).toString());
return null;
}
/**
*
* knownClasses
*
*
* @return a {@link java.util.Set} object.
*/
public Set knownClasses() {
return new LinkedHashSet<>(instructionMap.keySet());
}
/**
*
* knownMethods
*
*
* @param className
* a {@link java.lang.String} object.
* @return a {@link java.util.Set} object.
*/
public Set knownMethods(String className) {
Set r = new LinkedHashSet<>();
if (instructionMap.get(className) != null)
r.addAll(instructionMap.get(className).keySet());
return r;
}
public boolean hasMethod(String className, String methodName) {
if (instructionMap.get(className) != null)
return instructionMap.get(className).containsKey(methodName);
return false;
}
/**
*
* getInstructionsIn
*
*
* @param className
* a {@link java.lang.String} object.
* @param methodName
* a {@link java.lang.String} object.
* @return a {@link java.util.List} object.
*/
public List getInstructionsIn(String className, String methodName) {
if (instructionMap.get(className) == null
|| instructionMap.get(className).get(methodName) == null)
return null;
List r = new ArrayList<>();
r.addAll(instructionMap.get(className).get(methodName));
return r;
}
public List getInstructionsIn(String className) {
if (instructionMap.get(className) == null)
return null;
List r = new ArrayList<>();
Map> methodMap = instructionMap.get(className);
for(List methodInstructions : methodMap.values()) {
r.addAll(methodInstructions);
}
return r;
}
public List getAllInstructions() {
List r = new ArrayList<>();
for(String className : instructionMap.keySet()) {
Map> methodMap = instructionMap.get(className);
for(List methodInstructions : methodMap.values()) {
r.addAll(methodInstructions);
}
}
return r;
}
/**
*
* logInstructionsIn
*
*
* @param className
* a {@link java.lang.String} object.
* @param methodName
* a {@link java.lang.String} object.
*/
public void logInstructionsIn(String className, String methodName) {
logger.debug("Printing instructions in " + className + "." + methodName + ":");
List instructions = getInstructionsIn(className, methodName);
if (instructions == null) {
logger.debug("..unknown method");
} else {
for (BytecodeInstruction instruction : instructions) {
logger.debug("\t" + instruction);
}
}
}
/**
*
* createFakeInstruction
*
*
* @param className
* a {@link java.lang.String} object.
* @param methodName
* a {@link java.lang.String} object.
* @return a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object.
*/
public BytecodeInstruction createFakeInstruction(String className, String methodName) {
AbstractInsnNode fakeNode = new InsnNode(Opcodes.NOP);
int instructionId = getInstructionsIn(className, methodName).size();
BytecodeInstruction instruction = new BytecodeInstruction(classLoader, className,
methodName, instructionId, -1, fakeNode);
registerInstruction(instruction);
return instruction;
}
/**
*
* clear
*
*/
public void clear() {
instructionMap.clear();
knownMethodNodes.clear();
}
public static void clearAll() {
BytecodeInstructionPool.instanceMap.clear();
}
/**
*
* clear
*
*
* @param className
* a {@link java.lang.String} object.
*/
public void clear(String className) {
instructionMap.remove(className);
}
public static void clearAll(String className) {
for (BytecodeInstructionPool pool : instanceMap.values()) {
pool.clear(className);
}
}
/**
*
* clear
*
*
* @param className
* a {@link java.lang.String} object.
* @param methodName
* a {@link java.lang.String} object.
*/
public void clear(String className, String methodName) {
if (instructionMap.containsKey(className))
instructionMap.get(className).remove(methodName);
}
public static void clearAll(String className, String methodName) {
for (BytecodeInstructionPool pool : instanceMap.values()) {
pool.clear(className, methodName);
}
}
/**
*
* forgetInstruction
*
*
* @param ins
* a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object.
* @return a boolean.
*/
public boolean forgetInstruction(BytecodeInstruction ins) {
if (!instructionMap.containsKey(ins.getClassName()))
return false;
if (!instructionMap.get(ins.getClassName()).containsKey(ins.getMethodName()))
return false;
return instructionMap.get(ins.getClassName()).get(ins.getMethodName()).remove(ins);
}
public int getFirstLineNumberOfMethod(String className, String methodName) {
if (instructionMap.get(className) == null)
throw new IllegalArgumentException("unknown class " + className);
if (instructionMap.get(className).get(methodName) == null)
throw new IllegalArgumentException("unknown method " + methodName
+ " in class " + className);
if (instructionMap.get(className).get(methodName).isEmpty())
throw new IllegalArgumentException("no instructions in method " + methodName
+ " in class " + className);
int r = Integer.MAX_VALUE;
for (BytecodeInstruction ins : instructionMap.get(className).get(methodName)) {
if (ins.getLineNumber() < r)
r = ins.getLineNumber();
}
return r;
}
public BytecodeInstruction getFirstInstructionAtLineNumber(String className, String methodName, int lineNumber) {
// TODO
if (instructionMap.get(className) == null)
return null;
if (instructionMap.get(className).get(methodName) == null)
return null;
if (instructionMap.get(className).get(methodName).isEmpty())
return null;
for (BytecodeInstruction ins : instructionMap.get(className).get(methodName)) {
if (ins.getLineNumber() == lineNumber)
return ins;
}
return null;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy