/* * 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.LinkedList; import org.joogie.GlobalsCache; import org.joogie.Options; import org.joogie.util.CustomNullnessAnalysis; import org.joogie.util.Log; import org.joogie.util.MhpInfo; import org.joogie.util.TranslationHelpers; import soot.ArrayType; import soot.DoubleType; import soot.FloatType; import soot.Immediate; import soot.Local; import soot.NullType; import soot.RefType; import soot.jimple.AddExpr; import soot.jimple.AndExpr; import soot.jimple.ArrayRef; import soot.jimple.BinopExpr; import soot.jimple.CastExpr; import soot.jimple.CaughtExceptionRef; import soot.jimple.ClassConstant; import soot.jimple.CmpExpr; import soot.jimple.CmpgExpr; import soot.jimple.CmplExpr; import soot.jimple.DivExpr; import soot.jimple.DoubleConstant; import soot.jimple.DynamicInvokeExpr; import soot.jimple.EqExpr; import soot.jimple.FieldRef; import soot.jimple.FloatConstant; import soot.jimple.GeExpr; import soot.jimple.GtExpr; import soot.jimple.InstanceFieldRef; import soot.jimple.InstanceOfExpr; import soot.jimple.IntConstant; import soot.jimple.InterfaceInvokeExpr; import soot.jimple.JimpleValueSwitch; import soot.jimple.LeExpr; import soot.jimple.LengthExpr; import soot.jimple.LongConstant; import soot.jimple.LtExpr; import soot.jimple.MethodHandle; import soot.jimple.MulExpr; import soot.jimple.NeExpr; import soot.jimple.NegExpr; import soot.jimple.NewArrayExpr; import soot.jimple.NewExpr; import soot.jimple.NewMultiArrayExpr; import soot.jimple.NullConstant; import soot.jimple.OrExpr; import soot.jimple.ParameterRef; import soot.jimple.RemExpr; import soot.jimple.ShlExpr; import soot.jimple.ShrExpr; import soot.jimple.SpecialInvokeExpr; import soot.jimple.StaticFieldRef; import soot.jimple.StaticInvokeExpr; import soot.jimple.StringConstant; import soot.jimple.SubExpr; import soot.jimple.ThisRef; import soot.jimple.UshrExpr; import soot.jimple.VirtualInvokeExpr; import soot.jimple.XorExpr; import boogie.ProgramFactory; import boogie.ast.Attribute; import boogie.ast.declaration.FunctionDeclaration; import boogie.ast.expression.Expression; import boogie.ast.expression.IdentifierExpression; import boogie.ast.statement.Statement; import boogie.enums.BinaryOperator; import boogie.enums.UnaryOperator; import boogie.type.BoogieType; /** * @author schaef */ public class SootValueSwitch implements JimpleValueSwitch { public boolean isLeftHandSide = false; private SootProcedureInfo procInfo; private SootStmtSwitch stmtSwitch; private ProgramFactory pf; public SootValueSwitch(SootProcedureInfo pinfo, SootStmtSwitch stmtswitch) { this.procInfo = pinfo; this.pf = GlobalsCache.v().getPf(); this.stmtSwitch = stmtswitch; } private LinkedList<Expression> expressionStack = new LinkedList<Expression>(); public Expression getExpression() { return this.expressionStack.pop(); } /* * (non-Javadoc) * * @see * soot.jimple.ConstantSwitch#caseClassConstant(soot.jimple.ClassConstant) */ @Override public void caseClassConstant(ClassConstant arg0) { this.expressionStack.push(GlobalsCache.v().lookupClassConstant(arg0)); } /* * (non-Javadoc) * * @see * soot.jimple.ConstantSwitch#caseDoubleConstant(soot.jimple.DoubleConstant) */ @Override public void caseDoubleConstant(DoubleConstant arg0) { this.expressionStack.push(GlobalsCache.v().lookupInternDouble(arg0)); } /* * (non-Javadoc) * * @see * soot.jimple.ConstantSwitch#caseFloatConstant(soot.jimple.FloatConstant) */ @Override public void caseFloatConstant(FloatConstant arg0) { this.expressionStack.push(GlobalsCache.v().lookupInternFloat(arg0)); } /* * (non-Javadoc) * * @see soot.jimple.ConstantSwitch#caseIntConstant(soot.jimple.IntConstant) */ @Override public void caseIntConstant(IntConstant arg0) { expressionStack.push(this.pf.mkIntLiteral(String.valueOf(arg0.value))); } /* * (non-Javadoc) * * @see * soot.jimple.ConstantSwitch#caseLongConstant(soot.jimple.LongConstant) */ @Override public void caseLongConstant(LongConstant arg0) { long value = arg0.value; if (value >= Integer.MAX_VALUE || value <= Integer.MIN_VALUE) { Log.debug("Used abstract value for long constant that didn't fit in int"); this.expressionStack.push(GlobalsCache.v().lookupInternLong(arg0)); } else { expressionStack.push(this.pf.mkIntLiteral(String.valueOf(value))); } } /* * (non-Javadoc) * * @see * soot.jimple.ConstantSwitch#caseNullConstant(soot.jimple.NullConstant) */ @Override public void caseNullConstant(NullConstant arg0) { expressionStack.push(SootPrelude.v().getNullConstant()); } /* * (non-Javadoc) * * @see * soot.jimple.ConstantSwitch#caseStringConstant(soot.jimple.StringConstant) */ @Override public void caseStringConstant(StringConstant arg0) { this.expressionStack.push(GlobalsCache.v().lookupInternString(arg0)); } private void translateBinOp(BinopExpr arg0) { this.isLeftHandSide = false; arg0.getOp1().apply(this); Expression lhs = this.expressionStack.pop(); arg0.getOp2().apply(this); Expression rhs = this.expressionStack.pop(); // cast if necessary. This has to be done because soot treats boolean as // integers if (lhs.getType() != rhs.getType()) { rhs = TranslationHelpers.castBoogieTypes(rhs, lhs.getType()); } if (arg0.getType() instanceof FloatType || arg0.getType() instanceof DoubleType) { FunctionDeclaration fun = SootPrelude.v().lookupRealOperator( arg0.getSymbol()); Expression[] arguments = { lhs, rhs }; this.expressionStack.push(this.pf.mkFunctionApplication(fun, arguments)); return; } // if it is not Float or Double, proceed normally. createBinOp(arg0.getSymbol(), lhs, rhs); } public void createBinOp(String op, Expression left, Expression right) { BinaryOperator operator; BoogieType rettype; op = op.trim(); if (op.compareTo("+") == 0) { rettype = left.getType(); operator = BinaryOperator.ARITHPLUS; } else if (op.compareTo("-") == 0) { rettype = left.getType(); operator = BinaryOperator.ARITHMINUS; } else if (op.compareTo("*") == 0) { this.expressionStack.push(SootPrelude.v().mulInt(left, right)); return; } else if (op.compareTo("/") == 0) { // make sure that "right" is an Integer // then assert that it is different from 0 if (this.stmtSwitch != null) this.stmtSwitch.getErrorModel().createDivByZeroGuard(right); this.expressionStack.push(SootPrelude.v().divInt(left, right)); return; } else if (op.compareTo("%") == 0) { if (this.stmtSwitch != null) this.stmtSwitch.getErrorModel().createDivByZeroGuard(right); this.expressionStack.push(SootPrelude.v().modInt(left, right)); return; } else if (op.compareTo("cmp") == 0 || op.compareTo("cmpl") == 0 || op.compareTo("cmpg") == 0) { this.expressionStack.push(SootPrelude.v().compareExpr(left, right)); return; } else if (op.compareTo("==") == 0) { rettype = this.pf.getBoolType(); operator = BinaryOperator.COMPEQ; } else if (op.compareTo("<") == 0) { rettype = this.pf.getBoolType(); operator = BinaryOperator.COMPLT; } else if (op.compareTo(">") == 0) { rettype = this.pf.getBoolType(); operator = BinaryOperator.COMPGT; } else if (op.compareTo("<=") == 0) { rettype = this.pf.getBoolType(); operator = BinaryOperator.COMPLEQ; } else if (op.compareTo(">=") == 0) { rettype = this.pf.getBoolType(); operator = BinaryOperator.COMPGEQ; } else if (op.compareTo("!=") == 0) { rettype = this.pf.getBoolType(); operator = BinaryOperator.COMPNEQ; } else if (op.compareTo("&") == 0) { this.expressionStack.push(SootPrelude.v().bitAndExpr(left, right)); return; } else if (op.compareTo("|") == 0) { this.expressionStack.push(SootPrelude.v().bitOrExpr(left, right)); return; } else if (op.compareTo("<<") == 0) { // Shiftl this.expressionStack.push(SootPrelude.v().shiftLeft(left, right)); return; } else if (op.compareTo(">>") == 0) { // Shiftr this.expressionStack.push(SootPrelude.v().shiftRight(left, right)); return; } else if (op.compareTo(">>>") == 0) { // UShiftr this.expressionStack.push(SootPrelude.v().uShiftRight(left, right)); return; } else if (op.compareTo("^") == 0) { // XOR this.expressionStack.push(SootPrelude.v().xorExpr(left, right)); return; } else { throw new RuntimeException("UNKNOWN Jimple operator " + op); } this.expressionStack.push(this.pf.mkBinaryExpression(rettype, operator, left, right)); } /* * (non-Javadoc) * * @see soot.jimple.ExprSwitch#caseAddExpr(soot.jimple.AddExpr) */ @Override public void caseAddExpr(AddExpr arg0) { translateBinOp(arg0); } /* * (non-Javadoc) * * @see soot.jimple.ExprSwitch#caseAndExpr(soot.jimple.AndExpr) */ @Override public void caseAndExpr(AndExpr arg0) { translateBinOp(arg0); } /* * (non-Javadoc) * * @see soot.jimple.ExprSwitch#caseCastExpr(soot.jimple.CastExpr) */ @Override public void caseCastExpr(CastExpr arg0) { this.isLeftHandSide = false; arg0.getOp().apply(this); Expression exp = this.getExpression(); // if the jimple types are the same, just return the expression if (arg0.getOp().getType() == arg0.getCastType()) { this.expressionStack.push(exp); return; } BoogieType boogieTargetType = GlobalsCache.v().getBoogieType( arg0.getCastType()); // ////////////////////////////////////////////////////////////////////// if (boogieTargetType == this.pf.getIntType() || boogieTargetType == this.pf.getRealType() || boogieTargetType == this.pf.getBoolType()) { // in that case, exp is also of primitive type // otherwise, java or soot would translate it // into a more complex expression that uses the // appropriate java methods. this.expressionStack.push(TranslationHelpers.castBoogieTypes(exp, boogieTargetType)); return; // ////////////////////////////////////////////////////////////////////// } else if (arg0.getCastType() instanceof RefType) { final RefType targetType = (RefType) arg0.getCastType(); if (targetType.getClassName().equals("java.lang.Object")) { // should always be ok, nothing to assert this.expressionStack.push(exp); } else if (arg0.getOp().getType() instanceof RefType) { // Guard that typeof(exp) <: targetType if (this.stmtSwitch != null) this.stmtSwitch.getErrorModel().createClassCastGuard( this.getClassTypeFromExpression(exp, false), GlobalsCache.v().lookupClassVariable( targetType.getSootClass())); this.expressionStack.push(exp); } else if (arg0.getOp().getType() instanceof NullType) { // should always be ok, nothing to assert this.expressionStack.push(exp); } else { /* Log.error */ System.out.println("Don't know how to cast from " + arg0.getOp().getType() + " to " + arg0.getCastType()); this.expressionStack.push(createHavocedExpression(GlobalsCache .v().getBoogieType(arg0.getType()))); } return; // ////////////////////////////////////////////////////////////////////// } else if (arg0.getCastType() instanceof ArrayType) { final ArrayType targetType = (ArrayType) arg0.getCastType(); if (this.stmtSwitch != null) this.stmtSwitch.getErrorModel().createClassCastGuard( this.getClassTypeFromExpression(exp, false), GlobalsCache.v().lookupArrayType(targetType)); this.expressionStack.push(exp); return; // ////////////////////////////////////////////////////////////////////// // old, should be removed at some point } else if (boogieTargetType == SootPrelude.v().getReferenceType()) { // ILocation loc = exp.getLocation(); if (arg0.getCastType() instanceof RefType) { RefType rtype = (RefType) arg0.getCastType(); // Guard that typeof(exp) <: targetType if (this.stmtSwitch != null) this.stmtSwitch.getErrorModel().createClassCastGuard( this.getClassTypeFromExpression(exp, false), GlobalsCache.v().lookupClassVariable( rtype.getSootClass())); this.expressionStack.push(exp); return; } else { throw new RuntimeException("Cast from " + arg0.getOp().getType() + " to " + arg0.getCastType() + " not implemented"); } } throw new RuntimeException("Cast from " + arg0.getOp().getType() + " to " + arg0.getCastType() + " not implemented"); } /* * (non-Javadoc) * * @see soot.jimple.ExprSwitch#caseCmpExpr(soot.jimple.CmpExpr) */ @Override public void caseCmpExpr(CmpExpr arg0) { translateBinOp(arg0); } /* * (non-Javadoc) * * @see soot.jimple.ExprSwitch#caseCmpgExpr(soot.jimple.CmpgExpr) */ @Override public void caseCmpgExpr(CmpgExpr arg0) { translateBinOp(arg0); } /* * (non-Javadoc) * * @see soot.jimple.ExprSwitch#caseCmplExpr(soot.jimple.CmplExpr) */ @Override public void caseCmplExpr(CmplExpr arg0) { translateBinOp(arg0); } /* * (non-Javadoc) * * @see soot.jimple.ExprSwitch#caseDivExpr(soot.jimple.DivExpr) */ @Override public void caseDivExpr(DivExpr arg0) { translateBinOp(arg0); } /* * (non-Javadoc) * * @see soot.jimple.ExprSwitch#caseEqExpr(soot.jimple.EqExpr) */ @Override public void caseEqExpr(EqExpr arg0) { translateBinOp(arg0); } /* * (non-Javadoc) * * @see soot.jimple.ExprSwitch#caseGeExpr(soot.jimple.GeExpr) */ @Override public void caseGeExpr(GeExpr arg0) { translateBinOp(arg0); } /* * (non-Javadoc) * * @see soot.jimple.ExprSwitch#caseGtExpr(soot.jimple.GtExpr) */ @Override public void caseGtExpr(GtExpr arg0) { translateBinOp(arg0); } /* * (non-Javadoc) * * @see * soot.jimple.ExprSwitch#caseInstanceOfExpr(soot.jimple.InstanceOfExpr) */ @Override public void caseInstanceOfExpr(InstanceOfExpr arg0) { this.isLeftHandSide = false; arg0.getOp().apply(this); Expression lhs = this.getExpression(); if (arg0.getCheckType() instanceof RefType) { RefType rtype = (RefType) arg0.getCheckType(); Expression rhs = GlobalsCache.v().lookupClassVariable( rtype.getSootClass()); Expression isNonNull = this.pf.mkBinaryExpression( this.pf.getBoolType(), BinaryOperator.COMPNEQ, lhs, SootPrelude.v().getNullConstant()); Expression isSubtype = this.pf.mkBinaryExpression( this.pf.getBoolType(), BinaryOperator.COMPPO, this.getClassTypeFromExpression(lhs, false), rhs); this.expressionStack.push(this.pf.mkBinaryExpression( this.pf.getBoolType(), BinaryOperator.LOGICAND, isNonNull, isSubtype)); } else { Log.error("instanceof for arrays not implemented"); this.expressionStack.push(createHavocedExpression(GlobalsCache.v() .getBoogieType(arg0.getType()))); } } /** * takes a fake global of appropriate type, adds a havoc statement before it * and returns the identifier expression for it. * * @param t * @return */ private IdentifierExpression createHavocedExpression(BoogieType t) { Attribute[] arrtibutes = {}; if (this.stmtSwitch != null) { arrtibutes = TranslationHelpers .javaLocation2Attribute(this.stmtSwitch .getCurrentStatement()); } IdentifierExpression ide = GlobalsCache.v().getHavocGlobal(t); if (this.stmtSwitch != null) { this.stmtSwitch.addStatement(this.pf.mkHavocStatement(arrtibutes, ide)); } return ide; } /* * (non-Javadoc) * * @see soot.jimple.ExprSwitch#caseInterfaceInvokeExpr(soot.jimple. * InterfaceInvokeExpr) */ @Override public void caseInterfaceInvokeExpr(InterfaceInvokeExpr arg0) { throw new RuntimeException("This must be handeled by SootStmtSwitch!"); } /* * (non-Javadoc) * * @see soot.jimple.ExprSwitch#caseLeExpr(soot.jimple.LeExpr) */ @Override public void caseLeExpr(LeExpr arg0) { translateBinOp(arg0); } /* * (non-Javadoc) * * @see soot.jimple.ExprSwitch#caseLengthExpr(soot.jimple.LengthExpr) */ @Override public void caseLengthExpr(LengthExpr arg0) { this.isLeftHandSide = false; arg0.getOp().apply(this); Expression base = this.getExpression(); this.stmtSwitch.getErrorModel().createNonNullGuard(base); this.expressionStack .push(GlobalsCache.v().getArraySizeExpression(base)); } /* * (non-Javadoc) * * @see soot.jimple.ExprSwitch#caseLtExpr(soot.jimple.LtExpr) */ @Override public void caseLtExpr(LtExpr arg0) { translateBinOp(arg0); } /* * (non-Javadoc) * * @see soot.jimple.ExprSwitch#caseMulExpr(soot.jimple.MulExpr) */ @Override public void caseMulExpr(MulExpr arg0) { translateBinOp(arg0); } /* * (non-Javadoc) * * @see soot.jimple.ExprSwitch#caseNeExpr(soot.jimple.NeExpr) */ @Override public void caseNeExpr(NeExpr arg0) { translateBinOp(arg0); } /* * (non-Javadoc) * * @see soot.jimple.ExprSwitch#caseNegExpr(soot.jimple.NegExpr) */ @Override public void caseNegExpr(NegExpr arg0) { // this is arithmetic negative! // logic neg is already translated by soot this.isLeftHandSide = false; arg0.getOp().apply(this); Expression e = this.expressionStack.pop(); this.expressionStack.push(this.pf.mkUnaryExpression(e.getType(), UnaryOperator.ARITHNEGATIVE, e)); } /* * (non-Javadoc) * * @see soot.jimple.ExprSwitch#caseNewArrayExpr(soot.jimple.NewArrayExpr) */ @Override public void caseNewArrayExpr(NewArrayExpr arg0) { throw new RuntimeException("Must be handeled in SootStmtSwitch"); } /* * (non-Javadoc) * * @see soot.jimple.ExprSwitch#caseNewExpr(soot.jimple.NewExpr) */ @Override public void caseNewExpr(NewExpr arg0) { throw new RuntimeException("Must be handeled in SootStmtSwitch"); } /* * (non-Javadoc) * * @see * soot.jimple.ExprSwitch#caseNewMultiArrayExpr(soot.jimple.NewMultiArrayExpr * ) */ @Override public void caseNewMultiArrayExpr(NewMultiArrayExpr arg0) { throw new RuntimeException("Must be handeled in SootStmtSwitch"); } /* * (non-Javadoc) * * @see soot.jimple.ExprSwitch#caseOrExpr(soot.jimple.OrExpr) */ @Override public void caseOrExpr(OrExpr arg0) { translateBinOp(arg0); } /* * (non-Javadoc) * * @see soot.jimple.ExprSwitch#caseRemExpr(soot.jimple.RemExpr) */ @Override public void caseRemExpr(RemExpr arg0) { translateBinOp(arg0); } /* * (non-Javadoc) * * @see soot.jimple.ExprSwitch#caseShlExpr(soot.jimple.ShlExpr) */ @Override public void caseShlExpr(ShlExpr arg0) { translateBinOp(arg0); } /* * (non-Javadoc) * * @see soot.jimple.ExprSwitch#caseShrExpr(soot.jimple.ShrExpr) */ @Override public void caseShrExpr(ShrExpr arg0) { translateBinOp(arg0); } /* * (non-Javadoc) * * @see * soot.jimple.ExprSwitch#caseSpecialInvokeExpr(soot.jimple.SpecialInvokeExpr * ) */ @Override public void caseSpecialInvokeExpr(SpecialInvokeExpr arg0) { throw new RuntimeException("This must be handeled by SootStmtSwitch!"); } /* * (non-Javadoc) * * @see * soot.jimple.ExprSwitch#caseStaticInvokeExpr(soot.jimple.StaticInvokeExpr) */ @Override public void caseStaticInvokeExpr(StaticInvokeExpr arg0) { throw new RuntimeException("This must be handeled by SootStmtSwitch!"); } /* * (non-Javadoc) * * @see soot.jimple.ExprSwitch#caseSubExpr(soot.jimple.SubExpr) */ @Override public void caseSubExpr(SubExpr arg0) { translateBinOp(arg0); } /* * (non-Javadoc) * * @see soot.jimple.ExprSwitch#caseUshrExpr(soot.jimple.UshrExpr) */ @Override public void caseUshrExpr(UshrExpr arg0) { translateBinOp(arg0); } /* * (non-Javadoc) * * @see * soot.jimple.ExprSwitch#caseVirtualInvokeExpr(soot.jimple.VirtualInvokeExpr * ) */ @Override public void caseVirtualInvokeExpr(VirtualInvokeExpr arg0) { throw new RuntimeException("This must be handeled by SootStmtSwitch!"); } @Override public void caseDynamicInvokeExpr(DynamicInvokeExpr arg0) { throw new RuntimeException("This must be handeled by SootStmtSwitch!"); } /* * (non-Javadoc) * * @see soot.jimple.ExprSwitch#caseXorExpr(soot.jimple.XorExpr) */ @Override public void caseXorExpr(XorExpr arg0) { translateBinOp(arg0); } /* * (non-Javadoc) * * @see soot.jimple.RefSwitch#caseArrayRef(soot.jimple.ArrayRef) */ @Override public void caseArrayRef(ArrayRef arg0) { BoogieType btype = GlobalsCache.v().getBoogieType(arg0.getType()); Expression arrayvar; BoogieType arrtype; if (btype == this.pf.getIntType()) { arrayvar = SootPrelude.v().getIntArrHeapVariable(); arrtype = SootPrelude.v().getIntArrType(); } else if (btype == this.pf.getBoolType()) { arrayvar = SootPrelude.v().getBoolArrHeapVariable(); arrtype = SootPrelude.v().getBoolArrType(); // } else if (btype == this.pf.getRealType() ) { // arrayvar = SootPrelude.v().getRealArrHeapVariable(); // arrtype= SootPrelude.v().getRealArrType(); } else if (btype == SootPrelude.v().getReferenceType()) { arrayvar = SootPrelude.v().getRefArrHeapVariable(); arrtype = SootPrelude.v().getRefArrType(); } else { throw new RuntimeException("do not understand array of type: " + btype.toString()); } // tranlate base and index this.isLeftHandSide = false; arg0.getBase().apply(this); Expression baseExpression = this.expressionStack.pop(); arg0.getIndex().apply(this); Expression indexExpression = this.expressionStack.pop(); // guard out-of-bounds exceptions this.stmtSwitch.getErrorModel().createArrayBoundGuard(baseExpression, indexExpression); // construct the expression Expression[] base = { baseExpression }; Expression array = this.pf.mkArrayAccessExpression(arrtype, arrayvar, base); Expression[] indices = { indexExpression }; this.expressionStack.push(this.pf.mkArrayAccessExpression(btype, array, indices)); } /* * (non-Javadoc) * * @see * soot.jimple.RefSwitch#caseCaughtExceptionRef(soot.jimple.CaughtExceptionRef * ) */ @Override public void caseCaughtExceptionRef(CaughtExceptionRef arg0) { if (arg0.getType() instanceof RefType) { RefType rtype = (RefType) arg0.getType(); // assume that the exception variable now has the type of the caught // exception // assume $heap[$exception,$type] <: arg0.getType() if (this.stmtSwitch != null) { // ensure that $exception is not null. //TODO: actually don't! Because this creates //a dead else branch in some cases. //the non-nullness is ensured by the compiler. // this.stmtSwitch.getErrorModel() // .createNonNullViolationException( // this.procInfo.getExceptionVariable()); Expression typefield = this.getClassTypeFromExpression( this.procInfo.getExceptionVariable(), false); this.stmtSwitch.addStatement(pf.mkAssumeStatement( new Attribute[] {}, GlobalsCache.v().compareTypeExpressions( typefield, GlobalsCache.v().lookupClassVariable( rtype.getSootClass())))); } this.expressionStack.push(this.procInfo.getExceptionVariable()); return; } throw new RuntimeException( "this case of exception handling has not beend implemented"); } private void checkFieldAnnotations(Expression expr, FieldRef fr) { LinkedList<SootAnnotations.Annotation> annot = SootAnnotations .parseFieldTags(fr.getField()); if (annot.contains(SootAnnotations.Annotation.NonNull)) { this.stmtSwitch.addStatement(this.stmtSwitch.getErrorModel() .createAssumeNonNull(expr)); } } private boolean checkSharedField(FieldRef ref, Expression field) { if (!Options.v().useSoundThreads()) { return false; } else { if (!this.stmtSwitch.isInMonitor()) { if (MhpInfo.v().getSharedFields(this.procInfo.getSootMethod()) .contains(ref.getField())) { // if the field can be modified in another thread, add a // havoc statement before it is used. return true; } else { // do nothing } } else { Log.info("Was in monitor ... "); } } return false; } private void havocField(Expression field, Expression base) { if (base == null) { // the field is static IdentifierExpression identifier = (IdentifierExpression) field; if (identifier != null) { Statement s = this.pf.mkHavocStatement(TranslationHelpers .javaLocation2Attribute(this.stmtSwitch .getCurrentStatement()), identifier); this.stmtSwitch.addStatement(s); } else { throw new RuntimeException("Not Implemented"); } } else { Expression heapAccess = this.makeHeapAccessExpression(base, field, true); IdentifierExpression havocval = GlobalsCache.v().getHavocGlobal( heapAccess.getType()); this.stmtSwitch.addStatement(this.pf.mkHavocStatement( TranslationHelpers.javaLocation2Attribute(this.stmtSwitch .getCurrentStatement()), havocval)); Expression[] indices = { base, field }; Expression heapupdate = pf.mkArrayStoreExpression(SootPrelude.v() .getHeapVariable().getType(), SootPrelude.v() .getHeapVariable(), indices, havocval); Statement s = pf.mkAssignmentStatement(SootPrelude.v() .getHeapVariable(), heapupdate); this.stmtSwitch.addStatement(s); } } /* * (non-Javadoc) * * @see * soot.jimple.RefSwitch#caseInstanceFieldRef(soot.jimple.InstanceFieldRef) */ @Override public void caseInstanceFieldRef(InstanceFieldRef arg0) { boolean islhs = this.isLeftHandSide; this.isLeftHandSide = false; arg0.getBase().apply(this); Expression base = this.getExpression(); Expression field = GlobalsCache.v().lookupSootField(arg0.getField()); boolean nullCheckNeeded = true; // check if the field may be modified by another thread. if (!islhs && checkSharedField(arg0, field)) { havocField(field, base); nullCheckNeeded = false; } SootProcedureInfo pinfo = this.stmtSwitch.getProcInfo(); // check if base is trivially non-null if (pinfo != null) { CustomNullnessAnalysis nna = this.stmtSwitch.getProcInfo() .getNullnessAnalysis(); if (nna != null && arg0.getBase() instanceof Immediate) { if (nna.isAlwaysNonNullBefore( this.stmtSwitch.getCurrentStatement(), (Immediate) arg0.getBase())) { nullCheckNeeded = false; } } //TODO: this is a very clumsy way of obtaining thislocal // but it is not obvious when it throws an exception. Local thislocal = null; try { thislocal = pinfo.getSootMethod().getActiveBody().getThisLocal(); } catch (Exception e) { thislocal = null; } //check if base is "this" or a local variable //that is an alias of "this". if (arg0.getBase() ==thislocal || arg0.getBase() instanceof ThisRef) { nullCheckNeeded = false; // TODO: check if this is actually needed. } } // We are checking if this is a @NonNull field // if so, we add an assume to ensure that it actually is // not null here. checkFieldAnnotations( this.makeHeapAccessExpression(base, field, false), arg0); this.expressionStack.push(this.makeHeapAccessExpression(base, field, nullCheckNeeded)); } /* * (non-Javadoc) * * @see soot.jimple.RefSwitch#caseParameterRef(soot.jimple.ParameterRef) */ @Override public void caseParameterRef(ParameterRef arg0) { this.expressionStack.push(this.procInfo.lookupParameterVariable(arg0)); } /* * (non-Javadoc) * * @see soot.jimple.RefSwitch#caseStaticFieldRef(soot.jimple.StaticFieldRef) */ @Override public void caseStaticFieldRef(StaticFieldRef arg0) { // TODO: we are checking if this is a @NonNull field // if so, we add an assume to ensure that it actually is // not null here. May be better ways to do this. checkFieldAnnotations( GlobalsCache.v().lookupSootField(arg0.getField()), arg0); Expression field = GlobalsCache.v().lookupSootField(arg0.getField()); // check if the field may be modified by another thread. // (unless it is a lhs expression. In that case we don't care. if (!this.isLeftHandSide && checkSharedField(arg0, field)) { havocField(field, null); } this.expressionStack.push(field); } /* * (non-Javadoc) * * @see soot.jimple.RefSwitch#caseThisRef(soot.jimple.ThisRef) */ @Override public void caseThisRef(ThisRef arg0) { this.expressionStack.push(this.procInfo.getThisReference()); } /* * (non-Javadoc) * * @see soot.jimple.JimpleValueSwitch#caseLocal(soot.Local) */ @Override public void caseLocal(Local arg0) { this.expressionStack.push(this.procInfo.lookupLocalVariable(arg0)); } /* * (non-Javadoc) * * @see soot.jimple.ConstantSwitch#defaultCase(java.lang.Object) */ @Override public void defaultCase(Object arg0) { Log.error("BoogieValueSwitch: case not covered"); assert (false); } /** * Make a heap access and the necessary assertion * * @param field * @return */ public Expression makeHeapAccessExpression(Expression base, Expression field, boolean guarded) { if (guarded && this.stmtSwitch != null) { this.stmtSwitch.getErrorModel().createNonNullGuard(base); } // Assemble the $heap[base, field] expression return SootPrelude.v().heapAccess(base, field); } /** * gets the field of expr that denotes its Java type E.g., let's say we have * a variable c of type C. The call getExprssionJavaClass(c) returns * $heap[c, $type] which, in this case, would be C. * * @param expr * @param guarded * @return */ public Expression getExprssionJavaClass(Expression expr) { return getClassTypeFromExpression(expr, true); } /** * gets the field of expr that denotes its Java type E.g., let's say we have * a variable c of type C. The call getExprssionJavaClass(c,false) returns * $heap[c, $type] which, in this case, would be C. * * @param expr * @param guarded * @return */ public Expression getClassTypeFromExpression(Expression expr, boolean guarded) { return makeHeapAccessExpression(expr, SootPrelude.v() .getFieldClassVariable(), guarded); } @Override public void caseMethodHandle(MethodHandle arg0) { throw new RuntimeException("Not implemented "+arg0); } }