/*
* jimple2boogie - Translates Jimple (or Java) Programs to Boogie
* Copyright (C) 2013 Martin Schaeaeaeaeaeaeaeaeaef 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;
import java.util.HashMap;
import java.util.HashSet;
import org.joogie.soot.SootPrelude;
import org.joogie.soot.SootProcedureInfo;
import org.joogie.util.Log;
import org.joogie.util.TranslationHelpers;
import soot.ArrayType;
import soot.BooleanType;
import soot.ByteType;
import soot.CharType;
import soot.DoubleType;
import soot.FloatType;
import soot.IntType;
import soot.LongType;
import soot.NullType;
import soot.RefType;
import soot.ShortType;
import soot.SootClass;
import soot.SootField;
import soot.SootMethod;
import soot.Type;
import soot.Value;
import soot.VoidType;
import soot.jimple.ClassConstant;
import soot.jimple.DoubleConstant;
import soot.jimple.EnterMonitorStmt;
import soot.jimple.FloatConstant;
import soot.jimple.LongConstant;
import soot.jimple.Stmt;
import soot.jimple.StringConstant;
import boogie.ProgramFactory;
import boogie.ast.Attribute;
import boogie.ast.expression.Expression;
import boogie.ast.expression.IdentifierExpression;
import boogie.ast.statement.Statement;
import boogie.enums.BinaryOperator;
import boogie.type.BoogieType;
/**
* @author schaef
*
*/
public class GlobalsCache {
private ProgramFactory pf = null;
public ProgramFactory getPf() {
return pf;
}
private HashMap<SootMethod, SootProcedureInfo> procedureMap;
private HashMap<SootField, Expression> fieldMap;
private HashMap<Stmt, String> unitLabelMap;
private HashMap<SootClass, IdentifierExpression> classTypeMap = new HashMap<SootClass, IdentifierExpression>();
private HashMap<String, IdentifierExpression> cConstantTypeMap = new HashMap<String, IdentifierExpression>();
private HashMap<String, IdentifierExpression> stringInternMap = new HashMap<String, IdentifierExpression>();
private HashMap<String, IdentifierExpression> floatInternMap = new HashMap<String, IdentifierExpression>();
private HashMap<String, IdentifierExpression> doubleInternMap = new HashMap<String, IdentifierExpression>();
private HashMap<String, IdentifierExpression> longInternMap = new HashMap<String, IdentifierExpression>();
private long unitLabelCounter = 0L;
private final String blockPrefix = "block";
private static GlobalsCache instance = null;
public SootMethod currentMethod = null;
public HashMap<EnterMonitorStmt, HashSet<Value>> modifiedInMonitor;
public static GlobalsCache v() {
if (GlobalsCache.instance == null) {
GlobalsCache.instance = new GlobalsCache();
}
return GlobalsCache.instance;
}
public static void resetInstance() {
if (instance != null) {
GlobalsCache.instance.procedureMap.clear();
GlobalsCache.instance.fieldMap.clear();
GlobalsCache.instance.unitLabelMap.clear();
GlobalsCache.instance.classTypeMap.clear();
GlobalsCache.instance.classTypeMap.clear();
GlobalsCache.instance.stringInternMap.clear();
GlobalsCache.instance.floatInternMap.clear();
GlobalsCache.instance.doubleInternMap.clear();
if (GlobalsCache.instance.modifiedInMonitor!=null) GlobalsCache.instance.modifiedInMonitor.clear();
}
GlobalsCache.instance = null;
}
private GlobalsCache() {
this.procedureMap = new HashMap<SootMethod, SootProcedureInfo>();
this.fieldMap = new HashMap<SootField, Expression>();
this.unitLabelMap = new HashMap<Stmt, String>();
pf = new ProgramFactory();
}
public boolean hasUnitLabel(Stmt u) {
return this.unitLabelMap.containsKey(u);
}
public String getUnitLabel(Stmt u) {
if (!this.unitLabelMap.containsKey(u)) {
this.unitLabelCounter++;
this.unitLabelMap
.put(u, this.blockPrefix + (this.unitLabelCounter));
}
return this.unitLabelMap.get(u);
}
public String getBlockLabel() {
this.unitLabelCounter++;
return this.blockPrefix + (this.unitLabelCounter);
}
public SootProcedureInfo lookupProcedure(SootMethod m) {
if (!this.procedureMap.containsKey(m)) {
SootProcedureInfo procinfo = new SootProcedureInfo(m);
this.procedureMap.put(m, procinfo);
}
return this.procedureMap.get(m);
}
public IdentifierExpression lookupInternString(StringConstant s) {
if (!stringInternMap.containsKey(s.value)) {
String name = "$StringConst"+stringInternMap.size();
stringInternMap.put(s.value, this.pf.mkIdentifierExpression(this.getBoogieType(s.getType()), name, true, true, true));
}
return stringInternMap.get(s.value);
}
public IdentifierExpression lookupInternFloat(FloatConstant s) {
if (!floatInternMap.containsKey(s.toString())) {
String name = "$FloatConst"+floatInternMap.size();
floatInternMap.put(s.toString(), this.pf.mkIdentifierExpression(this.getBoogieType(s.getType()), name, true, true, true));
}
return floatInternMap.get(s.toString());
}
public IdentifierExpression lookupInternDouble(DoubleConstant s) {
if (!doubleInternMap.containsKey(s.toString())) {
String name = "$DoubleConst"+doubleInternMap.size();
doubleInternMap.put(s.toString(), this.pf.mkIdentifierExpression(this.getBoogieType(s.getType()), name, true, true, true));
}
return doubleInternMap.get(s.toString());
}
public IdentifierExpression lookupInternLong(LongConstant s) {
if (!longInternMap.containsKey(s.toString())) {
String name = "$LongConst"+longInternMap.size();
longInternMap.put(s.toString(), this.pf.mkIdentifierExpression(this.getBoogieType(s.getType()), name, true, true, true));
}
return longInternMap.get(s.toString());
}
/**
* Lookup a filed in a class.
*
* @param field
* @return
*/
public Expression lookupSootField(SootField field) {
if (!this.fieldMap.containsKey(field)) {
String cleanname = TranslationHelpers.getQualifiedName(field);
BoogieType btype;
if (!field.isStatic()) {
BoogieType[] params = { this.getBoogieType(field.getType()) };
btype = pf.mkSubstituteType(SootPrelude.v().getFieldType(),
params);
} else {
btype = this.getBoogieType(field.getType());
}
this.fieldMap.put(field, pf.mkIdentifierExpression(
btype, cleanname,
false, true, true));
}
return this.fieldMap.get(field);
}
private int freshglobalcounter = 0;
public IdentifierExpression makeFreshGlobal(BoogieType type,
boolean isConst, boolean isUnique) {
return pf.mkIdentifierExpression( type, "$freshglobal_"
+ (this.freshglobalcounter++), isConst, true, isUnique);
}
private HashMap<BoogieType, IdentifierExpression> havocGloabls = new HashMap<BoogieType, IdentifierExpression>();
/**
* Get a special global in case you want to havoc something and need a
* otherwise unused gloabl of the same type.
* @param type
* @return
*/
public IdentifierExpression getHavocGlobal(BoogieType type) {
if (!this.havocGloabls.containsKey(type)) {
this.havocGloabls.put(type, pf.mkIdentifierExpression( type, "$havoc"
+ (this.freshglobalcounter++), false, true, false));
}
return this.havocGloabls.get(type);
}
public BoogieType getBoogieType(Type type) {
BoogieType ret = null;
if (type instanceof DoubleType || type instanceof FloatType) {
// ret = pf.getRealType();
ret = pf.getIntType();
} else if (type instanceof IntType || type instanceof LongType
|| type instanceof ByteType || type instanceof CharType
|| type instanceof ShortType || type instanceof BooleanType) {
ret = pf.getIntType();
} else if (type instanceof RefType) {
ret = SootPrelude.v().getReferenceType();
} else if (type instanceof ArrayType) {
ret = SootPrelude.v().getReferenceType();
} else if (type == NullType.v()) {
ret = SootPrelude.v().getNullConstant().getType();
} else if (type instanceof VoidType) {
ret = SootPrelude.v().getVoidType();
} else {
Log.error("Unknown Type " + type.toString()
+ ": BoogieTypeFactory.lookupPrimitiveType");
ret = null;
}
return ret;
}
public IdentifierExpression lookupClassVariable(SootClass c) {
if (this.classTypeMap.containsKey(c)) {
return this.classTypeMap.get(c);
}
HashSet<IdentifierExpression> parents = new HashSet<IdentifierExpression>();
if (c.hasSuperclass()) {
parents.add(lookupClassVariable(c.getSuperclass()));
}
for (SootClass interf : c.getInterfaces()) {
parents.add(lookupClassVariable(interf));
}
Attribute[] attributes = TranslationHelpers.javaLocation2Attribute(c.getTags());
IdentifierExpression cvar = pf.mkIdentifierExpression(attributes, SootPrelude
.v().getJavaClassType(),
TranslationHelpers.getQualifiedName(c), true, true, true,
parents.toArray(new IdentifierExpression[parents.size()]));
this.classTypeMap.put(c, cvar);
return cvar;
}
public Expression lookupArrayType(ArrayType t) {
Type elementType = t.getElementType();
Expression elementTypeExpr;
if (elementType instanceof ArrayType) {
elementTypeExpr = lookupArrayType((ArrayType)elementType);
} else if (elementType instanceof RefType) {
elementTypeExpr = lookupClassVariable(((RefType)elementType).getSootClass());
} else if (elementType instanceof DoubleType || elementType instanceof FloatType) {
//TODO: @Philipp macht das Sinn? Ich mache es so wie in getBoogieType.
return SootPrelude.v().getIntArrayConstructor();
} else if (elementType instanceof IntType || elementType instanceof LongType
|| elementType instanceof ByteType || elementType instanceof CharType
|| elementType instanceof ShortType || elementType instanceof BooleanType) {
//TODO: @Philipp macht das Sinn? Ich mache es so wie in getBoogieType.
return SootPrelude.v().getIntArrayConstructor();
} else if (elementType instanceof ByteType) {
return SootPrelude.v().getByteArrayConstructor();
} else if (elementType instanceof CharType) {
return SootPrelude.v().getCharArrayConstructor();
} else if (elementType instanceof LongType) {
return SootPrelude.v().getLongArrayConstructor();
} else if (elementType instanceof BooleanType) {
return SootPrelude.v().getBoolArrayConstructor();
} else {
System.out.println(elementType);
assert(false);
return null;
}
return
pf.mkFunctionApplication( SootPrelude.v().getArrayTypeConstructor(),
new Expression[] { elementTypeExpr });
}
public Statement setArraySizeStatement(Expression arrayvar,
Expression arraysize) {
Expression[] indices = { arrayvar };
return pf.mkAssignmentStatement( SootPrelude.v()
.getArrSizeHeapVariable(), pf.mkArrayStoreExpression(
pf.getIntType(), SootPrelude.v().getArrSizeHeapVariable(),
indices, arraysize));
}
public Expression getArraySizeExpression(Expression expression) {
Expression[] indices = { expression };
return pf.mkArrayAccessExpression(
pf.getIntType(), SootPrelude.v().getArrSizeHeapVariable(),
indices);
}
/**
* checks if subtype is a subtype of supertype
* @param subtype
* @param supertype
* @return
*/
public Expression compareTypeExpressions(Expression subtype, Expression supertype) {
return GlobalsCache
.v()
.getPf()
.mkBinaryExpression(
GlobalsCache.v().getPf().getBoolType(),
BinaryOperator.COMPPO, subtype,
supertype);
}
public Expression sameTypeExpression(Expression typeA, Expression typeB) {
//TODO: can this be done with equals as well?
return GlobalsCache
.v()
.getPf()
.mkBinaryExpression(
GlobalsCache.v().getPf().getBoolType(),
BinaryOperator.LOGICAND,
compareTypeExpressions(typeA, typeB),
compareTypeExpressions(typeB, typeA));
}
public boolean isProperSubType(SootClass sub, SootClass sup) {
if (isSubTypeOrEqual(sub,sup) && sup!=sub) {
return true;
}
return false;
}
public boolean isSubTypeOrEqual(SootClass sub, SootClass sup) {
SootClass c = sub;
while (c!=null) {
if (c==sup) return true;
try {
c = c.getSuperclass();
} catch (Exception e) {
//the exception it thrown, if we try
//to access the superclass of object
return false;
}
}
return false;
}
public boolean inThrowsClause(SootClass exception, SootProcedureInfo procinfo) {
for (SootClass sc : procinfo.getThrowsClasses()) {
if (GlobalsCache.v().isSubTypeOrEqual(exception, sc)) {
return true;
}
}
return false;
}
public Expression lookupClassConstant(ClassConstant cc) {
if (!cConstantTypeMap.containsKey(cc.getValue())) {
String name = TranslationHelpers.replaceIllegalChars("CC$"+cc.value);
IdentifierExpression ide = this.pf.mkIdentifierExpression(this.getBoogieType(cc.getType()), name, true, true, true);
cConstantTypeMap.put(cc.getValue(), ide);
}
return cConstantTypeMap.get(cc.getValue());
}
}