Skip to content

Commit 956cdd3

Browse files
mxprshnCaelmBleidd
andauthored
Targets API (#52)
--------- Co-authored-by: Alexey Menshutin <alex.menshutin99@gmail.com>
1 parent 667fa82 commit 956cdd3

File tree

71 files changed

+2009
-487
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+2009
-487
lines changed

usvm-core/src/main/kotlin/org/usvm/CallStack.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ data class UStackTraceFrame<Method, Statement>(
1212

1313
class UCallStack<Method, Statement> private constructor(
1414
private val stack: ArrayDeque<UCallStackFrame<Method, Statement>>,
15-
) : Collection<UCallStackFrame<Method, Statement>> by stack {
15+
) : List<UCallStackFrame<Method, Statement>> by stack {
1616
constructor() : this(ArrayDeque())
1717
constructor(method: Method) : this(
1818
ArrayDeque<UCallStackFrame<Method, Statement>>().apply {

usvm-core/src/main/kotlin/org/usvm/Context.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ open class UContext(
348348
// Type hack to be able to intern the initial location for inheritors.
349349
private val initialLocation = RootNode<Nothing, Nothing>()
350350

351-
fun <State : UState<*, *, Statement, *, State>, Statement> mkInitialLocation()
351+
fun <State : UState<*, *, Statement, *, *, State>, Statement> mkInitialLocation()
352352
: PathsTrieNode<State, Statement> = initialLocation.uncheckedCast()
353353

354354
fun mkUValueSampler(): KSortVisitor<KExpr<*>> {

usvm-core/src/main/kotlin/org/usvm/Machine.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,13 @@ import org.usvm.stopstrategies.StopStrategy
66
import org.usvm.util.bracket
77
import org.usvm.util.debug
88

9+
val logger = object : KLogging() {}.logger
10+
911
/**
1012
* An abstract symbolic machine.
1113
*
1214
* @see [run]
1315
*/
14-
15-
val logger = object : KLogging() {}.logger
16-
1716
abstract class UMachine<State> : AutoCloseable {
1817
/**
1918
* Runs symbolic execution loop.

usvm-core/src/main/kotlin/org/usvm/Merging.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ interface UMerger<Entity> {
77
fun merge(left: Entity, right: Entity): Entity?
88
}
99

10-
open class UStateMerger<State : UState<*, *, *, *, State>> : UMerger<State> {
10+
open class UStateMerger<State : UState<*, *, *, *, *, State>> : UMerger<State> {
1111
// Never merge for now
1212
override fun merge(left: State, right: State) = null
1313
}

usvm-core/src/main/kotlin/org/usvm/PathTrieNode.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package org.usvm
33
/**
44
* Symbolic execution tree node.
55
*/
6-
sealed class PathsTrieNode<State : UState<*, *, Statement, *, State>, Statement> {
6+
sealed class PathsTrieNode<State : UState<*, *, Statement, *, *, State>, Statement> {
77
/**
88
* Forked states' nodes.
99
*/
@@ -65,7 +65,7 @@ sealed class PathsTrieNode<State : UState<*, *, Statement, *, State>, Statement>
6565
}
6666
}
6767

68-
class PathsTrieNodeImpl<State : UState<*, *, Statement, *, State>, Statement> private constructor(
68+
class PathsTrieNodeImpl<State : UState<*, *, Statement, *, *, State>, Statement> private constructor(
6969
override val depth: Int,
7070
override val states: MutableSet<State>,
7171
// Note: order is important for tests
@@ -101,7 +101,7 @@ class PathsTrieNodeImpl<State : UState<*, *, Statement, *, State>, Statement> pr
101101
override fun toString(): String = "Depth: $depth, statement: $statement"
102102
}
103103

104-
class RootNode<State : UState<*, *, Statement, *, State>, Statement> : PathsTrieNode<State, Statement>() {
104+
class RootNode<State : UState<*, *, Statement, *, *, State>, Statement> : PathsTrieNode<State, Statement>() {
105105
override val children: MutableMap<Statement, PathsTrieNodeImpl<State, Statement>> = mutableMapOf()
106106

107107
override val states: MutableSet<State> = hashSetOf()

usvm-core/src/main/kotlin/org/usvm/State.kt

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package org.usvm
22

33
import io.ksmt.expr.KInterpretedValue
4+
import kotlinx.collections.immutable.PersistentList
5+
import kotlinx.collections.immutable.toPersistentList
46
import org.usvm.constraints.UPathConstraints
57
import org.usvm.memory.UMemory
68
import org.usvm.model.UModelBase
@@ -10,15 +12,18 @@ import org.usvm.solver.UUnsatResult
1012

1113
typealias StateId = UInt
1214

13-
abstract class UState<Type, Method, Statement, Context : UContext, State : UState<Type, Method, Statement, Context, State>>(
15+
abstract class UState<Type, Method, Statement, Context, Target, State>(
1416
// TODO: add interpreter-specific information
1517
ctx: UContext,
1618
open val callStack: UCallStack<Method, Statement>,
1719
open val pathConstraints: UPathConstraints<Type, Context>,
1820
open val memory: UMemory<Type, Method>,
1921
open var models: List<UModelBase<Type>>,
2022
open var pathLocation: PathsTrieNode<State, Statement>,
21-
) {
23+
targets: List<Target> = emptyList(),
24+
) where Context : UContext,
25+
Target : UTarget<Statement, Target, State>,
26+
State : UState<Type, Method, Statement, Context, Target, State> {
2227
/**
2328
* Deterministic state id.
2429
* TODO: Can be replaced with overridden hashCode
@@ -54,7 +59,7 @@ abstract class UState<Type, Method, Statement, Context : UContext, State : UStat
5459
if (this === other) return true
5560
if (javaClass != other?.javaClass) return false
5661

57-
other as UState<*, *, *, *, *>
62+
other as UState<*, *, *, *, *, *>
5863

5964
return id == other.id
6065
}
@@ -73,6 +78,46 @@ abstract class UState<Type, Method, Statement, Context : UContext, State : UStat
7378
* A property containing information about whether the state is exceptional or not.
7479
*/
7580
abstract val isExceptional: Boolean
81+
82+
protected var targetsImpl: PersistentList<Target> = targets.toPersistentList()
83+
private set
84+
85+
private val reachedTerminalTargetsImpl = mutableSetOf<Target>()
86+
87+
/**
88+
* Collection of state's current targets.
89+
* TODO: clean removed targets sometimes
90+
*/
91+
val targets: Sequence<Target> get() = targetsImpl.asSequence().filterNot { it.isRemoved }
92+
93+
/**
94+
* Reached targets with no children.
95+
*/
96+
val reachedTerminalTargets: Set<Target> = reachedTerminalTargetsImpl
97+
98+
/**
99+
* If the [target] is not removed and is contained in this state's target collection,
100+
* removes it from there and adds there all its children.
101+
*
102+
* @return true if the [target] was successfully removed.
103+
*/
104+
internal fun tryPropagateTarget(target: Target): Boolean {
105+
val previousTargetCount = targetsImpl.size
106+
targetsImpl = targetsImpl.remove(target)
107+
108+
if (previousTargetCount == targetsImpl.size || !target.isRemoved) {
109+
return false
110+
}
111+
112+
if (target.isTerminal) {
113+
reachedTerminalTargetsImpl.add(target)
114+
return true
115+
}
116+
117+
targetsImpl = targetsImpl.addAll(target.children)
118+
119+
return true
120+
}
76121
}
77122

78123
data class ForkResult<T>(
@@ -96,7 +141,7 @@ private const val OriginalState = false
96141
* forked state.
97142
*
98143
*/
99-
private fun <T : UState<Type, *, *, Context, T>, Type, Context : UContext> forkIfSat(
144+
private fun <T : UState<Type, *, *, Context, *, T>, Type, Context : UContext> forkIfSat(
100145
state: T,
101146
newConstraintToOriginalState: UBoolExpr,
102147
newConstraintToForkedState: UBoolExpr,
@@ -156,7 +201,7 @@ private fun <T : UState<Type, *, *, Context, T>, Type, Context : UContext> forkI
156201
* 2. makes not more than one query to USolver;
157202
* 3. if both [condition] and ![condition] are satisfiable, then [ForkResult.positiveState] === [state].
158203
*/
159-
fun <T : UState<Type, *, *, Context, T>, Type, Context : UContext> fork(
204+
fun <T : UState<Type, *, *, Context, *, T>, Type, Context : UContext> fork(
160205
state: T,
161206
condition: UBoolExpr,
162207
): ForkResult<T> {
@@ -217,7 +262,7 @@ fun <T : UState<Type, *, *, Context, T>, Type, Context : UContext> fork(
217262
* @return a list of states for each condition - `null` state
218263
* means [UUnknownResult] or [UUnsatResult] of checking condition.
219264
*/
220-
fun <T : UState<Type, *, *, Context, T>, Type, Context : UContext> forkMulti(
265+
fun <T : UState<Type, *, *, Context, *, T>, Type, Context : UContext> forkMulti(
221266
state: T,
222267
conditions: Iterable<UBoolExpr>,
223268
): List<T?> {

usvm-core/src/main/kotlin/org/usvm/StepScope.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import org.usvm.StepScope.StepScopeState.DEAD
1818
*
1919
* @param originalState an initial state.
2020
*/
21-
class StepScope<T : UState<Type, *, *, Context, T>, Type, Context : UContext>(
21+
class StepScope<T : UState<Type, *, *, Context, *, T>, Type, Context : UContext>(
2222
private val originalState: T,
2323
) {
2424
private val forkedStates = mutableListOf<T>()
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package org.usvm
2+
3+
/**
4+
* Base class for a symbolic execution target. A target can be understood as a 'task' for symbolic machine
5+
* which it tries to complete. For example, a task can have an attached location which should be visited by a state
6+
* to consider the task completed. Also, the targets can produce some effects on states visiting them.
7+
*
8+
* Tasks can have 'child' tasks which should be completed only after its parent has been completed. For example,
9+
* it allows to force the execution along the specific path.
10+
*
11+
* Targets are designed to be shared between all the symbolic execution states. Due to this, once there is
12+
* a state which has reached the target which has no children, it is logically removed from the targets tree.
13+
* The other states ignore such removed targets.
14+
*/
15+
abstract class UTarget<Statement, Target, State>(
16+
/**
17+
* Optional location of the target.
18+
*/
19+
val location: Statement? = null,
20+
) where Target : UTarget<Statement, Target, State>,
21+
State : UState<*, *, Statement, *, Target, State> {
22+
private val childrenImpl = mutableListOf<Target>()
23+
private var parent: Target? = null
24+
25+
/**
26+
* List of the child targets which should be reached after this target.
27+
*/
28+
val children: List<Target> = childrenImpl
29+
30+
/**
31+
* True if this target has no children.
32+
*/
33+
val isTerminal get() = childrenImpl.isEmpty()
34+
35+
/**
36+
* True if this target is logically removed from the tree.
37+
*/
38+
var isRemoved = false
39+
private set
40+
41+
/**
42+
* Adds a child target to this target.
43+
* TODO: avoid possible recursion
44+
*
45+
* @return this target (for convenient target tree building).
46+
*/
47+
fun addChild(child: Target): Target {
48+
check(!isRemoved) { "Cannot add child to removed target" }
49+
check(child.parent == null) { "Cannot add child target with existing parent" }
50+
childrenImpl.add(child)
51+
@Suppress("UNCHECKED_CAST")
52+
child.parent = this as Target
53+
return child
54+
}
55+
56+
/**
57+
* This method should be called by concrete targets to signal that [byState]
58+
* should try to propagate the target. If the target without children has been
59+
* visited, it is logically removed from tree.
60+
*/
61+
protected fun propagate(byState: State) {
62+
@Suppress("UNCHECKED_CAST")
63+
if (byState.tryPropagateTarget(this as Target) && isTerminal) {
64+
remove()
65+
}
66+
}
67+
68+
private fun remove() {
69+
check(childrenImpl.all { it.isRemoved }) { "Cannot remove target when some of its children are not removed" }
70+
if (isRemoved) {
71+
return
72+
}
73+
isRemoved = true
74+
val parent = parent
75+
if (parent != null && parent.childrenImpl.all { it.isRemoved }) {
76+
parent.remove()
77+
}
78+
}
79+
}

usvm-core/src/main/kotlin/org/usvm/api/EngineApi.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import org.usvm.UHeapRef
55
import org.usvm.UState
66

77

8-
fun UState<*, *, *, *, *>.assume(expr: UBoolExpr) {
8+
fun UState<*, *, *, *, *, *>.assume(expr: UBoolExpr) {
99
pathConstraints += expr
1010
}
1111

12-
fun UState<*, *, *, *, *>.objectTypeEquals(lhs: UHeapRef, rhs: UHeapRef): UBoolExpr {
12+
fun UState<*, *, *, *, *, *>.objectTypeEquals(lhs: UHeapRef, rhs: UHeapRef): UBoolExpr {
1313
TODO("Objects types equality check: $lhs, $rhs")
1414
}

usvm-core/src/main/kotlin/org/usvm/api/MockApi.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ import org.usvm.uctx
99

1010
// TODO: special mock api for variables
1111

12-
fun <Method, T : USort> UState<*, Method, *, *, *>.makeSymbolicPrimitive(
12+
fun <Method, T : USort> UState<*, Method, *, *, *, *>.makeSymbolicPrimitive(
1313
sort: T
1414
): UExpr<T> {
1515
check(sort != sort.uctx.addressSort) { "$sort is not primitive" }
1616
return memory.mock { call(lastEnteredMethod, emptySequence(), sort) }
1717
}
1818

19-
fun <Type, Method> UState<Type, Method, *, *, *>.makeSymbolicRef(type: Type): UHeapRef {
19+
fun <Type, Method> UState<Type, Method, *, *, *, *>.makeSymbolicRef(type: Type): UHeapRef {
2020
val ref = memory.mock { call(lastEnteredMethod, emptySequence(), memory.ctx.addressSort) }
2121

2222
memory.types.addSubtype(ref, type)
@@ -25,7 +25,7 @@ fun <Type, Method> UState<Type, Method, *, *, *>.makeSymbolicRef(type: Type): UH
2525
return ref
2626
}
2727

28-
fun <Type, Method> UState<Type, Method, *, *, *>.makeSymbolicArray(arrayType: Type, size: USizeExpr): UHeapRef {
28+
fun <Type, Method> UState<Type, Method, *, *, *, *>.makeSymbolicArray(arrayType: Type, size: USizeExpr): UHeapRef {
2929
val ref = memory.mock { call(lastEnteredMethod, emptySequence(), memory.ctx.addressSort) }
3030

3131
memory.types.addSubtype(ref, arrayType)

0 commit comments

Comments
 (0)