Skip to content

Commit 41d3f04

Browse files
committed
Register throw statements of the wrapper's methods in the global graph
1 parent c13be06 commit 41d3f04

File tree

6 files changed

+121
-19
lines changed

6 files changed

+121
-19
lines changed

utbot-framework/src/main/java/org/utbot/engine/overrides/collections/UtArrayList.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ void preconditionCheck() {
8282

8383
int size = elementData.end;
8484
assume(elementData.begin == 0);
85-
assume(size >= 0 & size <= MAX_LIST_SIZE);
85+
assume(size >= 0);
86+
assume(size <= MAX_LIST_SIZE);
8687

8788
visit(this);
8889
}

utbot-framework/src/main/java/org/utbot/engine/overrides/collections/UtLinkedList.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ private void preconditionCheck() {
7878
parameter(elementData);
7979
parameter(elementData.storage);
8080
assume(elementData.begin == 0);
81-
assume(elementData.end >= 0 & elementData.end <= MAX_LIST_SIZE);
81+
assume(elementData.end >= 0);
82+
assume(elementData.end <= MAX_LIST_SIZE);
8283

8384
visit(this);
8485
}

utbot-framework/src/main/kotlin/org/utbot/engine/InterProceduralUnitGraph.kt

Lines changed: 111 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import soot.jimple.Stmt
77
import soot.jimple.SwitchStmt
88
import soot.jimple.internal.JReturnStmt
99
import soot.jimple.internal.JReturnVoidStmt
10+
import soot.jimple.internal.JThrowStmt
1011
import soot.toolkits.graph.ExceptionalUnitGraph
1112

1213
private 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

225325
private fun ExceptionalUnitGraph.succ(stmt: Stmt): Sequence<Stmt> = exceptionalSucc(stmt) + unexceptionalSucc(stmt)

utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/DistanceStatistics.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@ class DistanceStatistics(
3535
graph.stmts.associateWithTo(mutableMapOf()) { 0 }
3636

3737
/**
38-
* Drops executionState if all the edges on path are covered and
39-
* there is no reachable uncovered statement
38+
* Drops executionState if all the edges on path are covered (with respect to uncovered
39+
* throw statements of the methods they belong to) and there is no reachable and uncovered statement.
4040
*/
4141
override fun shouldDrop(state: ExecutionState) =
42-
state.edges.all { graph.isCovered(it) } && distanceToUncovered(state) == Int.MAX_VALUE
42+
state.edges.all { graph.isCoveredWithAllThrowStatements(it) } && distanceToUncovered(state) == Int.MAX_VALUE
4343

4444
fun isCovered(edge: Edge): Boolean = graph.isCovered(edge)
4545

utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/EdgeVisitCountingStatistics.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class EdgeVisitCountingStatistics(
3131
* - all statements are already covered and execution is complete
3232
*/
3333
override fun shouldDrop(state: ExecutionState): Boolean {
34-
return state.edges.all { graph.isCovered(it) } && state.isComplete()
34+
return state.edges.all { graph.isCoveredWithAllThrowStatements(it) } && state.isComplete()
3535
}
3636

3737
/**

utbot-framework/src/test/kotlin/org/utbot/examples/strings/StringExamplesTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ internal class StringExamplesTest : AbstractTestCaseGeneratorTest(
328328
fun testSubstringWithEndIndex() {
329329
checkWithException(
330330
StringExamples::substringWithEndIndex,
331-
between(6..8),
331+
ignoreExecutionsNumber,
332332
{ s, _, _, r -> s == null && r.isException<NullPointerException>() },
333333
{ s, b, e, r -> s != null && b < 0 || e > s.length || b > e && r.isException<StringIndexOutOfBoundsException>() },
334334
{ s, b, e, r -> r.getOrThrow() == s.substring(b, e) && s.substring(b, e) != "password" },
@@ -346,7 +346,7 @@ internal class StringExamplesTest : AbstractTestCaseGeneratorTest(
346346
fun testSubstringWithEndIndexNotEqual() {
347347
checkWithException(
348348
StringExamples::substringWithEndIndexNotEqual,
349-
between(3..4),
349+
ignoreExecutionsNumber,
350350
{ s, _, r -> s == null && r.isException<NullPointerException>() },
351351
{ s, e, r -> s != null && e < 1 || e > s.length && r.isException<StringIndexOutOfBoundsException>() },
352352
{ s, e, r -> s != null && r.getOrThrow() == s.substring(1, e) },

0 commit comments

Comments
 (0)