edu.umd.cs.findbugs.SelfCalls Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spotbugs Show documentation
Show all versions of spotbugs Show documentation
SpotBugs: Because it's easy!
The newest version!
/*
* FindBugs - Find bugs in Java programs
* Copyright (C) 2003,2004 University of Maryland
*
* This library 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 2.1 of the License, or (at your option) any later version.
*
* This library 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 General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package edu.umd.cs.findbugs;
import java.util.HashSet;
import java.util.Iterator;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.INVOKESTATIC;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InvokeInstruction;
import org.apache.bcel.generic.MONITORENTER;
import org.apache.bcel.generic.MONITOREXIT;
import org.apache.bcel.generic.MethodGen;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import edu.umd.cs.findbugs.ba.BasicBlock;
import edu.umd.cs.findbugs.ba.CFG;
import edu.umd.cs.findbugs.ba.CFGBuilderException;
import edu.umd.cs.findbugs.ba.ClassContext;
/**
* Build a call graph of the self calls in a class.
*/
public class SelfCalls {
private static final Logger LOG = LoggerFactory.getLogger(SelfCalls.class);
private final ClassContext classContext;
private final CallGraph callGraph;
private final HashSet calledMethodSet;
private boolean hasSynchronization;
/**
* Constructor.
*
* @param classContext
* the ClassContext for the class
*/
public SelfCalls(ClassContext classContext) {
this.classContext = classContext;
this.callGraph = new CallGraph();
this.calledMethodSet = new HashSet<>();
this.hasSynchronization = false;
}
/**
* Find the self calls.
*/
public void execute() throws CFGBuilderException {
JavaClass jclass = classContext.getJavaClass();
Method[] methods = jclass.getMethods();
LOG.debug("Class has {} methods", methods.length);
// Add call graph nodes for all methods
for (Method method : methods) {
callGraph.addNode(method);
}
LOG.debug("Added {} nodes to graph", callGraph.getNumVertices());
// Scan methods for self calls
for (Method method : methods) {
MethodGen mg = classContext.getMethodGen(method);
if (mg == null) {
continue;
}
scan(callGraph.getNodeForMethod(method));
}
LOG.debug("Found {} self calls", callGraph.getNumEdges());
}
/**
* Get the self call graph for the class.
*/
public CallGraph getCallGraph() {
return callGraph;
}
/**
* Get an Iterator over self-called methods.
*/
public Iterator calledMethodIterator() {
return calledMethodSet.iterator();
}
/**
* Determine whether we are interested in calls for the given method.
* Subclasses may override. The default version returns true for every
* method.
*
* @param method
* the method
* @return true if we want call sites for the method, false if not
*/
public boolean wantCallsFor(Method method) {
return true;
}
/**
* Get an Iterator over all self call sites.
*/
public Iterator callSiteIterator() {
return new Iterator() {
private final Iterator iter = callGraph.edgeIterator();
@Override
public boolean hasNext() {
return iter.hasNext();
}
@Override
public CallSite next() {
return iter.next().getCallSite();
}
@Override
public void remove() {
iter.remove();
}
};
}
/**
* Does this class contain any explicit synchronization?
*/
public boolean hasSynchronization() {
return hasSynchronization;
}
/**
* Scan a method for self call sites.
*
* @param node
* the CallGraphNode for the method to be scanned
*/
private void scan(CallGraphNode node) throws CFGBuilderException {
Method method = node.getMethod();
CFG cfg = classContext.getCFG(method);
if (method.isSynchronized()) {
hasSynchronization = true;
}
Iterator i = cfg.blockIterator();
while (i.hasNext()) {
BasicBlock block = i.next();
Iterator j = block.instructionIterator();
while (j.hasNext()) {
InstructionHandle handle = j.next();
Instruction ins = handle.getInstruction();
if (ins instanceof InvokeInstruction) {
InvokeInstruction inv = (InvokeInstruction) ins;
Method called = isSelfCall(inv);
if (called != null) {
// Add edge to call graph
CallSite callSite = new CallSite(method, block, handle);
callGraph.createEdge(node, callGraph.getNodeForMethod(called), callSite);
// Add to called method set
calledMethodSet.add(called);
}
} else if (ins instanceof MONITORENTER || ins instanceof MONITOREXIT) {
hasSynchronization = true;
}
}
}
}
/**
* Is the given instruction a self-call?
*/
private Method isSelfCall(InvokeInstruction inv) {
ConstantPoolGen cpg = classContext.getConstantPoolGen();
JavaClass jclass = classContext.getJavaClass();
String calledClassName = inv.getClassName(cpg);
// FIXME: is it possible we would see a superclass name here?
// Not a big deal for now, as we are mostly just interested in calls
// to private methods, for which we will definitely see the right
// called class name.
if (!calledClassName.equals(jclass.getClassName())) {
return null;
}
String calledMethodName = inv.getMethodName(cpg);
String calledMethodSignature = inv.getSignature(cpg);
boolean isStaticCall = (inv instanceof INVOKESTATIC);
// Scan methods for one that matches.
Method[] methods = jclass.getMethods();
for (Method method : methods) {
String methodName = method.getName();
String signature = method.getSignature();
boolean isStatic = method.isStatic();
if (methodName.equals(calledMethodName) && signature.equals(calledMethodSignature) && isStatic == isStaticCall) {
// This method looks like a match.
return wantCallsFor(method) ? method : null;
}
}
// Hmm...no matching method found.
// This is almost certainly because the named method
// was inherited from a superclass.
LOG.debug("No method found for {}.{} : {}", calledClassName, calledMethodName, calledMethodSignature);
return null;
}
}