/**
*
*/
package soottocfg.soot.util;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.base.Verify;
import soot.Local;
import soot.RefType;
import soot.SootField;
import soot.SootMethod;
import soot.Unit;
import soot.VoidType;
import soot.jimple.ParameterRef;
import soottocfg.cfg.SourceLocation;
import soottocfg.cfg.expression.Expression;
import soottocfg.cfg.expression.IdentifierExpression;
import soottocfg.cfg.method.CfgBlock;
import soottocfg.cfg.method.CfgEdge;
import soottocfg.cfg.method.Method;
import soottocfg.cfg.type.Type;
import soottocfg.cfg.variable.Variable;
/**
* @author schaef
*
*/
public class MethodInfo {
private final String sourceFileName;
public static final String returnVariableName = "$ret_";
public static final String exceptionVariableName = "$ex_";
private static final String thisVariableName = "$this_";
private static final String constructorOutVarName = "$co_";
private final SootMethod sootMethod;
private final Method cfgMethod;
private CfgBlock source = null, sink = null;
private Map<Unit, CfgBlock> unitToBlockMap = new HashMap<Unit, CfgBlock>();
private Map<Local, Variable> localsMap = new HashMap<Local, Variable>();
private Variable thisVariable;
private Variable exceptionVariable;
private List<Variable> returnVariables;
private final Set<Variable> freshLocals = new LinkedHashSet<Variable>();
private SourceLocation methodLoc = null;
public MethodInfo(SootMethod sm, String sourceFileName) {
sootMethod = sm;
cfgMethod = SootTranslationHelpers.v().lookupOrCreateMethod(sootMethod);
methodLoc = SootTranslationHelpers.v().getSourceLocation(sm);
sink = new CfgBlock(getMethod());
getMethod().setSink(sink);
this.sourceFileName = sourceFileName;
this.returnVariables = new LinkedList<Variable>();
this.exceptionVariable = new Variable(exceptionVariableName, SootTranslationHelpers.v().getMemoryModel()
.lookupType(RefType.v("java.lang.Throwable")));
this.returnVariables.add(this.exceptionVariable);
if (!(sm.getReturnType() instanceof VoidType)) {
// create a return variable if the method does not
// return void.
this.returnVariables.add(new Variable(returnVariableName,
SootTranslationHelpers.v().getMemoryModel().lookupType(sm.getReturnType())));
}
if (sm.isConstructor()) {
int i=0;
for (SootField sf : SootTranslationHelpers.findNonStaticFieldsRecursively(sm.getDeclaringClass())) {
this.returnVariables.add(new Variable(constructorOutVarName+(++i), SootTranslationHelpers.v().getMemoryModel().lookupType(sf.getType())));
}
}
// If the method is not static, create a this-variable which is
// passed as the first parameter to the method.
if (!sm.isStatic()) {
this.thisVariable = new Variable(thisVariableName,
SootTranslationHelpers.v().getMemoryModel().lookupType(sm.getDeclaringClass().getType()));
}
}
public Method getMethod() {
return cfgMethod;
}
/**
* Called after the method has been translated. Looks up the corresponding
* Method object in Program and fills in all the information. Should only be
* called once per method.
*/
public void finalizeAndAddToProgram() {
Method m = cfgMethod;
Collection<Variable> locals = new LinkedHashSet<Variable>();
locals.addAll(this.localsMap.values());
locals.addAll(this.freshLocals);
m.initialize(this.thisVariable, this.returnVariables, locals, source, false);
// System.out.println("Initialized method " + m);
CfgBlock uniqueSink = m.findOrCreateUniqueSink();
if (sink != uniqueSink) {
System.err.println("Something strange with the CFG. More than one sink found for " + m.getMethodName());
}
if (m.inDegreeOf(sink) == 0) {
System.err.println("Method " + sootMethod.getSignature() + " has no sink! Ignoring.");
for (CfgEdge e : new HashSet<CfgEdge>(m.edgeSet())) {
m.removeEdge(e);
}
for (CfgBlock b : new HashSet<CfgBlock>(m.vertexSet())) {
if (!b.equals(sink) && !b.equals(source)) {
m.removeVertex(b);
}
}
m.addEdge(source, sink);
}
sink = uniqueSink;
}
public String getSourceFileName() {
return this.sourceFileName;
}
public IdentifierExpression getReturnVariable() {
// TODO this is a hack that assumes that we only use that if there
// is a single return variable.
Verify.verify(this.returnVariables.size() >= 2);
return new IdentifierExpression(methodLoc, this.returnVariables.get(1));
}
public IdentifierExpression getExceptionVariable() {
// TODO this is a hack that assumes that we only use that if there
// is a single return variable.
Verify.verify(this.returnVariables.size() >= 1);
return new IdentifierExpression(methodLoc, this.returnVariables.get(0));
}
public Variable getOutVariable(int i) {
Verify.verify(this.returnVariables.size() >= 2 && i>=0 && i<this.returnVariables.size(), "Index bound 0<="+i+"<"+this.returnVariables.size());
return this.returnVariables.get(i);
}
public List<Variable> getOutVariables() {
return this.returnVariables;
}
// public Expression getExceptionVariable() {
// return new IdentifierExpression(this.exceptionalReturnVariable);
// }
public Expression getThisVariable() {
return new IdentifierExpression(methodLoc, this.thisVariable);
}
public Expression lookupParameterRef(ParameterRef arg0) {
int offset = thisVariable == null ? 0 : 1;
// offset += Options.v().passCallerIdIntoMethods() ? 1 : 0;
return new IdentifierExpression(methodLoc, cfgMethod.getInParam(arg0.getIndex() + offset));
}
public Variable lookupLocalVariable(Local arg0) {
if (!localsMap.containsKey(arg0)) {
localsMap.put(arg0, createLocalVariable(arg0));
}
return localsMap.get(arg0);
}
public Expression lookupLocal(Local arg0) {
return new IdentifierExpression(methodLoc, lookupLocalVariable(arg0));
}
private Variable createLocalVariable(Local arg0) {
return new Variable(arg0.getName(), SootTranslationHelpers.v().getMemoryModel().lookupType(arg0.getType()),
false, false);
}
public Variable createFreshLocal(String prefix, Type t, boolean constant, boolean unique) {
// List<Type> elementTypes = new LinkedList<Type>();
// elementTypes.add(newType);
// TupleType ttype = new TupleType(elementTypes);
// TupleVariable tupleVar = new TupleVariable("$new", ttype, true,
// true);
//
Variable v = new Variable(prefix + this.freshLocals.size(), t, constant, unique);
this.freshLocals.add(v);
return v;
}
public CfgBlock lookupCfgBlock(Unit u) {
if (!unitToBlockMap.containsKey(u)) {
unitToBlockMap.put(u, new CfgBlock(getMethod()));
}
return unitToBlockMap.get(u);
}
public CfgBlock findBlock(Unit u) {
return unitToBlockMap.get(u);
}
public CfgBlock getSource() {
return this.source;
}
public void setSource(CfgBlock source) {
this.source = source;
}
public CfgBlock getSink() {
return sink;
}
}