package soottocfg.cfg.optimization;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import com.google.common.base.Verify;
import soot.SootMethod;
import soottocfg.cfg.Program;
import soottocfg.cfg.SourceLocation;
import soottocfg.cfg.expression.BinaryExpression;
import soottocfg.cfg.expression.BinaryExpression.BinaryOperator;
import soottocfg.cfg.expression.Expression;
import soottocfg.cfg.expression.IdentifierExpression;
import soottocfg.cfg.expression.TupleAccessExpression;
import soottocfg.cfg.expression.literal.IntegerLiteral;
import soottocfg.cfg.expression.literal.NullLiteral;
import soottocfg.cfg.method.CfgBlock;
import soottocfg.cfg.method.Method;
import soottocfg.cfg.statement.AssignStatement;
import soottocfg.cfg.statement.AssumeStatement;
import soottocfg.cfg.statement.NewStatement;
import soottocfg.cfg.statement.PushStatement;
import soottocfg.cfg.type.IntType;
import soottocfg.cfg.type.ReferenceType;
import soottocfg.cfg.type.Type;
import soottocfg.cfg.variable.ClassVariable;
import soottocfg.cfg.variable.Variable;
import soottocfg.soot.transformers.ArrayTransformer;
import soottocfg.soot.util.MethodInfo;
import soottocfg.soot.util.SootTranslationHelpers;
/**
* @author schaef
* @author rodykers
*
*/
public class CfgStubber {
public void stubUnboundFieldsAndMethods(Program program) {
Queue<Method> todo = new LinkedList<Method>();
todo.addAll(Arrays.asList(program.getMethods()));
while (!todo.isEmpty()) {
Method method = todo.remove();
if (method.getSource() == null) {
/*
* If the method does not have a body, we just add a non-det
* assignment to the
* exception global to indicate that this method might have
* thrown an exception.
*
* TODO: We should add some support to look for user provided
* specs here!
*/
CfgBlock block = new CfgBlock(method);
SourceLocation loc = method.getLocation();
if (method.getMethodName().contains(SootMethod.constructorName)) {
// ensure that no exception is thrown
LinkedList<Variable> outParams = new LinkedList<Variable>();
// when stubbing, set the exceptional return to null.
Variable exceptionalRetVar = new Variable("exc", method.getReturnType().get(0));
outParams.add(exceptionalRetVar);
AssignStatement noException = new AssignStatement(loc,
new IdentifierExpression(loc, exceptionalRetVar),
SootTranslationHelpers.v().getMemoryModel().mkNullConstant());
block.addStatement(noException);
Variable thisPointer = method.getInParams().get(0);
ReferenceType rt = getRefTypeFrom(thisPointer);
List<Expression> rhs = new LinkedList<Expression>();
int i = 0;
for (Variable v : rt.getClassVariable().getAssociatedFields()) {
++i;
Variable outVar = new Variable("$out" + i, v.getType());
outParams.add(outVar);
Variable other;
other = new Variable("undef_field" + i, v.getType());
// assign the outParams to fresh values.
block.addStatement(new AssignStatement(loc, new IdentifierExpression(loc, outVar),
new IdentifierExpression(loc, other)));
// add an element to the push.
rhs.add(new IdentifierExpression(loc, other));
}
PushStatement push = new PushStatement(loc, rt.getClassVariable(),
new IdentifierExpression(loc, thisPointer), rhs);
block.addStatement(push);
// verify that the size is correct.
Verify.verify(outParams.size() == method.getReturnType().size(),
outParams.size() + "!=" + method.getReturnType().size());
method.setOutParam(outParams);
} else if (method.getReturnType().size() > 0) {
LinkedList<Variable> rets = new LinkedList<Variable>();
// when stubbing, set the exceptional return to null.
// System.out.println("Return types of " +
// method.getMethodName() + ": " + method.getReturnType());
Variable exceptionalRetVar = new Variable("exc", method.getReturnType().get(0));
rets.add(exceptionalRetVar);
AssignStatement noException = new AssignStatement(loc,
new IdentifierExpression(loc, exceptionalRetVar),
SootTranslationHelpers.v().getMemoryModel().mkNullConstant());
block.addStatement(noException);
// start from 1 because 0 is already the exceptional return.
int f = 0;
for (int i = 1; i < method.getReturnType().size(); i++) {
Type t = method.getReturnType().get(i);
// add push with undef values to havoc methods
if (t instanceof ReferenceType) {
// Rody: because of issue 116, create
// non-deterministic choice between
// null and instance with undef fields
CfgBlock caseNull = new CfgBlock(method);
method.addEdge(block, caseNull);
CfgBlock caseInstance = new CfgBlock(method);
method.addEdge(block, caseInstance);
ReferenceType rt = (ReferenceType) t;
List<Expression> rhs = new LinkedList<Expression>();
if (t.toString().contains(ArrayTransformer.arrayTypeName)) {
// for an array, make sure we add the constraint
// that length >= 0
Variable sizeLocal = new Variable("undef_size", IntType.instance());
AssumeStatement asm = new AssumeStatement(loc,
new BinaryExpression(loc, BinaryOperator.Ge,
new IdentifierExpression(loc, sizeLocal), IntegerLiteral.zero()));
caseInstance.addStatement(0, asm);
rhs.add(new IdentifierExpression(loc, sizeLocal));
ClassVariable c = rt.getClassVariable();
// TODO: @Rody: the dynamic type is not pushed
// anymore.
// rhs.add(new IdentifierExpression(loc, c));
// this is an array, so initialize the remaining
// fields with sth as well
int n = rhs.size();
while (n < c.getAssociatedFields().length) {
Variable undefLocal = new Variable("undef_field" + (f++),
c.getAssociatedFields()[n++].getType());
rhs.add(new IdentifierExpression(loc, undefLocal));
}
} else {
for (Variable v : rt.getClassVariable().getAssociatedFields()) {
Variable undefLocal = new Variable("undef_field" + (f++), v.getType());
rhs.add(new IdentifierExpression(loc, undefLocal));
}
}
Variable outVar = new Variable(MethodInfo.returnVariableName, rt);
rets.add(outVar);
IdentifierExpression ret = new IdentifierExpression(loc, outVar);
caseInstance.addStatement(new NewStatement(loc, ret, rt.getClassVariable()));
PushStatement push = new PushStatement(loc, rt.getClassVariable(), ret, rhs);
caseInstance.addStatement(push);
caseNull.addStatement(new AssignStatement(loc, ret, new NullLiteral(loc)));
} else {
Variable outVar = new Variable(MethodInfo.returnVariableName, t);
rets.add(outVar);
}
}
method.setOutParam(rets);
method.findOrCreateUniqueSink();
}
} else if (method.isProgramEntryPoint()) {
if (method.getInParams().size() == 1 && method.getMethodName().contains("main")) {
CfgBlock entry = method.getSource();
SourceLocation loc = method.getLocation();
Variable argsParam = method.getInParams().get(0);
ReferenceType argsType = getRefTypeFrom(argsParam);
ClassVariable c = argsType.getClassVariable();
Verify.verify("JayArray_java_lang_String".equals(c.getName()),
"Main needs a string array as argument");
/*
* The length field is now part of the tuple.
*/
TupleAccessExpression tae = new TupleAccessExpression(loc, argsParam,
SootTranslationHelpers.lengthFieldName);
AssumeStatement asm = new AssumeStatement(loc,
new BinaryExpression(loc, BinaryOperator.Ge, tae, IntegerLiteral.zero()));
entry.addStatement(0, asm);
// push(JayHornArr12, r0, [JayHornArr12.$length,
// JayHornArr12.$elType, JayHornArr12.$dynamicType])
List<Expression> rhs = new LinkedList<Expression>();
/*
* TODO: @Rody, the push doesn't take the the dynamic
* type and size as first two parameters because stuff
* like this should be part of the tuples.
*/
// rhs.add(new IdentifierExpression(loc, sizeLocal));
// rhs.add(new IdentifierExpression(loc, c));
// this is an array, so initialize the remaining fields with
// sth as well
int i = 0;
for (Variable fieldVar : c.getAssociatedFields()) {
if (fieldVar.getName().equals(SootTranslationHelpers.lengthFieldName)) {
// TODO: this should be unreachable later since the
// associated
// fields should not contain any final fields.
rhs.add(new TupleAccessExpression(loc, argsParam,
SootTranslationHelpers.lengthFieldName));
} else {
Variable undefLocal = new Variable("undef_field" + i++, fieldVar.getType());
rhs.add(new IdentifierExpression(loc, undefLocal));
}
}
IdentifierExpression argsLocal = new IdentifierExpression(loc, argsParam);
// AssumeStatement asmNotNull = new AssumeStatement(loc,
// new BinaryExpression(loc, BinaryOperator.Ne,
// argsLocal, new NullLiteral(loc)));
// entry.addStatement(1, asmNotNull);
PushStatement push = new PushStatement(loc, c, argsLocal, rhs);
entry.addStatement(1, push);
}
}
}
}
private ReferenceType getRefTypeFrom(Variable var) {
return (ReferenceType) var.getType();
}
}