Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation of the JacoDB interpreter #18

Merged
merged 26 commits into from
Jun 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
c0fb819
Add: JVM interpreter
sergeypospelov May 19, 2023
2049fd8
Implement a test runner for Java interpreter (#24)
CaelmBleidd Jun 14, 2023
93d670f
Add: some test improvements
sergeypospelov Jun 14, 2023
0ea771a
Fix: product region for input array indices
sergeypospelov Jun 15, 2023
288007f
Fix: comment
sergeypospelov Jun 15, 2023
3fb46ad
Add: test and fix umodel resolving
sergeypospelov Jun 15, 2023
937ec71
Fix: interpreter reference equality
sergeypospelov Jun 15, 2023
92d79c4
Restructure
sergeypospelov Jun 15, 2023
c6f18cc
Add: some comments
sergeypospelov Jun 16, 2023
752c1b6
Fix: state return and throw utility functions
sergeypospelov Jun 16, 2023
32a3713
Fix: disable test
sergeypospelov Jun 16, 2023
dbfa987
Revert: callStack
sergeypospelov Jun 16, 2023
a603332
Minor fixes
sergeypospelov Jun 16, 2023
e40e81a
Add: jc expressions
sergeypospelov Jun 19, 2023
78cb2d4
Refactor: replace `JcTypedMethod` with `JcMethod`
sergeypospelov Jun 19, 2023
53a39cc
Refactor: symbolic bit-vectors representing integral expressions have…
sergeypospelov Jun 20, 2023
b2b0f51
Add: cosmetic fixes
sergeypospelov Jun 20, 2023
8e19099
Add: cosmetic fixed in tests
sergeypospelov Jun 20, 2023
1375a79
Add: some comments
sergeypospelov Jun 20, 2023
897ae70
Fix: comments
sergeypospelov Jun 21, 2023
e1dd307
Add: unary operator tests
sergeypospelov Jun 21, 2023
5a82d3a
Fix: exact method types resolving
sergeypospelov Jun 23, 2023
f42416e
Fix: review comments
sergeypospelov Jun 28, 2023
6e1d827
Fix: review comments
sergeypospelov Jun 29, 2023
e775533
Add: caching with filter test
sergeypospelov Jun 29, 2023
2c9e6bd
Add: JcVoidSort
sergeypospelov Jun 29, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ repositories {
gradlePluginPortal()
}

val kotlinVersion = "1.7.10"
val kotlinVersion = "1.8.22"

dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
Expand Down
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ object Versions {
const val ksmt = "0.5.3"
const val collections = "0.3.5"
const val coroutines = "1.6.4"
const val jcdb = "1.0_main-SNAPSHOT"
const val jcdb = "1.0.0"
const val mockk = "1.13.4"
}
2 changes: 2 additions & 0 deletions usvm-core/src/main/kotlin/org/usvm/ApplicationGraph.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ interface ApplicationGraph<Method, Statement> {
fun exitPoints(method: Method): Sequence<Statement>

fun methodOf(node: Statement): Method

fun statementsOf(method: Method): Sequence<Statement>
}
8 changes: 5 additions & 3 deletions usvm-core/src/main/kotlin/org/usvm/CallStack.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package org.usvm

data class UCallStackFrame<Method, Statement>(val method: Method, val returnSite: Statement?)
data class UCallStackFrame<Method, Statement>(
val method: Method,
val returnSite: Statement?,
)

class UCallStack<Method, Statement> private constructor(
private val stack: ArrayDeque<UCallStackFrame<Method, Statement>>,
) :
Collection<UCallStackFrame<Method, Statement>> by stack {
) : Collection<UCallStackFrame<Method, Statement>> by stack {
constructor() : this(ArrayDeque())
constructor(method: Method) : this(
ArrayDeque<UCallStackFrame<Method, Statement>>().apply {
Expand Down
2 changes: 1 addition & 1 deletion usvm-core/src/main/kotlin/org/usvm/Composition.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ open class UComposer<Field, Type>(

override fun <Sort : USort> transform(
expr: URegisterReading<Sort>,
): UExpr<Sort> = with(expr) { stackEvaluator.eval(idx, sort) }
): UExpr<Sort> = with(expr) { stackEvaluator.readRegister(idx, sort) }

override fun <Sort : USort> transform(expr: UHeapReading<*, *, *>): UExpr<Sort> =
error("You must override `transform` function in org.usvm.UComposer for ${expr::class}")
Expand Down
2 changes: 1 addition & 1 deletion usvm-core/src/main/kotlin/org/usvm/Context.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ open class UContext(
) : KContext(operationMode, astManagementMode, simplificationMode) {

private val solver by lazy { components.mkSolver(this) }
private val typeSystem = components.mkTypeSystem(this)
private val typeSystem by lazy { components.mkTypeSystem(this) }

@Suppress("UNCHECKED_CAST")
fun <Field, Type, Method> solver(): USolverBase<Field, Type, Method> =
Expand Down
31 changes: 18 additions & 13 deletions usvm-core/src/main/kotlin/org/usvm/Expressions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ typealias UBoolSort = KBoolSort
typealias UBvSort = KBvSort
typealias UBv32Sort = KBv32Sort
typealias USizeSort = KBv32Sort
typealias UFpSort = KFpSort

typealias UExpr<Sort> = KExpr<Sort>
typealias UBoolExpr = UExpr<UBoolSort>
Expand Down Expand Up @@ -110,25 +111,30 @@ class UNullRef internal constructor(
//region LValues
open class ULValue(val sort: USort)

class URegisterRef(sort: USort, val idx: Int) : ULValue(sort)
class URegisterLValue(sort: USort, val idx: Int) : ULValue(sort)

class UFieldRef<Field>(fieldSort: USort, val ref: UHeapRef, val field: Field) : ULValue(fieldSort)
class UFieldLValue<Field>(fieldSort: USort, val ref: UHeapRef, val field: Field) : ULValue(fieldSort)

class UArrayIndexRef<ArrayType>(
class UArrayIndexLValue<ArrayType>(
cellSort: USort,
val ref: UHeapRef,
val index: USizeExpr,
val arrayType: ArrayType
val arrayType: ArrayType,
) : ULValue(cellSort)

class UArrayLengthLValue<ArrayType>(
val ref: UHeapRef,
val arrayType: ArrayType,
) : ULValue(ref.uctx.sizeSort)

//endregion

//region Read Expressions

class URegisterReading<Sort : USort> internal constructor(
ctx: UContext,
val idx: Int,
override val sort: Sort
override val sort: Sort,
) : USymbol<Sort>(ctx) {
override fun accept(transformer: KTransformerBase): KExpr<Sort> {
require(transformer is UExprTransformer<*, *>)
Expand All @@ -146,7 +152,7 @@ class URegisterReading<Sort : USort> internal constructor(

abstract class UHeapReading<RegionId : URegionId<Key, Sort, RegionId>, Key, Sort : USort>(
ctx: UContext,
val region: USymbolicMemoryRegion<RegionId, Key, Sort>
val region: USymbolicMemoryRegion<RegionId, Key, Sort>,
) : USymbol<Sort>(ctx) {
override val sort: Sort get() = region.sort
}
Expand Down Expand Up @@ -212,7 +218,7 @@ class UInputArrayReading<ArrayType, Sort : USort> internal constructor(
ctx: UContext,
region: UInputArrayRegion<ArrayType, Sort>,
val address: UHeapRef,
val index: USizeExpr
val index: USizeExpr,
) : UHeapReading<UInputArrayId<ArrayType, Sort>, USymbolicArrayIndex, Sort>(ctx, region) {
init {
require(address !is UNullRef)
Expand Down Expand Up @@ -277,15 +283,14 @@ class UInputArrayLengthReading<ArrayType> internal constructor(

//region Mocked Expressions

abstract class UMockSymbol<Sort : USort>(ctx: UContext, override val sort: Sort) : USymbol<Sort>(ctx) {
}
abstract class UMockSymbol<Sort : USort>(ctx: UContext, override val sort: Sort) : USymbol<Sort>(ctx)

// TODO: make indices compositional!
class UIndexedMethodReturnValue<Method, Sort : USort> internal constructor(
ctx: UContext,
val method: Method,
val callIndex: Int,
override val sort: Sort
override val sort: Sort,
) : UMockSymbol<Sort>(ctx, sort) {
override fun accept(transformer: KTransformerBase): KExpr<Sort> {
require(transformer is UExprTransformer<*, *>)
Expand All @@ -308,7 +313,7 @@ class UIndexedMethodReturnValue<Method, Sort : USort> internal constructor(
class UIsExpr<Type> internal constructor(
ctx: UContext,
val ref: UHeapRef,
val type: Type
val type: Type,
) : USymbol<UBoolSort>(ctx) {
override val sort = ctx.boolSort

Expand All @@ -332,7 +337,7 @@ class UIsExpr<Type> internal constructor(

//region Utils

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

//endregion
28 changes: 9 additions & 19 deletions usvm-core/src/main/kotlin/org/usvm/Machine.kt
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
package org.usvm

import org.usvm.ps.stopstrategies.StoppingStrategy


/**
* An abstract symbolic machine.
*
* @see [run]
*/
abstract class UMachine<State : UState<*, *, *, *>, Target> : AutoCloseable {
abstract class UMachine<State> : AutoCloseable {
/**
* The main entry point. Template method for running the machine on a specified [target].
*
* @param target a generic target to run on.
* @param onState called on every forked state. Can be used for collecting results.
* @param continueAnalyzing filtering function for states. If it returns `false`, a state
* won't be analyzed further. It is called on an original state and every forked state as well.
* @param shouldStop is called on every step, before peeking a next state from path selector.
* @param stoppingStrategy is called on every step, before peeking a next state from the path selector.
* Returning `true` aborts analysis.
*/
fun run(
target: Target,
interpreter: UInterpreter<State>,
pathSelector: UPathSelector<State>,
onState: (State) -> Unit,
continueAnalyzing: (State) -> Boolean,
shouldStop: () -> Boolean = { false },
stoppingStrategy: StoppingStrategy = StoppingStrategy { false },
) {
val interpreter = getInterpreter(target)
val pathSelector = getPathSelector(target)

while (!pathSelector.isEmpty() && !shouldStop()) {
while (!pathSelector.isEmpty() && !stoppingStrategy.shouldStop()) {
pathSelector.peekAndUpdate { state ->
val (forkedStates, stateAlive) = interpreter.step(state)

Expand Down Expand Up @@ -54,16 +54,6 @@ abstract class UMachine<State : UState<*, *, *, *>, Target> : AutoCloseable {
} else {
remove(state)
}
add(forkedStates)
add(forkedStates.toList())
}

/**
* @return a configured interpreter suitable for a [target].
*/
protected abstract fun getInterpreter(target: Target): UInterpreter<State>

/**
* @return a configured path selector with initial states obtained from a [target].
*/
protected abstract fun getPathSelector(target: Target): UPathSelector<State>
}
2 changes: 1 addition & 1 deletion usvm-core/src/main/kotlin/org/usvm/PathSelector.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ interface UPathSelector<State> {
/**
* Adds [states] to the path selector.
*/
fun add(states: Sequence<State>)
fun add(states: Collection<State>)

/**
* Updates the internal priority of the [state].
Expand Down
20 changes: 10 additions & 10 deletions usvm-core/src/main/kotlin/org/usvm/State.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@ import io.ksmt.expr.KInterpretedValue
import kotlinx.collections.immutable.PersistentList
import org.usvm.constraints.UPathConstraints
import org.usvm.memory.UMemoryBase
import org.usvm.model.UModel
import org.usvm.model.UModelBase
import org.usvm.solver.USatResult
import org.usvm.solver.UUnknownResult
import org.usvm.solver.UUnsatResult

abstract class UState<Type, Field, Method, Statement>(
// TODO: add interpreter-specific information
val ctx: UContext,
val callStack: UCallStack<Method, Statement>,
val pathConstraints: UPathConstraints<Type>,
val memory: UMemoryBase<Field, Type, Method>,
var models: List<UModel>,
var path: PersistentList<Statement>,
open val ctx: UContext,
open val callStack: UCallStack<Method, Statement>,
open val pathConstraints: UPathConstraints<Type>,
open val memory: UMemoryBase<Field, Type, Method>,
open var models: List<UModelBase<Field, Type>>,
open var path: PersistentList<Statement>,
) {
/**
* Creates new state structurally identical to this.
Expand Down Expand Up @@ -51,7 +51,7 @@ class ForkResult<T>(
* - if [forkToSatisfied], then c = [conditionToCheck]
* - if ![forkToSatisfied], then c = [satisfiedCondition]
*/
private fun <T : UState<Type, *, *, *>, Type> forkIfSat(
private fun <T : UState<Type, Field, *, *>, Type, Field> forkIfSat(
state: T,
satisfiedCondition: UBoolExpr,
conditionToCheck: UBoolExpr,
Expand All @@ -64,7 +64,7 @@ private fun <T : UState<Type, *, *, *>, Type> forkIfSat(
return null
}

val solver = state.ctx.solver<Any?, Type, Any?>()
val solver = state.ctx.solver<Field, Type, Any?>()
val satResult = solver.check(pathConstraints, useSoftConstraints = true)

return when (satResult) {
Expand Down Expand Up @@ -111,7 +111,7 @@ private fun <T : UState<Type, *, *, *>, Type> forkIfSat(
* 2. makes not more than one query to USolver;
* 3. if both [condition] and ![condition] are satisfiable, then [ForkResult.positiveState] === [state].
*/
fun <T : UState<Type, *, *, *>, Type> fork(
fun <T : UState<Type, Field, *, *>, Type, Field> fork(
state: T,
condition: UBoolExpr,
): ForkResult<T> {
Expand Down
6 changes: 3 additions & 3 deletions usvm-core/src/main/kotlin/org/usvm/StepScope.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ package org.usvm
*
* @param originalState an initial state.
*/
class StepScope<T : UState<Type, *, *, *>, Type>(
val uctx: UContext,
class StepScope<T : UState<Type, Field, *, *>, Type, Field>(
originalState: T,
) {
private val forkedStates = mutableListOf<T>()
private var curState: T? = originalState
private var alive: Boolean = true
var alive: Boolean = true
private set

/**
* @return forked states and the status of initial state.
Expand Down
16 changes: 10 additions & 6 deletions usvm-core/src/main/kotlin/org/usvm/constraints/PathConstraints.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ open class UPathConstraints<Type> private constructor(
/**
* Constraints solved by type solver.
*/
val typeConstraints: UTypeConstraints<Type> = UTypeConstraints(ctx.typeSystem(), equalityConstraints)
val typeConstraints: UTypeConstraints<Type> = UTypeConstraints(
ctx.typeSystem(),
equalityConstraints
),
) {
/**
* Constraints solved by SMT solver.
Expand All @@ -39,8 +42,8 @@ open class UPathConstraints<Type> private constructor(

open val isFalse: Boolean
get() = equalityConstraints.isContradiction ||
typeConstraints.isContradiction ||
logicalConstraints.singleOrNull() is UFalse
typeConstraints.isContradiction ||
logicalConstraints.singleOrNull() is UFalse

@Suppress("UNCHECKED_CAST")
open operator fun plusAssign(constraint: UBoolExpr): Unit =
Expand All @@ -50,9 +53,8 @@ open class UPathConstraints<Type> private constructor(

constraint == trueExpr || constraint in logicalConstraints -> {}

constraint is UEqExpr<*> && isSymbolicHeapRef(constraint.lhs) && isSymbolicHeapRef(constraint.rhs) -> {
constraint is UEqExpr<*> && isSymbolicHeapRef(constraint.lhs) && isSymbolicHeapRef(constraint.rhs) ->
equalityConstraints.addReferenceEquality(constraint.lhs as UHeapRef, constraint.rhs as UHeapRef)
}

constraint is UIsExpr<*> -> typeConstraints.cast(constraint.ref, constraint.type as Type)

Expand All @@ -61,7 +63,9 @@ open class UPathConstraints<Type> private constructor(
constraint is UNotExpr -> {
val notConstraint = constraint.arg
when {
notConstraint is UEqExpr<*> && isSymbolicHeapRef(notConstraint.lhs) && isSymbolicHeapRef(notConstraint.rhs) -> {
notConstraint is UEqExpr<*> &&
isSymbolicHeapRef(notConstraint.lhs) &&
isSymbolicHeapRef(notConstraint.rhs) -> {
require(notConstraint.rhs.sort == addressSort)
equalityConstraints.addReferenceDisequality(
notConstraint.lhs as UHeapRef,
Expand Down
2 changes: 1 addition & 1 deletion usvm-core/src/main/kotlin/org/usvm/memory/Heap.kt
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class UAddressCounter {
}
}

data class URegionHeap<Field, ArrayType>(
class URegionHeap<Field, ArrayType>(
private val ctx: UContext,
private var lastAddress: UAddressCounter = UAddressCounter(),
private var allocatedFields: PersistentMap<Pair<UConcreteHeapAddress, Field>, UExpr<out USort>> = persistentMapOf(),
Expand Down
Loading