package kr.ac.snu.selab.soot.callgraph;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import kr.ac.snu.selab.soot.analyzer.AbstractAnalyzer;
import kr.ac.snu.selab.soot.core.AbstractProject;
import kr.ac.snu.selab.soot.util.MyUtil;
import soot.Body;
import soot.Hierarchy;
import soot.SootClass;
import soot.SootMethod;
import soot.Unit;
import soot.jimple.internal.JAssignStmt;
import soot.jimple.internal.JInvokeStmt;
public class CallGraphTXTCreator extends AbstractAnalyzer {
private String callGraphPath;
public static final String CALL_GRAPH_TXT_FILE_NAME = "callgraph.txt";
public CallGraphTXTCreator(AbstractProject project) {
super(project);
}
private List<Unit> getUnits(SootMethod aMethod) {
List<Unit> unitList = new ArrayList<Unit>();
if (aMethod.hasActiveBody()) {
Body body = aMethod.getActiveBody();
unitList.addAll(body.getUnits());
}
return unitList;
}
public List<SootClass> getSubTypeClassOf(SootClass aType,
Hierarchy aHierarchy) {
if (aType.isInterface()) {
return aHierarchy.getImplementersOf(aType);
} else {
return aHierarchy.getSubclassesOf(aType);
}
}
public List<SootMethod> getOverrideMethodsOf(SootMethod aMethod,
Hierarchy aHierarchy,
Map<Map<SootClass, String>, SootMethod> aMethodMapBySubSignature) {
List<SootMethod> result = new ArrayList<SootMethod>();
SootClass receiverType = aMethod.getDeclaringClass();
List<SootClass> subTypeClassList = getSubTypeClassOf(receiverType,
aHierarchy);
if (!subTypeClassList.isEmpty()) {
for (SootClass aClass : subTypeClassList) {
Map<SootClass, String> key = new HashMap<SootClass, String>();
key.put(aClass, aMethod.getSubSignature());
SootMethod overrideMethod = aMethodMapBySubSignature.get(key);
if (overrideMethod != null) {
result.add(overrideMethod);
}
}
}
return result;
}
@Override
protected void preAnalysis() {
String fileName2 = CALL_GRAPH_TXT_FILE_NAME;
callGraphPath = MyUtil.getPath(project.getOutputDirectory(), fileName2);
}
@Override
protected void analyze(List<SootClass> classList, Hierarchy hierarchy) {
// This is a map that has keys of (class, subsignature of method) pair.
Map<Map<SootClass, String>, SootMethod> methodMapBySubSignature = new HashMap<Map<SootClass, String>, SootMethod>();
for (SootClass aClass : classList) {
for (SootMethod aMethod : aClass.getMethods()) {
Map<SootClass, String> key = new HashMap<SootClass, String>();
key.put(aClass, aMethod.getSubSignature());
methodMapBySubSignature.put(key, aMethod);
}
}
PrintWriter writer = null;
try {
File outputFile = new File(callGraphPath);
File dir = outputFile.getParentFile();
if (!dir.exists()) {
dir.mkdirs();
}
writer = new PrintWriter(new FileWriter(callGraphPath));
for (SootClass aClass : classList) {
for (SootMethod aMethod : aClass.getMethods()) {
for (Unit aUnit : getUnits(aMethod)) {
if (aUnit instanceof JInvokeStmt) {
JInvokeStmt jInvokeStatement = (JInvokeStmt) aUnit;
SootMethod invokedMethod = jInvokeStatement
.getInvokeExpr().getMethod();
SootClass receiverClass = invokedMethod
.getDeclaringClass();
if (classList.contains(receiverClass)) {
writer.print(aMethod.toString());
writer.print("\t");
writer.println(invokedMethod.toString());
// If there is a call to abstract type class,
// its subclass' methods should be added to a
// call graph.
if (!invokedMethod.getName().equals("<init>")) {
List<SootMethod> overrideMethodList = getOverrideMethodsOf(
invokedMethod, hierarchy,
methodMapBySubSignature);
for (SootMethod overrideMethod : overrideMethodList) {
writer.print(aMethod.toString());
writer.print("\t");
writer.println(overrideMethod
.toString());
}
}
}
}
if (aUnit instanceof JAssignStmt) {
JAssignStmt jAssignStatement = (JAssignStmt) aUnit;
if (jAssignStatement.containsInvokeExpr()) {
SootMethod invokedMethod = jAssignStatement
.getInvokeExpr().getMethod();
SootClass receiverClass = invokedMethod
.getDeclaringClass();
if (classList.contains(receiverClass)) {
writer.print(aMethod.toString());
writer.print("\t");
writer.println(invokedMethod.toString());
// If there is a call to abstract type
// class, its subclass' methods should be
// added to a call graph.
if (!invokedMethod.getName().equals(
"<init>")) {
List<SootMethod> overrideMethodList = getOverrideMethodsOf(
invokedMethod, hierarchy,
methodMapBySubSignature);
for (SootMethod overrideMethod : overrideMethodList) {
writer.print(aMethod.toString());
writer.print("\t");
writer.println(overrideMethod
.toString());
}
}
}
}
}
}
}
}
writer.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (writer != null)
writer.close();
}
}
}