-
Notifications
You must be signed in to change notification settings - Fork 16
/
JcMachine.kt
139 lines (119 loc) · 5.11 KB
/
JcMachine.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package org.usvm.machine
import mu.KLogging
import org.jacodb.api.JcClasspath
import org.jacodb.api.JcMethod
import org.jacodb.api.cfg.JcInst
import org.jacodb.api.ext.methods
import org.usvm.CoverageZone
import org.usvm.StateCollectionStrategy
import org.usvm.UMachine
import org.usvm.UMachineOptions
import org.usvm.api.targets.JcTarget
import org.usvm.machine.interpreter.JcInterpreter
import org.usvm.machine.state.JcMethodResult
import org.usvm.machine.state.JcState
import org.usvm.machine.state.lastStmt
import org.usvm.ps.createPathSelector
import org.usvm.statistics.CompositeUMachineObserver
import org.usvm.statistics.CoverageStatistics
import org.usvm.statistics.TerminatedStateRemover
import org.usvm.statistics.TransitiveCoverageZoneObserver
import org.usvm.statistics.UMachineObserver
import org.usvm.statistics.collectors.CoveredNewStatesCollector
import org.usvm.statistics.collectors.TargetsReachedStatesCollector
import org.usvm.statistics.distances.CfgStatisticsImpl
import org.usvm.statistics.distances.PlainCallGraphStatistics
import org.usvm.stopstrategies.createStopStrategy
import org.usvm.targets.UTargetController
val logger = object : KLogging() {}.logger
class JcMachine(
cp: JcClasspath,
private val options: UMachineOptions,
private val interpreterObserver: JcInterpreterObserver? = null
) : UMachine<JcState>() {
private val applicationGraph = JcApplicationGraph(cp)
private val typeSystem = JcTypeSystem(cp)
private val components = JcComponents(typeSystem, options.solverType)
private val ctx = JcContext(cp, components)
private val interpreter = JcInterpreter(ctx, applicationGraph, interpreterObserver)
private val cfgStatistics = CfgStatisticsImpl(applicationGraph)
fun analyze(method: JcMethod, targets: List<JcTarget<UTargetController>> = emptyList()): List<JcState> {
logger.debug("{}.analyze({}, {})", this, method, targets)
val initialState = interpreter.getInitialState(method, targets)
val methodsToTrackCoverage =
when (options.coverageZone) {
CoverageZone.METHOD -> setOf(method)
CoverageZone.TRANSITIVE -> setOf(method)
// TODO: more adequate method filtering. !it.isConstructor is used to exclude default constructor which is often not covered
CoverageZone.CLASS -> method.enclosingClass.methods.filter {
it.enclosingClass == method.enclosingClass && !it.isConstructor
}.toSet()
}
val coverageStatistics: CoverageStatistics<JcMethod, JcInst, JcState> = CoverageStatistics(
methodsToTrackCoverage,
applicationGraph
)
val callGraphStatistics =
when (options.targetSearchDepth) {
0u -> PlainCallGraphStatistics()
else -> JcCallGraphStatistics(
options.targetSearchDepth,
applicationGraph,
typeSystem.topTypeStream(),
subclassesToTake = 10
)
}
val pathSelector = createPathSelector(
initialState,
options,
applicationGraph,
{ coverageStatistics },
{ cfgStatistics },
{ callGraphStatistics }
)
val statesCollector =
when (options.stateCollectionStrategy) {
StateCollectionStrategy.COVERED_NEW -> CoveredNewStatesCollector<JcState>(coverageStatistics) {
it.methodResult is JcMethodResult.JcException
}
StateCollectionStrategy.REACHED_TARGET -> TargetsReachedStatesCollector()
}
val stopStrategy = createStopStrategy(
options,
targets,
coverageStatistics = { coverageStatistics },
getCollectedStatesCount = { statesCollector.collectedStates.size }
)
val observers = mutableListOf<UMachineObserver<JcState>>(coverageStatistics)
observers.add(TerminatedStateRemover())
if (interpreterObserver is UMachineObserver<*>) {
@Suppress("UNCHECKED_CAST")
observers.add(interpreterObserver as UMachineObserver<JcState>)
}
if (options.coverageZone == CoverageZone.TRANSITIVE) {
observers.add(
TransitiveCoverageZoneObserver(
initialMethod = method,
methodExtractor = { state -> state.lastStmt.location.method },
addCoverageZone = { coverageStatistics.addCoverageZone(it) },
ignoreMethod = { false } // TODO replace with a configurable setting
)
)
}
observers.add(statesCollector)
run(
interpreter,
pathSelector,
observer = CompositeUMachineObserver(observers),
isStateTerminated = ::isStateTerminated,
stopStrategy = stopStrategy,
)
return statesCollector.collectedStates
}
private fun isStateTerminated(state: JcState): Boolean {
return state.callStack.isEmpty()
}
override fun close() {
components.close()
}
}