sp-05/src/ch/usi/inf/sp/callgraph/CallGraphBuilder.java

92 lines
4.0 KiB
Java

package ch.usi.inf.sp.callgraph;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
import java.util.Objects;
import ch.usi.inf.sp.framework.ClassAnalyzer;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.*;
/**
* Build a call graph (as part of the class hierarchy)
* consisting of CallSite nodes pointing to Method nodes.
*
* @author maggicl@usi.ch
* @author Matthias.Hauswirth@usi.ch
*/
public final class CallGraphBuilder implements ClassAnalyzer {
private final ClassHierarchy hierarchy;
public CallGraphBuilder(final ClassHierarchy hierarchy) {
this.hierarchy = hierarchy;
}
public void analyze(final String location, final ClassNode classNode) {
try {
final ClassType type = hierarchy.getOrCreateClass(classNode.name);
final List<MethodNode> methodNodes = classNode.methods;
for (final MethodNode methodNode : methodNodes) {
final Method method = type.getMethod(methodNode.name, methodNode.desc);
Objects.requireNonNull(method, "method is null");
final InsnList instructions = methodNode.instructions;
for (int i = 0; i < instructions.size(); i++) {
final AbstractInsnNode insn = instructions.get(i);
// invokedynamic deliberately ignored
if (insn.getType() == AbstractInsnNode.METHOD_INSN) {
final MethodInsnNode methodInsn = (MethodInsnNode) insn;
final int opCode = methodInsn.getOpcode();
final CallSite callSite = new CallSite(insn.getOpcode(), methodInsn.owner, methodInsn.name,
methodInsn.desc);
method.addCallSite(callSite);
final String targetClazz = callSite.getDeclaredTargetClassName();
final ClassType clazz = hierarchy.getOrCreateClass(targetClazz);
// ignoring if a class is unresolved on purpose, as analysis is constrained on user-defined
// classes (i.e. the contents of pacman-src.jar)
if (opCode == Opcodes.INVOKESTATIC || opCode == Opcodes.INVOKESPECIAL) {
// target is static, no fancy search needed
callSite.addPossibleTargetClass(clazz); // Cave Johnson, we're done here
} else if (opCode == Opcodes.INVOKEVIRTUAL) {
final Deque<ClassType> subClasses = new ArrayDeque<>();
subClasses.add(clazz);
// BFS over class and subclasses and add them all as possible targets
while (!subClasses.isEmpty()) {
final ClassType subClazz = subClasses.pop();
if (!subClazz.isAbstract()) {
callSite.addPossibleTargetClass(subClazz);
}
subClasses.addAll(subClazz.getSubTypes());
}
} else { // opCode == Opcodes.INVOKEINTERFACE
// brute force our way through the type hierarchy to find compatible classes which may
// implement the interface
for (final Type aType : hierarchy.getTypes()) {
if (aType instanceof ClassType) {
final ClassType aClazz = (ClassType) aType;
if (aClazz.getInterfaces().contains(clazz)) {
callSite.addPossibleTargetClass(aClazz);
}
}
}
}
}
}
}
} catch (final TypeInconsistencyException ex) {
System.err.println(ex);
}
}
}