org.evosuite.graphs.cfg.BytecodeInstruction 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 org.evosuite.coverage.branch.Branch;
import org.evosuite.coverage.branch.BranchPool;
import org.evosuite.coverage.dataflow.DefUsePool;
import org.evosuite.graphs.GraphPool;
import org.evosuite.graphs.cdg.ControlDependenceGraph;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.*;
import org.objectweb.asm.tree.analysis.SourceValue;
import java.io.Serializable;
import java.util.Set;
/**
* Internal representation of a BytecodeInstruction
*
* Extends ASMWrapper which serves as an interface to the ASM library.
*
* Known super classes are DefUse and Branch which yield specific functionality
* needed to achieve theirs respective coverage criteria
*
* Old: Node of the control flow graph
*
* @author Gordon Fraser, Andre Mis
*/
public class BytecodeInstruction extends ASMWrapper implements Serializable,
Comparable {
private static final long serialVersionUID = 3630449183355518857L;
// identification of a byteCode instruction inside EvoSuite
protected ClassLoader classLoader;
protected String className;
protected String methodName;
protected int instructionId;
protected int bytecodeOffset;
// auxiliary information
private int lineNumber = -1;
// experiment: also searching through all CFG nodes in order to determine an
// instruction BasicBlock might be a little to expensive too just to safe
// space for one reference
private BasicBlock basicBlock;
/**
* Generates a ByteCodeInstruction instance that represents a byteCode
* instruction as indicated by the given ASMNode in the given method and
* class
*
* @param className
* a {@link java.lang.String} object.
* @param methodName
* a {@link java.lang.String} object.
* @param instructionId
* a int.
* @param bytecodeOffset
* a int.
* @param asmNode
* a {@link org.objectweb.asm.tree.AbstractInsnNode} object.
*/
public BytecodeInstruction(ClassLoader classLoader, String className,
String methodName, int instructionId, int bytecodeOffset, AbstractInsnNode asmNode) {
if (className == null || methodName == null || asmNode == null)
throw new IllegalArgumentException("null given");
if (instructionId < 0)
throw new IllegalArgumentException(
"expect instructionId to be positive, not " + instructionId);
this.instructionId = instructionId;
this.bytecodeOffset = bytecodeOffset;
this.asmNode = asmNode;
this.classLoader = classLoader;
setClassName(className);
setMethodName(methodName);
}
/**
* Can represent any byteCode instruction
*
* @param wrap
* a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object.
*/
public BytecodeInstruction(BytecodeInstruction wrap) {
this(wrap.classLoader, wrap.className, wrap.methodName, wrap.instructionId,
wrap.bytecodeOffset, wrap.asmNode, wrap.lineNumber, wrap.basicBlock);
this.frame = wrap.frame;
}
/**
*
* Constructor for BytecodeInstruction.
*
*
* @param className
* a {@link java.lang.String} object.
* @param methodName
* a {@link java.lang.String} object.
* @param instructionId
* a int.
* @param bytecodeOffset
* a int.
* @param asmNode
* a {@link org.objectweb.asm.tree.AbstractInsnNode} object.
* @param lineNumber
* a int.
* @param basicBlock
* a {@link org.evosuite.graphs.cfg.BasicBlock} object.
*/
public BytecodeInstruction(ClassLoader classLoader, String className,
String methodName, int instructionId, int bytecodeOffset, AbstractInsnNode asmNode,
int lineNumber, BasicBlock basicBlock) {
this(classLoader, className, methodName, instructionId, bytecodeOffset, asmNode,
lineNumber);
this.basicBlock = basicBlock;
}
/**
*
* Constructor for BytecodeInstruction.
*
*
* @param className
* a {@link java.lang.String} object.
* @param methodName
* a {@link java.lang.String} object.
* @param instructionId
* a int.
* @param bytecodeOffset
* a int.
* @param asmNode
* a {@link org.objectweb.asm.tree.AbstractInsnNode} object.
* @param lineNumber
* a int.
*/
public BytecodeInstruction(ClassLoader classLoader, String className,
String methodName, int instructionId, int bytecodeOffset, AbstractInsnNode asmNode,
int lineNumber) {
this(classLoader, className, methodName, instructionId, bytecodeOffset, asmNode);
if (lineNumber != -1)
setLineNumber(lineNumber);
}
// getter + setter
private void setMethodName(String methodName) {
if (methodName == null)
throw new IllegalArgumentException("null given");
this.methodName = methodName;
}
private void setClassName(String className) {
if (className == null)
throw new IllegalArgumentException("null given");
this.className = className;
}
/**
*
* setCFGFrame
*
*
* @param frame
* a {@link org.evosuite.graphs.cfg.CFGFrame} object.
*/
public void setCFGFrame(CFGFrame frame) {
this.frame = frame;
}
// --- Field Management ---
/** {@inheritDoc} */
@Override
public int getInstructionId() {
return instructionId;
}
/**
*
* getBytecodeOffset
*
*
* @return a int.
*/
public int getBytecodeOffset() {
return bytecodeOffset;
}
/** {@inheritDoc} */
@Override
public String getMethodName() {
return methodName;
}
/**
*
* Getter for the field className
.
*
*
* @return a {@link java.lang.String} object.
*/
@Override
public String getClassName() {
return className;
}
/**
*
* getName
*
*
* @return a {@link java.lang.String} object.
*/
public String getName() {
return "BytecodeInstruction " + instructionId + " in " + methodName;
}
/**
* Return's the BasicBlock that contain's this instruction in it's CFG.
*
* If no BasicBlock containing this instruction was created yet, null is
* returned.
*
* @return a {@link org.evosuite.graphs.cfg.BasicBlock} object.
*/
public BasicBlock getBasicBlock() {
if (!hasBasicBlockSet())
retrieveBasicBlock();
return basicBlock;
}
private void retrieveBasicBlock() {
if (basicBlock == null)
basicBlock = getActualCFG().getBlockOf(this);
}
/**
* Once the CFG has been asked for this instruction's BasicBlock it sets
* this instance's internal basicBlock field.
*
* @param block
* a {@link org.evosuite.graphs.cfg.BasicBlock} object.
*/
public void setBasicBlock(BasicBlock block) {
if (block == null)
throw new IllegalArgumentException("null given");
if (!block.getClassName().equals(getClassName())
|| !block.getMethodName().equals(getMethodName()))
throw new IllegalArgumentException(
"expect block to be for the same method and class as this instruction");
if (this.basicBlock != null)
throw new IllegalArgumentException(
"basicBlock already set! not allowed to overwrite");
this.basicBlock = block;
}
/**
* Checks whether this instance's basicBlock has already been set by the CFG
* or
*
* @return a boolean.
*/
public boolean hasBasicBlockSet() {
return basicBlock != null;
}
/** {@inheritDoc} */
@Override
public int getLineNumber() {
if (lineNumber == -1 && isLineNumber())
retrieveLineNumber();
return lineNumber;
}
/**
*
* Setter for the field lineNumber
.
*
*
* @param lineNumber
* a int.
*/
public void setLineNumber(int lineNumber) {
if (lineNumber <= 0)
throw new IllegalArgumentException(
"expect lineNumber value to be positive");
if (isLabel())
return;
if (isLineNumber()) {
int asmLine = super.getLineNumber();
// sanity check
if (lineNumber != -1 && asmLine != lineNumber)
throw new IllegalStateException(
"linenumber instruction has lineNumber field set to a value different from instruction linenumber");
this.lineNumber = asmLine;
} else {
this.lineNumber = lineNumber;
}
}
/**
* At first, if this instruction constitutes a line number instruction this
* method tries to retrieve the lineNumber from the underlying asmNode and
* set the lineNumber field to the value given by the asmNode.
*
* This can lead to an IllegalStateException, should the lineNumber field
* have been set to another value previously
*
* After that, if the lineNumber field is still not initialized, this method
* returns false Otherwise it returns true
*
* @return a boolean.
*/
public boolean hasLineNumberSet() {
retrieveLineNumber();
return lineNumber != -1;
}
/**
* If the underlying ASMNode is a LineNumberNode the lineNumber field of
* this instance will be set to the lineNumber contained in that
* LineNumberNode
*
* Should the lineNumber field have been set to a value different from that
* contained in the asmNode, this method throws an IllegalStateExeption
*/
private void retrieveLineNumber() {
if (isLineNumber()) {
int asmLine = super.getLineNumber();
// sanity check
if (this.lineNumber != -1 && asmLine != this.lineNumber)
throw new IllegalStateException(
"lineNumber field was manually set to a value different from the actual lineNumber contained in LineNumberNode");
this.lineNumber = asmLine;
}
}
// --- graph section ---
/**
* Returns the ActualControlFlowGraph of this instructions method
*
* Convenience method. Redirects the call to GraphPool.getActualCFG()
*
* @return a {@link org.evosuite.graphs.cfg.ActualControlFlowGraph} object.
*/
public ActualControlFlowGraph getActualCFG() {
ActualControlFlowGraph myCFG = GraphPool.getInstance(classLoader).getActualCFG(className,
methodName);
if (myCFG == null)
throw new IllegalStateException(
"expect GraphPool to know CFG for every method for which an instruction is known");
return myCFG;
}
/**
* Returns the RawControlFlowGraph of this instructions method
*
* Convenience method. Redirects the call to GraphPool.getRawCFG()
*
* @return a {@link org.evosuite.graphs.cfg.RawControlFlowGraph} object.
*/
public RawControlFlowGraph getRawCFG() {
RawControlFlowGraph myCFG = GraphPool.getInstance(classLoader).getRawCFG(className,
methodName);
if (myCFG == null)
throw new IllegalStateException(
"expect GraphPool to know CFG for every method for which an instruction is known");
return myCFG;
}
/**
* 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;
}
// --- CDG-Section ---
/**
* 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() {
BasicBlock myBlock = getBasicBlock();
// return new
// HashSet(myBlock.getControlDependencies());
return myBlock.getControlDependencies();
}
/**
* This method returns a random Branch among all Branches this instruction
* is control dependent on
*
* If this instruction is only dependent on the root branch, this method
* returns null
*
* Since EvoSuite was previously unable to detect multiple control
* dependencies for one instruction this method serves as a backwards
* compatibility bridge
*
* @return a {@link org.evosuite.coverage.branch.Branch} object.
*/
public Branch getControlDependentBranch() {
Set controlDependentBranches = getControlDependencies();
for (ControlDependency cd : controlDependentBranches)
return cd.getBranch();
return null; // root branch
}
/**
* 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() {
BasicBlock myBlock = getBasicBlock();
return myBlock.getControlDependentBranchIds();
}
/**
* Determines whether or not this instruction is control dependent on the
* root branch of it's method by calling getControlDependentBranchIds() to
* see if the return contains -1.
*
* @return a boolean.
*/
public boolean isRootBranchDependent() {
return getControlDependencies().isEmpty();
}
/**
* This method returns a random branchId among all branchIds this
* instruction is control dependent on.
*
* This method returns -1 if getControlDependentBranch() returns null,
* otherwise that Branch's branchId is returned
*
* Note: The returned branchExpressionValue comes from the same Branch
* getControlDependentBranch() and getControlDependentBranchId() return
*
* Since EvoSuite was previously unable to detect multiple control
* dependencies for one instruction this method serves as a backwards
* compatibility bridge
*
* @return a int.
*/
public int getControlDependentBranchId() {
Branch b = getControlDependentBranch();
if (b == null)
return -1;
return b.getActualBranchId();
}
/**
* This method returns the branchExpressionValue from a random Branch among
* all Branches this instruction is control dependent on.
*
* This method returns true if getControlDependentBranch() returns null,
* otherwise that Branch's branchExpressionValue is returned
*
* Note: The returned branchExpressionValue comes from the same Branch
* getControlDependentBranch() and getControlDependentBranchId() return
*
* Since EvoSuite was previously unable to detect multiple control
* dependencies for one instruction this method serves as a backwards
* compatibility bridge
*
* @return a boolean.
*/
public boolean getControlDependentBranchExpressionValue() {
Branch b = getControlDependentBranch();
return getBranchExpressionValue(b);
}
/**
*
* getBranchExpressionValue
*
*
* @param b
* a {@link org.evosuite.coverage.branch.Branch} object.
* @return a boolean.
*/
public boolean getBranchExpressionValue(Branch b) {
if (!isDirectlyControlDependentOn(b))
throw new IllegalArgumentException(
"this method can only be called for branches that this instruction is directly control dependent on.");
if (b == null)
return true; // root branch special case
return getControlDependency(b).getBranchExpressionValue();
}
/**
* Determines whether this BytecodeInstruction is directly control dependent
* on the given Branch. Meaning within this instruction CDG there is an
* incoming ControlFlowEdge to this instructions BasicBlock holding the
* given Branch as it's branchInstruction.
*
* If the given Branch is null, this method checks whether the this
* instruction is control dependent on the root branch of it's method.
*
* @param branch
* a {@link org.evosuite.coverage.branch.Branch} object.
* @return a boolean.
*/
public boolean isDirectlyControlDependentOn(Branch branch) {
if (branch == null)
return getControlDependentBranchIds().contains(-1);
for (ControlDependency cd : getControlDependencies())
if (cd.getBranch().equals(branch))
return true;
return false;
}
/**
*
* getControlDependency
*
*
* @param branch
* a {@link org.evosuite.coverage.branch.Branch} object.
* @return a {@link org.evosuite.graphs.cfg.ControlDependency} object.
*/
public ControlDependency getControlDependency(Branch branch) {
if (!isDirectlyControlDependentOn(branch))
throw new IllegalArgumentException(
"instruction not directly control dependent on given branch");
for (ControlDependency cd : getControlDependencies())
if (cd.getBranch().equals(branch))
return cd;
throw new IllegalStateException(
"expect getControlDependencies() to contain a CD for each branch that isDirectlyControlDependentOn() returns true on");
}
// /**
// * WARNING: better don't user this method right now TODO
// *
// * Determines whether the CFGVertex is transitively control dependent on
// the
// * given Branch
// *
// * A CFGVertex is transitively control dependent on a given Branch if the
// * Branch and the vertex are in the same method and the vertex is either
// * directly control dependent on the Branch - look at
// * isDirectlyControlDependentOn(Branch) - or the CFGVertex of the control
// * dependent branch of this CFGVertex is transitively control dependent on
// * the given branch.
// *
// */
// public boolean isTransitivelyControlDependentOn(Branch branch) {
// if (!getClassName().equals(branch.getClassName()))
// return false;
// if (!getMethodName().equals(branch.getMethodName()))
// return false;
//
// // TODO: this method does not take into account, that there might be
// // multiple branches this instruction is control dependent on
//
// BytecodeInstruction vertexHolder = this;
// do {
// if (vertexHolder.isDirectlyControlDependentOn(branch))
// return true;
// vertexHolder = vertexHolder.getControlDependentBranch()
// .getInstruction();
// } while (vertexHolder != null);
//
// return false;
// }
// /**
// * WARNING: better don't user this method right now TODO
// *
// * Determines the number of branches that have to be passed in order to
// pass
// * this CFGVertex
// *
// * Used to determine TestFitness difficulty
// */
/**
*
* getCDGDepth
*
*
* @return a int.
*/
public int getCDGDepth() {
int min = Integer.MAX_VALUE;
Set dependencies = getControlDependencies();
if (dependencies.isEmpty())
min = 1;
for (ControlDependency dependency : dependencies) {
int depth = getCDG().getControlDependenceDepth(dependency);
if (depth < min)
min = depth;
}
return min;
/*
* // TODO: this method does not take into account, that there might be
* // multiple branches this instruction is control dependent on Branch
* current = getControlDependentBranch(); int r = 1; while (current !=
* null) { r++; current =
* current.getInstruction().getControlDependentBranch(); } return r;
*/
}
// String methods
/**
*
* explain
*
*
* @return a {@link java.lang.String} object.
*/
public String explain() {
if (isBranch()) {
if (BranchPool.getInstance(classLoader).isKnownAsBranch(this)) {
Branch b = BranchPool.getInstance(classLoader).getBranchForInstruction(this);
if (b == null)
throw new IllegalStateException(
"expect BranchPool to be able to return Branches for instructions fullfilling BranchPool.isKnownAsBranch()");
return "Branch " + b.getActualBranchId() + " - "
+ getInstructionType();
}
return "UNKNOWN Branch I" + instructionId + " "
+ getInstructionType() +", jump to "+((JumpInsnNode)asmNode).label.getLabel();
// + " - " + ((JumpInsnNode) asmNode).label.getLabel();
}
return getASMNodeString();
}
/**
*
* getASMNodeString
*
*
* @return a {@link java.lang.String} object.
*/
public String getASMNodeString() {
String type = getType();
String opcode = getInstructionType();
String stack = "";
if (frame == null)
stack = "null";
else
for (int i = 0; i < frame.getStackSize(); i++) {
stack += frame.getStack(i) + ",";
}
if (asmNode instanceof LabelNode) {
return "LABEL " + ((LabelNode) asmNode).getLabel().toString();
} else if (asmNode instanceof FieldInsnNode)
return "Field" + " " + ((FieldInsnNode) asmNode).owner + "."
+ ((FieldInsnNode) asmNode).name + " Type=" + type
+ ", Opcode=" + opcode;
else if (asmNode instanceof FrameNode)
return "Frame" + " " + asmNode.getOpcode() + " Type=" + type
+ ", Opcode=" + opcode;
else if (asmNode instanceof IincInsnNode)
return "IINC " + ((IincInsnNode) asmNode).var + " Type=" + type
+ ", Opcode=" + opcode;
else if (asmNode instanceof InsnNode)
return "" + opcode;
else if (asmNode instanceof IntInsnNode)
return "INT " + ((IntInsnNode) asmNode).operand + " Type=" + type
+ ", Opcode=" + opcode;
else if (asmNode instanceof MethodInsnNode)
return opcode + " " + ((MethodInsnNode) asmNode).owner + "." + ((MethodInsnNode) asmNode).name + ((MethodInsnNode) asmNode).desc;
else if (asmNode instanceof JumpInsnNode)
return "JUMP " + ((JumpInsnNode) asmNode).label.getLabel()
+ " Type=" + type + ", Opcode=" + opcode + ", Stack: "
+ stack + " - Line: " + lineNumber;
else if (asmNode instanceof LdcInsnNode)
return "LDC " + ((LdcInsnNode) asmNode).cst + " Type=" + type; // +
// ", Opcode=";
// + opcode; // cst starts with mutationid if
// this is location of mutation
else if (asmNode instanceof LineNumberNode)
return "LINE " + " " + ((LineNumberNode) asmNode).line;
else if (asmNode instanceof LookupSwitchInsnNode)
return "LookupSwitchInsnNode" + " " + asmNode.getOpcode()
+ " Type=" + type + ", Opcode=" + opcode;
else if (asmNode instanceof MultiANewArrayInsnNode)
return "MULTIANEWARRAY " + " " + asmNode.getOpcode() + " Type="
+ type + ", Opcode=" + opcode;
else if (asmNode instanceof TableSwitchInsnNode)
return "TableSwitchInsnNode" + " " + asmNode.getOpcode() + " Type="
+ type + ", Opcode=" + opcode;
else if (asmNode instanceof TypeInsnNode) {
switch(asmNode.getOpcode()) {
case Opcodes.NEW:
return "NEW " + ((TypeInsnNode) asmNode).desc;
case Opcodes.ANEWARRAY:
return "ANEWARRAY " + ((TypeInsnNode) asmNode).desc;
case Opcodes.CHECKCAST:
return "CHECKCAST " + ((TypeInsnNode) asmNode).desc;
case Opcodes.INSTANCEOF:
return "INSTANCEOF " + ((TypeInsnNode) asmNode).desc;
default:
return "Unknown node" + " Type=" + type + ", Opcode=" + opcode;
}
}
// return "TYPE " + " " + node.getOpcode() + " Type=" + type
// + ", Opcode=" + opcode;
else if (asmNode instanceof VarInsnNode)
return opcode + " " + ((VarInsnNode) asmNode).var;
else
return "Unknown node" + " Type=" + type + ", Opcode=" + opcode;
}
/**
*
* printFrameInformation
*
*/
public void printFrameInformation() {
System.out.println("Frame STACK:");
for (int i = 0; i < frame.getStackSize(); i++) {
SourceValue v = (SourceValue) frame.getStack(i);
System.out.print(" " + i + "(" + v.insns.size() + "): ");
for (Object n : v.insns) {
AbstractInsnNode node = (AbstractInsnNode) n;
BytecodeInstruction ins = BytecodeInstructionPool.getInstance(classLoader).getInstruction(className,
methodName,
node);
System.out.print(ins.toString() + ", ");
}
System.out.println();
}
System.out.println("Frame LOCALS:");
for (int i = 1; i < frame.getLocals(); i++) {
SourceValue v = (SourceValue) frame.getLocal(i);
System.out.print(" " + i + "(" + v.insns.size() + "): ");
for (Object n : v.insns) {
AbstractInsnNode node = (AbstractInsnNode) n;
BytecodeInstruction ins = BytecodeInstructionPool.getInstance(classLoader).getInstruction(className,
methodName,
node);
System.out.print(ins.toString() + ", ");
}
System.out.println();
}
}
// --- Inherited from Object ---
/** {@inheritDoc} */
@Override
public String toString() {
String r = "I" + instructionId;
r += " (" + + bytecodeOffset + ")";
r += " " + explain();
if (hasLineNumberSet() && !isLineNumber())
r += " l" + getLineNumber();
return r;
}
/**
* Convenience method:
*
* If this instruction is known by the BranchPool to be a Branch, you can
* call this method in order to retrieve the corresponding Branch object
* registered within the BranchPool.
*
* Otherwise this method will return null;
*
* @return a {@link org.evosuite.coverage.branch.Branch} object.
*/
public Branch toBranch() {
try {
return BranchPool.getInstance(classLoader).getBranchForInstruction(this);
} catch (Exception e) {
return null;
}
}
/**
*
* proceedsOwnConstructorInvocation
*
*
* @return a boolean.
*/
public boolean proceedsOwnConstructorInvocation() {
RawControlFlowGraph cfg = getRawCFG();
for (BytecodeInstruction other : cfg.vertexSet())
if (other.isConstructorInvocation()
&& other.isMethodCallOnSameObject())
if (getInstructionId() < other.getInstructionId())
return true;
return false;
}
/**
*
* isWithinConstructor
*
*
* @return a boolean.
*/
public boolean isWithinConstructor() {
return getMethodName().startsWith("");
}
/**
*
* isLastInstructionInMethod
*
*
* @return a boolean.
*/
public boolean isLastInstructionInMethod() {
return equals(getRawCFG().getInstructionWithBiggestId());
}
/**
*
* canBeExitPoint
*
*
* @return a boolean.
*/
public boolean canBeExitPoint() {
return canReturnFromMethod() || isLastInstructionInMethod();
}
/**
* Returns the RawCFG of the method called by this instruction
*
* @return a {@link org.evosuite.graphs.cfg.RawControlFlowGraph} object.
*/
public RawControlFlowGraph getCalledCFG() {
if (!isMethodCall())
return null;
return GraphPool.getInstance(classLoader).getRawCFG(getCalledMethodsClass(),
getCalledMethod());
}
/**
* Determines whether this instruction calls a method on its own Object
* ('this')
*
* This is done using the getSourceOfMethodInvocationInstruction() method
* and checking if the return of that method loads this using loadsReferenceToThis()
*
* @return a boolean.
*/
public boolean isMethodCallOnSameObject() {
BytecodeInstruction srcInstruction = getSourceOfMethodInvocationInstruction();
if (srcInstruction == null)
return false;
return srcInstruction.loadsReferenceToThis();
}
/**
* Determines whether this instruction calls a method on a field variable
*
* This is done using the getSourceOfMethodInvocationInstruction() method
* and checking if the return of that method is a field use instruction
*
* @return a boolean.
*/
public boolean isMethodCallOfField() {
if(!this.isMethodCall())
return false;
if (this.isInvokeStatic())
return false;
// If the instruction belongs to static initialization block of the
// class, then the method call cannot be done on a fields.
if (this.methodName.contains(""))
return false;
BytecodeInstruction srcInstruction = getSourceOfMethodInvocationInstruction();
if (srcInstruction == null)
return false;
//is a field use? But field uses are also "GETSTATIC"
if (srcInstruction.isFieldNodeUse()) {
//is static? if not, return yes. This control is not necessary in theory, but you never know...
if (srcInstruction.isStaticDefUse()) {
//is static, check if the name of the class that contain the static field is equals to the current class name
//if is equals, return true, otherwise we are in a case where we are calling a field over an external static class
//e.g. System.out
if (srcInstruction.asmNode instanceof FieldInsnNode) {
String classNameField = ((FieldInsnNode) srcInstruction.asmNode).owner;
classNameField = classNameField.replace('/', '.');
if (classNameField.equals(className)) {
return true;
}
}
} else {
return true;
}
}
return false;
}
/**
* Determines the name of the field variable this method call is invoked on
*
* This is done using the getSourceOfMethodInvocationInstruction() method
* and returning its variable name
*
* @return a {@link java.lang.String} object.
*/
@Override
public String getFieldMethodCallName() {
BytecodeInstruction srcInstruction = getSourceOfMethodInvocationInstruction();
if (srcInstruction == null)
return null;
return srcInstruction.getVariableName();
}
/**
* If this is a method call instruction this method will return the
* instruction that loaded the reference of the Object the method is invoked
* onto the stack.
*
* This is done using getSourceOfStackInstruction()
*
* The reference is found on top of the stack minus the number of the called
* methods argument
*/
public BytecodeInstruction getSourceOfMethodInvocationInstruction() {
if (!isMethodCall())
return null;
// the object on which this method is called is on top of the stack
// minus the number of arguments the called method has
return getSourceOfStackInstruction(getCalledMethodsArgumentCount());
}
/**
* If this instruction is an array instruction this method will return the
* BytecodeInstruction that loaded the reference of the array onto the
* stack.
*
* This is done using getSourceOfStackMethod()
*
* The reference is found on top of the stack minus two
*/
public BytecodeInstruction getSourceOfArrayReference() {
if(isArrayStoreInstruction()) {
// when reaching an array store instruction the stack should end in
// ,,. so the array reference is on top of the
// stack minus two
return getSourceOfStackInstruction(2);
} else if(isArrayLoadInstruction()) {
// when reaching an array store instruction the stack should end in
// ,. so the array reference is on top of the
// stack minus one
return getSourceOfStackInstruction(1);
} else {
return null;
}
}
/**
* This method returns the BytecodeInstruction that loaded the reference
* which is located on top of the stack minus positionFromTop when this
* instruction is executed.
*
* This is done using the CFGFrame created by the SourceInterpreter() of the
* BytecodeAnalyzer via the CFGGenerator
*
* Note that this method may return null. This can happen when aliasing is
* involved. For example for method invocations on objects this can happen
* when you first store the object in a local variable and then call a
* method on that variable
*
* see PairTestClass.sourceCallerTest() for an even worse example.
*
* TODO: this could be done better by following the SourceValues even
* further.
*/
public BytecodeInstruction getSourceOfStackInstruction(int positionFromTop) {
if (frame == null)
throw new IllegalStateException(
"expect each BytecodeInstruction to have its CFGFrame set");
int stackPos = frame.getStackSize() - (1 + positionFromTop);
if (stackPos < 0){
StackTraceElement[] se = new Throwable().getStackTrace();
int t=0;
System.out.println("Stack trace: ");
while(t
* isCallToPublicMethod
*
*
* @return a boolean.
*/
public boolean isCallToPublicMethod() {
if (!isMethodCall())
return false;
if (getCalledCFG() == null) {
// TODO not sure if I am supposed to throw an Exception at this
// point
return false;
}
return getCalledCFG().isPublicMethod();
}
/**
*
* isCallToStaticMethod
*
*
* @return a boolean.
*/
public boolean isCallToStaticMethod() {
if (!isMethodCall())
return false;
if (getCalledCFG() == null) {
// TODO not sure if I am supposed to throw an Exception at this
// point
return false;
}
return getCalledCFG().isStaticMethod();
}
/**
*
* canBeInstrumented
*
*
* @return a boolean.
*/
public boolean canBeInstrumented() {
if (isWithinConstructor() && proceedsOwnConstructorInvocation()) {
// System.out.println("i cant be instrumented "+toString());
return false;
}
return true;
}
/** {@inheritDoc} */
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((className == null) ? 0 : className.hashCode());
result = prime * result + instructionId;
result = prime * result
+ ((methodName == null) ? 0 : methodName.hashCode());
return result;
}
/** {@inheritDoc} */
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
BytecodeInstruction other = (BytecodeInstruction) obj;
if (className == null) {
if (other.className != null)
return false;
} else if (!className.equals(other.className))
return false;
if (instructionId != other.instructionId)
return false;
if (methodName == null) {
if (other.methodName != null)
return false;
} else if (!methodName.equals(other.methodName))
return false;
return true;
}
// inherited from Object
/*
* (non-Javadoc)
*
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
@Override
public int compareTo(BytecodeInstruction o) {
return getLineNumber() - o.getLineNumber();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy