Skip to content

Commit 1dc45cf

Browse files
committed
Extract common API + comment
1 parent 6e81b3c commit 1dc45cf

File tree

7 files changed

+78
-246
lines changed

7 files changed

+78
-246
lines changed

.github/workflows/build-and-run-tests.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ jobs:
3939
REPO_URL="https://gitee.com/Lipenx/arkanalyzer.git"
4040
DEST_DIR="arkanalyzer"
4141
MAX_RETRIES=10
42-
RETRY_DELAY=3 # Delay between retries in seconds
42+
RETRY_DELAY=3 # Delay between retries in seconds
43+
# Set the same as in jacodb/neo branch, since we get jacodb artifact from that branch.
4344
BRANCH="neo/2024-08-16"
4445

4546
for ((i=1; i<=MAX_RETRIES; i++)); do

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,9 +315,47 @@ class UIsSupertypeExpr<Type> internal constructor(
315315

316316
//endregion
317317

318+
//region Utility Expressions
319+
320+
/**
321+
* Utility class for merging expressions with [UBoolSort] sort.
322+
*
323+
* Mainly created for [not] function used in StateForker.
324+
*/
325+
class UJoinedBoolExpr(
326+
ctx: UContext<*>,
327+
val exprs: List<UBoolExpr>
328+
) : UBoolExpr(ctx) {
329+
override val sort: UBoolSort
330+
get() = ctx.boolSort
331+
332+
private val joinedExprs = ctx.mkAnd(exprs)
333+
334+
fun not(): UBoolExpr = ctx.mkAnd(exprs.map(ctx::mkNot))
335+
336+
override fun accept(transformer: KTransformerBase): KExpr<UBoolSort> {
337+
return transformer.apply(joinedExprs)
338+
}
339+
340+
override fun internEquals(other: Any): Boolean = structurallyEqual(other)
341+
342+
override fun internHashCode(): Int = hash()
343+
344+
override fun print(printer: ExpressionPrinter) {
345+
printer.append("joined(")
346+
joinedExprs.print(printer)
347+
printer.append(")")
348+
}
349+
}
350+
351+
//endregion
352+
318353
//region Utils
319354

320355
val UBoolExpr.isFalse get() = this == ctx.falseExpr
321356
val UBoolExpr.isTrue get() = this == ctx.trueExpr
322357

358+
fun UExpr<*>.unwrapJoinedExpr(ctx: UContext<*>): UExpr<out USort> =
359+
if (this is UJoinedBoolExpr) ctx.mkAnd(exprs) else this
360+
323361
//endregion

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

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

3-
import org.usvm.StateForker.Companion.splitModelsByCondition
3+
import io.ksmt.utils.cast
44
import org.usvm.collections.immutable.internal.MutabilityOwnership
55
import org.usvm.model.UModelBase
66
import org.usvm.solver.USatResult
@@ -12,39 +12,7 @@ private typealias StateToCheck = Boolean
1212
private const val ForkedState = true
1313
private const val OriginalState = false
1414

15-
interface StateForker {
16-
17-
companion object {
18-
/**
19-
* Splits the passed [models] with this [condition] to the three categories:
20-
* - models that satisfy this [condition];
21-
* - models that are in contradiction with this [condition];
22-
* - models that can not evaluate this [condition].
23-
*/
24-
fun <Type> splitModelsByCondition(
25-
models: List<UModelBase<Type>>,
26-
condition: UBoolExpr,
27-
): SplittedModels<Type> {
28-
val trueModels = mutableListOf<UModelBase<Type>>()
29-
val falseModels = mutableListOf<UModelBase<Type>>()
30-
val unknownModels = mutableListOf<UModelBase<Type>>()
31-
32-
models.forEach { model ->
33-
val holdsInModel = model.eval(condition)
34-
35-
when {
36-
holdsInModel.isTrue -> trueModels += model
37-
holdsInModel.isFalse -> falseModels += model
38-
// Sometimes we cannot evaluate the condition – for example, a result for a division by symbolic expression
39-
// that is evaluated to 0 is unknown
40-
else -> unknownModels += model
41-
}
42-
}
43-
44-
return SplittedModels(trueModels, falseModels, unknownModels)
45-
}
46-
}
47-
15+
sealed interface StateForker {
4816
/**
4917
* Implements symbolic branching.
5018
* Checks if [condition] and ![condition] are satisfiable within [state].
@@ -78,9 +46,10 @@ object WithSolverStateForker : StateForker {
7846
state: T,
7947
condition: UBoolExpr,
8048
): ForkResult<T> {
81-
val (trueModels, falseModels, _) = splitModelsByCondition(state.models, condition)
49+
val unwrappedCondition: UBoolExpr = condition.unwrapJoinedExpr(state.ctx).cast()
50+
val (trueModels, falseModels, _) = splitModelsByCondition(state.models, unwrappedCondition)
8251

83-
val notCondition = state.ctx.mkNot(condition)
52+
val notCondition = if (condition is UJoinedBoolExpr) condition.not() else state.ctx.mkNot(unwrappedCondition)
8453
val (posState, negState) = when {
8554

8655
trueModels.isNotEmpty() && falseModels.isNotEmpty() -> {
@@ -89,23 +58,23 @@ object WithSolverStateForker : StateForker {
8958

9059
posState.models = trueModels
9160
negState.models = falseModels
92-
posState.pathConstraints += condition
61+
posState.pathConstraints += unwrappedCondition
9362
negState.pathConstraints += notCondition
9463

9564
posState to negState
9665
}
9766

9867
trueModels.isNotEmpty() -> state to forkIfSat(
9968
state,
100-
newConstraintToOriginalState = condition,
69+
newConstraintToOriginalState = unwrappedCondition,
10170
newConstraintToForkedState = notCondition,
10271
stateToCheck = ForkedState
10372
)
10473

10574
falseModels.isNotEmpty() -> {
10675
val forkedState = forkIfSat(
10776
state,
108-
newConstraintToOriginalState = condition,
77+
newConstraintToOriginalState = unwrappedCondition,
10978
newConstraintToForkedState = notCondition,
11079
stateToCheck = OriginalState
11180
)
@@ -287,12 +256,41 @@ object NoSolverStateForker : StateForker {
287256
}
288257
}
289258

259+
/**
260+
* Splits the passed [models] with this [condition] to the three categories:
261+
* - models that satisfy this [condition];
262+
* - models that are in contradiction with this [condition];
263+
* - models that can not evaluate this [condition].
264+
*/
265+
private fun <Type> splitModelsByCondition(
266+
models: List<UModelBase<Type>>,
267+
condition: UBoolExpr,
268+
): SplittedModels<Type> {
269+
val trueModels = mutableListOf<UModelBase<Type>>()
270+
val falseModels = mutableListOf<UModelBase<Type>>()
271+
val unknownModels = mutableListOf<UModelBase<Type>>()
272+
273+
models.forEach { model ->
274+
val holdsInModel = model.eval(condition)
275+
276+
when {
277+
holdsInModel.isTrue -> trueModels += model
278+
holdsInModel.isFalse -> falseModels += model
279+
// Sometimes we cannot evaluate the condition – for example, a result for a division by symbolic expression
280+
// that is evaluated to 0 is unknown
281+
else -> unknownModels += model
282+
}
283+
}
284+
285+
return SplittedModels(trueModels, falseModels, unknownModels)
286+
}
287+
290288
data class ForkResult<T>(
291289
val positiveState: T?,
292290
val negativeState: T?,
293291
)
294292

295-
data class SplittedModels<Type>(
293+
private data class SplittedModels<Type>(
296294
val trueModels: List<UModelBase<Type>>,
297295
val falseModels: List<UModelBase<Type>>,
298296
val unknownModels: List<UModelBase<Type>>,

usvm-ts/src/main/kotlin/org/usvm/TSComponents.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import io.ksmt.symfpu.solver.KSymFpuSolver
66
import org.jacodb.ets.base.EtsType
77
import org.usvm.solver.USolverBase
88
import org.usvm.solver.UTypeSolver
9-
import org.usvm.state.TSStateForker
109
import org.usvm.types.UTypeSystem
1110

1211
class TSComponents(
@@ -41,8 +40,6 @@ class TSComponents(
4140
return USolverBase(ctx, smtSolver, typeSolver, translator, decoder, options.solverTimeout)
4241
}
4342

44-
override fun mkStatesForkProvider(): StateForker = TSStateForker
45-
4643
fun close() {
4744
closeableResources.forEach(AutoCloseable::close)
4845
}

usvm-ts/src/main/kotlin/org/usvm/TSExpressions.kt

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -36,37 +36,6 @@ class TSUndefinedValue(ctx: TSContext) : UExpr<TSUndefinedSort>(ctx) {
3636
}
3737
}
3838

39-
/**
40-
* Utility class for merging expressions with [UBoolSort] sort.
41-
*
42-
* Mainly created for [not] function used in TSStateForker.
43-
*/
44-
class UJoinedBoolExpr(
45-
ctx: TSContext,
46-
val exprs: List<UBoolExpr>
47-
) : UBoolExpr(ctx) {
48-
override val sort: UBoolSort
49-
get() = ctx.boolSort
50-
51-
private val joinedExprs = ctx.mkAnd(exprs)
52-
53-
fun not(): UBoolExpr = ctx.mkAnd(exprs.map(ctx::mkNot))
54-
55-
override fun accept(transformer: KTransformerBase): KExpr<UBoolSort> {
56-
return transformer.apply(joinedExprs)
57-
}
58-
59-
override fun internEquals(other: Any): Boolean = structurallyEqual(other)
60-
61-
override fun internHashCode(): Int = hash()
62-
63-
override fun print(printer: ExpressionPrinter) {
64-
printer.append("joined(")
65-
joinedExprs.print(printer)
66-
printer.append(")")
67-
}
68-
}
69-
7039
/**
7140
* [UExpr] wrapper that handles type coercion.
7241
*

usvm-ts/src/main/kotlin/org/usvm/Utils.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,6 @@ fun UContext<*>.boolToFpSort(expr: UExpr<UBoolSort>) =
1616
fun UContext<*>.fpToBoolSort(expr: UExpr<KFp64Sort>) =
1717
mkIte(mkFpEqualExpr(expr, mkFp64(0.0)), mkFalse(), mkTrue())
1818

19-
fun UExpr<*>.unwrapJoinedExpr(ctx: UContext<*>): UExpr<out USort> =
20-
if (this is UJoinedBoolExpr) ctx.mkAnd(exprs) else this
21-
2219
fun UExpr<out USort>.extractOrThis(): UExpr<out USort> = if (this is TSWrappedValue) value else this
2320

2421
fun <K, V> MutableMap<K, MutableSet<V>>.copy(): MutableMap<K, MutableSet<V>> = this.entries.associate { (k, v) ->

0 commit comments

Comments
 (0)