/*
* jimple2boogie - Translates Jimple (or Java) Programs to Boogie
* Copyright (C) 2013 Martin Schaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaef and Stephan Arlt
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.joogie.soot;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import org.joogie.GlobalsCache;
import org.joogie.util.CustomNullnessAnalysis;
import org.joogie.util.TranslationHelpers;
import soot.Local;
import soot.PrimType;
import soot.RefType;
import soot.SootClass;
import soot.SootMethod;
import soot.Type;
import soot.VoidType;
import soot.jimple.IfStmt;
import soot.jimple.ParameterRef;
import soot.jimple.StaticFieldRef;
import soot.tagkit.Tag;
import soot.tagkit.VisibilityAnnotationTag;
import soot.toolkits.exceptions.UnitThrowAnalysis;
import soot.toolkits.graph.ExceptionalUnitGraph;
import util.Log;
import boogie.ProgramFactory;
import boogie.ast.Attribute;
import boogie.ast.VarList;
import boogie.ast.declaration.Implementation;
import boogie.ast.declaration.ProcedureDeclaration;
import boogie.ast.expression.Expression;
import boogie.ast.expression.IdentifierExpression;
import boogie.ast.specification.Specification;
import boogie.ast.statement.AssumeStatement;
import boogie.enums.BinaryOperator;
import boogie.type.BoogieType;
/**
* @author schaef
*
*/
public class SootProcedureInfo {
private HashMap<Local, IdentifierExpression> localVariable = new HashMap<Local, IdentifierExpression>();
private LinkedList<IdentifierExpression> inParameters;
private LinkedList<IdentifierExpression> outParameters;
private LinkedList<Specification> specification;
private IdentifierExpression returnVariable;
private IdentifierExpression exceptionVariable;
private IdentifierExpression containingClassVariable;
private ExceptionalUnitGraph exceptionalUnitGraph;
private CustomNullnessAnalysis nullnessAnalysis;
private IdentifierExpression thisVariable;
private final SootMethod sootMethod;
private final String cleanName;
private ProcedureDeclaration procedureDeclaration;
private Implementation boogieProcedure = null;
public HashSet<AssumeStatement> typeAssumptions = new HashSet<AssumeStatement>();
public HashSet<StaticFieldRef> usedStaticFields = new HashSet<StaticFieldRef>();
//this is used to suppress false positives originating from
//else if cases.
public HashSet<IfStmt> duplicatedIfStatement = new HashSet<IfStmt>();
private LinkedList<IdentifierExpression> idexpFromVarlist(VarList[] vla) {
LinkedList<IdentifierExpression> ret = new LinkedList<IdentifierExpression>();
ProgramFactory pf = GlobalsCache.v().getPf();
for (VarList vl : vla) {
for (String name : vl.getIdentifiers()) {
ret.add(pf.mkIdentifierExpression(
pf.boogieTypeFromAstType(vl.getType()), name, false,
false, false));
}
}
return ret;
}
private void assumeParameterType(IdentifierExpression id, Type t) {
ProgramFactory pf = GlobalsCache.v().getPf();
//create an assumption about the type to put in the body.
if (t instanceof RefType) {
RefType rtype = (RefType) t;
Expression rhs = GlobalsCache.v().lookupClassVariable(
rtype.getSootClass());
Expression ofType = pf.mkBinaryExpression(
pf.getBoolType(), BinaryOperator.COMPPO,
SootPrelude.v().heapAccess(id, SootPrelude.v()
.getFieldClassVariable()), rhs);
typeAssumptions.add((AssumeStatement) pf.mkAssumeStatement(new Attribute[0], ofType));
}
}
public SootProcedureInfo(SootMethod m) {
ProgramFactory pf = GlobalsCache.v().getPf();
Attribute[] attributes = TranslationHelpers.javaLocation2Attribute(m
.getTags());
// Check if this procedure has already been declared in the prelude:
this.sootMethod = m;
this.cleanName = TranslationHelpers.getQualifiedName(this.sootMethod);
// If the procedure has already been declared in the prelude, use this
// information instead.
ProcedureDeclaration decl = pf.findProcedureDeclaration(this.cleanName);
if (decl != null) {
this.specification = new LinkedList<Specification>(
Arrays.asList(decl.getSpecification()));
this.inParameters = new LinkedList<IdentifierExpression>(
idexpFromVarlist(decl.getInParams()));
this.outParameters = new LinkedList<IdentifierExpression>(
idexpFromVarlist(decl.getOutParams()));
this.procedureDeclaration = decl;
return;
}
if (this.cleanName
.contains("java.lang.Object$java.lang.Object$clone$43"))
throw new RuntimeException(
"This was in the prelude! should not reach this line! "
+ this.cleanName);
this.specification = new LinkedList<Specification>();
this.inParameters = new LinkedList<IdentifierExpression>();
this.containingClassVariable = GlobalsCache.v().lookupClassVariable(
this.sootMethod.getDeclaringClass());
if (!this.sootMethod.isStatic()) {
this.thisVariable = pf.mkIdentifierExpression(SootPrelude.v()
.getReferenceType(), "$this", false, false, false);
this.inParameters.add(this.thisVariable);
} else {
this.thisVariable = null;
}
if (this.sootMethod.isAbstract()) {
// if the method is abstract assume the worst possible modifies
// clause
this.specification.add(pf.mkModifiesSpecification(true,
new IdentifierExpression[] { SootPrelude.v()
.getHeapVariable() }));
}
// collect the annotations
LinkedList<LinkedList<SootAnnotations.Annotation>> pannot = SootAnnotations
.parseParameterAnnotations(this.sootMethod);
for (int i = 0; i < this.sootMethod.getParameterCount(); i++) {
BoogieType type = GlobalsCache.v().getBoogieType(
this.sootMethod.getParameterType(i));
String param_name = "$in_parameter__" + i;
IdentifierExpression id = pf.mkIdentifierExpression(type,
param_name, false, false, false);
this.inParameters.add(id);
assumeParameterType(id, this.sootMethod.getParameterType(i));
// add precondition if parameter has @NonNull annotation
if (pannot.size() != 0) { // may be null if there are no annotations
// at all
LinkedList<SootAnnotations.Annotation> annot = pannot.get(i);
if (annot.contains(SootAnnotations.Annotation.NonNull)) {
Expression formula = pf.mkBinaryExpression(
pf.getBoolType(), BinaryOperator.COMPNEQ, id,
SootPrelude.v().getNullConstant());
this.specification.add(pf.mkRequiresSpecification(
attributes, false, formula));
}
}
}
this.outParameters = new LinkedList<IdentifierExpression>();
if (!(this.sootMethod.getReturnType() instanceof VoidType)) {
Type returntype = this.sootMethod.getReturnType();
BoogieType type = GlobalsCache.v().getBoogieType(returntype);
String param_name = "$return";
IdentifierExpression id = pf.mkIdentifierExpression(type,
param_name, false, false, false);
this.outParameters.add(id);
this.returnVariable = id;
assumeParameterType(id, returntype);
// compute the type variable if the method return a reference type
// this is used to ensure the type in assume-guarantee reasoning
// this.returnTypeVariable = null;
// if (returntype instanceof RefType) {
// RefType rt = (RefType) returntype;
// this.returnTypeVariable = GlobalsCache.v().lookupClassVariable(
// rt.getSootClass());
// }
// only makes sense to add non-null annotation for non-prim types
if (!(returntype instanceof PrimType)) {
// check if the procedure retrun has @NonNull annotation
LinkedList<SootAnnotations.Annotation> annot = null;
for (Tag tag : this.sootMethod.getTags()) {
if (tag instanceof VisibilityAnnotationTag) {
if (annot != null) {
Log.error("Didn't expect so many tags for procedure! Check that!");
break;
}
annot = SootAnnotations
.parseAnnotations((VisibilityAnnotationTag) tag);
}
}
if (annot != null
&& annot.contains(SootAnnotations.Annotation.NonNull)) {
Expression formula = pf.mkBinaryExpression(
pf.getBoolType(), BinaryOperator.COMPNEQ,
this.returnVariable, SootPrelude.v()
.getNullConstant());
this.specification.add(pf.mkEnsuresSpecification(
attributes, false, formula));
}
}
} else {
this.returnVariable = null;
}
/*
* We create an additional out variable called $exception. This variable
* is used to handle all kinds of exceptions that may be thrown in the
* procedure.
*/
String exname = "$exception";
IdentifierExpression id = pf.mkIdentifierExpression(SootPrelude.v()
.getReferenceType(), exname, false, false, false);
this.outParameters.add(id);
this.exceptionVariable = id;
Specification[] spec = this.specification
.toArray(new Specification[this.specification.size()]);
this.procedureDeclaration = pf.mkProcedureDeclaration(this.cleanName,
this.getInParamters(), this.getOutParamters(), spec);
// TODO: make the exceptional return flag a postcondition instead!
// now create the exceptional unit graph, which will be used later to
// check
// where throw statements can jump to
if (this.sootMethod.hasActiveBody()) {
this.exceptionalUnitGraph = new ExceptionalUnitGraph(
this.sootMethod.getActiveBody(), UnitThrowAnalysis.v());
this.nullnessAnalysis = new CustomNullnessAnalysis(this.exceptionalUnitGraph);
} else {
this.exceptionalUnitGraph = null;
this.nullnessAnalysis = null;
}
}
public SootMethod getSootMethod() {
return this.sootMethod;
}
public String getBoogieName() {
return cleanName;
}
public List<SootClass> getThrowsClasses() {
return this.sootMethod.getExceptions();
}
public IdentifierExpression[] getLocalVariables() {
HashSet<IdentifierExpression> alllocals = new HashSet<IdentifierExpression>(
this.localVariable.values());
alllocals.addAll(this.fakeLocals);
return alllocals.toArray(new IdentifierExpression[alllocals.size()]);
}
public IdentifierExpression[] getInParamters() {
return this.inParameters
.toArray(new IdentifierExpression[this.inParameters.size()]);
}
public IdentifierExpression[] getOutParamters() {
return this.outParameters
.toArray(new IdentifierExpression[this.outParameters.size()]);
}
public IdentifierExpression getReturnVariable() {
return this.returnVariable;
}
public IdentifierExpression getExceptionVariable() {
return this.exceptionVariable;
}
public IdentifierExpression getThisReference() {
return thisVariable;
}
public IdentifierExpression getContainingClassVariable() {
return containingClassVariable;
}
public ExceptionalUnitGraph getExceptionalUnitGraph() {
return exceptionalUnitGraph;
}
public CustomNullnessAnalysis getNullnessAnalysis() {
return this.nullnessAnalysis;
}
public boolean isStatic() {
return this.sootMethod.isStatic();
}
//public IdentifierExpression thisRefLocal = null;
public IdentifierExpression lookupLocalVariable(Local local) {
if (!this.localVariable.containsKey(local)) {
BoogieType type = GlobalsCache.v().getBoogieType(local.getType());
String cleanname = TranslationHelpers.getQualifiedName(local);
IdentifierExpression id = GlobalsCache
.v()
.getPf()
.mkIdentifierExpression(type, cleanname, false, false,
false);
this.localVariable.put(local, id);
}
return this.localVariable.get(local);
}
private int fakeLocalCount = 0;
private HashSet<IdentifierExpression> fakeLocals = new HashSet<IdentifierExpression>();
public IdentifierExpression createLocalVariable(BoogieType type) {
IdentifierExpression id = GlobalsCache
.v()
.getPf()
.mkIdentifierExpression(type,
"$fakelocal_" + (fakeLocalCount++), false, false, false);
this.fakeLocals.add(id);
return id;
}
public IdentifierExpression lookupParameterVariable(ParameterRef param) {
return this.inParameters.get(param.getIndex()
+ ((this.sootMethod.isStatic()) ? 0 : 1));
}
public ProcedureDeclaration getProcedureDeclaration() {
return this.procedureDeclaration;
}
public Implementation getBoogieProcedure() {
return boogieProcedure;
}
public void setProcedureImplementation(Implementation impl) {
this.boogieProcedure = impl;
}
}