diff --git a/README.md b/README.md index 38444a1..1aa9ad2 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,9 @@ This is a scala implementation of a framework that builds a control dependence e ```{xml} - br.ufpe.cin.control-dependence-analysis_2 - 12 - 0.1.0-SNAPSHOT + br.ufpe.cin + control-dependence-analysis_2.12 + 0.1.2 ``` diff --git a/build.sbt b/build.sbt index d543e6b..9cee035 100644 --- a/build.sbt +++ b/build.sbt @@ -3,7 +3,7 @@ scalaVersion := "2.12.8" name := "control-dependence-analysis" organization := "br.ufpe.cin" -version := "0.1.0-SNAPSHOT" +version := "0.1.3" githubOwner := "spgroup" githubRepository := "control-dependence-analysis" diff --git a/src/main/scala/br/ufpe/cin/soot/cda/CDA.scala b/src/main/scala/br/ufpe/cin/soot/cda/CDA.scala index e16023a..967d87a 100644 --- a/src/main/scala/br/ufpe/cin/soot/cda/CDA.scala +++ b/src/main/scala/br/ufpe/cin/soot/cda/CDA.scala @@ -128,8 +128,8 @@ abstract class CDA { for (i <- edgeNodes) { var x = i.value.label - var cd = new StringLabel("ControlDependency") - var cdFalse = new StringLabel("ControlDependencyFalse") + var cd = new StringLabel("TrueEdge") + var cdFalse = new StringLabel("FalseEdge") if (x.isInstanceOf[StringLabel]) { var auxStr = "" @@ -146,7 +146,7 @@ abstract class CDA { var cdEdge = (cd.asInstanceOf[StringLabel]).edgeType.toString var cdEdgeFalse = (cdFalse.asInstanceOf[StringLabel]).edgeType.toString var a = (x.asInstanceOf[StringLabel]).edgeType.toString - if (a.equals(cdEdge)) { //If is Control Dependency Edge + if (a.equals(cdEdge)) { //If is Control Dependence Edge s ++= " "+auxStr + "[penwidth=3][label=\"T\"]" + "\n" } else if (a.equals(cdEdgeFalse)) { s ++= " "+auxStr + "[penwidth=3][label=\"F\"]" + "\n" diff --git a/src/main/scala/br/ufpe/cin/soot/cda/jimple/JCDA.scala b/src/main/scala/br/ufpe/cin/soot/cda/jimple/JCDA.scala index b228f1a..450547d 100644 --- a/src/main/scala/br/ufpe/cin/soot/cda/jimple/JCDA.scala +++ b/src/main/scala/br/ufpe/cin/soot/cda/jimple/JCDA.scala @@ -1,17 +1,17 @@ package br.ufpe.cin.soot.cda.jimple -import br.ufpe.cin.soot.graph.{ControlDependency, ControlDependencyFalse, LambdaNode, SimpleNode, Stmt, StmtNode, StringLabel} -import br.ufpe.cin.soot.cda.{CDA, SourceSinkDef} +import br.ufpe.cin.soot.graph.{FalseEdge, LambdaNode, SimpleNode, Stmt, StmtNode, StringLabel, TrueEdge, UnitDummy, UnitGraphNodes} +import br.ufpe.cin.soot.cda.CDA import br.ufpe.cin.soot.cda.rules.ArrayCopyRule import java.util import soot.toolkits.graph._ -import br.ufpe.cin.soot.graph.StmtNode import br.ufpe.cin.soot.cda.SourceSinkDef import com.typesafe.scalalogging.LazyLogging import soot.jimple.GotoStmt import soot.toolkits.graph.LoopNestTree import soot.toolkits.graph.ExceptionalBlockGraph +import soot.toolkits.graph.pdg.HashMutablePDG import soot.toolkits.scalar.SimpleLocalDefs import soot.{Local, Scene, SceneTransformer, SootMethod, Transform} @@ -46,11 +46,6 @@ abstract class JCDA extends CDA with Analysis with FieldSensitiveness with Sourc initAllocationSites() Scene.v().getEntryPoints.forEach(method => { - -// LoopConditionUnroller x = new LoopConditionUnroller().transform(method.retrieveActiveBody()) -// -// UnreachableCodeEliminator.v().transform(method.retrieveActiveBody()) - traverse(method) methods = methods + 1 }) @@ -62,335 +57,93 @@ abstract class JCDA extends CDA with Analysis with FieldSensitiveness with Sourc return } - traversedMethods.add(method) - - val body = method.retrieveActiveBody() - val blocks = new ExceptionalBlockGraph(body) - val EP = createNode(method) - - addCDEdges(method, EP, ListBuffer[ValueNodeType](), blocks, 0, true, ListBuffer[Int]()) - - } - - class ValueNodeType(va: Int, stmt: StmtNode) { - val value: Int = va - val nodeStmt: StmtNode = stmt - - def show(): String = stmt.stmt.toString - - override def equals(o: Any): Boolean = { - o match { - case stmt: StmtNode => stmt.value == value && stmt.stmt == nodeStmt - case _ => false - } - } - } - - def ValueNodeType(va: Int, stmt: StmtNode) { - val value: Int = va - val nodeStmt: StmtNode = stmt - - - } - - def addCDEdges(method: SootMethod, fromNode: StmtNode, visitedBlocks: ListBuffer[ValueNodeType], blocks: ExceptionalBlockGraph, value: Int, typeEdge: Boolean, exitList: ListBuffer[Int]): Unit = { - - visitedBlocks.foreach( x =>{ - if (x.value == value && x.nodeStmt == fromNode) { - return - } - }) - - addEdgesToBlock(method, fromNode, blocks, value, typeEdge) - - var block = blocks.getBlocks.get(value) - - visitedBlocks += new ValueNodeType(value, fromNode) - - if (block.getSuccs.size() == 2) { - - var ifStmt = blockToIfstatement(blocks, value) - var stmNodeX = createNode(method, ifStmt).stmt - visitedBlocks.foreach( x =>{ - if (x.nodeStmt.stmt.stmt == stmNodeX.stmt) { - return - } - }) - var succ = block.getSuccs - var pred = block.getPreds - - var enterBlock = block.getSuccs.get(0).getIndexInMethod - var exitBlock = block.getSuccs.get(1).getIndexInMethod - - var listContainsPathsLoop = ListBuffer[Boolean]() - var blockListContainsPathsLoop = ListBuffer[Int]() - - hasAPathFromTo(blocks.getBlocks.get(enterBlock), block, listContainsPathsLoop, blockListContainsPathsLoop) - var hasAPathLoop = false - if (listContainsPathsLoop.contains(true)){ - hasAPathLoop = true - } - - var hasReturnEdge = false - pred.forEach(x =>{ - if (x.getIndexInMethod > block.getIndexInMethod){ - hasReturnEdge = true - } - }) - - var valueHDC = HCD(blocks, value) - - var isAnIf = false - - var l2 = ListBuffer[Boolean]() - var l3 = ListBuffer[Int]() - l3 += block.getIndexInMethod - hasAPathFromTo(block.getSuccs.get(0), block.getSuccs.get(1), l2, l3) - if (l2.contains(true)){ - isAnIf = true - } - - var listContainsDoWhile = ListBuffer[Boolean]() - var blockListContainsDoWhile = ListBuffer[Int]() - - blockListContainsDoWhile += exitBlock - var itsDoWhile = true - hasAPathFromToDoWhile(blocks.getBlocks.get(enterBlock), block, exitBlock, listContainsDoWhile, blockListContainsDoWhile) - if (listContainsDoWhile.contains(true)){ - itsDoWhile = false - } - - var isALoop = false - var loopsList = new LoopNestTree(method.getActiveBody()) - - loopsList.forEach(l=> { - var st1 = l.getBackJumpStmt - var st2 = l.getHead - - if (ifStmt.equals(st2)){ - isALoop = true - } - }) - - var itsWhile = false - if (hasAPathLoop){ - itsWhile = true - } - - - var isAnIfElse = false - - if (enterBlock < valueHDC && exitBlock < valueHDC && valueHDC != -1 || (!isAnIf && !isALoop)) { - isAnIfElse = true - } - -// if (!isALoop && !isAnIfElse){ -// isAnIf = true -// } -/* - if (isAnIf){ - println("IF") - } - - if (isAnIfElse) { - println("IF-ELSE") - } - if (itsDoWhile){ - println("DO-WHILE") - } - if (itsWhile){ - println("WHILE") - } -*/ - if (valueHDC == -1 || isAnIf) { //It's an IF, WHILE, DO-WHILE, FOR and others - var exitNotVisited = true - if (isAnIf && !itsDoWhile) { - exitList.foreach(x =>{ - if (x == exitBlock) { - exitNotVisited = false + try { + + traversedMethods.add(method) + val body = method.retrieveActiveBody() + + val unitGraph= new UnitGraphNodes(body) + + val analysis = new MHGPostDominatorsFinder(unitGraph) + + unitGraph.forEach(unit => { + var edges = unitGraph.getSuccsOf(unit) + var ADominators = analysis.getDominators(unit) + + //Find a path with from unit to edges, using the post-dominator tree, excluding the LCA node + //Add True and False edge + var typeEd = true + var count = 0 + edges.forEach(unitAux =>{ + var BDominators = analysis.getDominators(unitAux) + var dItB = BDominators.iterator + while (dItB.hasNext()) { + val dsB = dItB.next() + if (!ADominators.contains(dsB)){ + if (count > 0){ + typeEd = false + } else { + typeEd = true //The first time is true + } + addControlDependenceEdge(unit, dsB, typeEd, method) } - }) - if (hasReturnEdge){ - exitNotVisited = true } - - exitList += exitBlock - } - if (itsDoWhile){ //swap enter and exit blocks - var aux = exitBlock - exitBlock = enterBlock - enterBlock = aux - } - - if (exitBlock > value+1 && exitNotVisited || itsDoWhile) { - addCDEdges(method, fromNode, visitedBlocks, blocks, exitBlock, typeEdge, exitList) - } - - addCDEdges(method, createNode(method, ifStmt), visitedBlocks, blocks, enterBlock, false, exitList) - - }else{ //It's an IF-ELSE - - addCDEdges(method, fromNode, visitedBlocks, blocks, valueHDC, false, exitList) - - visitedBlocks += new ValueNodeType(valueHDC, createNode(method, ifStmt)) - exitList += valueHDC - - addCDEdges(method, createNode(method, ifStmt), visitedBlocks, blocks, exitBlock, false, exitList) - addCDEdges(method, createNode(method, ifStmt), visitedBlocks, blocks, enterBlock, true, exitList) - - } - - }else if (block.getSuccs.size() == 1){ - exitList.foreach(x =>{ - if (x == block.getSuccs.get(0).getIndexInMethod) { - return - } + count = count + 1 + }) }) - if (isNotContainsGoto(block.getSuccs.get(0)) && isNotContainsGoto(block)) { - addCDEdges(method, fromNode, visitedBlocks, blocks, block.getSuccs.get(0).getIndexInMethod, typeEdge, exitList) + } catch { + case e: NullPointerException => { + println ("Error creating node, an invalid statement.") } - } - } - - def isNotContainsGoto(block: Block): Boolean = { - var contains = true - block.forEach(x => { - if (x.isInstanceOf[GotoStmt]) { - contains = false + case e: Exception => { + println ("An invalid statement.") } - }) - return contains - } - - def hasAPathFromToDoWhile(enter: Block, block: Block, exit: Int, visitedPaths: ListBuffer[Boolean], visitedBlock: ListBuffer[Int]): Unit = { - - if (visitedBlock.contains(enter.getIndexInMethod)) return - - visitedBlock += enter.getIndexInMethod - - val suc = enter.getSuccs - - if (suc.size() > 0){ - suc.forEach(x => { - - if (x.getIndexInMethod == block.getIndexInMethod || x.getIndexInMethod == exit) { - visitedPaths += true - return visitedPaths - } - if (!visitedBlock.contains(x.getIndexInMethod) && x.getIndexInMethod > block.getIndexInMethod){ - hasAPathFromToDoWhile(x, block, exit, visitedPaths, visitedBlock) - } - - }) } } - def hasAPathFromTo(enter: Block, exit: Block, visitedPaths: ListBuffer[Boolean], visitedBlock: ListBuffer[Int]): Unit = { - - if (visitedBlock.contains(enter.getIndexInMethod)) return - - visitedBlock += enter.getIndexInMethod - - val suc = enter.getSuccs + def addControlDependenceEdge(s: soot.Unit, t: soot.Unit, typeEdge: Boolean, method: SootMethod): Unit = { + if (s.isInstanceOf[GotoStmt] || t.isInstanceOf[GotoStmt]) return + var source = createNode(method, s) + var target = createNode(method, t) - if (suc.size() > 0){ - suc.forEach(x => { - - if (x.getIndexInMethod == exit.getIndexInMethod) { - visitedPaths += true - return visitedPaths - } - if (!visitedBlock.contains(x.getIndexInMethod)){ - hasAPathFromTo(x, exit, visitedPaths, visitedBlock) - } - - }) + if (s.isInstanceOf[UnitDummy]) { + source = createDummyNode(s, method) } - } - def blockToIfstatement(blocks: ExceptionalBlockGraph, value: Int): soot.Unit ={ - blocks.getBlocks.get(value).forEach(unit => { - if (unit.isInstanceOf[soot.jimple.IfStmt]){ - return unit - } - }) - return null - } - - def HCD(blocks: ExceptionalBlockGraph, pos: Int): Int = { - var left = ListBuffer[Int]() - var right = ListBuffer[Int]() - - left += pos - right += pos - //Succs of left - var lIndex = blocks.getBlocks.get(pos).getSuccs.get(0).getIndexInMethod - getSuccsDFS(blocks, lIndex, left) - - //Succs of right - var rIndex = blocks.getBlocks.get(pos).getSuccs.get(1).getIndexInMethod - getSuccsDFS(blocks, rIndex, right) - -// println(pos+" Left:"+left) -// println(pos+" Left:"+right) - // TODO: to use Binary Search - for (l <- left) { - if (right.contains(l) && l != pos){ - return l - } + if (t.isInstanceOf[UnitDummy]){ + target = createDummyNode(t, method) } - return -1 - } - def getSuccsDFS(blocks: ExceptionalBlockGraph, v: Int, visited: ListBuffer[Int]): Unit = { - if (visited.contains(v)) { - return - }else { - visited += v - var block = blocks.getBlocks.get(v).getSuccs - for (i <- 0 to block.size()-1){ - var vActual = block.get(i).getIndexInMethod - if (!visited.contains(vActual)) { - getSuccsDFS(blocks, vActual, visited) - } - } - } - } - - def addEdgesToBlock(method: SootMethod, sourceStmt: StmtNode, blocks: ExceptionalBlockGraph, value: Int, typeEdge: Boolean): Unit = { - blocks.getBlocks.get(value).forEach(unit => { - if (sourceStmt==null){ - addNodeEP(method, unit) - }else{ - addCDEdgeFromIf(sourceStmt, unit, method, typeEdge) - } - }) + addEdgeControlDependence(source, target, typeEdge) } - def addEPEdges(method: SootMethod, block: Block): Unit = { - block.forEach(unit => { - addNodeEP(method, unit) - }) - } + def createDummyNode(unit: soot.Unit, method: SootMethod): StmtNode = { + var node = createNode(method, unit) - def addNodeEP(method: SootMethod, unit: soot.Unit): Boolean = { - val source = createNode(method) - val target = createNode(method, unit) - addEdgeControlDependency(source, target, true) + if (unit.toString().contains("EntryPoint")) { + node = createEntryPointNode(method) + } else if (unit.toString().contains("Start")) { + node = createStartNode(method) + } else if (unit.toString().contains("Stop")) { + node = createStopNode(method) + } + return node } - def addCDEdgeFromIf(sourceStmt: StmtNode, targetStmt: soot.Unit, method: SootMethod, typeEdge: Boolean): Unit ={ - val target = createNode(method, targetStmt) - if (!targetStmt.isInstanceOf[GotoStmt]){ - addEdgeControlDependency(sourceStmt, target, typeEdge) + def addEdgeControlDependence(source: LambdaNode, target: LambdaNode, typeEdge: Boolean): Boolean = { + var res = false + if(!runInFullSparsenessMode() || true) { + val label = new StringLabel( if (typeEdge) (TrueEdge.toString) else (FalseEdge.toString)) + svg.addEdge(source, target, label) + res = true } + return res } - def addEdgeControlDependency(source: LambdaNode, target: LambdaNode, typeEdge: Boolean): Boolean = { + def addEdge(source: LambdaNode, target: LambdaNode): Boolean = { var res = false if(!runInFullSparsenessMode() || true) { - val label = new StringLabel( if (typeEdge) (ControlDependency.toString) else (ControlDependencyFalse.toString)) - svg.addEdge(source, target, label) + svg.addEdge(source, target) res = true } return res @@ -416,12 +169,50 @@ abstract class JCDA extends CDA with Analysis with FieldSensitiveness with Sourc /* * creates a graph node from a sootMethod / sootUnit */ - def createNode(method: SootMethod, stmt: soot.Unit): StmtNode = - new StmtNode(Stmt(method.getDeclaringClass.toString, method.getSignature, stmt.toString, stmt.getJavaSourceStartLineNumber), analyze(stmt)) + def createNode(method: SootMethod, stmt: soot.Unit): StmtNode = { + try{ + return new StmtNode(Stmt(method.getDeclaringClass.toString, method.getSignature, stmt.toString, stmt.getJavaSourceStartLineNumber), analyze(stmt)) + }catch { + case e: NullPointerException => { + println("Error creating node, an invalid statement.") + return null + } + } + } - def createNode(method: SootMethod): StmtNode = - new StmtNode(Stmt(method.getDeclaringClass.toString, method.getSignature, "Entry Point", 0), SimpleNode) + def createEntryPointNode(method: SootMethod): StmtNode = { + try { + return new StmtNode(Stmt(method.getDeclaringClass.toString, method.getSignature, "Entry Point", 0), SimpleNode) + } catch { + case e: NullPointerException => { + println ("Error creating node, an invalid statement.") + return null + } + } + } + + def createStartNode(method: SootMethod): StmtNode = { + try { + return new StmtNode(Stmt(method.getDeclaringClass.toString, method.getSignature, "Start", 0), SimpleNode) + } catch { + case e: NullPointerException => { + println ("Error creating node, an invalid statement.") + return null + } + } + } + + def createStopNode(method: SootMethod): StmtNode = { + try { + return new StmtNode(Stmt(method.getDeclaringClass.toString, method.getSignature, "Stop", 0), SimpleNode) + } catch { + case e: NullPointerException => { + println ("Error creating node, an invalid statement.") + return null + } + } + } /** * Override this method in the case that diff --git a/src/main/scala/br/ufpe/cin/soot/graph/AbstractDummyStmt.java b/src/main/scala/br/ufpe/cin/soot/graph/AbstractDummyStmt.java new file mode 100644 index 0000000..6d1016a --- /dev/null +++ b/src/main/scala/br/ufpe/cin/soot/graph/AbstractDummyStmt.java @@ -0,0 +1,31 @@ +package br.ufpe.cin.soot.graph; + +import soot.jimple.internal.AbstractStmt; +import java.util.List; +import java.util.ArrayList; + +import soot.Value; +import soot.ValueBox; + +@SuppressWarnings("serial") +public abstract class AbstractDummyStmt extends AbstractStmt { + + final ValueBox opBox; + + protected AbstractDummyStmt(ValueBox opBox) { + this.opBox = opBox; + } + + final public Value getOp() { + return opBox.getValue(); + } + + final public void setOp(Value op) { + opBox.setValue(op); + } + + final public ValueBox getOpBox() { + return opBox; + } + +} \ No newline at end of file diff --git a/src/main/scala/br/ufpe/cin/soot/graph/Graph.scala b/src/main/scala/br/ufpe/cin/soot/graph/Graph.scala index 1666b0f..451353d 100644 --- a/src/main/scala/br/ufpe/cin/soot/graph/Graph.scala +++ b/src/main/scala/br/ufpe/cin/soot/graph/Graph.scala @@ -1,6 +1,10 @@ package br.ufpe.cin.soot.graph import scalax.collection.edge.LDiEdge +import soot.options.Options +import soot.{Body, Timers} +import soot.toolkits.graph.UnitGraph + import scala.collection.immutable.HashSet sealed trait NodeType @@ -14,8 +18,8 @@ sealed trait EdgeType case object CallSiteOpenEdge extends EdgeType { def instance: CallSiteOpenEdge.type = this } case object CallSiteCloseEdge extends EdgeType { def instance: CallSiteCloseEdge.type = this } case object SimpleEdge extends EdgeType { def instance: SimpleEdge.type = this } -case object ControlDependency extends EdgeType { def instance: ControlDependency.type = this } -case object ControlDependencyFalse extends EdgeType { def instance: ControlDependencyFalse.type = this } +case object TrueEdge extends EdgeType { def instance: TrueEdge.type = this } +case object FalseEdge extends EdgeType { def instance: FalseEdge.type = this } case class Stmt(className: String, method: String, stmt: String, line: Int) @@ -34,10 +38,10 @@ class StringLabel(label: String) extends LambdaLabel { object EdgeType { def convert(edge: String): EdgeType = { - if(edge.equals(ControlDependency.toString)) { - ControlDependency - } else if (edge.equals(ControlDependencyFalse.toString)) { - ControlDependencyFalse + if(edge.equals(TrueEdge.toString)) { + TrueEdge + } else if (edge.equals(FalseEdge.toString)) { + FalseEdge } else SimpleEdge } @@ -123,7 +127,8 @@ case class StmtNode(stmt: Stmt, stmtType: NodeType) extends LambdaNode { override def hashCode(): Int = 2 * stmt.hashCode() + nodeType.hashCode() } -class Graph() { +class Graph(){ + val graph = scalax.collection.mutable.Graph.empty[LambdaNode, LDiEdge] var fullGraph: Boolean = false diff --git a/src/main/scala/br/ufpe/cin/soot/graph/InterfaceUnitDummyJava.java b/src/main/scala/br/ufpe/cin/soot/graph/InterfaceUnitDummyJava.java new file mode 100644 index 0000000..697b2be --- /dev/null +++ b/src/main/scala/br/ufpe/cin/soot/graph/InterfaceUnitDummyJava.java @@ -0,0 +1,63 @@ +package br.ufpe.cin.soot.graph; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 1999 Patrice Pominville, Raja Vallee-Rai + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 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 Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import soot.Body; +import soot.Trap; +import soot.Unit; +import soot.Value; +import soot.baf.ThrowInst; +import soot.jimple.ThrowStmt; + +/** + *

+ * Represents a control flow graph for a {@link Body} instance where the nodes are {@link Unit} instances, and where control + * flow associated with exceptions is taken into account. + *

+ * + *

+ * To describe precisely the circumstances under which exceptional edges are added to the graph, we need to distinguish the + * exceptions thrown explicitly by a throw instruction from the exceptions which are thrown implicitly by the VM + * to signal an error it encounters in the course of executing an instruction, which need not be a throw. + *

+ * + *

+ * For every {@link ThrowInst} or {@link ThrowStmt} Unit which may explicitly throw an exception that would be + * caught by a {@link Trap} in the Body, there will be an edge from the throw Unit to + * the Trap handler's first Unit. + *

+ * + *

+ * For every Unit which may implicitly throw an exception that could be caught by a Trap in the + * Body, there will be an edge from each of the excepting Unit's predecessors to the + * Trap handler's first Unit (since any of those predecessors may have been the last + * Unit to complete execution before the handler starts execution). If the excepting Unit might + * have the side effect of changing some field, then there will definitely be an edge from the excepting Unit + * itself to its handlers, since the side effect might occur before the exception is raised. If the excepting + * Unit has no side effects, then parameters passed to the ExceptionalUnitGraph constructor + * determine whether or not there is an edge from the excepting Unit itself to the handler Unit. + *

+ */ +public interface InterfaceUnitDummyJava extends Unit{ + public Value getUnitValue(); +} \ No newline at end of file diff --git a/src/main/scala/br/ufpe/cin/soot/graph/LocalDummy.java b/src/main/scala/br/ufpe/cin/soot/graph/LocalDummy.java new file mode 100644 index 0000000..f16d456 --- /dev/null +++ b/src/main/scala/br/ufpe/cin/soot/graph/LocalDummy.java @@ -0,0 +1,178 @@ +package br.ufpe.cin.soot.graph; + +import soot.*; +import soot.baf.*; +import soot.jimple.*; + + +/** + *

+ * Represents a control flow graph for a {@link Body} instance where the nodes are {@link Unit} instances, and where control + * flow associated with exceptions is taken into account. + *

+ * + *

+ * To describe precisely the circumstances under which exceptional edges are added to the graph, we need to distinguish the + * exceptions thrown explicitly by a throw instruction from the exceptions which are thrown implicitly by the VM + * to signal an error it encounters in the course of executing an instruction, which need not be a throw. + *

+ * + *

+ * For every {@link ThrowInst} or {@link ThrowStmt} Unit which may explicitly throw an exception that would be + * caught by a {@link Trap} in the Body, there will be an edge from the throw Unit to + * the Trap handler's first Unit. + *

+ * + *

+ * For every Unit which may implicitly throw an exception that could be caught by a Trap in the + * Body, there will be an edge from each of the excepting Unit's predecessors to the + * Trap handler's first Unit (since any of those predecessors may have been the last + * Unit to complete execution before the handler starts execution). If the excepting Unit might + * have the side effect of changing some field, then there will definitely be an edge from the excepting Unit + * itself to its handlers, since the side effect might occur before the exception is raised. If the excepting + * Unit has no side effects, then parameters passed to the ExceptionalUnitGraph constructor + * determine whether or not there is an edge from the excepting Unit itself to the handler Unit. + *

+ */ + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 1999 Patrick Lam + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 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 Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import java.util.Collections; +import java.util.List; + +import soot.Local; +import soot.Scene; +import soot.Type; +import soot.Unit; +import soot.UnitPrinter; +import soot.ValueBox; +import soot.baf.Baf; +import soot.jimple.ConvertToBaf; +import soot.jimple.JimpleToBafContext; +import soot.jimple.JimpleValueSwitch; +import soot.util.Numberer; +import soot.util.Switch; + +public class LocalDummy implements Local, ConvertToBaf { + protected String name; + Type type; + + public LocalDummy(String name, Type type) { + setName(name); + setType(type); + Numberer numberer = Scene.v().getLocalNumberer(); + if (numberer != null) { + numberer.add(this); + } + } + + /** Returns true if the given object is structurally equal to this one. */ + @Override + public boolean equivTo(Object o) { + return this.equals(o); + } + + /** + * Returns a hash code for this object, consistent with structural equality. + */ + @Override + public int equivHashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; + } + + /** Returns a clone of the current JimpleLocal. */ + @Override + public Object clone() { + // do not intern the name again + LocalDummy local = new LocalDummy(null, type); + local.name = name; + return local; + } + + /** Returns the name of this object. */ + @Override + public String getName() { + return name; + } + + /** Sets the name of this object as given. */ + @Override + public void setName(String name) { + this.name = (name == null) ? null : name.intern(); + } + + /** Returns the type of this local. */ + @Override + public Type getType() { + return type; + } + + /** Sets the type of this local. */ + @Override + public void setType(Type t) { + this.type = t; + } + + @Override + public String toString() { + return getName(); + } + + @Override + public void toString(UnitPrinter up) { + up.local(this); + } + + @Override + public final List getUseBoxes() { + return Collections.emptyList(); + } + + @Override + public void apply(Switch sw) { + ((JimpleValueSwitch) sw).caseLocal(this); + } + + @Override + public void convertToBaf(JimpleToBafContext context, List out) { + Unit u = Baf.v().newLoadInst(getType(), context.getBafLocalOfJimpleLocal(this)); + u.addAllTagsOf(context.getCurrentUnit()); + out.add(u); + } + + @Override + public final int getNumber() { + return number; + } + + @Override + public void setNumber(int number) { + this.number = number; + } + + private volatile int number = 0; +} \ No newline at end of file diff --git a/src/main/scala/br/ufpe/cin/soot/graph/UnitDummy.java b/src/main/scala/br/ufpe/cin/soot/graph/UnitDummy.java new file mode 100644 index 0000000..5037823 --- /dev/null +++ b/src/main/scala/br/ufpe/cin/soot/graph/UnitDummy.java @@ -0,0 +1,52 @@ +package br.ufpe.cin.soot.graph; + +import soot.Unit; +import soot.UnitPrinter; +import soot.Value; +import soot.ValueBox; +import soot.jimple.*; +import java.util.List; + +public class UnitDummy extends AbstractDummyStmt implements soot.jimple.Stmt { + private String dummyNode; + + public UnitDummy(Value op, String node) { + this(Jimple.v().newVariableBox(op)); + this.dummyNode = node; + } + + public String getDummyNode(){ + return this.dummyNode; + } + + public void setDummyNode(String dummyNode){ + this.dummyNode = dummyNode; + } + + protected UnitDummy(ValueBox opBox) { + super(opBox); + } + + public Object clone() { + return null; + } + + public String toString() { + return this.dummyNode; + } + + public void toString(UnitPrinter up) { + up.literal(this.dummyNode); + } + + public void convertToBaf(JimpleToBafContext context, List out) {} + + public boolean fallsThrough() { + return true; + } + + public boolean branches() { + return false; + } + +} \ No newline at end of file diff --git a/src/main/scala/br/ufpe/cin/soot/graph/UnitGraphNodes.java b/src/main/scala/br/ufpe/cin/soot/graph/UnitGraphNodes.java new file mode 100644 index 0000000..1861a42 --- /dev/null +++ b/src/main/scala/br/ufpe/cin/soot/graph/UnitGraphNodes.java @@ -0,0 +1,858 @@ +package br.ufpe.cin.soot.graph; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 1999 Patrice Pominville, Raja Vallee-Rai + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 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 Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import soot.*; +import soot.baf.Inst; +import soot.baf.NewInst; +import soot.baf.StaticGetInst; +import soot.baf.StaticPutInst; +import soot.baf.ThrowInst; +import soot.jimple.*; +import soot.options.Options; +import soot.toolkits.exceptions.ThrowAnalysis; +import soot.toolkits.exceptions.ThrowableSet; +import soot.toolkits.graph.ExceptionalGraph; +import soot.toolkits.graph.UnitGraph; +import soot.util.ArraySet; +import soot.util.Chain; + +/** + *

+ * Represents a control flow graph for a {@link Body} instance where the nodes are {@link Unit} instances, and where control + * flow associated with exceptions is taken into account. + *

+ * + *

+ * To describe precisely the circumstances under which exceptional edges are added to the graph, we need to distinguish the + * exceptions thrown explicitly by a throw instruction from the exceptions which are thrown implicitly by the VM + * to signal an error it encounters in the course of executing an instruction, which need not be a throw. + *

+ * + *

+ * For every {@link ThrowInst} or {@link ThrowStmt} Unit which may explicitly throw an exception that would be + * caught by a {@link Trap} in the Body, there will be an edge from the throw Unit to + * the Trap handler's first Unit. + *

+ * + *

+ * For every Unit which may implicitly throw an exception that could be caught by a Trap in the + * Body, there will be an edge from each of the excepting Unit's predecessors to the + * Trap handler's first Unit (since any of those predecessors may have been the last + * Unit to complete execution before the handler starts execution). If the excepting Unit might + * have the side effect of changing some field, then there will definitely be an edge from the excepting Unit + * itself to its handlers, since the side effect might occur before the exception is raised. If the excepting + * Unit has no side effects, then parameters passed to the ExceptionalUnitGraph constructor + * determine whether or not there is an edge from the excepting Unit itself to the handler Unit. + *

+ */ +public class UnitGraphNodes extends UnitGraph implements ExceptionalGraph { + protected Map> unitToUnexceptionalSuccs; // If there are no + // Traps within + protected Map> unitToUnexceptionalPreds; // the method, + + protected UnitDummy STOP; + protected UnitDummy START; + protected UnitDummy ENTRYPOINT; + + // these will be + // the + // same maps as unitToSuccs and + // unitToPreds. + + protected Map> unitToExceptionalSuccs; + protected Map> unitToExceptionalPreds; + protected Map> unitToExceptionDests; + + protected ThrowAnalysis throwAnalysis; // Cached reference to the + + // analysis used to generate this + // graph, for generating responses + // to getExceptionDests() on the + // fly for nodes from which all + // exceptions escape the method. + + /** + * Constructs the graph for a given Body instance, using the ThrowAnalysis and + * omitExceptingUnitEdges value that are passed as parameters. + * + * @param body + * the Body from which to build a graph. + * + * @param throwAnalysis + * the source of information about the exceptions which each {@link Unit} may throw. + * + * @param omitExceptingUnitEdges + * indicates whether the CFG should omit edges to a handler from trapped Units which may implicitly + * throw an exception which the handler catches but which have no potential side effects. The CFG will contain + * edges to the handler from all predecessors of Units which may implicitly throw a caught exception + * regardless of the setting for this parameter. If this parameter is false, there will also be edges + * to the handler from all the potentially excepting Units themselves. If this parameter is + * true, there will be edges to the handler from the excepting Units themselves only if + * they have potential side effects (or if they are themselves the predecessors of other potentially excepting + * Units). A setting of true produces CFGs which allow for more precise analyses, since + * a Unit without side effects has no effect on the computational state when it throws an exception. + * Use settings of false for compatibility with more conservative analyses, or to cater to + * conservative bytecode verifiers. + */ + public UnitGraphNodes(Body body, ThrowAnalysis throwAnalysis, boolean omitExceptingUnitEdges) { + super(body); + initialize(throwAnalysis, omitExceptingUnitEdges); + } + + /** + * Constructs the graph from a given Body instance using the passed {@link ThrowAnalysis} and a default value, provided by + * the {@link Options} class, for the omitExceptingUnitEdges parameter. + * + * @param body + * the {@link Body} from which to build a graph. + * + * @param throwAnalysis + * the source of information about the exceptions which each {@link Unit} may throw. + * + */ + public UnitGraphNodes(Body body, ThrowAnalysis throwAnalysis) { + this(body, throwAnalysis, Options.v().omit_excepting_unit_edges()); + } + + /** + * Constructs the graph from a given Body instance, using the {@link Scene} 's default {@link ThrowAnalysis} to estimate + * the set of exceptions that each {@link Unit} might throw and a default value, provided by the {@link Options} class, for + * the omitExceptingUnitEdges parameter. + * + * @param body + * the Body from which to build a graph. + * + */ + public UnitGraphNodes(Body body) { + this(body, Scene.v().getDefaultThrowAnalysis(), Options.v().omit_excepting_unit_edges()); + } + + /** + *

+ * Allocates an ExceptionalUnitGraph object without initializing it. This “partial constructor” is + * provided for the benefit of subclasses whose constructors need to perform some subclass-specific processing before + * actually creating the graph edges (because, for example, the subclass overrides a utility method like + * {@link #buildExceptionDests(ThrowAnalysis)} or {@link #buildExceptionalEdges(ThrowAnalysis, Map, Map, Map, boolean)} + * with a replacement method that depends on additional parameters passed to the subclass's constructor). The subclass + * constructor is responsible for calling {@link #initialize(ThrowAnalysis, boolean)}, or otherwise performing the + * initialization required to implement ExceptionalUnitGraph's interface. + *

+ * + *

+ * Clients who opt to extend ExceptionalUnitGraph should be warned that the class has not been carefully + * designed for inheritance; code that uses the protected members of this class may need to be rewritten for + * each new Soot release. + *

+ * + * @param body + * the Body from which to build a graph. + * + * @param ignoredBogusParameter + * a meaningless placeholder, which exists solely to distinguish this constructor from the public + * {@link #ExceptionalUnitGraph(Body)} constructor. + */ + protected UnitGraphNodes(Body body, boolean ignoredBogusParameter) { + super(body); + } + + /** + * Performs the real work of constructing an ExceptionalUnitGraph, factored out of the constructors so that + * subclasses have the option to delay creating the graph's edges until after they have performed some subclass-specific + * initialization. + * + * @param throwAnalysis + * the source of information about the exceptions which each {@link Unit} may throw. + * + * @param omitExceptingUnitEdges + * indicates whether the CFG should omit edges to a handler from trapped Units which may throw an + * exception which the handler catches but which have no potential side effects. + */ + protected void initialize(ThrowAnalysis throwAnalysis, boolean omitExceptingUnitEdges) { + int size = unitChain.size(); + Set trapUnitsThatAreHeads = Collections.emptySet(); + + if (Options.v().time()) { + Timers.v().graphTimer.start(); + } + + unitToUnexceptionalSuccs = new LinkedHashMap>(size * 2 + 1, 0.7f); + unitToUnexceptionalPreds = new LinkedHashMap>(size * 2 + 1, 0.7f); + buildUnexceptionalEdges(unitToUnexceptionalSuccs, unitToUnexceptionalPreds); + this.throwAnalysis = throwAnalysis; + + if (body.getTraps().size() == 0) { + // No handlers, so all exceptional control flow exits the + // method. + unitToExceptionDests = Collections.emptyMap(); + unitToExceptionalSuccs = Collections.emptyMap(); + unitToExceptionalPreds = Collections.emptyMap(); + unitToSuccs = unitToUnexceptionalSuccs; + unitToPreds = unitToUnexceptionalPreds; + + } else { + unitToExceptionDests = buildExceptionDests(throwAnalysis); + unitToExceptionalSuccs = new LinkedHashMap>(unitToExceptionDests.size() * 2 + 1, 0.7f); + unitToExceptionalPreds = new LinkedHashMap>(body.getTraps().size() * 2 + 1, 0.7f); + trapUnitsThatAreHeads = buildExceptionalEdges(throwAnalysis, unitToExceptionDests, unitToExceptionalSuccs, + unitToExceptionalPreds, omitExceptingUnitEdges); + + // We'll need separate maps for the combined + // exceptional and unexceptional edges: + unitToSuccs = combineMapValues(unitToUnexceptionalSuccs, unitToExceptionalSuccs); + unitToPreds = combineMapValues(unitToUnexceptionalPreds, unitToExceptionalPreds); + } + + buildHeadsAndTails(trapUnitsThatAreHeads); + + Local dummyLocal = new LocalDummy("Start", soot.NullType.v()); + UnitDummy startStmt = new UnitDummy(dummyLocal, "Start"); + + dummyLocal = new LocalDummy("Stop", soot.NullType.v()); + UnitDummy stopStmt = new UnitDummy(dummyLocal, "Stop"); + + dummyLocal = new LocalDummy("EntryPoint", soot.NullType.v()); + UnitDummy entryPointStmt = new UnitDummy(dummyLocal, "EntryPoint"); + + unitChain.addFirst(entryPointStmt); + unitChain.addFirst(startStmt); + unitChain.addFirst(stopStmt); + + this.ENTRYPOINT = entryPointStmt; + this.START = startStmt; + this.STOP = stopStmt; + + //Add Start to heads + List preds = new ArrayList(); + preds.add(entryPointStmt); + List succs = new ArrayList(); + for (Unit h: heads){ + succs.add(h); + } + unitToSuccs.put(startStmt, succs); + unitToPreds.put(startStmt, preds); + + //Add Entry to Start and Stop + preds = new ArrayList(); + succs = new ArrayList(); + succs.add(startStmt); + succs.add(stopStmt); + unitToSuccs.put(entryPointStmt, succs); + unitToPreds.put(entryPointStmt, preds); + + + //Add Stop from tails + preds = new ArrayList(); + succs = new ArrayList(); + + for (Unit t: tails){ + preds.add(t); + } + + unitToSuccs.put(stopStmt, succs); + unitToPreds.put(stopStmt, preds); + + //Add edge from tails to Stop + + succs = new ArrayList(); + succs.add(stopStmt); + + for (Unit s : tails) { + unitToSuccs.put(s, succs); + } + + this.heads.clear(); + this.heads.add(entryPointStmt); + this.tails.clear(); + this.tails.add(stopStmt); + + if (Options.v().time()) { + Timers.v().graphTimer.end(); + } + + soot.util.PhaseDumper.v().dumpGraph(this); + } + + /** + *

+ * Utility method used in the construction of {@link soot.toolkits.graph.UnitGraph UnitGraph} variants which include + * exceptional control flow. It determines which {@link Unit}s may throw exceptions that would be caught by {@link Trap}s + * within the method. + *

+ * + * @param throwAnalysis + * The source of information about which exceptions each Unit may throw. + * + * @return null if no Units in the method throw any exceptions caught within the method. + * Otherwise, a {@link Map} from Units to {@link Collection}s of {@link ExceptionDest}s. + * + *

+ * The returned map has an idiosyncracy which is hidden from most client code, but which is exposed to subclasses + * extending ExceptionalUnitGraph. If a Unit throws one or more exceptions which are + * caught within the method, it will be mapped to a Collection of ExceptionDests + * describing the sets of exceptions that the Unit might throw to each {@link Trap}. But if all of a + * Unit's exceptions escape the method, it will be mapped to nullCollection containing a single ExceptionDest with a null trap. (The + * special case for Units with no caught exceptions allows buildExceptionDests() to + * ignore completely Units which are outside the scope of all Traps.) + *

+ */ + protected Map> buildExceptionDests(ThrowAnalysis throwAnalysis) { + Chain units = body.getUnits(); + Map unitToUncaughtThrowables = new LinkedHashMap(units.size()); + Map> result = null; + + // Record the caught exceptions. + for (Trap trap : body.getTraps()) { + RefType catcher = trap.getException().getType(); + for (Iterator unitIt = units.iterator(trap.getBeginUnit(), units.getPredOf(trap.getEndUnit())); unitIt + .hasNext();) { + Unit unit = unitIt.next(); + ThrowableSet thrownSet = unitToUncaughtThrowables.get(unit); + if (thrownSet == null) { + thrownSet = throwAnalysis.mightThrow(unit); + } + + ThrowableSet.Pair catchableAs = thrownSet.whichCatchableAs(catcher); + if (!catchableAs.getCaught().equals(ThrowableSet.Manager.v().EMPTY)) { + result = addDestToMap(result, unit, trap, catchableAs.getCaught()); + unitToUncaughtThrowables.put(unit, catchableAs.getUncaught()); + } else { + assert thrownSet.equals(catchableAs.getUncaught()) : "ExceptionalUnitGraph.buildExceptionDests(): " + + "catchableAs.caught == EMPTY, but catchableAs.uncaught != thrownSet" + System.getProperty("line.separator") + + body.getMethod().getSubSignature() + " Unit: " + unit.toString() + System.getProperty("line.separator") + + " catchableAs.getUncaught() == " + catchableAs.getUncaught().toString() + + System.getProperty("line.separator") + " thrownSet == " + thrownSet.toString(); + } + } + } + + for (Map.Entry entry : unitToUncaughtThrowables.entrySet()) { + Unit unit = entry.getKey(); + ThrowableSet escaping = entry.getValue(); + if (escaping != ThrowableSet.Manager.v().EMPTY) { + result = addDestToMap(result, unit, null, escaping); + } + } + if (result == null) { + result = Collections.emptyMap(); + } + return result; + } + + /** + * A utility method for recording the exceptions that a Unit throws to a particular Trap. Note + * that this method relies on the fact that the call to add escaping exceptions for a Unit will always follow + * all calls for its caught exceptions. + * + * @param map + * A Map from Units to Collections of ExceptionDests. + * null if no exceptions have been recorded yet. + * + * @param u + * The Unit throwing the exceptions. + * + * @param t + * The Trap which catches the exceptions, or null if the exceptions escape the method. + * + * @param caught + * The set of exception types thrown by u which are caught by t. + * + * @return a Map which whose contents are equivalent to the input map, plus the information that + * u throws caught to t. + */ + private Map> addDestToMap(Map> map, Unit u, Trap t, + ThrowableSet caught) { + Collection dests = (map == null ? null : map.get(u)); + if (dests == null) { + if (t == null) { + // All exceptions from u escape, so don't record any. + return map; + } else { + if (map == null) { + map = new LinkedHashMap>(unitChain.size() * 2 + 1); + } + dests = new ArrayList(3); + map.put(u, dests); + } + } + dests.add(new ExceptionDest(t, caught)); + return map; + } + + /** + * Method to compute the edges corresponding to exceptional control flow. + * + * @param throwAnalysis + * the source of information about the exceptions which each {@link Unit} may throw. + * + * @param unitToExceptionDests + * A Map from {@link Unit}s to {@link Collection}s of {@link ExceptionalUnitGraph.ExceptionDest + * ExceptionDest}s which represent the handlers that might catch exceptions thrown by the Unit. This + * is an ``in parameter''. + * + * @param unitToSuccs + * A Map from Units to {@link List}s of Units. This is an ``out + * parameter''; buildExceptionalEdges will add a mapping from every Unit in the body + * that may throw an exception that could be caught by a {@link Trap} in the body to a list of its exceptional + * successors. + * + * @param unitToPreds + * A Map from Units to Lists of Units. This is an ``out + * parameter''; buildExceptionalEdges will add a mapping from each handler unit that may catch an + * exception to the list of Units whose exceptions it may catch. + * @param omitExceptingUnitEdges + * Indicates whether to omit exceptional edges from excepting units which lack side effects + * + * @return a {@link Set} of trap Units that might catch exceptions thrown by the first Unit in + * the {@link Body} associated with the graph being constructed. Such trap Units may need to be added + * to the list of heads (depending on your definition of heads), since they can be the first Unit in + * the Body which actually completes execution. + */ + protected Set buildExceptionalEdges(ThrowAnalysis throwAnalysis, + Map> unitToExceptionDests, Map> unitToSuccs, + Map> unitToPreds, boolean omitExceptingUnitEdges) { + Set trapsThatAreHeads = new ArraySet(); + Unit entryPoint = unitChain.getFirst(); + for (Entry> entry : unitToExceptionDests.entrySet()) { + Unit thrower = entry.getKey(); + List throwersPreds = getUnexceptionalPredsOf(thrower); + Collection dests = entry.getValue(); + + // We need to recognize: + // - caught exceptions for which we must add edges from the + // thrower's predecessors to the catcher: + // - all exceptions of non-throw instructions; + // - implicit exceptions of throw instructions. + // + // - caught exceptions where we must add edges from the + // thrower itself to the catcher: + // - any exception of non-throw instructions if + // omitExceptingUnitEdges is not set. + // - any exception of non-throw instructions with side effects. + // - explicit exceptions of throw instructions + // - implicit exceptions of throw instructions if + // omitExceptingUnitEdges is not set. + // - implicit exceptions of throw instructions with possible + // side effects (this is only possible for the grimp + // IR, where the throw's argument may be an + // expression---probably a NewInvokeExpr---which + // might have executed partially before the + // exception arose). + // + // Note that a throw instruction may be capable of throwing a given + // Throwable type both implicitly and explicitly. + // + // We track these situations using predThrowables and + // selfThrowables. Essentially predThrowables is the set + // of Throwable types to whose catchers there should be + // edges from predecessors of the thrower, while + // selfThrowables is the set of Throwable types to whose + // catchers there should be edges from the thrower itself, + // but we we take some short cuts to avoid calling + // ThrowableSet.catchableAs() when we can avoid it. + + boolean alwaysAddSelfEdges = ((!omitExceptingUnitEdges) || mightHaveSideEffects(thrower)); + ThrowableSet predThrowables = null; + ThrowableSet selfThrowables = null; + if (thrower instanceof ThrowInst) { + ThrowInst throwInst = (ThrowInst) thrower; + predThrowables = throwAnalysis.mightThrowImplicitly(throwInst); + selfThrowables = throwAnalysis.mightThrowExplicitly(throwInst); + } else if (thrower instanceof ThrowStmt) { + ThrowStmt throwStmt = (ThrowStmt) thrower; + predThrowables = throwAnalysis.mightThrowImplicitly(throwStmt); + selfThrowables = throwAnalysis.mightThrowExplicitly(throwStmt); + } + + for (ExceptionDest dest : dests) { + if (dest.getTrap() != null) { + Unit catcher = dest.getTrap().getHandlerUnit(); + RefType trapsType = dest.getTrap().getException().getType(); + if (predThrowables == null || predThrowables.catchableAs(trapsType)) { + // Add edges from the thrower's predecessors to the + // catcher. + if (thrower == entryPoint) { + trapsThatAreHeads.add(catcher); + } + for (Unit pred : throwersPreds) { + addEdge(unitToSuccs, unitToPreds, pred, catcher); + } + } + if (alwaysAddSelfEdges || (selfThrowables != null && selfThrowables.catchableAs(trapsType))) { + addEdge(unitToSuccs, unitToPreds, thrower, catcher); + } + } + } + } + + // Now we have to worry about transitive exceptional + // edges, when a handler might itself throw an exception + // that is caught within the method. For that we need a + // worklist containing CFG edges that lead to such a handler. + class CFGEdge { + Unit head; // If null, represents an edge to the handler + // from the fictitious "predecessor" of the + // very first unit in the chain. I.e., tail + // is a handler which might be reached as a + // result of an exception thrown by the + // first Unit in the Body. + Unit tail; + + CFGEdge(Unit head, Unit tail) { + if (tail == null) { + throw new RuntimeException("invalid CFGEdge(" + (head == null ? "null" : head.toString()) + ',' + "null" + ')'); + } + this.head = head; + this.tail = tail; + } + + @Override + public boolean equals(Object rhs) { + if (rhs == this) { + return true; + } + if (!(rhs instanceof CFGEdge)) { + return false; + } + CFGEdge rhsEdge = (CFGEdge) rhs; + return ((this.head == rhsEdge.head) && (this.tail == rhsEdge.tail)); + } + + @Override + public int hashCode() { + // Following Joshua Bloch's recipe in "Effective Java", Item 8: + int result = 17; + result = 37 * result + this.head.hashCode(); + result = 37 * result + this.tail.hashCode(); + return result; + } + } + + LinkedList workList = new LinkedList(); + + for (Trap trap : body.getTraps()) { + Unit handlerStart = trap.getHandlerUnit(); + if (mightThrowToIntraproceduralCatcher(handlerStart)) { + List handlerPreds = getUnexceptionalPredsOf(handlerStart); + for (Unit pred : handlerPreds) { + workList.addLast(new CFGEdge(pred, handlerStart)); + } + handlerPreds = getExceptionalPredsOf(handlerStart); + for (Unit pred : handlerPreds) { + workList.addLast(new CFGEdge(pred, handlerStart)); + } + if (trapsThatAreHeads.contains(handlerStart)) { + workList.addLast(new CFGEdge(null, handlerStart)); + } + } + } + + // Now for every CFG edge that leads to a handler that may + // itself throw an exception catchable within the method, add + // edges from the head of that edge to the unit that catches + // the handler's exception. + while (workList.size() > 0) { + CFGEdge edgeToThrower = workList.removeFirst(); + Unit pred = edgeToThrower.head; + Unit thrower = edgeToThrower.tail; + Collection throwerDests = getExceptionDests(thrower); + for (ExceptionDest dest : throwerDests) { + if (dest.getTrap() != null) { + Unit handlerStart = dest.getTrap().getHandlerUnit(); + boolean edgeAdded = false; + if (pred == null) { + if (!trapsThatAreHeads.contains(handlerStart)) { + trapsThatAreHeads.add(handlerStart); + edgeAdded = true; + } + } else { + if (!getExceptionalSuccsOf(pred).contains(handlerStart)) { + addEdge(unitToSuccs, unitToPreds, pred, handlerStart); + edgeAdded = true; + } + } + if (edgeAdded && mightThrowToIntraproceduralCatcher(handlerStart)) { + workList.addLast(new CFGEdge(pred, handlerStart)); + } + } + } + } + return trapsThatAreHeads; + } + + /** + *

+ * Utility method for checking if a {@link Unit} might have side effects. It simply returns true for any unit which invokes + * a method directly or which might invoke static initializers indirectly (by creating a new object or by refering to a + * static field; see sections 2.17.4, 2.17.5, and 5.5 of the Java Virtual Machine Specification). + *

+ * + * mightHaveSideEffects() is declared package-private so that it is available to unit tests that are part of + * this package. + * + * @param u + * The unit whose potential for side effects is to be checked. + * + * @return whether or not u has the potential for side effects. + */ + static boolean mightHaveSideEffects(Unit u) { + if (u instanceof Inst) { + Inst i = (Inst) u; + return (i.containsInvokeExpr() || (i instanceof StaticPutInst) || (i instanceof StaticGetInst) + || (i instanceof NewInst)); + } else if (u instanceof Stmt) { + for (ValueBox vb : u.getUseBoxes()) { + Value v = vb.getValue(); + if ((v instanceof StaticFieldRef) || (v instanceof InvokeExpr) || (v instanceof NewExpr)) { + return true; + } + } + } + return false; + } + + /** + * Utility method for checking if a Unit might throw an exception which may be caught by a {@link Trap} within this method. + * + * @param u + * The unit for whose exceptions are to be checked + * + * @return whether or not u may throw an exception which may be caught by a Trap in this method, + */ + private boolean mightThrowToIntraproceduralCatcher(Unit u) { + Collection dests = getExceptionDests(u); + for (ExceptionDest dest : dests) { + if (dest.getTrap() != null) { + return true; + } + } + return false; + } + + /** + *

+ * A placeholder that overrides {@link UnitGraph#buildHeadsAndTails()} with a method which always throws an exception. The + * placeholder serves to indicate that ExceptionalUnitGraph does not use buildHeadsAndTails(), + * and to document the conditions under which ExceptionalUnitGraph considers a node to be a head or + * tail. + *

+ * + *

+ * ExceptionalUnitGraph defines the graph's set of heads to include the first {@link Unit} in the graph's + * body, together with the first Unit in any exception handler which might catch an exception thrown by the + * first Unit in the body (because any of those Units might be the first to successfully complete + * execution). ExceptionalUnitGraph defines the graph's set of tails to include all Units which + * represent some variety of return bytecode or an athrow bytecode whose argument might escape the method. + *

+ */ + @Override + protected void buildHeadsAndTails() throws IllegalStateException { + throw new IllegalStateException("ExceptionalUnitGraph uses buildHeadsAndTails(List) instead of buildHeadsAndTails()"); + } + + /** + * Utility method, to be called only after the unitToPreds and unitToSuccs maps have been built. It defines the graph's set + * of heads to include the first {@link Unit} in the graph's body, together with all the Units in + * additionalHeads. It defines the graph's set of tails to include all Units which represent some + * sort of return bytecode or an athrow bytecode which may escape the method. + */ + private void buildHeadsAndTails(Set additionalHeads) { + heads = new ArrayList(additionalHeads.size() + 1); + heads.addAll(additionalHeads); + + if (unitChain.isEmpty()) { + throw new IllegalStateException("No body for method " + body.getMethod().getSignature()); + } + + Unit entryPoint = unitChain.getFirst(); + if (!heads.contains(entryPoint)) { + heads.add(entryPoint); + } + + tails = new ArrayList(); + for (Unit u : unitChain) { + if (u instanceof soot.jimple.ReturnStmt || u instanceof soot.jimple.ReturnVoidStmt || u instanceof soot.baf.ReturnInst + || u instanceof soot.baf.ReturnVoidInst) { + tails.add(u); + } else if (u instanceof soot.jimple.ThrowStmt || u instanceof soot.baf.ThrowInst) { + Collection dests = getExceptionDests(u); + int escapeMethodCount = 0; + for (ExceptionDest dest : dests) { + if (dest.getTrap() == null) { + escapeMethodCount++; + } + } + if (escapeMethodCount > 0) { + tails.add(u); + } + } + } + } + + /** + * Returns a collection of {@link ExceptionalUnitGraph.ExceptionDest ExceptionDest} objects which represent how exceptions + * thrown by a specified unit will be handled. + * + * @param u + * The unit for which to provide exception information. ( u must be a Unit, though the + * parameter is declared as an Object to satisfy the interface of + * {@link soot.toolkits.graph.ExceptionalGraph ExceptionalGraph}. + * + * @return a collection of ExceptionDest objects describing the traps, if any, which catch the exceptions + * which may be thrown by u. + */ + @Override + public Collection getExceptionDests(final Unit u) { + Collection result = unitToExceptionDests.get(u); + if (result == null) { + ExceptionDest e = new ExceptionDest(null, null) { + private ThrowableSet throwables; + + @Override + public ThrowableSet getThrowables() { + if (null == throwables) { + throwables = throwAnalysis.mightThrow(u); + } + return throwables; + } + }; + return Collections.singletonList(e); + } + return result; + } + + public static class ExceptionDest implements ExceptionalGraph.ExceptionDest { + private Trap trap; + private ThrowableSet throwables; + + protected ExceptionDest(Trap trap, ThrowableSet throwables) { + this.trap = trap; + this.throwables = throwables; + } + + @Override + public Trap getTrap() { + return trap; + } + + @Override + public ThrowableSet getThrowables() { + return throwables; + } + + @Override + public Unit getHandlerNode() { + if (trap == null) { + return null; + } else { + return trap.getHandlerUnit(); + } + } + + @Override + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append(getThrowables()); + buf.append(" -> "); + if (trap == null) { + buf.append("(escapes)"); + } else { + buf.append(trap.toString()); + } + return buf.toString(); + } + } + + @Override + public List getUnexceptionalPredsOf(Unit u) { + List preds = unitToUnexceptionalPreds.get(u); + return preds == null ? Collections.emptyList() : preds; + } + + @Override + public List getUnexceptionalSuccsOf(Unit u) { + List succs = unitToUnexceptionalSuccs.get(u); + return succs == null ? Collections.emptyList() : succs; + } + + @Override + public List getExceptionalPredsOf(Unit u) { + List preds = unitToExceptionalPreds.get(u); + return preds == null ? Collections.emptyList() : preds; + } + + @Override + public List getExceptionalSuccsOf(Unit u) { + List succs = unitToExceptionalSuccs.get(u); + return succs == null ? Collections.emptyList() : succs; + } + + /** + *

+ * Return the {@link ThrowAnalysis} used to construct this graph, if the graph contains no {@link Trap}s, or + * null if the graph does contain Traps. A reference to the ThrowAnalysis is kept + * when there are no Traps so that the graph can generate responses to {@link #getExceptionDests(Object)} on + * the fly, rather than precomputing information that may never be needed. + *

+ * + *

+ * This method is package-private because it exposes a detail of the implementation of ExceptionalUnitGraph so + * that the {@link soot.toolkits.graph.ExceptionalBlockGraph ExceptionalBlockGraph} constructor can cache the same + * ThrowAnalysis for the same purpose. + * + * @return the {@link ThrowAnalysis} used to generate this graph if the graph contains no {@link Trap}s, or + * null if the graph contains one or more {@link Trap}s. + */ + ThrowAnalysis getThrowAnalysis() { + return throwAnalysis; + } + + @Override + public String toString() { + StringBuffer buf = new StringBuffer(); + for (Unit u : unitChain) { + buf.append(" preds: " + getPredsOf(u) + "\n"); + buf.append(" unexceptional preds: " + getUnexceptionalPredsOf(u) + "\n"); + buf.append(" exceptional preds: " + getExceptionalPredsOf(u) + "\n"); + buf.append(u.toString() + '\n'); + buf.append(" exception destinations: " + getExceptionDests(u) + "\n"); + buf.append(" unexceptional succs: " + getUnexceptionalSuccsOf(u) + "\n"); + buf.append(" exceptional succs: " + getExceptionalSuccsOf(u) + "\n"); + buf.append(" succs " + getSuccsOf(u) + "\n\n"); + } + + return buf.toString(); + } +} \ No newline at end of file diff --git a/src/test/java/samples/BlackBoard.java b/src/test/java/samples/BlackBoard.java index ce344fa..3702f57 100644 --- a/src/test/java/samples/BlackBoard.java +++ b/src/test/java/samples/BlackBoard.java @@ -1,17 +1,18 @@ package samples; public class BlackBoard { - public static String text = ""; - public static void main(){ - if (text != "" && hasWhiteSpace()){ - normalizeWhiteSpace(); - removeDuplicateWords(); + public static void main() throws Exception{ + try { + int x = 0; //source + if (x==0){ + x = x + 1; + } + x = x + 2; //sink + } + finally { + System.out.println("Message"); } } - public static void normalizeWhiteSpace(){}; - public static boolean hasWhiteSpace(){ - return true; - }; - public static void removeDuplicateWords(){}; + } \ No newline at end of file diff --git a/src/test/java/samples/OneDoWhileAndOneIf.java b/src/test/java/samples/OneDoWhileAndOneIf.java index 08b5275..54bbb32 100644 --- a/src/test/java/samples/OneDoWhileAndOneIf.java +++ b/src/test/java/samples/OneDoWhileAndOneIf.java @@ -5,10 +5,10 @@ public static void main() { do{ x = 2; if (x == 0){ - x = 3; + x = 3; //source } x = 5; - }while (x < 1); + }while (x < 1); //sink x = 4; } } \ No newline at end of file diff --git a/src/test/java/samples/OneReturnZeroConflict.java b/src/test/java/samples/OneReturnZeroConflict.java new file mode 100644 index 0000000..bd7f55f --- /dev/null +++ b/src/test/java/samples/OneReturnZeroConflict.java @@ -0,0 +1,10 @@ +package samples; +public class OneReturnZeroConflict { + public static int main(){ + int x = 0; + if (x==1){ //source + x = 1; + } + return x; //sink + } +} \ No newline at end of file diff --git a/src/test/java/samples/TwoReturnOneConflict.java b/src/test/java/samples/TwoReturnOneConflict.java new file mode 100644 index 0000000..904c77b --- /dev/null +++ b/src/test/java/samples/TwoReturnOneConflict.java @@ -0,0 +1,10 @@ +package samples; +public class TwoReturnOneConflict { + public static int main(){ + int x = 0; + if (x==1){ //source + return 1; + } + return x; //sink + } +} \ No newline at end of file diff --git a/src/test/scala/br/ufpe/cin/soot/OneReturnZeroConflictTest.scala b/src/test/scala/br/ufpe/cin/soot/OneReturnZeroConflictTest.scala new file mode 100644 index 0000000..273ce59 --- /dev/null +++ b/src/test/scala/br/ufpe/cin/soot/OneReturnZeroConflictTest.scala @@ -0,0 +1,45 @@ +package br.ufpe.cin.soot + +import br.ufpe.cin.soot.graph.{NodeType, SimpleNode, SinkNode, SourceNode} +import soot.jimple.{AssignStmt, InvokeExpr, InvokeStmt} + +class OneReturnZeroConflictTest(leftchangedlines: Array[Int], rightchangedlines: Array[Int]) extends JCDATest { + override def getClassName(): String = "samples.OneReturnZeroConflict" + override def getMainMethod(): String = "main" + + def this(){ + this(Array.empty[Int], Array.empty[Int]) + } + + override def analyze(unit: soot.Unit): NodeType = { + + if (!leftchangedlines.isEmpty && !rightchangedlines.isEmpty){ + if (leftchangedlines.contains(unit.getJavaSourceStartLineNumber)){ + return SourceNode + } else if (rightchangedlines.contains(unit.getJavaSourceStartLineNumber)){ + return SinkNode + } + } + + if(unit.isInstanceOf[InvokeStmt]) { + val invokeStmt = unit.asInstanceOf[InvokeStmt] + return analyzeInvokeStmt(invokeStmt.getInvokeExpr) + } + if(unit.isInstanceOf[soot.jimple.AssignStmt]) { + val assignStmt = unit.asInstanceOf[AssignStmt] + if(assignStmt.getRightOp.isInstanceOf[InvokeExpr]) { + val invokeStmt = assignStmt.getRightOp.asInstanceOf[InvokeExpr] + return analyzeInvokeStmt(invokeStmt) + } + } + return SimpleNode + } + + def analyzeInvokeStmt(exp: InvokeExpr) : NodeType = + exp.getMethod.getName match { + case "source" => SourceNode + case "sink" => SinkNode + case _ => SimpleNode + } + +} \ No newline at end of file diff --git a/src/test/scala/br/ufpe/cin/soot/TestSuite.scala b/src/test/scala/br/ufpe/cin/soot/TestSuite.scala index 5707ae2..3ae95ff 100644 --- a/src/test/scala/br/ufpe/cin/soot/TestSuite.scala +++ b/src/test/scala/br/ufpe/cin/soot/TestSuite.scala @@ -1,15 +1,11 @@ package br.ufpe.cin.soot -import br.ufpe.cin.soot.graph.{Graph, LambdaNode} import org.scalatest.{BeforeAndAfter, FunSuite} -import scala.collection.mutable.ListBuffer - class TestSuite extends FunSuite with BeforeAndAfter { - - test("we should correctly compute the number of nodes and edges in the BlackBoardTest sample") { - val controlDependence = new BlackBoardTest( Array (5), Array (7)) + test ("we should correctly compute the number of nodes and edges in the BlackBoardTest sample") { + val controlDependence = new BlackBoardTest( Array (5), Array (9)) // val controlDependence = new BlackBoardTest() controlDependence.buildCDA() var x = controlDependence.findConflictingPaths() @@ -17,225 +13,235 @@ class TestSuite extends FunSuite with BeforeAndAfter { println(controlDependence.svgToDotModel()) } - def thereIsPath(source: ListBuffer[LambdaNode], target: ListBuffer[LambdaNode], graph: Graph): Boolean ={ - var thereIs = false - source.foreach(s =>{ - target.foreach(t =>{ - val path = graph.findPath(s, t) - if (path.size>0) { - println("There is a path from \""+s.show()+"\" to \""+ t.show()+"\"") - thereIs = true - } - }) - }) - return thereIs + test ("we should correctly compute the number of conflicts in the OneReturnZeroConflitc sample") { + val controlDependence = new OneReturnZeroConflictTest( Array (5), Array (8)) + controlDependence.buildCDA() + var x = controlDependence.findConflictingPaths() +// println("Conflits: "+controlDependence.findConflictingPaths().size); + println(controlDependence.svgToDotModel()) + assert(controlDependence.findConflictingPaths().size == 0) + assert(controlDependence.svg.numberOfNodes() == 6) + assert(controlDependence.svg.numberOfEdges() == 5) + } + + test ("we should correctly compute the number of conflicts in the TwoReturnOneConflitc sample") { + val controlDependence = new TwoReturnOneConflictTest( Array (5), Array (8)) + controlDependence.buildCDA() + var x = controlDependence.findConflictingPaths() +// println("Conflits: "+controlDependence.findConflictingPaths().size); + println(controlDependence.svgToDotModel()) + assert(controlDependence.findConflictingPaths().size == 1) + assert(controlDependence.svg.numberOfNodes() == 6) + assert(controlDependence.svg.numberOfEdges() == 5) } test("we should correctly compute the number of nodes and edges in the NestedAll1Test sample") { val controlDependence = new NestedAll1Test() controlDependence.buildCDA() // println(controlDependence.svgToDotModel()) - assert(controlDependence.svg.numberOfNodes() == 18) - assert(controlDependence.svg.numberOfEdges() == 17) + assert(controlDependence.svg.numberOfNodes() == 19) + assert(controlDependence.svg.numberOfEdges() == 18) } test("we should correctly compute the number of nodes and edges in the NestedAll2Test sample") { val controlDependence = new NestedAll2Test() controlDependence.buildCDA() // println(controlDependence.svgToDotModel()) - assert(controlDependence.svg.numberOfNodes() == 18) - assert(controlDependence.svg.numberOfEdges() == 17) + assert(controlDependence.svg.numberOfNodes() == 19) + assert(controlDependence.svg.numberOfEdges() == 18) } test("we should correctly compute the number of nodes and edges in the NestedIfTest sample") { val controlDependence = new NestedIfTest() controlDependence.buildCDA() // println(controlDependence.svgToDotModel()) - assert(controlDependence.svg.numberOfNodes() == 8) - assert(controlDependence.svg.numberOfEdges() == 7) + assert(controlDependence.svg.numberOfNodes() == 9) + assert(controlDependence.svg.numberOfEdges() == 8) } test("we should correctly compute the number of nodes and edges in the NestedIfElseIfTest sample") { val controlDependence = new NestedIfElseIfTest() controlDependence.buildCDA() // println(controlDependence.svgToDotModel()) - assert(controlDependence.svg.numberOfNodes() == 16) - assert(controlDependence.svg.numberOfEdges() == 15) + assert(controlDependence.svg.numberOfNodes() == 17) + assert(controlDependence.svg.numberOfEdges() == 16) } test("we should correctly compute the number of nodes and edges in the NestedIfWhileIfElseTest sample") { val controlDependence = new NestedIfWhileIfElseTest() controlDependence.buildCDA() // println(controlDependence.svgToDotModel()) - assert(controlDependence.svg.numberOfNodes() == 10) - assert(controlDependence.svg.numberOfEdges() == 9) + assert(controlDependence.svg.numberOfNodes() == 11) + assert(controlDependence.svg.numberOfEdges() == 10) } test("we should correctly compute the number of nodes and edges in the NestedThreeIfTest sample") { val controlDependence = new NestedThreeIfTest() controlDependence.buildCDA() // println(controlDependence.svgToDotModel()) - assert(controlDependence.svg.numberOfNodes() == 12) - assert(controlDependence.svg.numberOfEdges() == 11) + assert(controlDependence.svg.numberOfNodes() == 13) + assert(controlDependence.svg.numberOfEdges() == 12) } test("we should correctly compute the number of nodes and edges in the NestedThreeWhileTest sample") { val controlDependence = new NestedThreeWhileTest() controlDependence.buildCDA() // println(controlDependence.svgToDotModel()) - assert(controlDependence.svg.numberOfNodes() == 9) - assert(controlDependence.svg.numberOfEdges() == 8) + assert(controlDependence.svg.numberOfNodes() == 10) + assert(controlDependence.svg.numberOfEdges() == 9) } test("we should correctly compute the number of nodes and edges in the NestedTwoWhileIfTest sample") { val controlDependence = new NestedTwoWhileIfTest() controlDependence.buildCDA() // println(controlDependence.svgToDotModel()) - assert(controlDependence.svg.numberOfNodes() == 10) - assert(controlDependence.svg.numberOfEdges() == 9) + assert(controlDependence.svg.numberOfNodes() == 11) + assert(controlDependence.svg.numberOfEdges() == 10) } test("we should correctly compute the number of nodes and edges in the NestedWhileAndIfTest sample") { val controlDependence = new NestedWhileAndIfTest() controlDependence.buildCDA() // println(controlDependence.svgToDotModel()) - assert(controlDependence.svg.numberOfNodes() == 12) - assert(controlDependence.svg.numberOfEdges() == 11) + assert(controlDependence.svg.numberOfNodes() == 13) + assert(controlDependence.svg.numberOfEdges() == 12) } test("we should correctly compute the number of nodes and edges in the NestedWhileDoWhileIfTest sample") { val controlDependence = new NestedWhileDoWhileIfTest() controlDependence.buildCDA() -// println(controlDependence.svgToDotModel()) - assert(controlDependence.svg.numberOfNodes() == 11) - assert(controlDependence.svg.numberOfEdges() == 13) + println(controlDependence.svgToDotModel()) + assert(controlDependence.svg.numberOfNodes() == 12) + assert(controlDependence.svg.numberOfEdges() == 14) } test("we should correctly compute the number of nodes and edges in the NestedWhileIfWhileTest sample") { val controlDependence = new NestedWhileIfWhileTest() controlDependence.buildCDA() // println(controlDependence.svgToDotModel()) - assert(controlDependence.svg.numberOfNodes() == 9) - assert(controlDependence.svg.numberOfEdges() == 8) + assert(controlDependence.svg.numberOfNodes() == 10) + assert(controlDependence.svg.numberOfEdges() == 9) } test("we should correctly compute the number of nodes and edges in the NestedWhileWhileIfWhileTest sample") { val controlDependence = new NestedWhileWhileIfWhileTest() controlDependence.buildCDA() // println(controlDependence.svgToDotModel()) - assert(controlDependence.svg.numberOfNodes() == 14) - assert(controlDependence.svg.numberOfEdges() == 13) + assert(controlDependence.svg.numberOfNodes() == 15) + assert(controlDependence.svg.numberOfEdges() == 14) } test("we should correctly compute the number of nodes and edges in the OneDoWhileAndOneIfElseTest sample") { val controlDependence = new OneDoWhileAndOneIfElseTest() controlDependence.buildCDA() // println(controlDependence.svgToDotModel()) - assert(controlDependence.svg.numberOfNodes() == 10) - assert(controlDependence.svg.numberOfEdges() == 12) + assert(controlDependence.svg.numberOfNodes() == 11) + assert(controlDependence.svg.numberOfEdges() == 13) } test("we should correctly compute the number of nodes and edges in the OneDoWhileAndOneIfTest sample") { - val controlDependence = new OneDoWhileAndOneIfTest() + val controlDependence = new OneDoWhileAndOneIfTest(Array (11), Array (6)) controlDependence.buildCDA() +// println("Conflits: "+controlDependence.findConflictingPaths().size); // println(controlDependence.svgToDotModel()) - assert(controlDependence.svg.numberOfNodes() == 9) - assert(controlDependence.svg.numberOfEdges() == 11) + assert(controlDependence.findConflictingPaths().size == 1) + assert(controlDependence.svg.numberOfNodes() == 10) + assert(controlDependence.svg.numberOfEdges() == 12) } test("we should correctly compute the number of nodes and edges in the OneDoWhileAndWhileTest sample") { val controlDependence = new OneDoWhileAndWhileTest() controlDependence.buildCDA() // println(controlDependence.svgToDotModel()) - assert(controlDependence.svg.numberOfNodes() == 9) - assert(controlDependence.svg.numberOfEdges() == 11) + assert(controlDependence.svg.numberOfNodes() == 10) + assert(controlDependence.svg.numberOfEdges() == 12) } test("we should correctly compute the number of nodes and edges in the OneDoWhileTest sample") { val controlDependence = new OneDoWhileTest() controlDependence.buildCDA() // println(controlDependence.svgToDotModel()) - assert(controlDependence.svg.numberOfNodes() == 6) - assert(controlDependence.svg.numberOfEdges() == 6) + assert(controlDependence.svg.numberOfNodes() == 7) + assert(controlDependence.svg.numberOfEdges() == 7) } test("we should correctly compute the number of nodes and edges in the OneForTest sample") { val controlDependence = new OneForTest() controlDependence.buildCDA() // println(controlDependence.svgToDotModel()) - assert(controlDependence.svg.numberOfNodes() == 8) - assert(controlDependence.svg.numberOfEdges() == 7) + assert(controlDependence.svg.numberOfNodes() == 9) + assert(controlDependence.svg.numberOfEdges() == 8) } test("we should correctly compute the number of nodes and edges in the OneIfElseAndOneWhileTest sample") { val controlDependence = new OneIfElseAndOneWhileTest() controlDependence.buildCDA() // println(controlDependence.svgToDotModel()) - assert(controlDependence.svg.numberOfNodes() == 10) - assert(controlDependence.svg.numberOfEdges() == 9) + assert(controlDependence.svg.numberOfNodes() == 11) + assert(controlDependence.svg.numberOfEdges() == 10) } test("we should correctly compute the number of nodes and edges in the OneIfElseTest sample") { val controlDependence = new OneIfElseTest() controlDependence.buildCDA() // println(controlDependence.svgToDotModel()) - assert(controlDependence.svg.numberOfNodes() == 7) - assert(controlDependence.svg.numberOfEdges() == 6) + assert(controlDependence.svg.numberOfNodes() == 8) + assert(controlDependence.svg.numberOfEdges() == 7) } test("we should correctly compute the number of nodes and edges in the OneIfTest sample") { val controlDependence = new OneIfTest() controlDependence.buildCDA() // println(controlDependence.svgToDotModel()) - assert(controlDependence.svg.numberOfNodes() == 6) - assert(controlDependence.svg.numberOfEdges() == 5) + assert(controlDependence.svg.numberOfNodes() == 7) + assert(controlDependence.svg.numberOfEdges() == 6) } test("we should correctly compute the number of nodes and edges in the OneWhileAndNestedIfElseTest sample") { val controlDependence = new OneWhileAndNestedIfElseTest() controlDependence.buildCDA() // println(controlDependence.svgToDotModel()) - assert(controlDependence.svg.numberOfNodes() == 10) - assert(controlDependence.svg.numberOfEdges() == 9) + assert(controlDependence.svg.numberOfNodes() == 11) + assert(controlDependence.svg.numberOfEdges() == 10) } test("we should correctly compute the number of nodes and edges in the OneWhileAndNestedIfTest sample") { val controlDependence = new OneWhileAndNestedIfTest() controlDependence.buildCDA() // println(controlDependence.svgToDotModel()) - assert(controlDependence.svg.numberOfNodes() == 9) - assert(controlDependence.svg.numberOfEdges() == 8) + assert(controlDependence.svg.numberOfNodes() == 10) + assert(controlDependence.svg.numberOfEdges() == 9) } test("we should correctly compute the number of nodes and edges in the OneWhileAndOneIfElseTest sample") { val controlDependence = new OneWhileAndOneIfElseTest() controlDependence.buildCDA() // println(controlDependence.svgToDotModel()) - assert(controlDependence.svg.numberOfNodes() == 10) - assert(controlDependence.svg.numberOfEdges() == 9) + assert(controlDependence.svg.numberOfNodes() == 11) + assert(controlDependence.svg.numberOfEdges() == 10) } test("we should correctly compute the number of nodes and edges in the OneWhileTest sample") { val controlDependence = new OneWhileTest() controlDependence.buildCDA() // println(controlDependence.svgToDotModel()) - assert(controlDependence.svg.numberOfNodes() == 6) - assert(controlDependence.svg.numberOfEdges() == 5) + assert(controlDependence.svg.numberOfNodes() == 7) + assert(controlDependence.svg.numberOfEdges() == 6) } test("we should correctly compute the number of nodes and edges in the TwoIfElseTest sample") { val controlDependence = new TwoIfElseTest() controlDependence.buildCDA() // println(controlDependence.svgToDotModel()) - assert(controlDependence.svg.numberOfNodes() == 11) - assert(controlDependence.svg.numberOfEdges() == 10) + assert(controlDependence.svg.numberOfNodes() == 12) + assert(controlDependence.svg.numberOfEdges() == 11) } test("we should correctly compute the number of nodes and edges in the WhileIfIfElseDoWhileTest sample") { val controlDependence = new WhileIfIfElseDoWhileTest() controlDependence.buildCDA() // println(controlDependence.svgToDotModel()) - assert(controlDependence.svg.numberOfNodes() == 16) - assert(controlDependence.svg.numberOfEdges() == 16) + assert(controlDependence.svg.numberOfNodes() == 17) + assert(controlDependence.svg.numberOfEdges() == 17) } } diff --git a/src/test/scala/br/ufpe/cin/soot/TwoReturnOneConflictTest.scala b/src/test/scala/br/ufpe/cin/soot/TwoReturnOneConflictTest.scala new file mode 100644 index 0000000..d661b95 --- /dev/null +++ b/src/test/scala/br/ufpe/cin/soot/TwoReturnOneConflictTest.scala @@ -0,0 +1,45 @@ +package br.ufpe.cin.soot + +import br.ufpe.cin.soot.graph.{NodeType, SimpleNode, SinkNode, SourceNode} +import soot.jimple.{AssignStmt, InvokeExpr, InvokeStmt} + +class TwoReturnOneConflictTest(leftchangedlines: Array[Int], rightchangedlines: Array[Int]) extends JCDATest { + override def getClassName(): String = "samples.TwoReturnOneConflict" + override def getMainMethod(): String = "main" + + def this(){ + this(Array.empty[Int], Array.empty[Int]) + } + + override def analyze(unit: soot.Unit): NodeType = { + + if (!leftchangedlines.isEmpty && !rightchangedlines.isEmpty){ + if (leftchangedlines.contains(unit.getJavaSourceStartLineNumber)){ + return SourceNode + } else if (rightchangedlines.contains(unit.getJavaSourceStartLineNumber)){ + return SinkNode + } + } + + if(unit.isInstanceOf[InvokeStmt]) { + val invokeStmt = unit.asInstanceOf[InvokeStmt] + return analyzeInvokeStmt(invokeStmt.getInvokeExpr) + } + if(unit.isInstanceOf[soot.jimple.AssignStmt]) { + val assignStmt = unit.asInstanceOf[AssignStmt] + if(assignStmt.getRightOp.isInstanceOf[InvokeExpr]) { + val invokeStmt = assignStmt.getRightOp.asInstanceOf[InvokeExpr] + return analyzeInvokeStmt(invokeStmt) + } + } + return SimpleNode + } + + def analyzeInvokeStmt(exp: InvokeExpr) : NodeType = + exp.getMethod.getName match { + case "source" => SourceNode + case "sink" => SinkNode + case _ => SimpleNode + } + +} \ No newline at end of file