/* * jimple2boogie - Translates Jimple (or Java) Programs to Boogie * Copyright (C) 2013 Martin Schaef 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.io.InputStream; import java.util.HashMap; import org.joogie.GlobalsCache; import util.Log; import boogie.ProgramFactory; import boogie.ast.Attribute; import boogie.ast.declaration.FunctionDeclaration; import boogie.ast.declaration.ProcedureDeclaration; import boogie.ast.expression.Expression; import boogie.ast.expression.IdentifierExpression; import boogie.ast.statement.Statement; import boogie.type.BoogieType; import boogie.type.ConstructedType; /** * @author schaef * */ public class SootPrelude { private static SootPrelude instance = null; public static SootPrelude v() { if (SootPrelude.instance == null) { SootPrelude.instance = new SootPrelude(); } return SootPrelude.instance; } public static void resetInstance() { if (instance != null) { // TODO } instance = null; } private BoogieType referenceType; private BoogieType voidType; private BoogieType fieldType; private BoogieType javaClassType; private BoogieType intArrType; private BoogieType refArrType; private BoogieType realArrType; private BoogieType boolArrType; private IdentifierExpression nullConstant; private IdentifierExpression heapVariable; private IdentifierExpression fieldAllocVariable; private IdentifierExpression fieldClassVariable; private FunctionDeclaration arrayTypeConstructor; private IdentifierExpression intArrayConstructor; private IdentifierExpression byteArrayConstructor; private IdentifierExpression charArrayConstructor; private IdentifierExpression longArrayConstructor; private IdentifierExpression boolArrayConstructor; private IdentifierExpression intArrHeapVariable; private IdentifierExpression realArrHeapVariable; private IdentifierExpression boolArrHeapVariable; private IdentifierExpression refArrHeapVariable; private IdentifierExpression stringSizeHeapVariable; private IdentifierExpression arrSizeHeapVariable; private FunctionDeclaration int2bool, bool2int, ref2bool; private FunctionDeclaration int2real, real2int; private FunctionDeclaration cmpBool, cmpInt, cmpReal, cmpRef; private FunctionDeclaration shlInt, shrInt, ushrInt, xorInt; private FunctionDeclaration mulInt, divInt, modInt; private FunctionDeclaration bitAnd, bitOr; private ProcedureDeclaration newObject; private String fieldTypeName = "Field"; private void loadPreludeFile() { if (org.joogie.Options.v().getPreludeFileName() != null) { try { Log.info("Loading user prelude: " + org.joogie.Options.v().getPreludeFileName()); GlobalsCache .v() .getPf() .importBoogieFile( org.joogie.Options.v().getPreludeFileName()); } catch (Exception e) { throw new RuntimeException("Loading prelude failed: " + e.toString()); } } else { loadPreludeFromResources("/basic_prelude.bpl"); } } private void loadPreludeFromResources(String name) { try { InputStream stream = SootPrelude.class.getResourceAsStream(name); GlobalsCache.v().getPf().importBoogieFile(name, stream); stream.close(); } catch (Exception e1) { throw new RuntimeException( "Prelude file not available. Something failed during the build!"); } } private SootPrelude() { ProgramFactory pf = GlobalsCache.v().getPf(); // now load the prelude file. loadPreludeFile(); this.referenceType = pf.findTypeByName("ref"); this.voidType = pf.findTypeByName("void"); this.javaClassType = pf.getNamedType("javaType"); this.fieldType = pf.findTypeByName("Field"); // this.heapType = pf.findTypeByName("$heap_type"); this.nullConstant = pf.findGlobalByName("$null"); this.heapVariable = pf.findGlobalByName("$heap"); this.fieldAllocVariable = pf.findGlobalByName("$alloc"); this.fieldClassVariable = pf.findGlobalByName("$type"); // functions to represent Java/Soot array types this.arrayTypeConstructor = pf.findFunctionDeclaration("$arrayType"); this.intArrayConstructor = pf.findGlobalByName("$intArrayType"); this.byteArrayConstructor = pf.findGlobalByName("$byteArrayType"); this.charArrayConstructor = pf.findGlobalByName("$charArrayType"); this.longArrayConstructor = pf.findGlobalByName("$longArrayType"); this.boolArrayConstructor = pf.findGlobalByName("$boolArrayType"); // the following creates the heap variables for arrays: // each array is represented by one variable of type "ref" on the $heap. // this variable refers to the actual array on the array heap of // corresponding // type. That is: // a[x] = 3 translates to a variable a : ref and the read access whould // be // $intArrHeap[a][x] := 3 this.intArrType = pf.findTypeByName("intArrHeap_type"); this.refArrType = pf.findTypeByName("reflArrHeap_type"); this.realArrType = pf.findTypeByName("realArrHeap_type"); this.boolArrType = pf.findTypeByName("boolArrHeap_type"); this.intArrHeapVariable = pf.findGlobalByName("$intArrHeap"); this.refArrHeapVariable = pf.findGlobalByName("$refArrHeap"); this.realArrHeapVariable = pf.findGlobalByName("$realArrHeap"); this.boolArrHeapVariable = pf.findGlobalByName("$boolArrHeap"); // an array that stores the size of java arrays. // an array that stores the size of java string. this.arrSizeHeapVariable = pf.findGlobalByName("$arrSizeHeap"); this.stringSizeHeapVariable = pf.findGlobalByName("$stringSizeHeap"); this.int2bool = pf.findFunctionDeclaration("$intToBool"); this.bool2int = pf.findFunctionDeclaration("$boolToInt"); this.ref2bool = pf.findFunctionDeclaration("$refToBool"); this.int2real = pf.findFunctionDeclaration("$intToReal"); this.real2int = pf.findFunctionDeclaration("$realToInt"); this.cmpInt = pf.findFunctionDeclaration("$cmpInt"); this.cmpReal = pf.findFunctionDeclaration("$cmpReal"); this.cmpRef = pf.findFunctionDeclaration("$cmpRef"); this.cmpBool = pf.findFunctionDeclaration("$cmpBool"); this.mulInt = pf.findFunctionDeclaration("$mulInt"); this.divInt = pf.findFunctionDeclaration("$divInt"); this.modInt = pf.findFunctionDeclaration("$modInt"); this.shlInt = pf.findFunctionDeclaration("$shlInt"); this.shrInt = pf.findFunctionDeclaration("$shrInt"); this.ushrInt = pf.findFunctionDeclaration("$ushrInt"); this.xorInt = pf.findFunctionDeclaration("$xorInt"); this.bitAnd = pf.findFunctionDeclaration("$bitAnd"); this.bitOr = pf.findFunctionDeclaration("$bitOr"); this.newObject = pf.findProcedureDeclaration("$new"); if (org.joogie.Options.v().getPreludeFileName() == null) { loadPreludeFromResources("/java_lang.bpl"); } } private HashMap<String, FunctionDeclaration> realOperators = new HashMap<String, FunctionDeclaration>(); /** * TODO: Maybe these guys should be removed later once we have a proper way * of dealing with floats and doubles. * * @param op * @param left * @param right * @return */ public FunctionDeclaration lookupRealOperator(String op) { if (!this.realOperators.containsKey(op)) { Attribute[] attributes = {}; BoogieType integer = GlobalsCache.v().getPf().getIntType(); IdentifierExpression x = GlobalsCache.v().getPf() .mkIdentifierExpression(integer, "x", false, false, false); IdentifierExpression y = GlobalsCache.v().getPf() .mkIdentifierExpression(integer, "y", false, false, false); IdentifierExpression[] in = { x, y }; IdentifierExpression outParam = GlobalsCache .v() .getPf() .mkIdentifierExpression(integer, "$ret", false, false, false); this.realOperators.put( op, GlobalsCache .v() .getPf() .mkFunctionDeclaration(attributes, "$realOp" + op.hashCode(), in, outParam, null)); Log.error("Created function that should be in Prelude: " + op); } return this.realOperators.get(op); } public Expression getNullConstant() { return this.nullConstant; } public IdentifierExpression getHeapVariable() { return this.heapVariable; } public BoogieType getReferenceType() { return this.referenceType; } public BoogieType getVoidType() { return this.voidType; } public BoogieType getFieldType() { return this.fieldType; } public BoogieType getJavaClassType() { return this.javaClassType; } public IdentifierExpression getFieldAllocVariable() { return fieldAllocVariable; } public IdentifierExpression getFieldClassVariable() { return fieldClassVariable; } public FunctionDeclaration getArrayTypeConstructor() { return arrayTypeConstructor; } public IdentifierExpression getIntArrayConstructor() { return intArrayConstructor; } public IdentifierExpression getByteArrayConstructor() { return byteArrayConstructor; } public IdentifierExpression getCharArrayConstructor() { return charArrayConstructor; } public IdentifierExpression getLongArrayConstructor() { return longArrayConstructor; } public IdentifierExpression getBoolArrayConstructor() { return boolArrayConstructor; } public IdentifierExpression getIntArrHeapVariable() { return this.intArrHeapVariable; } public IdentifierExpression getRealArrHeapVariable() { return this.realArrHeapVariable; } public IdentifierExpression getBoolArrHeapVariable() { return this.boolArrHeapVariable; } public IdentifierExpression getRefArrHeapVariable() { return this.refArrHeapVariable; } public IdentifierExpression getStringSizeHeapVariable() { return this.stringSizeHeapVariable; } public IdentifierExpression getArrSizeHeapVariable() { return this.arrSizeHeapVariable; } public BoogieType getIntArrType() { return this.intArrType; } public BoogieType getRefArrType() { return this.refArrType; } public BoogieType getRealArrType() { return this.realArrType; } public BoogieType getBoolArrType() { return this.boolArrType; } public BoogieType getFieldType(BoogieType type) { if (type instanceof ConstructedType) { ConstructedType ctype = (ConstructedType) type; if (ctype.getConstr().getName().equals(this.fieldTypeName)) { if (ctype.getConstr().getParamCount() == 1) { return ctype.getParameter(0); } } } throw new RuntimeException("The type " + type + " is not a Field type. "); } public Expression intToBool(Expression exp) { Expression args[] = { exp }; return GlobalsCache.v().getPf() .mkFunctionApplication(this.int2bool, args); } public Expression boolToInt(Expression exp) { Expression args[] = { exp }; return GlobalsCache.v().getPf() .mkFunctionApplication(this.bool2int, args); } public Expression refToBool(Expression exp) { Expression args[] = { exp }; return GlobalsCache.v().getPf() .mkFunctionApplication(this.ref2bool, args); } public Expression intToReal(Expression exp) { Expression args[] = { exp }; return GlobalsCache.v().getPf() .mkFunctionApplication(this.int2real, args); } public Expression realToInt(Expression exp) { Expression args[] = { exp }; return GlobalsCache.v().getPf() .mkFunctionApplication(this.real2int, args); } public Expression compareExpr(Expression left, Expression right) { if (left.getType() != right.getType()) { throw new RuntimeException( "can only compare expression of same type"); } FunctionDeclaration operator; if (left.getType() == GlobalsCache.v().getPf().getIntType()) { operator = this.cmpInt; } else if (left.getType() == GlobalsCache.v().getPf().getRealType()) { operator = this.cmpReal; } else if (left.getType() == GlobalsCache.v().getPf().getBoolType()) { operator = this.cmpBool; } else if (left.getType() == this.referenceType) { operator = this.cmpRef; } else { throw new RuntimeException("cannot compare expressions of type " + left.getType()); } Expression args[] = { left, right }; return GlobalsCache.v().getPf().mkFunctionApplication(operator, args); } public Expression mulInt(Expression left, Expression right) { Expression args[] = { left, right }; return GlobalsCache.v().getPf() .mkFunctionApplication(this.mulInt, args); } public Expression divInt(Expression left, Expression right) { Expression args[] = { left, right }; return GlobalsCache.v().getPf() .mkFunctionApplication(this.divInt, args); } public Expression modInt(Expression left, Expression right) { Expression args[] = { left, right }; return GlobalsCache.v().getPf() .mkFunctionApplication(this.modInt, args); } public Expression shiftLeft(Expression left, Expression right) { Expression args[] = { left, right }; return GlobalsCache.v().getPf() .mkFunctionApplication(this.shlInt, args); } public Expression shiftRight(Expression left, Expression right) { Expression args[] = { left, right }; return GlobalsCache.v().getPf() .mkFunctionApplication(this.shrInt, args); } public Expression uShiftRight(Expression left, Expression right) { Expression args[] = { left, right }; return GlobalsCache.v().getPf() .mkFunctionApplication(this.ushrInt, args); } public Expression xorExpr(Expression left, Expression right) { if (left.getType() != right.getType()) { throw new RuntimeException( "can only compare expression of same type"); } FunctionDeclaration operator; if (left.getType() == GlobalsCache.v().getPf().getIntType()) { operator = this.xorInt; } else { throw new RuntimeException("cannot compare expressions of type " + left.getType()); } Expression args[] = { left, right }; return GlobalsCache.v().getPf().mkFunctionApplication(operator, args); } public Expression bitAndExpr(Expression left, Expression right) { if (left.getType() != right.getType()) { throw new RuntimeException( "can only compare expression of same type"); } FunctionDeclaration operator; if (left.getType() == GlobalsCache.v().getPf().getIntType()) { operator = this.bitAnd; } else { throw new RuntimeException("cannot compare expressions of type " + left.getType()); } Expression args[] = { left, right }; return GlobalsCache.v().getPf().mkFunctionApplication(operator, args); } public Expression bitOrExpr(Expression left, Expression right) { if (left.getType() != right.getType()) { throw new RuntimeException( "can only compare expression of same type"); } FunctionDeclaration operator; if (left.getType() == GlobalsCache.v().getPf().getIntType()) { operator = this.bitOr; } else { throw new RuntimeException("cannot compare expressions of type " + left.getType()); } Expression args[] = { left, right }; return GlobalsCache.v().getPf().mkFunctionApplication(operator, args); } public Statement newObject(Attribute[] attr, IdentifierExpression var, Expression obj_type) { ProgramFactory pf = GlobalsCache.v().getPf(); return pf.mkCallStatement(attr, false, new IdentifierExpression[] { var }, this.newObject.getIdentifier(), new Expression[] { obj_type }); } public Expression heapAccess(Expression base, Expression field) { ProgramFactory pf = GlobalsCache.v().getPf(); // Assemble the $heap[base, field] expression Expression[] indices = { base, field }; return pf.mkArrayAccessExpression( SootPrelude.v().getFieldType(field.getType()), SootPrelude.v() .getHeapVariable(), indices); } }