Skip to content

Сonstraint model synthesis #966

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

Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
fb0adca
Draft: model synthesis
Jun 20, 2022
11c47d9
m
AbdullinAM Jun 20, 2022
4aaa8e5
first prototype, needs debugging and tuning
AbdullinAM Jun 22, 2022
9c63ba0
prototype
AbdullinAM Jun 28, 2022
707fa6d
refactor
AbdullinAM Jun 29, 2022
10c4193
very early prototype
AbdullinAM Jul 8, 2022
4285a5c
UtConstraintModel implemented
AbdullinAM Jul 12, 2022
22e2266
partial UtConstraint to UtExpression convertion
AbdullinAM Jul 14, 2022
6b47a9a
working prototypes
AbdullinAM Jul 19, 2022
a75a840
bugfixes + expression support in constraints
AbdullinAM Jul 21, 2022
fb392f6
m
AbdullinAM Jul 26, 2022
5f35311
everything kind of works, but some constraints need to be added
AbdullinAM Jul 27, 2022
ba9bb62
first working protoype with arrays
AbdullinAM Aug 1, 2022
065924f
renaming
AbdullinAM Aug 1, 2022
8cc7515
first tests and fixes
AbdullinAM Aug 2, 2022
4a14606
more expressions supported
AbdullinAM Aug 3, 2022
bbf525b
some cleanup + more expressions supported
AbdullinAM Aug 8, 2022
850b530
cleanup
AbdullinAM Aug 8, 2022
8799691
support for multidimensional arrays + fix for array generation
AbdullinAM Aug 9, 2022
60df4a4
first support of lists
AbdullinAM Aug 10, 2022
8963a7e
support sets
AbdullinAM Aug 10, 2022
1578c96
small refactorings
AbdullinAM Aug 11, 2022
1dd4b25
maps
AbdullinAM Aug 12, 2022
313757d
some cleanup and parameters
AbdullinAM Aug 12, 2022
faa0047
test write fix
AbdullinAM Aug 12, 2022
94740c9
m
AbdullinAM Aug 22, 2022
9920283
Merge branch 'main' into abdullin/constraint-model-synthesis
AbdullinAM Aug 22, 2022
fd22077
merge with master
AbdullinAM Aug 24, 2022
4e97b5f
first prototype of constraint scoring selector
AbdullinAM Aug 26, 2022
2544b31
m
AbdullinAM Aug 30, 2022
e585371
fixes
AbdullinAM Aug 31, 2022
d2564e9
Merge branch 'main' into abdullin/constraint-model-synthesis
AbdullinAM Sep 7, 2022
bb1acbc
Merge branch 'main' into abdullin/constraint-model-synthesis
AbdullinAM Sep 7, 2022
c6fbeb0
Split constraint models into a set of non-intersecting subsets before…
AbdullinAM Sep 12, 2022
2553ef7
m
AbdullinAM Sep 12, 2022
e943a16
some cleanup
AbdullinAM Sep 13, 2022
cd5fff8
more cleanup
AbdullinAM Sep 13, 2022
c2a7d31
order models in the subsets
AbdullinAM Sep 13, 2022
d1b09c8
simple caching of synthesis unit contexts
AbdullinAM Sep 13, 2022
eedccb9
option to enable/disable caching of synthesis contexts
AbdullinAM Sep 14, 2022
11eea4d
m
AbdullinAM Sep 14, 2022
9712e70
tests
AbdullinAM Sep 14, 2022
1c6b679
unit tests fixed
AbdullinAM Sep 19, 2022
4e37763
Merge branch 'main' into abdullin/constraint-model-synthesis
AbdullinAM Sep 28, 2022
34ed6fe
merge with main
AbdullinAM Sep 28, 2022
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
Prev Previous commit
Next Next commit
working prototypes
  • Loading branch information
AbdullinAM committed Jul 19, 2022
commit 6b47a9a66fb910d421f76c70bf5bb777165fa26b
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,12 @@ object UtSettings {
*/
var singleSelector by getBooleanProperty(true)


/**
* Flag for enabling model synthesis
*/
var enableSynthesis = true

override fun toString(): String =
properties
.entries
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ import org.utbot.framework.PathSelectorType
import org.utbot.framework.UtSettings
import org.utbot.framework.UtSettings.checkSolverTimeoutMillis
import org.utbot.framework.UtSettings.enableFeatureProcess
import org.utbot.framework.UtSettings.enableSynthesis
import org.utbot.framework.UtSettings.pathSelectorStepsLimit
import org.utbot.framework.UtSettings.pathSelectorType
import org.utbot.framework.UtSettings.preferredCexOption
Expand Down Expand Up @@ -3684,12 +3685,14 @@ class UtBotSymbolicEngine(
val stateAfter = modelsAfter.constructStateForMethod(state.executionStack.first().method)
require(stateBefore.parameters.size == stateAfter.parameters.size)

val resolvedConstraints = ConstraintResolver(
updatedMemory,
holder,
typeRegistry,
typeResolver
).run { resolveModels(resolvedParameters) }
val resolvedConstraints = if (enableSynthesis) {
ConstraintResolver(
updatedMemory,
holder,
typeRegistry,
typeResolver
).run { resolveModels(resolvedParameters) }
} else null

val symbolicUtExecution = UtExecution(
stateBefore,
Expand Down
60 changes: 34 additions & 26 deletions utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,51 @@ import org.utbot.engine.*
import org.utbot.engine.z3.intValue
import org.utbot.framework.plugin.api.*
import org.utbot.framework.plugin.api.UtConstraintParameter
import org.utbot.framework.plugin.api.util.objectClassId
import org.utbot.framework.plugin.api.util.*
import soot.ArrayType
import soot.PrimType
import soot.RefType
import soot.Type



class UtVarBuilder(
val holder: UtSolverStatusSAT,
val typeRegistry: TypeRegistry,
val typeResolver: TypeResolver,
) : UtExpressionVisitor<UtConstraintVariable> {
private val internalAddrs = mutableMapOf<Address, UtConstraintVariable>()

override fun visit(expr: UtArraySelectExpression): UtConstraintVariable {
when (val array = expr.arrayExpression) {
is UtMkArrayExpression -> {
when (array.name) {
"arraysLength" -> {
val instance = expr.index.accept(this)
return UtConstraintArrayLengthAccess(instance)
}
"RefValues_Arrays" -> {
val instance = expr.index.accept(this)
return instance
}
else -> {
val (type, field) = array.name.split("_")
val instance = expr.index.accept(this)
return UtConstraintFieldAccess(instance, FieldId(ClassId(type), field))
}
override fun visit(expr: UtArraySelectExpression): UtConstraintVariable = when (val array = expr.arrayExpression) {
is UtMkArrayExpression -> {
when (array.name) {
"arraysLength" -> {
val instance = expr.index.accept(this)
UtConstraintArrayLengthAccess(instance)
}
}
is UtArraySelectExpression -> when (val array2 = array.arrayExpression) {
is UtMkArrayExpression -> when (array2.name) {
"RefValues_Arrays" -> return expr.index.accept(this)
else -> error("Unexpected")
"RefValues_Arrays" -> {
val instance = expr.index.accept(this)
instance
}
else -> {
val (type, field) = array.name.split("_")
val instance = expr.index.accept(this)
UtConstraintFieldAccess(instance, FieldId(ClassId(type), field))
}
}
}
is UtArraySelectExpression -> when (val array2 = array.arrayExpression) {
is UtMkArrayExpression -> when (array2.name) {
"RefValues_Arrays" -> expr.index.accept(this)
"int_Arrays" -> expr.index.accept(this)
else -> error("Unexpected")
}
else -> error("Unexpected")
}
else -> {
val array2 = expr.arrayExpression.accept(this)
val index = expr.index.accept(this)
UtConstraintArrayAccess(array2, index, array2.classId.elementClassId!!)
}
}

override fun visit(expr: UtConstArrayExpression): UtConstraintVariable {
Expand Down Expand Up @@ -80,11 +82,17 @@ class UtVarBuilder(

override fun visit(expr: UtAddrExpression): UtConstraintVariable {
return when (val internal = expr.internal) {
is UtBvConst -> UtConstraintParameter((expr.internal as UtBvConst).name, holder.findBaseTypeOrNull(expr)?.classId ?: objectClassId)
is UtBvConst -> UtConstraintParameter(
(expr.internal as UtBvConst).name,
holder.findBaseTypeOrNull(expr)?.classId ?: objectClassId
)
is UtBvLiteral -> when (internal.value) {
0 -> NullUtConstraintVariable(objectClassId)
else -> internalAddrs.getOrPut(internal.value.toInt()) {
UtConstraintParameter("object${internal.value}", holder.findBaseTypeOrNull(expr)?.classId ?: objectClassId)
UtConstraintParameter(
"object${internal.value}",
holder.findBaseTypeOrNull(expr)?.classId ?: objectClassId
)
}
}
else -> expr.internal.accept(this)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import kotlinx.coroutines.yield
import mu.KotlinLogging
import org.utbot.engine.*
import org.utbot.engine.selectors.strategies.ScoringStrategyBuilder
import org.utbot.framework.UtSettings.enableSynthesis
import org.utbot.framework.modifications.StatementsStorage
import org.utbot.framework.synthesis.ConstrainedSynthesizer
import org.utbot.framework.synthesis.Synthesizer
Expand Down Expand Up @@ -404,36 +405,39 @@ object UtBotTestCaseGenerator : TestCaseGenerator {
method: UtMethod<*>,
mockStrategy: MockStrategyApi,
): UtTestCase {
val executions = generateWithPostCondition(
method.toSootMethod(), mockStrategy, EmptyPostCondition, ScoringStrategyBuilder()
)
if (enableSynthesis) {
val executions = generateWithPostCondition(
method.toSootMethod(), mockStrategy, EmptyPostCondition, ScoringStrategyBuilder()
)

return UtTestCase(
method,
executions.toAssemble(),
jimpleBody(method),
)
// logger.trace { "UtSettings:${System.lineSeparator()}" + UtSettings.toString() }
//
// if (isCanceled()) return UtTestCase(method)
//
// val executions = mutableListOf<UtExecution>()
// val errors = mutableMapOf<String, Int>()
//
//
// runIgnoringCancellationException {
// runBlockingWithCancellationPredicate(isCanceled) {
// generateAsync(EngineController(), method.toSootMethod(), mockStrategy).collect {
// when (it) {
// is UtExecution -> executions += it
// is UtError -> errors.merge(it.description, 1, Int::plus)
// }
// }
// }
// }
//
// val minimizedExecutions = minimizeExecutions(executions)
// return UtTestCase(method, minimizedExecutions, jimpleBody(method), errors)
return UtTestCase(
method,
executions.toAssemble(),
jimpleBody(method),
)
} else {
logger.trace { "UtSettings:${System.lineSeparator()}" + UtSettings.toString() }

if (isCanceled()) return UtTestCase(method)

val executions = mutableListOf<UtExecution>()
val errors = mutableMapOf<String, Int>()


runIgnoringCancellationException {
runBlockingWithCancellationPredicate(isCanceled) {
generateAsync(EngineController(), method.toSootMethod(), mockStrategy).collect {
when (it) {
is UtExecution -> executions += it
is UtError -> errors.merge(it.description, 1, Int::plus)
}
}
}
}

val minimizedExecutions = minimizeExecutions(executions)
return UtTestCase(method, minimizedExecutions, jimpleBody(method), errors)
}
}

private fun toAssembleModel(model: UtModel) = (model as? UtCompositeModel)?.let {
Expand Down Expand Up @@ -487,10 +491,10 @@ object UtBotTestCaseGenerator : TestCaseGenerator {
System.err.println((execution.hole as ResolvedExecution).modelsAfter)
System.err.println("-------------------------------")
val aa = ConstrainedSynthesizer((execution.hole as ResolvedExecution).modelsAfter)
aa.synthesize()
val synthesizedModels = aa.synthesize() ?: return this

val newThisModel = oldStateBefore.thisInstance?.let { toAssembleModel(it) }
val newParameters = oldStateBefore.parameters.map { toAssembleModel(it) }
val newThisModel = oldStateBefore.thisInstance?.let { synthesizedModels.first() }
val newParameters = oldStateBefore.thisInstance?.let { synthesizedModels.drop(1) } ?: synthesizedModels

execution.copy(
stateBefore = EnvironmentModels(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package org.utbot.framework.synthesis

import org.utbot.engine.*
import org.utbot.engine.ConstraintResolver
import org.utbot.framework.plugin.api.ConstructorId
import org.utbot.framework.plugin.api.MethodId
import org.utbot.framework.plugin.api.UtModel
import org.utbot.framework.synthesis.postcondition.constructors.toSoot
import org.utbot.framework.synthesis.postcondition.constructors.toSootType
import soot.RefType
Expand Down Expand Up @@ -61,6 +63,11 @@ class ConstrainedJimpleMethodSynthesizer {
}
}

fun resolve(parameterModels: List<UtModel>): List<UtModel> {
val resolver = Resolver(parameterModels, rootUnits, unitToParameter)
return rootUnits.map { resolver.resolve(it) }
}

private fun synthesizeUnit(unit: SynthesisUnit): JimpleLocal = when (unit) {
is ObjectUnit -> synthesizeCompositeUnit(unit)
is MethodUnit -> synthesizeMethodUnit(unit)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import org.utbot.engine.selectors.strategies.ScoringStrategyBuilder
import org.utbot.engine.variable
import org.utbot.framework.PathSelectorType
import org.utbot.framework.UtSettings
import org.utbot.framework.UtSettings.enableSynthesis
import org.utbot.framework.plugin.api.MockStrategyApi
import org.utbot.framework.plugin.api.UtBotTestCaseGenerator
import org.utbot.framework.plugin.api.UtModel
Expand All @@ -21,7 +22,7 @@ class ConstrainedSynthesisUnitChecker(

var id = 0

fun tryGenerate(units: List<SynthesisUnit>, parameters: ResolvedModels): UtModel? {
fun tryGenerate(units: List<SynthesisUnit>, parameters: ResolvedModels): List<UtModel>? {
if (units.any { !it.isFullyDefined() }) return null

val context = synthesizer.synthesize(units)
Expand All @@ -35,6 +36,7 @@ class ConstrainedSynthesisUnitChecker(
emptyMap()
)
val execution = withPathSelector(PathSelectorType.INHERITORS_SELECTOR) {
enableSynthesis = false
UtBotTestCaseGenerator.generateWithPostCondition(
method,
MockStrategyApi.NO_MOCKS,
Expand All @@ -43,16 +45,14 @@ class ConstrainedSynthesisUnitChecker(
units.map { context.unitToParameter[it] },
units.map { context.unitToLocal[it]?.variable }),
scoringStrategy
).firstOrNull()
).firstOrNull().also {
enableSynthesis = true
}
} ?: return null


logger.error { execution }
return null
// return context.resolve(listOfNotNull(execution.stateBefore.thisInstance) + execution.stateBefore.parameters)
// .also {
// println("Method body:\n ${method.activeBody}")
// }
return context.resolve(listOfNotNull(execution.stateBefore.thisInstance) + execution.stateBefore.parameters)
}

private fun <T> withPathSelector(pathSelectorType: PathSelectorType, body: () -> T): T {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,21 @@ import org.utbot.engine.ResolvedModels
import org.utbot.engine.UtBotSymbolicEngine
import org.utbot.framework.modifications.StatementsStorage
import org.utbot.framework.plugin.api.*
import org.utbot.framework.plugin.api.util.isArray
import org.utbot.framework.plugin.api.util.isPrimitive
import org.utbot.framework.plugin.api.util.objectClassId
import org.utbot.framework.synthesis.postcondition.constructors.ConstraintBasedPostConditionConstructor
import org.utbot.framework.synthesis.postcondition.constructors.toSoot

internal fun Collection<ClassId>.expandable() = filter { !it.isArray && !it.isPrimitive }.toSet()

class ConstrainedSynthesizer(
val parameters: ResolvedModels,
val depth: Int = 4
) {
private val logger = KotlinLogging.logger("ConstrainedSynthesizer")
private val statementStorage = StatementsStorage().also { storage ->
storage.update(parameters.parameters.map { it.classId }.toSet())
storage.update(parameters.parameters.map { it.classId }.expandable())
}

private val queue = MultipleSynthesisUnitQueue(
Expand All @@ -25,7 +29,7 @@ class ConstrainedSynthesizer(
)
private val unitChecker = ConstrainedSynthesisUnitChecker(objectClassId.toSoot())

fun synthesize(): UtModel? {
fun synthesize(): List<UtModel>? {
while (!queue.isEmpty()) {
val units = queue.poll()
logger.debug { "Visiting state: $units" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class JimpleMethodSynthesizer {
}

fun resolve(parameterModels: List<UtModel>): UtModel {
val resolver = Resolver(parameterModels, unitToParameter)
val resolver = Resolver(parameterModels, listOf(), unitToParameter)
return resolver.resolve(rootUnit)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
package org.utbot.framework.synthesis

import org.utbot.engine.nextDefaultModelId
import org.utbot.framework.plugin.api.ConstructorId
import org.utbot.framework.plugin.api.MethodId
import org.utbot.framework.plugin.api.UtAssembleModel
import org.utbot.framework.plugin.api.UtExecutableCallModel
import org.utbot.framework.plugin.api.UtModel
import org.utbot.framework.plugin.api.UtStatementModel
import org.utbot.framework.plugin.api.*
import org.utbot.framework.util.nextModelName
import java.util.IdentityHashMap

class Resolver(
parameterModels: List<UtModel>,
unitToParameter: IdentityHashMap<SynthesisUnit, SynthesisParameter>
val rootUnits: List<SynthesisUnit>,
unitToParameter: IdentityHashMap<SynthesisUnit, SynthesisParameter>,
) {
private val unitToModel = IdentityHashMap<SynthesisUnit, UtModel>().apply {
unitToParameter.toList().forEach { (it, parameter) -> this[it] = parameterModels[parameter.number] }
Expand All @@ -21,9 +17,10 @@ class Resolver(

fun resolve(unit: SynthesisUnit): UtModel =
when (unit) {
is MethodUnit -> resolveMethodUnit(unit)
is MethodUnit -> unitToModel.getOrPut(unit) { resolveMethodUnit(unit) }
is ObjectUnit -> unitToModel[unit] ?: error("Can't map $unit")
else -> TODO()
is NullUnit -> UtNullModel(unit.classId)
is RefUnit -> resolve(rootUnits[unit.referenceParam])
}

private fun resolveMethodUnit(unit: MethodUnit): UtModel =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class CompositeUnitExpander(
return emptyList()
}
if (objectUnit.classId !in statementsStorage.items.keys.map { it.classId }.toSet()) {
statementsStorage.update(setOf(objectUnit.classId))
statementsStorage.update(setOf(objectUnit.classId).expandable())
}
val mutators = findMutators(objectUnit.classId)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package org.utbot.framework.synthesis

import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.ExecutableId
import org.utbot.framework.plugin.api.util.isArray
import org.utbot.framework.plugin.api.util.primitives

sealed class SynthesisUnit {
Expand Down
Loading