/* * 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.soot; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.Map; import org.joogie.GlobalsCache; import org.joogie.Options; import org.joogie.util.Log; import org.joogie.util.TranslationHelpers; import soot.Body; import soot.BodyTransformer; import soot.SootClass; import soot.SootMethod; import soot.Trap; import soot.Unit; import soot.Value; import soot.ValueBox; import soot.jimple.BinopExpr; import soot.jimple.GotoStmt; import soot.jimple.IfStmt; import soot.jimple.NeExpr; import soot.jimple.NegExpr; import soot.jimple.ReturnStmt; import soot.jimple.StaticFieldRef; import soot.jimple.Stmt; import soot.jimple.ThrowStmt; import soot.jimple.internal.JEqExpr; import soot.tagkit.LineNumberTag; import soot.tagkit.SourceLnNamePosTag; import soot.tagkit.Tag; import soot.toolkits.graph.ExceptionalUnitGraph; import boogie.ProgramFactory; import boogie.ast.Attribute; import boogie.ast.declaration.Implementation; import boogie.ast.statement.ReturnStatement; import boogie.ast.statement.Statement; import boogie.enums.BinaryOperator; /** * Boogie Body Transformer * * @author schaef */ public class SootBodyTransformer extends BodyTransformer { /** * C-tor * * @param report * Report */ public SootBodyTransformer() { } @Override @SuppressWarnings("rawtypes") protected void internalTransform(Body arg0, String arg1, Map arg2) { if (Options.v().hasScope()) { SootMethod method = arg0.getMethod(); SootClass clazz = method.getDeclaringClass(); String packageName = clazz.getPackageName(); if (!packageName.startsWith(Options.v().getScope())) { return; // ignore current body } } // report.addMethod(sootMethod); GlobalsCache.v().currentMethod = arg0.getMethod(); try { transformStmtList(arg0); } catch (Throwable e) { Log.error("Failed to translate "+ arg0.getMethod().getName()); Log.error(e.toString()); throw e; } GlobalsCache.v().currentMethod = null; } /** * Transforms a list of statements * * @param body * Body */ private void transformStmtList(Body body) { SootProcedureInfo procInfo = GlobalsCache.v().lookupProcedure( body.getMethod()); if (procInfo.getBoogieProcedure()!=null) { Log.info("Procedure "+body.getMethod().getBytecodeSignature()+" already known from Prelude"); return; } LinkedList<Statement> boogieStatements = new LinkedList<Statement>(); //now add all assumptions about the types of the in and out parameters boogieStatements.addAll(procInfo.typeAssumptions); ExceptionalUnitGraph tug = procInfo.getExceptionalUnitGraph(); //in a first pass, check if statements have been duplicated //in the bytecode, e.g. for finally-blocks, which is used //later to generate attributes that suppress false alarms //during infeasible code detection. TranslationHelpers.clonedFinallyBlocks.clear(); TranslationHelpers.clonedFinallyBlocks.addAll(detectDuplicatedFinallyBlocks(tug.iterator(), procInfo)); // TranslationHelpers.clonedFinallyBlocks = detectDuplicatedFinallyBlocks_new(tug.iterator(), procInfo); //reset the iterator Iterator<Unit> stmtIt = tug.iterator(); while (stmtIt.hasNext()) { Stmt s = (Stmt) stmtIt.next(); SootStmtSwitch bss = new SootStmtSwitch(procInfo); s.apply(bss); LinkedList<Statement> stmts = bss.popAll(); boogieStatements.addAll(stmts); } Attribute[] attributes = TranslationHelpers.javaLocation2Attribute(body.getTags()); if (procInfo.getThisReference()!=null) { //for non-static procedures we have to assume that .this is non-null boogieStatements.addFirst( GlobalsCache.v().getPf().mkAssumeStatement(attributes, GlobalsCache.v().getPf().mkBinaryExpression( GlobalsCache.v().getPf().getBoolType(), BinaryOperator.COMPNEQ, procInfo.getThisReference(), SootPrelude.v().getNullConstant())) ); } //to be compatible with Microsoft Boogie, we have //to create a unified exit and only a single return //per procedure. boogieStatements = createUnifiedExit(boogieStatements); //now create the procedure implementation that combines //the signature procInfo and the body. Implementation proc = GlobalsCache .v() .getPf() .mkProcedure( procInfo.getProcedureDeclaration(), boogieStatements.toArray(new Statement[boogieStatements .size()]), procInfo.getLocalVariables()); procInfo.setProcedureImplementation(proc); //GlobalsCache.v().modifiedInMonitor.clear(); } private LinkedList<Statement> createUnifiedExit(LinkedList<Statement> stmts) { LinkedList<Statement> ret = new LinkedList<Statement>(); String label = GlobalsCache.v().getBlockLabel(); ProgramFactory pf = GlobalsCache.v().getPf(); for (Statement s : stmts) { if (s instanceof ReturnStatement) { ret.add(pf.mkGotoStatement(label)); } else { ret.add(s); } } ret.add(pf.mkLabel(label)); ret.add(pf.mkReturnStatement()); return ret; } /** * In the bytecode, finally-blocks are duplicated. To prevent false positives * during infeasible code detection it is vital to detect and flag these duplications. * The challenge is that they are not exact clones. Variables might be slightly different, * etc. * Hence we look for blocks of statements that: * a) each statement has the same java line number * b) the blocks are not connected * c) they have a subsequence of instructions with exactly the same types. * This is certainly unsound but seems to work so far. * @param stmtIt */ private HashSet<Stmt> detectDuplicatedFinallyBlocks(Iterator<Unit> stmtIt, SootProcedureInfo procInfo) { SootMethod sootMethod = procInfo.getSootMethod(); int first_trap_line = 1000000; for (Trap trap : sootMethod.getActiveBody().getTraps()) { for (Tag tag : trap.getHandlerUnit().getTags()) { if (tag instanceof LineNumberTag) { LineNumberTag t = (LineNumberTag)tag; if (first_trap_line > t.getLineNumber()) { first_trap_line = t.getLineNumber(); } } else if (tag instanceof SourceLnNamePosTag) { SourceLnNamePosTag t = (SourceLnNamePosTag)tag; if (first_trap_line > t.startLn()) { first_trap_line = t.startLn(); } } } } //TODO: instead of just returning the set of stmts that have duplicates //we could group them so that we can still report infeasible code //if all duplicates of one statement are infeasible. HashSet<Stmt> duplicates = new HashSet<Stmt>(); HashMap<Integer, LinkedList<Stmt>> subprogs = new HashMap<Integer, LinkedList<Stmt>>(); LinkedList<Stmt> subprog = null; int old_line = -100; // pick a negative constant that is not a line number //GlobalsCache.v().modifiedInMonitor = new HashMap<EnterMonitorStmt, HashSet<Value>>(); HashSet<IfStmt> ifstmts = new HashSet<IfStmt>(); while (stmtIt.hasNext()) { Stmt s = (Stmt) stmtIt.next(); //check for used static fields. //we need to collect them to havoc them later when //entering a monitor. for (ValueBox vb : s.getUseBoxes()) { if (vb.getValue() instanceof StaticFieldRef) { StaticFieldRef sr = (StaticFieldRef)vb.getValue(); procInfo.usedStaticFields.add(sr); } } if (s instanceof IfStmt) { IfStmt is = (IfStmt)s; for (IfStmt is2 : ifstmts) { if (is.getCondition().equivTo(is2.getCondition())) { //note that we do not add the first occurrence //this is a somewhat arbitrary optimization attempt. procInfo.duplicatedIfStatement.add(is2); procInfo.duplicatedIfStatement.add(is); break; } else { Value nonneg1 = normalizeNegations(is.getCondition()); Value nonneg2 = normalizeNegations(is2.getCondition()); if (nonneg1.equivTo(nonneg2)) { procInfo.duplicatedIfStatement.add(is2); procInfo.duplicatedIfStatement.add(is); } } } ifstmts.add(is); } //now check for finally blocks int line=-2; for (Tag tag : s.getTags()) { if (tag instanceof LineNumberTag) { LineNumberTag t = (LineNumberTag)tag; line = t.getLineNumber(); } else if (tag instanceof SourceLnNamePosTag) { SourceLnNamePosTag t = (SourceLnNamePosTag)tag; line = t.startLn(); } } // System.err.println(line+": "+s); if (line==old_line && subprog!=null) { subprog.add(s); } else { if (subprog!=null) { if (!subprogs.containsKey(old_line)) { subprogs.put(old_line, subprog); } else { if (compareSubprogs(subprog, subprogs.get(old_line)) && old_line>=first_trap_line) { // System.err.println("P1 " + old_line); for (Stmt st : subprogs.get(old_line)) { // System.err.println("\t"+st); duplicates.add(st); } // System.err.println("P2 " + old_line); for (Stmt st : subprog) { // System.err.println("\t"+st); duplicates.add(st); } } } } subprog = new LinkedList<Stmt>(); subprog.add(s); old_line = line; } } return duplicates; } private boolean compareSubprogs(LinkedList<Stmt> p1, LinkedList<Stmt> p2) { LinkedList<Stmt> l1, l2; if (p1.size()<p2.size()) { l2=p1; l1=p2; } else { l2=p2; l1=p1; } for (int i=0; i<l1.size();i++) { if (l1.size()-i<l2.size()) { //then they cannot be sublists anymore return false; } boolean sublist = true; for (int j=0; j<l2.size();j++) { if (!shallowCompareStatements(l1.get(i+j),l2.get(j))) { sublist = false; break; } } if (sublist) { return true; } } return false; } private Value normalizeNegations(Value v) { if (v instanceof NegExpr) { return ((NegExpr)v).getOp(); } else if (v instanceof BinopExpr) { BinopExpr bo = (BinopExpr)v; if (bo instanceof NeExpr) { return new JEqExpr(bo.getOp1(), bo.getOp2()); } } return v; } private boolean shallowCompareStatements(Stmt s1, Stmt s2) { if (s1.getClass() == s2.getClass()) { return true; } //also consider the case that throw, return, and goto might have been changed in the //copies. so do not compare them if (isJumpStmt(s1) && isJumpStmt(s2)) { return true; } return false; } private boolean isJumpStmt(Stmt st) { return ( st instanceof ThrowStmt || st instanceof GotoStmt || st instanceof ReturnStmt); } }