![JAR search and dependency download from the Maven repository](/logo.png)
org.evosuite.graphs.cfg.BasicBlock 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.io.Serializable;
import java.util.*;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import org.evosuite.graphs.GraphPool;
import org.evosuite.graphs.cdg.ControlDependenceGraph;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class is used to represent basic blocks in the control flow graph.
*
* A basic block is a list of instructions for which the following holds:
*
* Whenever control flow reaches the first instruction of this blocks list,
* control flow will pass through all the instructions of this list successively
* and not pass another instruction of the underlying method in the mean time.
* The first element in this blocks list does not have a parent in the CFG that
* can be prepended to the list and the same would still hold true Finally the
* last element in this list does not have a child inside the CFG that could be
* appended to the list such that the above still holds true
*
* In other words: - the first/last element of this blocks list has either 0 or
* >=2 parents/children in the CFG - every other element in the list has exactly
* 1 parent and exactly 1 child in the raw CFG
*
*
* Taken from:
*
* "Efficiently Computing Static Single Assignment Form and the Control
* Dependence Graph" RON CYTRON, JEANNE FERRANTE, BARRY K. ROSEN, and MARK N.
* WEGMAN IBM Research Division and F. KENNETH ZADECK Brown University 1991
*
* @see cfg.ActualControlFlowGraph
* @author Andre Mis
*/
public class BasicBlock implements Serializable, Iterable {
private static final long serialVersionUID = -3465486470017841484L;
private static Logger logger = LoggerFactory.getLogger(BasicBlock.class);
private static int blockCount = 0;
private int id = -1;
protected ClassLoader classLoader;
protected String className;
protected String methodName;
// experiment: since finding the control dependent branches in the CDG might
// take a little to long, we might want to remember them
private Set controlDependencies;
private Set controlDependentBranchIDs;
protected boolean isAuxiliaryBlock = false;
protected final List instructions = new ArrayList();
// DONE reference each BytecodeInstruction's BasicBlock at the instruction
// DONE determine ControlDependentBranches once for each BasicBlock, then
// ask BasicBloc, whenever instruction is asked
// TODO remember distance to each control dependent Branch in order to speed
// up ControlFlowDistance calculation even more
/**
*
* Constructor for BasicBlock.
*
*
* @param className
* a {@link java.lang.String} object.
* @param methodName
* a {@link java.lang.String} object.
* @param blockNodes
* a {@link java.util.List} object.
*/
public BasicBlock(ClassLoader classLoader, String className, String methodName,
List blockNodes) {
if (className == null || methodName == null || blockNodes == null)
throw new IllegalArgumentException("null given");
this.className = className;
this.methodName = methodName;
this.classLoader = classLoader;
setId();
setInstructions(blockNodes);
checkSanity();
}
/**
* Used by Entry- and ExitBlocks
*
* @param className
* a {@link java.lang.String} object.
* @param methodName
* a {@link java.lang.String} object.
*/
protected BasicBlock(String className, String methodName) {
if (className == null || methodName == null)
throw new IllegalArgumentException("null given");
this.className = className;
this.methodName = methodName;
this.isAuxiliaryBlock = true;
}
// CDs
/**
* Returns the ControlDependenceGraph of this instructions method
*
* Convenience method. Redirects the call to GraphPool.getCDG()
*
* @return a {@link org.evosuite.graphs.cdg.ControlDependenceGraph} object.
*/
public ControlDependenceGraph getCDG() {
ControlDependenceGraph myCDG = GraphPool.getInstance(classLoader).getCDG(className,
methodName);
if (myCDG == null)
throw new IllegalStateException(
"expect GraphPool to know CDG for every method for which an instruction is known");
return myCDG;
}
/**
* Returns all branchIds of Branches this instruction is directly control
* dependent on as determined by the ControlDependenceGraph for this
* instruction's method.
*
* If this instruction is control dependent on the root branch the id -1
* will be contained in this set
*
* @return a {@link java.util.Set} object.
*/
public Set getControlDependentBranchIds() {
ControlDependenceGraph myDependence = getCDG();
if (controlDependentBranchIDs == null) {
controlDependentBranchIDs = myDependence.getControlDependentBranchIds(this);
//be sure we can iterate over it deterministically
controlDependentBranchIDs =
controlDependentBranchIDs.stream().sorted().collect(Collectors.toCollection(LinkedHashSet::new));
}
return controlDependentBranchIDs;
}
/**
* Returns a cfg.Branch object for each branch this instruction is control
* dependent on as determined by the ControlDependenceGraph. If this
* instruction is only dependent on the root branch this method returns an
* empty set
*
* If this instruction is a Branch and it is dependent on itself - which can
* happen in loops for example - the returned set WILL contain this. If you
* do not need the full set in order to avoid loops, call
* getAllControlDependentBranches instead
*
* @return a {@link java.util.Set} object.
*/
public Set getControlDependencies() {
if (controlDependencies == null)
controlDependencies = getCDG().getControlDependentBranches(this);
// return new HashSet(controlDependentBranches);
return controlDependencies;
}
/**
*
* hasControlDependenciesSet
*
*
* @return a boolean.
*/
public boolean hasControlDependenciesSet() {
return controlDependencies != null;
}
// initialization
private void setInstructions(List blockNodes) {
for (BytecodeInstruction instruction : blockNodes) {
if (!appendInstruction(instruction))
throw new IllegalStateException(
"internal error while addind instruction to basic block list");
}
if (instructions.isEmpty())
throw new IllegalStateException(
"expect each basic block to contain at least one instruction");
}
protected boolean appendInstruction(BytecodeInstruction instruction) {
if (instruction == null)
throw new IllegalArgumentException("null given");
if (!className.equals(instruction.getClassName()))
throw new IllegalArgumentException(
"expect elements of a basic block to be inside the same class");
if (!methodName.equals(instruction.getMethodName()))
throw new IllegalArgumentException(
"expect elements of a basic block to be inside the same class");
if (instruction.hasBasicBlockSet())
throw new IllegalArgumentException(
"expect to get instruction without BasicBlock already set");
if (instructions.contains(instruction))
throw new IllegalArgumentException(
"a basic block can not contain the same element twice");
// not sure if this holds:
// .. apparently it doesn't. at least check
// fails for java2.util2.Pattern TODO
// BytecodeInstruction previousInstruction = getLastInstruction();
// if (previousInstruction != null
// && instruction.getInstructionId() < previousInstruction
// .getInstructionId())
// throw new IllegalStateException(
// "expect instructions in a basic block to be ordered by their instructionId");
instruction.setBasicBlock(this);
return instructions.add(instruction);
}
private void setId() {
blockCount++;
this.id = blockCount;
}
// retrieve information
/**
*
* containsInstruction
*
*
* @param instruction
* a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object.
* @return a boolean.
*/
public boolean containsInstruction(BytecodeInstruction instruction) {
if (instruction == null)
throw new IllegalArgumentException("null given");
return instructions.contains(instruction);
}
/**
*
* constainsInstruction
*
*
* @param insnNode
* a {@link org.objectweb.asm.tree.AbstractInsnNode} object.
* @return a boolean.
*/
public boolean constainsInstruction(AbstractInsnNode insnNode) {
for (BytecodeInstruction instruction : instructions) {
if (instruction.getASMNode().equals(insnNode))
return true;
}
return false;
}
/**
*
* getFirstInstruction
*
*
* @return a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object.
*/
public BytecodeInstruction getFirstInstruction() {
if (instructions.isEmpty())
return null;
return instructions.get(0);
}
/**
*
* getLastInstruction
*
*
* @return a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object.
*/
public BytecodeInstruction getLastInstruction() {
if (instructions.isEmpty())
return null;
return instructions.get(instructions.size() - 1);
}
/**
*
* getFirstLine
*
*
* @return a int.
*/
public int getFirstLine() {
for (BytecodeInstruction ins : instructions)
if (ins.hasLineNumberSet())
return ins.getLineNumber();
return -1;
}
/**
*
* getLastLine
*
*
* @return a int.
*/
public int getLastLine() {
int r = -1;
for (BytecodeInstruction ins : instructions)
if (ins.hasLineNumberSet())
r = ins.getLineNumber();
return r;
}
/**
*
* getName
*
*
* @return a {@link java.lang.String} object.
*/
public String getName() {
return (isAuxiliaryBlock ? "aux" : "") + "BasicBlock " + id;
// +" - "+methodName;
}
/**
*
* Getter for the field className
.
*
*
* @return a {@link java.lang.String} object.
*/
public String getClassName() {
return className;
}
/**
*
* Getter for the field methodName
.
*
*
* @return a {@link java.lang.String} object.
*/
public String getMethodName() {
return methodName;
}
/**
*
* explain
*
*
* @return a {@link java.lang.String} object.
*/
public String explain() {
StringBuilder r = new StringBuilder();
r.append(getName() + ":\n");
int i = 0;
for (BytecodeInstruction instruction : instructions) {
i++;
r.append("\t" + i + ")\t" + instruction.toString() + "\n");
}
return r.toString();
}
// inherited from Object
/** {@inheritDoc} */
@Override
public String toString() {
String r = "BB" + id;
if (instructions.size() < 5)
for (BytecodeInstruction ins : instructions)
r = r.trim() + " " + ins.getInstructionType();
else
r += " " + getFirstInstruction().getInstructionType() + " ... "
+ getLastInstruction().getInstructionType();
int startLine = getFirstLine();
int endLine = getLastLine();
r += " l" + (startLine == -1 ? "?" : startLine + "");
r += "-l" + (endLine == -1 ? "?" : endLine + "");
return r;
}
// sanity check
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((className == null) ? 0 : className.hashCode());
result = prime * result + id;
result = prime * result
+ ((instructions == null) ? 0 : instructions.hashCode());
result = prime * result + (isAuxiliaryBlock ? 1231 : 1237);
result = prime * result
+ ((methodName == null) ? 0 : methodName.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof BasicBlock))
return false;
BasicBlock other = (BasicBlock) obj;
if (id != other.id)
return false;
if (className == null) {
if (other.className != null)
return false;
} else if (!className.equals(other.className))
return false;
if (methodName == null) {
if (other.methodName != null)
return false;
} else if (!methodName.equals(other.methodName))
return false;
if (instructions == null) {
if (other.instructions != null)
return false;
} else if (!instructions.equals(other.instructions))
return false;
if (isEntryBlock() != other.isEntryBlock())
return false;
if (isExitBlock() != other.isExitBlock())
return false;
return true;
}
/**
*
* checkSanity
*
*/
public void checkSanity() {
logger.debug("checking sanity of " + toString());
// TODO
// not true, there are branches that don't really jump
// for example if you have no statement in a then-part:
// if (exp) { ; }
// you will not have a second outgoing edge for that if
// for(BytecodeInstruction instruction : instructions) {
// if (!instruction.equals(getLastInstruction())
// && instruction.isActualBranch())
// throw new IllegalStateException(
// "expect actual branches to always end a basic block: "+instruction.toString()+" \n"+explain());
// }
// TODO handle specialBlocks
}
/**
*
* isEntryBlock
*
*
* @return a boolean.
*/
public boolean isEntryBlock() {
return false;
}
/**
*
* isExitBlock
*
*
* @return a boolean.
*/
public boolean isExitBlock() {
return false;
}
/* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
/** {@inheritDoc} */
@Override
public Iterator iterator() {
return instructions.iterator();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy