@@ -7,6 +7,7 @@ import soot.jimple.Stmt
77import soot.jimple.SwitchStmt
88import soot.jimple.internal.JReturnStmt
99import soot.jimple.internal.JReturnVoidStmt
10+ import soot.jimple.internal.JThrowStmt
1011import soot.toolkits.graph.ExceptionalUnitGraph
1112
1213private const val EXCEPTION_DECISION_SHIFT = 100000
@@ -22,7 +23,16 @@ class InterProceduralUnitGraph(graph: ExceptionalUnitGraph) {
2223 private var attachAllowed = true
2324 val graphs = mutableListOf (graph)
2425 private val statistics = mutableListOf<TraverseGraphStatistics >()
25- private val methods = mutableListOf (graph.body.method)
26+ // All the methods in the InterProceduralUnitGraph
27+ private val methods: MutableSet <SootMethod > = mutableSetOf (graph.body.method)
28+ // Methods with registered edges
29+ private val registeredMethods: MutableSet <SootMethod > = mutableSetOf (graph.body.method)
30+
31+ // We use this field for exceptional statements of the methods from the [registeredMethods] map.
32+ // Points of interest are statements of the graph that must be covered during the exploration.
33+ // Therefore, there is no need to put here statements from registered methods since
34+ // we try to cover all of their edges.
35+ private val methodToUncoveredThrowStatements: MutableMap <SootMethod , MutableSet <Stmt >> = mutableMapOf ()
2636
2737 private val unexceptionalSuccsCache = mutableMapOf<ExceptionalUnitGraph , Map <Stmt , Set <Stmt >>>()
2838 private val exceptionalSuccsCache = mutableMapOf<ExceptionalUnitGraph , Map <Stmt , Set <Stmt >>>()
@@ -41,9 +51,10 @@ class InterProceduralUnitGraph(graph: ExceptionalUnitGraph) {
4151 private val unexceptionalSuccs: MutableMap <Stmt , Set <Stmt >> = graph.unexceptionalSuccs.toMutableMap()
4252
4353 private val stmtToGraph: MutableMap <Stmt , ExceptionalUnitGraph > = stmts.associateWithTo(mutableMapOf ()) { graph }
54+ private val edgeToGraph: MutableMap <Edge , ExceptionalUnitGraph > = graph.edges.associateWithTo(mutableMapOf ()) { graph }
4455
45- val registeredEdgesCount: MutableMap <Stmt , Int > = graph.outgoingEdgesCount
46- val outgoingEdgesCount: MutableMap <Stmt , Int > = graph.outgoingEdgesCount
56+ private val registeredEdgesCount: MutableMap <Stmt , Int > = graph.outgoingEdgesCount
57+ private val outgoingEdgesCount: MutableMap <Stmt , Int > = graph.outgoingEdgesCount
4758
4859 fun method (stmt : Stmt ): SootMethod = stmtToGraph[stmt]?.body?.method ? : error(" $stmt not in graph." )
4960
@@ -79,42 +90,65 @@ class InterProceduralUnitGraph(graph: ExceptionalUnitGraph) {
7990 private val ExceptionalUnitGraph .outgoingEdgesCount: MutableMap <Stmt , Int >
8091 get() = stmts.associateWithTo(mutableMapOf ()) { succ(it).count() }
8192
93+ /* *
94+ * Returns a method that the [edge] is belongs to. If the [edge] does not
95+ * have such method, null will be returned.
96+ *
97+ * Note: for example, implicit edges and edges produced by an invocation
98+ * does not have a method they could belong to.
99+ */
100+ private fun methodByEdge (edge : Edge ): SootMethod ? = edgeToGraph[edge]?.body?.method
82101
83102 /* *
84- * join new graph to stmt.
85- * @param registerEdges - if true than edges
103+ * Joins a new [graph] to the [stmt]. Depending on the [registerEdges], edges of the [graph]
104+ * might be either registered in the global graph or not.
105+ *
106+ * If they are registered, path selector tries to cover all edges from the [graph].
86107 */
87108 fun join (stmt : Stmt , graph : ExceptionalUnitGraph , registerEdges : Boolean ) {
88109 attachAllowed = false
110+
89111 val invokeEdge = Edge (stmt, graph.head, CALL_DECISION_NUM )
90- val alreadyJoined = graph.body.method in methods
112+ val method = graph.body.method
113+ val alreadyJoined = method in methods
114+
91115 if (! alreadyJoined) {
92116 graphs + = graph
93- methods + = graph.body. method
117+ methods + = method
94118 traps + = graph.traps
95119 exceptionalSuccs + = graph.exceptionalSuccs
96120 unexceptionalSuccs + = graph.unexceptionalSuccs
97121
98- val joinedStmts = graph.stmts
99- stmts + = joinedStmts
100- joinedStmts.forEach { stmtToGraph[it] = graph }
122+ graph.edges.forEach { edgeToGraph[it] = graph }
123+
124+ val joinedStmts = graph.stmts.onEach { stmtToGraph[it] = graph }
101125 outgoingEdgesCount + = graph.outgoingEdgesCount
126+ stmts + = joinedStmts
127+
102128 if (registerEdges) {
129+ registeredMethods + = method
103130 registeredEdgesCount + = graph.outgoingEdgesCount
104131 registeredEdges + = graph.edges
105132 registeredEdgesCount.computeIfPresent(stmt) { _, value ->
106133 value + 1
107134 }
108135 }
136+
137+ // it is important to have this method call after we register the given method
138+ // because we find uncoveredPoints for unregistered methods only
139+ method.uncoveredThrowStatements()
109140 }
141+
110142 invokeSuccessors.compute(stmt) { _, value ->
111143 value?.apply { add(graph.head) } ? : mutableSetOf (graph.head)
112144 }
145+
113146 registeredEdges + = invokeEdge
114147
115148 outgoingEdgesCount.computeIfPresent(stmt) { _, value ->
116149 value + 1
117150 }
151+
118152 statistics.forEach {
119153 it.onJoin(stmt, graph, registerEdges)
120154 }
@@ -129,9 +163,15 @@ class InterProceduralUnitGraph(graph: ExceptionalUnitGraph) {
129163 coveredOutgoingEdges.putIfAbsent(executionState.stmt, 0 )
130164
131165 for (edge in executionState.edges) {
166+ markAsCoveredStmt(edge.src)
167+ markAsCoveredStmt(edge.dst)
168+
132169 when (edge) {
133170 in implicitEdges -> coveredImplicitEdges + = edge
134- !in registeredEdges -> coveredOutgoingEdges.putIfAbsent(edge.src, 0 )
171+ !in registeredEdges -> {
172+ coveredOutgoingEdges.putIfAbsent(edge.src, 0 )
173+ coveredEdges + = edge
174+ }
135175 !in coveredEdges -> {
136176 coveredOutgoingEdges.compute(edge.src) { _, value -> (value ? : 0 ) + 1 }
137177 coveredEdges + = edge
@@ -143,6 +183,43 @@ class InterProceduralUnitGraph(graph: ExceptionalUnitGraph) {
143183 }
144184 }
145185
186+ /* *
187+ * Returns uncovered throw statements for [this] method.
188+ */
189+ private fun SootMethod.uncoveredThrowStatements (): Set <Stmt > {
190+ val throwStatements = methodToUncoveredThrowStatements[this ]
191+
192+ if (throwStatements != null ) {
193+ return throwStatements
194+ }
195+
196+ // We don't have to additionally specify throw statements
197+ // since for the registeredMethods we try to cover all of their edges.
198+ if (this in registeredMethods) {
199+ return emptySet()
200+ }
201+
202+ // We don't want to cover all the exceptions in not overridden library classes
203+ if (declaringClass.isLibraryClass && ! declaringClass.isOverridden) {
204+ return emptySet()
205+ }
206+
207+ val uncoveredThrowStatements = methodToUncoveredThrowStatements.getOrPut(this ) { mutableSetOf () }
208+
209+ if (! canRetrieveBody()) {
210+ return uncoveredThrowStatements
211+ }
212+
213+ uncoveredThrowStatements + = jimpleBody().units.filterIsInstance<JThrowStmt >()
214+
215+ return uncoveredThrowStatements
216+ }
217+
218+ private fun markAsCoveredStmt (stmt : Stmt ) {
219+ val method = methodByStmt(stmt)
220+ methodToUncoveredThrowStatements[method]?.remove(stmt)
221+ }
222+
146223 /* *
147224 * register new implicit edge, that is not represented in graph
148225 */
@@ -215,11 +292,34 @@ class InterProceduralUnitGraph(graph: ExceptionalUnitGraph) {
215292 (edge in coveredEdges || edge !in registeredEdges)
216293 }
217294
295+ /* *
296+ * If the [edge] does not have associated method, returns a result of [isCovered] call.
297+ * Otherwise, there are several cases:
298+ * * If the [edge] in [coveredEdges], returns true;
299+ * * If the [edge] belongs to a method of some overridden class and there is
300+ * uncovered throw statements in it, returns false;
301+ * * In all other cases, returns whether the [edge] absent in [registeredEdges].
302+ */
303+ fun isCoveredWithAllThrowStatements (edge : Edge ): Boolean {
304+ val method = methodByEdge(edge) ? : return isCovered(edge)
305+
306+ if (edge in coveredEdges) {
307+ return true
308+ }
309+
310+ if (method.declaringClass.isOverridden && method.uncoveredThrowStatements().isNotEmpty()) {
311+ return false
312+ }
313+
314+ return edge !in registeredEdges
315+ }
218316
219317 /* *
220318 * @return true if stmt has more than one outgoing edge
221319 */
222320 fun isFork (stmt : Stmt ): Boolean = (outgoingEdgesCount[stmt] ? : 0 ) > 1
321+
322+ private fun methodByStmt (stmt : Stmt ) = stmtToGraph.getValue(stmt).body.method
223323}
224324
225325private fun ExceptionalUnitGraph.succ (stmt : Stmt ): Sequence <Stmt > = exceptionalSucc(stmt) + unexceptionalSucc(stmt)
0 commit comments